Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
850978cbe3 | ||
|
|
e8c00296bd | ||
|
|
52b8033c37 | ||
|
|
6f2bc83806 | ||
|
|
2a48c9d272 | ||
|
|
27d0373a6e | ||
|
|
96b50f7ef0 | ||
|
|
dc7ff5f668 | ||
|
|
0c9c9737d9 | ||
|
|
8ef099d707 | ||
|
|
6da15306b0 | ||
|
|
699d0869fc | ||
|
|
83efa853fc | ||
|
|
75de4b443f | ||
|
|
42c7fb446e | ||
|
|
bce6b96417 | ||
|
|
6f82cf7f15 | ||
|
|
fac419dd94 | ||
|
|
c03d547ad8 | ||
|
|
e2bf8cefa2 |
@@ -148,6 +148,26 @@ OBJC_EXTERN void CLSNSLog(NSString *format, ...);
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* The CLSCrashReport protocol exposes methods that you can call on crash report objects passed
|
||||
* to delegate methods. If you want these values or the entire object to stay in memory retain
|
||||
* them or copy them.
|
||||
**/
|
||||
@protocol CLSCrashReport <NSObject>
|
||||
@optional
|
||||
|
||||
/**
|
||||
* Returns the session identifier for the crash report.
|
||||
**/
|
||||
- (NSString *)identifier;
|
||||
|
||||
/**
|
||||
* Returns the custom key value data for the crash report.
|
||||
**/
|
||||
- (NSDictionary *)customKeys;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
*
|
||||
* The CrashlyticsDelegate protocol provides a mechanism for your application to take
|
||||
@@ -169,4 +189,15 @@ OBJC_EXTERN void CLSNSLog(NSString *format, ...);
|
||||
**/
|
||||
- (void)crashlyticsDidDetectCrashDuringPreviousExecution:(Crashlytics *)crashlytics;
|
||||
|
||||
/**
|
||||
*
|
||||
* Just like crashlyticsDidDetectCrashDuringPreviousExecution this delegate method is
|
||||
* called once a Crashlytics instance has determined that the last execution of the
|
||||
* application ended in a crash. A CLSCrashReport is passed back that contains data about
|
||||
* the last crash report that was generated. See the CLSCrashReport protocol for method details.
|
||||
* This method is called after crashlyticsDidDetectCrashDuringPreviousExecution.
|
||||
*
|
||||
**/
|
||||
- (void)crashlytics:(Crashlytics *)crashlytics didDetectCrashDuringPreviousExecution:(id <CLSCrashReport>)crash;
|
||||
|
||||
@end
|
||||
|
||||
BIN
Crashlytics/Crashlytics.framework/run
vendored
2
External/Pearl
vendored
@@ -1,57 +1,70 @@
|
||||
//
|
||||
// LocalyticsDatabase.h
|
||||
// LocalyticsDemo
|
||||
//
|
||||
// Created by jkaufman on 5/26/11.
|
||||
// Copyright 2011 Localytics. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <sqlite3.h>
|
||||
|
||||
#define MAX_DATABASE_SIZE 500000 // The maximum allowed disk size of the primary database file at open, in bytes
|
||||
#define VACUUM_THRESHOLD 0.8 // The database is vacuumed after its size exceeds this proportion of the maximum.
|
||||
|
||||
@interface LocalyticsDatabase : NSObject {
|
||||
sqlite3 *_databaseConnection;
|
||||
}
|
||||
|
||||
+ (LocalyticsDatabase *)sharedLocalyticsDatabase;
|
||||
|
||||
- (NSUInteger)databaseSize;
|
||||
- (int)eventCount;
|
||||
- (NSTimeInterval)createdTimestamp;
|
||||
|
||||
- (BOOL)beginTransaction:(NSString *)name;
|
||||
- (BOOL)releaseTransaction:(NSString *)name;
|
||||
- (BOOL)rollbackTransaction:(NSString *)name;
|
||||
|
||||
- (BOOL)incrementLastUploadNumber:(int *)uploadNumber;
|
||||
- (BOOL)incrementLastSessionNumber:(int *)sessionNumber;
|
||||
|
||||
- (BOOL)addEventWithBlobString:(NSString *)blob;
|
||||
- (BOOL)addCloseEventWithBlobString:(NSString *)blob;
|
||||
- (BOOL)addFlowEventWithBlobString:(NSString *)blob;
|
||||
- (BOOL)removeLastCloseAndFlowEvents;
|
||||
|
||||
- (BOOL)addHeaderWithSequenceNumber:(int)number blobString:(NSString *)blob rowId:(sqlite3_int64 *)insertedRowId;
|
||||
- (int)unstagedEventCount;
|
||||
- (BOOL)stageEventsForUpload:(sqlite3_int64)headerId;
|
||||
- (BOOL)updateAppKey:(NSString *)appKey;
|
||||
- (NSString *)uploadBlobString;
|
||||
- (BOOL)deleteUploadedData;
|
||||
- (BOOL)resetAnalyticsData;
|
||||
- (BOOL)vacuumIfRequired;
|
||||
|
||||
- (NSTimeInterval)lastSessionStartTimestamp;
|
||||
- (BOOL)setLastsessionStartTimestamp:(NSTimeInterval)timestamp;
|
||||
|
||||
- (BOOL)isOptedOut;
|
||||
- (BOOL)setOptedOut:(BOOL)optOut;
|
||||
- (NSString *)installId;
|
||||
- (NSString *)appKey; // Most recent app key-- may not be that used to open the session.
|
||||
|
||||
- (NSString *)customDimension:(int)dimension;
|
||||
- (BOOL)setCustomDimension:(int)dimension value:(NSString *)value;
|
||||
|
||||
@end
|
||||
//
|
||||
// LocalyticsDatabase.h
|
||||
// Copyright (C) 2012 Char Software Inc., DBA Localytics
|
||||
//
|
||||
// This code is provided under the Localytics Modified BSD License.
|
||||
// A copy of this license has been distributed in a file called LICENSE
|
||||
// with this source code.
|
||||
//
|
||||
// Please visit www.localytics.com for more information.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <sqlite3.h>
|
||||
|
||||
#define MAX_DATABASE_SIZE 500000 // The maximum allowed disk size of the primary database file at open, in bytes
|
||||
#define VACUUM_THRESHOLD 0.8 // The database is vacuumed after its size exceeds this proportion of the maximum.
|
||||
|
||||
@interface LocalyticsDatabase : NSObject {
|
||||
sqlite3 *_databaseConnection;
|
||||
}
|
||||
|
||||
+ (LocalyticsDatabase *)sharedLocalyticsDatabase;
|
||||
|
||||
- (NSUInteger)databaseSize;
|
||||
- (int)eventCount;
|
||||
- (NSTimeInterval)createdTimestamp;
|
||||
|
||||
- (BOOL)beginTransaction:(NSString *)name;
|
||||
- (BOOL)releaseTransaction:(NSString *)name;
|
||||
- (BOOL)rollbackTransaction:(NSString *)name;
|
||||
|
||||
- (BOOL)incrementLastUploadNumber:(int *)uploadNumber;
|
||||
- (BOOL)incrementLastSessionNumber:(int *)sessionNumber;
|
||||
|
||||
- (BOOL)addEventWithBlobString:(NSString *)blob;
|
||||
- (BOOL)addCloseEventWithBlobString:(NSString *)blob;
|
||||
- (BOOL)queueCloseEventWithBlobString:(NSString *)blob;
|
||||
- (NSString *)dequeueCloseEventBlobString;
|
||||
- (BOOL)addFlowEventWithBlobString:(NSString *)blob;
|
||||
- (BOOL)removeLastCloseAndFlowEvents;
|
||||
|
||||
- (BOOL)addHeaderWithSequenceNumber:(int)number blobString:(NSString *)blob rowId:(sqlite3_int64 *)insertedRowId;
|
||||
- (int)unstagedEventCount;
|
||||
- (BOOL)stageEventsForUpload:(sqlite3_int64)headerId;
|
||||
- (BOOL)updateAppKey:(NSString *)appKey;
|
||||
- (NSString *)uploadBlobString;
|
||||
- (BOOL)deleteUploadedData;
|
||||
- (BOOL)resetAnalyticsData;
|
||||
- (BOOL)vacuumIfRequired;
|
||||
|
||||
- (NSTimeInterval)lastSessionStartTimestamp;
|
||||
- (BOOL)setLastsessionStartTimestamp:(NSTimeInterval)timestamp;
|
||||
|
||||
- (BOOL)isOptedOut;
|
||||
- (BOOL)setOptedOut:(BOOL)optOut;
|
||||
- (NSString *)installId;
|
||||
- (NSString *)appKey; // Most recent app key-- may not be that used to open the session.
|
||||
|
||||
- (NSString *)customDimension:(int)dimension;
|
||||
- (BOOL)setCustomDimension:(int)dimension value:(NSString *)value;
|
||||
|
||||
- (NSString *)customerId;
|
||||
- (BOOL)setCustomerId:(NSString *)newCustomerId;
|
||||
|
||||
- (NSInteger)safeIntegerValueFromDictionary:(NSDictionary *)dict forKey:(NSString *)key;
|
||||
- (NSString *)safeStringValueFromDictionary:(NSDictionary *)dict forKey:(NSString *)key;
|
||||
- (NSDictionary *)safeDictionaryFromDictionary:(NSDictionary *)dict forKey:(NSString *)key;
|
||||
- (NSArray *)safeListFromDictionary:(NSDictionary *)dict forKey:(NSString *)key;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,216 +1,251 @@
|
||||
// LocalyticsSession.h
|
||||
// Copyright (C) 2009 Char Software Inc., DBA Localytics
|
||||
//
|
||||
// This code is provided under the Localytics Modified BSD License.
|
||||
// A copy of this license has been distributed in a file called LICENSE
|
||||
// with this source code.
|
||||
//
|
||||
// Please visit www.localytics.com for more information.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
// Set this to true to enable localytics traces (useful for debugging)
|
||||
#define DO_LOCALYTICS_LOGGING false
|
||||
|
||||
/*!
|
||||
@class LocalyticsSession
|
||||
@discussion The class which manages creating, collecting, & uploading a Localytics session.
|
||||
Please see the following guides for information on how to best use this
|
||||
library, sample code, and other useful information:
|
||||
<ul>
|
||||
<li><a href="http://wiki.localytics.com/index.php?title=Developer's_Integration_Guide">Main Developer's Integration Guide</a></li>
|
||||
</ul>
|
||||
|
||||
<strong>Best Practices</strong>
|
||||
<ul>
|
||||
<li>Instantiate the LocalyticsSession object in applicationDidFinishLaunching.</li>
|
||||
<li>Open your session and begin your uploads in applicationDidFinishLaunching. This way the
|
||||
upload has time to complete and it all happens before your users have a
|
||||
chance to begin any data intensive actions of their own.</li>
|
||||
<li>Close the session in applicationWillTerminate, and in applicationDidEnterBackground.</li>
|
||||
<li>Resume the session in applicationWillEnterForeground.</li>
|
||||
<li>Do not call any Localytics functions inside a loop. Instead, calls
|
||||
such as <code>tagEvent</code> should follow user actions. This limits the
|
||||
amount of data which is stored and uploaded.</li>
|
||||
<li>Do not use multiple LocalticsSession objects to upload data with
|
||||
multiple application keys. This can cause invalid state.</li>
|
||||
</ul>
|
||||
|
||||
@author Localytics
|
||||
*/
|
||||
@interface LocalyticsSession : NSObject {
|
||||
|
||||
BOOL _hasInitialized; // Whether or not the session object has been initialized.
|
||||
BOOL _isSessionOpen; // Whether or not this session has been opened.
|
||||
float _backgroundSessionTimeout; // If an App stays in the background for more
|
||||
// than this many seconds, start a new session
|
||||
// when it returns to foreground.
|
||||
@private
|
||||
#pragma mark Member Variables
|
||||
dispatch_queue_t _queue; // Queue of Localytics block objects.
|
||||
dispatch_group_t _criticalGroup; // Group of blocks the must complete before backgrounding.
|
||||
NSString *_sessionUUID; // Unique identifier for this session.
|
||||
NSString *_applicationKey; // Unique identifier for the instrumented application
|
||||
NSTimeInterval _lastSessionStartTimestamp; // The start time of the most recent session.
|
||||
NSDate *_sessionResumeTime; // Time session was started or resumed.
|
||||
NSDate *_sessionCloseTime; // Time session was closed.
|
||||
NSMutableString *_unstagedFlowEvents; // Comma-delimited list of app screens and events tagged during this
|
||||
// session that have NOT been staged for upload.
|
||||
NSMutableString *_stagedFlowEvents; // App screens and events tagged during this session that HAVE been staged
|
||||
// for upload.
|
||||
NSMutableString *_screens; // Comma-delimited list of screens tagged during this session.
|
||||
NSTimeInterval _sessionActiveDuration; // Duration that session open.
|
||||
BOOL _sessionHasBeenOpen; // Whether or not this session has ever been open.
|
||||
}
|
||||
|
||||
@property dispatch_queue_t queue;
|
||||
@property dispatch_group_t criticalGroup;
|
||||
@property BOOL isSessionOpen;
|
||||
@property BOOL hasInitialized;
|
||||
@property float backgroundSessionTimeout;
|
||||
|
||||
#pragma mark Public Methods
|
||||
/*!
|
||||
@method sharedLocalyticsSession
|
||||
@abstract Accesses the Session object. This is a Singleton class which maintains
|
||||
a single session throughout your application. It is possible to manage your own
|
||||
session, but this is the easiest way to access the Localytics object throughout your code.
|
||||
The class is accessed within the code using the following syntax:
|
||||
[[LocalyticsSession sharedLocalyticsSession] functionHere]
|
||||
So, to tag an event, all that is necessary, anywhere in the code is:
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:@"MY_EVENT"];
|
||||
*/
|
||||
+ (LocalyticsSession *)sharedLocalyticsSession;
|
||||
|
||||
/*!
|
||||
@method LocalyticsSession
|
||||
@abstract Initializes the Localytics Object. Not necessary if you choose to use startSession.
|
||||
@param applicationKey The key unique for each application generated at www.localytics.com
|
||||
*/
|
||||
- (void)LocalyticsSession:(NSString *)appKey;
|
||||
|
||||
/*!
|
||||
@method startSession
|
||||
@abstract An optional convenience initialize method that also calls the LocalyticsSession, open &
|
||||
upload methods. Best Practice is to call open & upload immediately after Localytics Session when loading an app,
|
||||
this method fascilitates that behavior.
|
||||
It is recommended that this call be placed in <code>applicationDidFinishLaunching</code>.
|
||||
@param applicationKey The key unique for each application generated
|
||||
at www.localytics.com
|
||||
*/
|
||||
- (void)startSession:(NSString *)appKey;
|
||||
|
||||
/*!
|
||||
@method setOptIn
|
||||
@abstract (OPTIONAL) Allows the application to control whether or not it will collect user data.
|
||||
Even if this call is used, it is necessary to continue calling upload(). No new data will be
|
||||
collected, so nothing new will be uploaded but it is necessary to upload an event telling the
|
||||
server this user has opted out.
|
||||
@param optedIn True if the user is opted in, false otherwise.
|
||||
*/
|
||||
- (void)setOptIn:(BOOL)optedIn;
|
||||
|
||||
/*!
|
||||
@method isOptedIn
|
||||
@abstract (OPTIONAL) Whether or not this user has is opted in or out. The only way they can be
|
||||
opted out is if setOptIn(false) has been called before this. This function should only be
|
||||
used to pre-populate a checkbox in an options menu. It is not recommended that an application
|
||||
branch based on Localytics instrumentation because this creates an additional test case. If
|
||||
the app is opted out, all subsequent Localytics calls will return immediately.
|
||||
@result true if the user is opted in, false otherwise.
|
||||
*/
|
||||
- (BOOL)isOptedIn;
|
||||
|
||||
/*!
|
||||
@method open
|
||||
@abstract Opens the Localytics session. Not necessary if you choose to use startSession.
|
||||
The session time as presented on the website is the time between <code>open</code> and the
|
||||
final <code>close</code> so it is recommended to open the session as early as possible, and close
|
||||
it at the last moment. The session must be opened before any tags can
|
||||
be written. It is recommended that this call be placed in <code>applicationDidFinishLaunching</code>.
|
||||
<br>
|
||||
If for any reason this is called more than once every subsequent open call
|
||||
will be ignored.
|
||||
*/
|
||||
- (void)open;
|
||||
|
||||
/*!
|
||||
@method resume
|
||||
@abstract Resumes the Localytics session. When the App enters the background, the session is
|
||||
closed and the time of closing is recorded. When the app returns to the foreground, the session
|
||||
is resumed. If the time since closing is greater than BACKGROUND_SESSION_TIMEOUT, (15 seconds
|
||||
by default) a new session is created, and uploading is triggered. Otherwise, the previous session
|
||||
is reopened.
|
||||
*/
|
||||
- (void)resume;
|
||||
|
||||
/*!
|
||||
@method close
|
||||
@abstract Closes the Localytics session. This should be called in
|
||||
<code>applicationWillTerminate</code>.
|
||||
<br>
|
||||
If close is not called, the session will still be uploaded but no
|
||||
events will be processed and the session time will not appear. This is
|
||||
because the session is not yet closed so it should not be used in
|
||||
comparison with sessions which are closed.
|
||||
*/
|
||||
- (void)close;
|
||||
|
||||
/*!
|
||||
@method tagEvent
|
||||
@abstract Allows a session to tag a particular event as having occurred. For
|
||||
example, if a view has three buttons, it might make sense to tag
|
||||
each button click with the name of the button which was clicked.
|
||||
For another example, in a game with many levels it might be valuable
|
||||
to create a new tag every time the user gets to a new level in order
|
||||
to determine how far the average user is progressing in the game.
|
||||
<br>
|
||||
<strong>Tagging Best Practices</strong>
|
||||
<ul>
|
||||
<li>DO NOT use tags to record personally identifiable information.</li>
|
||||
<li>The best way to use tags is to create all the tag strings as predefined
|
||||
constants and only use those. This is more efficient and removes the risk of
|
||||
collecting personal information.</li>
|
||||
<li>Do not set tags inside loops or any other place which gets called
|
||||
frequently. This can cause a lot of data to be stored and uploaded.</li>
|
||||
</ul>
|
||||
<br>
|
||||
See the tagging guide at: http://wiki.localytics.com/
|
||||
@param event The name of the event which occurred.
|
||||
*/
|
||||
- (void)tagEvent:(NSString *)event;
|
||||
|
||||
- (void)tagEvent:(NSString *)event attributes:(NSDictionary *)attributes;
|
||||
|
||||
- (void)tagEvent:(NSString *)event attributes:(NSDictionary *)attributes reportAttributes:(NSDictionary *)reportAttributes;
|
||||
|
||||
/*!
|
||||
@method tagScreen
|
||||
@abstract Allows tagging the flow of screens encountered during the session.
|
||||
@param screen The name of the screen
|
||||
*/
|
||||
- (void)tagScreen:(NSString *)screen;
|
||||
|
||||
/*!
|
||||
@method upload
|
||||
@abstract Creates a low priority thread which uploads any Localytics data already stored
|
||||
on the device. This should be done early in the process life in order to
|
||||
guarantee as much time as possible for slow connections to complete. It is also reasonable
|
||||
to upload again when the application is exiting because if the upload is cancelled the data
|
||||
will just get uploaded the next time the app comes up.
|
||||
*/
|
||||
- (void)upload;
|
||||
|
||||
/*!
|
||||
@method setCustomDimension
|
||||
@abstract (ENTERPRISE ONLY) Sets the value of a custom dimension. Custom dimensions are dimensions
|
||||
which contain user defined data unlike the predefined dimensions such as carrier, model, and country.
|
||||
Once a value for a custom dimension is set, the device it was set on will continue to upload that value
|
||||
until the value is changed. To clear a value pass nil as the value.
|
||||
The proper use of custom dimensions involves defining a dimension with less than ten distinct possible
|
||||
values and assigning it to one of the four available custom dimensions. Once assigned this definition should
|
||||
never be changed without changing the App Key otherwise old installs of the application will pollute new data.
|
||||
*/
|
||||
- (void)setCustomDimension:(int)dimension value:(NSString *)value;
|
||||
|
||||
@end
|
||||
// LocalyticsSession.h
|
||||
// Copyright (C) 2012 Char Software Inc., DBA Localytics
|
||||
//
|
||||
// This code is provided under the Localytics Modified BSD License.
|
||||
// A copy of this license has been distributed in a file called LICENSE
|
||||
// with this source code.
|
||||
//
|
||||
// Please visit www.localytics.com for more information.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <CoreLocation/CoreLocation.h>
|
||||
|
||||
// Set this to true to enable localytics traces (useful for debugging)
|
||||
#define DO_LOCALYTICS_LOGGING false
|
||||
|
||||
/*!
|
||||
@class LocalyticsSession
|
||||
@discussion The class which manages creating, collecting, & uploading a Localytics session.
|
||||
Please see the following guides for information on how to best use this
|
||||
library, sample code, and other useful information:
|
||||
<ul>
|
||||
<li><a href="http://wiki.localytics.com/index.php?title=Developer's_Integration_Guide">Main Developer's Integration Guide</a></li>
|
||||
</ul>
|
||||
|
||||
<strong>Best Practices</strong>
|
||||
<ul>
|
||||
<li>Instantiate the LocalyticsSession object in applicationDidFinishLaunching.</li>
|
||||
<li>Open your session and begin your uploads in applicationDidFinishLaunching. This way the
|
||||
upload has time to complete and it all happens before your users have a
|
||||
chance to begin any data intensive actions of their own.</li>
|
||||
<li>Close the session in applicationWillTerminate, and in applicationDidEnterBackground.</li>
|
||||
<li>Resume the session in applicationWillEnterForeground.</li>
|
||||
<li>Do not call any Localytics functions inside a loop. Instead, calls
|
||||
such as <code>tagEvent</code> should follow user actions. This limits the
|
||||
amount of data which is stored and uploaded.</li>
|
||||
<li>Do not use multiple LocalticsSession objects to upload data with
|
||||
multiple application keys. This can cause invalid state.</li>
|
||||
</ul>
|
||||
|
||||
@author Localytics
|
||||
*/
|
||||
|
||||
@interface LocalyticsSession : NSObject {
|
||||
|
||||
BOOL _hasInitialized; // Whether or not the session object has been initialized.
|
||||
BOOL _isSessionOpen; // Whether or not this session has been opened.
|
||||
float _backgroundSessionTimeout; // If an App stays in the background for more
|
||||
// than this many seconds, start a new session
|
||||
// when it returns to foreground.
|
||||
@private
|
||||
#pragma mark Member Variables
|
||||
dispatch_queue_t _queue; // Queue of Localytics block objects.
|
||||
dispatch_group_t _criticalGroup; // Group of blocks the must complete before backgrounding.
|
||||
NSString *_sessionUUID; // Unique identifier for this session.
|
||||
NSString *_applicationKey; // Unique identifier for the instrumented application
|
||||
NSTimeInterval _lastSessionStartTimestamp; // The start time of the most recent session.
|
||||
NSDate *_sessionResumeTime; // Time session was started or resumed.
|
||||
NSDate *_sessionCloseTime; // Time session was closed.
|
||||
NSMutableString *_unstagedFlowEvents; // Comma-delimited list of app screens and events tagged during this
|
||||
// session that have NOT been staged for upload.
|
||||
NSMutableString *_stagedFlowEvents; // App screens and events tagged during this session that HAVE been staged
|
||||
// for upload.
|
||||
NSMutableString *_screens; // Comma-delimited list of screens tagged during this session.
|
||||
NSTimeInterval _sessionActiveDuration; // Duration that session open.
|
||||
BOOL _sessionHasBeenOpen; // Whether or not this session has ever been open.
|
||||
}
|
||||
|
||||
@property (nonatomic,readonly) dispatch_queue_t queue;
|
||||
@property (nonatomic,readonly) dispatch_group_t criticalGroup;
|
||||
@property BOOL isSessionOpen;
|
||||
@property BOOL hasInitialized;
|
||||
@property float backgroundSessionTimeout;
|
||||
|
||||
- (void)logMessage:(NSString *)message;
|
||||
@property (nonatomic, assign, readonly) NSTimeInterval lastSessionStartTimestamp;
|
||||
@property (nonatomic, assign, readonly) NSInteger sessionNumber;
|
||||
|
||||
|
||||
/*!
|
||||
@property enableHTTPS
|
||||
@abstract Determines whether or not HTTPS is used when calling the Localytics
|
||||
post URL. The default is NO.
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL enableHTTPS; // Defaults to NO.
|
||||
|
||||
#pragma mark Public Methods
|
||||
/*!
|
||||
@method sharedLocalyticsSession
|
||||
@abstract Accesses the Session object. This is a Singleton class which maintains
|
||||
a single session throughout your application. It is possible to manage your own
|
||||
session, but this is the easiest way to access the Localytics object throughout your code.
|
||||
The class is accessed within the code using the following syntax:
|
||||
[[LocalyticsSession sharedLocalyticsSession] functionHere]
|
||||
So, to tag an event, all that is necessary, anywhere in the code is:
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:@"MY_EVENT"];
|
||||
*/
|
||||
+ (LocalyticsSession *)sharedLocalyticsSession;
|
||||
|
||||
/*!
|
||||
@method LocalyticsSession
|
||||
@abstract Initializes the Localytics Object. Not necessary if you choose to use startSession.
|
||||
@param applicationKey The key unique for each application generated at www.localytics.com
|
||||
*/
|
||||
- (void)LocalyticsSession:(NSString *)appKey;
|
||||
|
||||
/*!
|
||||
@method startSession
|
||||
@abstract An optional convenience initialize method that also calls the LocalyticsSession, open &
|
||||
upload methods. Best Practice is to call open & upload immediately after Localytics Session when loading an app,
|
||||
this method fascilitates that behavior.
|
||||
It is recommended that this call be placed in <code>applicationDidFinishLaunching</code>.
|
||||
@param applicationKey The key unique for each application generated
|
||||
at www.localytics.com
|
||||
*/
|
||||
- (void)startSession:(NSString *)appKey;
|
||||
|
||||
/*!
|
||||
@method setOptIn
|
||||
@abstract (OPTIONAL) Allows the application to control whether or not it will collect user data.
|
||||
Even if this call is used, it is necessary to continue calling upload(). No new data will be
|
||||
collected, so nothing new will be uploaded but it is necessary to upload an event telling the
|
||||
server this user has opted out.
|
||||
@param optedIn True if the user is opted in, false otherwise.
|
||||
*/
|
||||
- (void)setOptIn:(BOOL)optedIn;
|
||||
|
||||
/*!
|
||||
@method isOptedIn
|
||||
@abstract (OPTIONAL) Whether or not this user has is opted in or out. The only way they can be
|
||||
opted out is if setOptIn(false) has been called before this. This function should only be
|
||||
used to pre-populate a checkbox in an options menu. It is not recommended that an application
|
||||
branch based on Localytics instrumentation because this creates an additional test case. If
|
||||
the app is opted out, all subsequent Localytics calls will return immediately.
|
||||
@result true if the user is opted in, false otherwise.
|
||||
*/
|
||||
- (BOOL)isOptedIn;
|
||||
|
||||
/*!
|
||||
@method open
|
||||
@abstract Opens the Localytics session. Not necessary if you choose to use startSession.
|
||||
The session time as presented on the website is the time between <code>open</code> and the
|
||||
final <code>close</code> so it is recommended to open the session as early as possible, and close
|
||||
it at the last moment. The session must be opened before any tags can
|
||||
be written. It is recommended that this call be placed in <code>applicationDidFinishLaunching</code>.
|
||||
<br>
|
||||
If for any reason this is called more than once every subsequent open call
|
||||
will be ignored.
|
||||
*/
|
||||
- (void)open;
|
||||
|
||||
/*!
|
||||
@method resume
|
||||
@abstract Resumes the Localytics session. When the App enters the background, the session is
|
||||
closed and the time of closing is recorded. When the app returns to the foreground, the session
|
||||
is resumed. If the time since closing is greater than BACKGROUND_SESSION_TIMEOUT, (15 seconds
|
||||
by default) a new session is created, and uploading is triggered. Otherwise, the previous session
|
||||
is reopened. It is possible to use the return value to determine whether or not a session was resumed.
|
||||
This may be useful to some customers looking to do conditional instrumentation at the close of a session.
|
||||
It is perfectly reasonable to ignore the return value.
|
||||
@result YES if the sesion was resumed NO if it wasn't (suggesting a new session was created instead).*/
|
||||
- (BOOL)resume;
|
||||
|
||||
/*!
|
||||
@method close
|
||||
@abstract Closes the Localytics session. This should be called in
|
||||
<code>applicationWillTerminate</code>.
|
||||
<br>
|
||||
If close is not called, the session will still be uploaded but no
|
||||
events will be processed and the session time will not appear. This is
|
||||
because the session is not yet closed so it should not be used in
|
||||
comparison with sessions which are closed.
|
||||
*/
|
||||
- (void)close;
|
||||
|
||||
/*!
|
||||
@method tagEvent
|
||||
@abstract Allows a session to tag a particular event as having occurred. For
|
||||
example, if a view has three buttons, it might make sense to tag
|
||||
each button click with the name of the button which was clicked.
|
||||
For another example, in a game with many levels it might be valuable
|
||||
to create a new tag every time the user gets to a new level in order
|
||||
to determine how far the average user is progressing in the game.
|
||||
<br>
|
||||
<strong>Tagging Best Practices</strong>
|
||||
<ul>
|
||||
<li>DO NOT use tags to record personally identifiable information.</li>
|
||||
<li>The best way to use tags is to create all the tag strings as predefined
|
||||
constants and only use those. This is more efficient and removes the risk of
|
||||
collecting personal information.</li>
|
||||
<li>Do not set tags inside loops or any other place which gets called
|
||||
frequently. This can cause a lot of data to be stored and uploaded.</li>
|
||||
</ul>
|
||||
<br>
|
||||
See the tagging guide at: http://wiki.localytics.com/
|
||||
@param event The name of the event which occurred.
|
||||
*/
|
||||
- (void)tagEvent:(NSString *)event;
|
||||
|
||||
- (void)tagEvent:(NSString *)event attributes:(NSDictionary *)attributes;
|
||||
|
||||
- (void)tagEvent:(NSString *)event attributes:(NSDictionary *)attributes reportAttributes:(NSDictionary *)reportAttributes;
|
||||
|
||||
/*!
|
||||
@method tagScreen
|
||||
@abstract Allows tagging the flow of screens encountered during the session.
|
||||
@param screen The name of the screen
|
||||
*/
|
||||
- (void)tagScreen:(NSString *)screen;
|
||||
|
||||
/*!
|
||||
@method upload
|
||||
@abstract Creates a low priority thread which uploads any Localytics data already stored
|
||||
on the device. This should be done early in the process life in order to
|
||||
guarantee as much time as possible for slow connections to complete. It is also reasonable
|
||||
to upload again when the application is exiting because if the upload is cancelled the data
|
||||
will just get uploaded the next time the app comes up.
|
||||
*/
|
||||
- (void)upload;
|
||||
|
||||
/*!
|
||||
@method setCustomDimension
|
||||
@abstract (ENTERPRISE ONLY) Sets the value of a custom dimension. Custom dimensions are dimensions
|
||||
which contain user defined data unlike the predefined dimensions such as carrier, model, and country.
|
||||
Once a value for a custom dimension is set, the device it was set on will continue to upload that value
|
||||
until the value is changed. To clear a value pass nil as the value.
|
||||
The proper use of custom dimensions involves defining a dimension with less than ten distinct possible
|
||||
values and assigning it to one of the four available custom dimensions. Once assigned this definition should
|
||||
never be changed without changing the App Key otherwise old installs of the application will pollute new data.
|
||||
*/
|
||||
- (void)setCustomDimension:(int)dimension value:(NSString *)value;
|
||||
|
||||
/*!
|
||||
@method setLocation
|
||||
@abstract Stores the user's location. This will be used in all event and session calls.
|
||||
If your application has already collected the user's location, it may be passed to Localytics
|
||||
via this function. This will cause all events and the session close to include the locatin
|
||||
information. It is not required that you call this function.
|
||||
@param deviceLocation The user's location.
|
||||
*/
|
||||
- (void)setLocation:(CLLocationCoordinate2D)deviceLocation;
|
||||
|
||||
/*!
|
||||
@method ampTrigger
|
||||
@abstract Displays the AMP message for the specific event.
|
||||
Is a stub implementation here to prevent crashes if this class is accidentally used inplace of
|
||||
the LocalyticsAmpSession
|
||||
@param event Name of the event.
|
||||
*/
|
||||
- (void)ampTrigger:(NSString *)event;
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// LocalyticsUploader.h
|
||||
// Copyright (C) 2009 Char Software Inc., DBA Localytics
|
||||
// Copyright (C) 2012 Char Software Inc., DBA Localytics
|
||||
//
|
||||
// This code is provided under the Localytics Modified BSD License.
|
||||
// A copy of this license has been distributed in a file called LICENSE
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
extern NSString * const kLocalyticsKeyResponseBody;
|
||||
|
||||
/*!
|
||||
@class LocalyticsUploader
|
||||
@discussion Singleton class to handle data uploads
|
||||
@@ -35,8 +37,28 @@
|
||||
writing data regardless of whether or not the upload succeeds. Files
|
||||
which have been renamed still count towards the total number of Localytics
|
||||
files which can be stored on the disk.
|
||||
|
||||
This version of the method now just calls the second version of it with a nil target and NULL callback method.
|
||||
@param localyticsApplicationKey the Localytics application ID
|
||||
@param useHTTPS Flag determining whether HTTP or HTTPS is used for the post URL.
|
||||
@param installId Install id passed to the server in the x-install-id header field.
|
||||
*/
|
||||
- (void)uploaderWithApplicationKey:(NSString *)localyticsApplicationKey;
|
||||
- (void)uploaderWithApplicationKey:(NSString *)localyticsApplicationKey useHTTPS:(BOOL)useHTTPS installId:(NSString *)installId;
|
||||
|
||||
/*!
|
||||
@method LocalyticsUploader
|
||||
@abstract Creates a thread which uploads all queued header and event data.
|
||||
All files starting with sessionFilePrefix are renamed,
|
||||
uploaded and deleted on upload. This way the sessions can continue
|
||||
writing data regardless of whether or not the upload succeeds. Files
|
||||
which have been renamed still count towards the total number of Localytics
|
||||
files which can be stored on the disk.
|
||||
@param localyticsApplicationKey the Localytics application ID
|
||||
@param useHTTPS Flag determining whether HTTP or HTTPS is used for the post URL.
|
||||
@param installId Install id passed to the server in the x-install-id header field.
|
||||
@param resultTarget Result target is the target for the callback method that knows how to handle response data
|
||||
@param callback Callback is the method of the target class that is to be called with the data begin returned by an upload
|
||||
*/
|
||||
- (void)uploaderWithApplicationKey:(NSString *)localyticsApplicationKey useHTTPS:(BOOL)useHTTPS installId:(NSString *)installId resultTarget:(id)target callback:(SEL)callbackMethod;
|
||||
|
||||
@end
|
||||
@@ -1,5 +1,5 @@
|
||||
// LocalyticsUploader.m
|
||||
// Copyright (C) 2009 Char Software Inc., DBA Localytics
|
||||
// Copyright (C) 2012 Char Software Inc., DBA Localytics
|
||||
//
|
||||
// This code is provided under the Localytics Modified BSD License.
|
||||
// A copy of this license has been distributed in a file called LICENSE
|
||||
@@ -10,16 +10,25 @@
|
||||
#import "LocalyticsUploader.h"
|
||||
#import "LocalyticsSession.h"
|
||||
#import "LocalyticsDatabase.h"
|
||||
#import "WebserviceConstants.h"
|
||||
#import <zlib.h>
|
||||
|
||||
#define LOCALYTICS_URL @"http://analytics.localytics.com/api/v2/applications/%@/uploads"
|
||||
#ifndef LOCALYTICS_URL
|
||||
#define LOCALYTICS_URL @"http://analytics.localytics.com/api/v2/applications/%@/uploads"
|
||||
#endif
|
||||
|
||||
#ifndef LOCALYTICS_URL_SECURED
|
||||
#define LOCALYTICS_URL_SECURED @"https://analytics.localytics.com/api/v2/applications/%@/uploads"
|
||||
#endif
|
||||
static LocalyticsUploader *_sharedUploader = nil;
|
||||
|
||||
NSString * const kLocalyticsKeyResponseBody = @"localytics.key.responseBody";
|
||||
|
||||
@interface LocalyticsUploader ()
|
||||
- (void)finishUpload;
|
||||
- (NSData *)gzipDeflatedDataWithData:(NSData *)data;
|
||||
- (void)logMessage:(NSString *)message;
|
||||
- (NSString *)uploadTimeStamp;
|
||||
|
||||
@property (readwrite) BOOL isUploading;
|
||||
|
||||
@@ -40,7 +49,13 @@ static LocalyticsUploader *_sharedUploader = nil;
|
||||
|
||||
#pragma mark - Class Methods
|
||||
|
||||
- (void)uploaderWithApplicationKey:(NSString *)localyticsApplicationKey {
|
||||
- (void)uploaderWithApplicationKey:(NSString *)localyticsApplicationKey useHTTPS:(BOOL)useHTTPS installId:(NSString *)installId
|
||||
{
|
||||
[self uploaderWithApplicationKey:localyticsApplicationKey useHTTPS:useHTTPS installId:installId resultTarget:nil callback:NULL];
|
||||
}
|
||||
|
||||
- (void)uploaderWithApplicationKey:(NSString *)localyticsApplicationKey useHTTPS:(BOOL)useHTTPS installId:(NSString *)installId resultTarget:(id)target callback:(SEL)callbackMethod;
|
||||
{
|
||||
|
||||
// Do nothing if already uploading.
|
||||
if (self.isUploading == true)
|
||||
@@ -77,17 +92,26 @@ static LocalyticsUploader *_sharedUploader = nil;
|
||||
NSData *requestData = [blobString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSString *myString = [[[NSString alloc] initWithData:requestData encoding:NSUTF8StringEncoding] autorelease];
|
||||
[self logMessage:[NSString stringWithFormat:@"Uploading data (length: %u)", [myString length]]];
|
||||
[self logMessage:myString];
|
||||
|
||||
// Step 2
|
||||
NSData *deflatedRequestData = [[self gzipDeflatedDataWithData:requestData] retain];
|
||||
|
||||
[pool drain];
|
||||
|
||||
NSString *apiUrlString = [NSString stringWithFormat:LOCALYTICS_URL, [localyticsApplicationKey stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
|
||||
NSString *urlStringFormat;
|
||||
if (useHTTPS) {
|
||||
urlStringFormat = LOCALYTICS_URL_SECURED;
|
||||
} else {
|
||||
urlStringFormat = LOCALYTICS_URL;
|
||||
}
|
||||
NSString *apiUrlString = [NSString stringWithFormat:urlStringFormat, [localyticsApplicationKey stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
|
||||
NSMutableURLRequest *submitRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:apiUrlString]
|
||||
cachePolicy:NSURLRequestReloadIgnoringCacheData
|
||||
timeoutInterval:60.0];
|
||||
[submitRequest setHTTPMethod:@"POST"];
|
||||
[submitRequest setValue:[self uploadTimeStamp] forHTTPHeaderField:HEADER_CLIENT_TIME];
|
||||
[submitRequest setValue:installId forHTTPHeaderField:HEADER_INSTALL_ID];
|
||||
[submitRequest setValue:@"application/x-gzip" forHTTPHeaderField:@"Content-Type"];
|
||||
[submitRequest setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"];
|
||||
[submitRequest setValue:[NSString stringWithFormat:@"%d", [deflatedRequestData length]] forHTTPHeaderField:@"Content-Length"];
|
||||
@@ -100,7 +124,7 @@ static LocalyticsUploader *_sharedUploader = nil;
|
||||
@try {
|
||||
NSURLResponse *response = nil;
|
||||
NSError *responseError = nil;
|
||||
[NSURLConnection sendSynchronousRequest:submitRequest returningResponse:&response error:&responseError];
|
||||
NSData *responseData = [NSURLConnection sendSynchronousRequest:submitRequest returningResponse:&response error:&responseError];
|
||||
NSInteger responseStatusCode = [(NSHTTPURLResponse *)response statusCode];
|
||||
|
||||
if (responseError) {
|
||||
@@ -123,6 +147,18 @@ static LocalyticsUploader *_sharedUploader = nil;
|
||||
[[LocalyticsDatabase sharedLocalyticsDatabase] deleteUploadedData];
|
||||
}
|
||||
}
|
||||
|
||||
if ([responseData length] > 0) {
|
||||
if (DO_LOCALYTICS_LOGGING) {
|
||||
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
|
||||
[self logMessage:[NSString stringWithFormat:@"Response body: %@", responseString]];
|
||||
[responseString release];
|
||||
}
|
||||
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:responseData forKey:kLocalyticsKeyResponseBody];
|
||||
if (target) {
|
||||
[target performSelector:callbackMethod withObject:userInfo];
|
||||
}
|
||||
}
|
||||
}
|
||||
@catch (NSException * e) {}
|
||||
|
||||
@@ -195,6 +231,15 @@ static LocalyticsUploader *_sharedUploader = nil;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@method uploadTimeStamp
|
||||
@abstract Gets the current time, along with local timezone, formatted as a DateTime for the webservice.
|
||||
@return a DateTime of the current local time and timezone.
|
||||
*/
|
||||
- (NSString *)uploadTimeStamp {
|
||||
return [ NSString stringWithFormat:@"%ld", (long)[[NSDate date] timeIntervalSince1970] ];
|
||||
}
|
||||
|
||||
#pragma mark - System Functions
|
||||
+ (id)allocWithZone:(NSZone *)zone {
|
||||
@synchronized(self) {
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
// UploaderThread.h
|
||||
// Copyright (C) 2009 Char Software Inc., DBA Localytics
|
||||
//
|
||||
// This code is provided under the Localytics Modified BSD License.
|
||||
// A copy of this license has been distributed in a file called LICENSE
|
||||
// with this source code.
|
||||
//
|
||||
// Please visit www.localytics.com for more information.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
/*!
|
||||
@class UploaderThread
|
||||
@discussion Singleton class to handle data uploads
|
||||
*/
|
||||
|
||||
@interface UploaderThread : NSObject {
|
||||
NSURLConnection *_uploadConnection; // The connection which uploads the bits
|
||||
NSInteger _responseStatusCode; // The HTTP response status code for the current connection
|
||||
|
||||
BOOL _isUploading; // A flag to gaurantee only one uploader instance can happen at once
|
||||
}
|
||||
|
||||
@property (nonatomic, retain) NSURLConnection *uploadConnection;
|
||||
|
||||
@property BOOL isUploading;
|
||||
|
||||
/*!
|
||||
@method sharedUploaderThread
|
||||
@abstract Establishes this as a Singleton Class allowing for data persistence.
|
||||
The class is accessed within the code using the following syntax:
|
||||
[[UploaderThread sharedUploaderThread] functionHere]
|
||||
*/
|
||||
+ (UploaderThread *)sharedUploaderThread;
|
||||
|
||||
/*!
|
||||
@method UploaderThread
|
||||
@abstract Creates a thread which uploads all queued header and event data.
|
||||
All files starting with sessionFilePrefix are renamed,
|
||||
uploaded and deleted on upload. This way the sessions can continue
|
||||
writing data regardless of whether or not the upload succeeds. Files
|
||||
which have been renamed still count towards the total number of Localytics
|
||||
files which can be stored on the disk.
|
||||
@param localyticsApplicationKey the Localytics application ID
|
||||
*/
|
||||
- (void)uploaderThreadwithApplicationKey:(NSString *)localyticsApplicationKey;
|
||||
|
||||
@end
|
||||
@@ -1,260 +0,0 @@
|
||||
// UploaderThread.m
|
||||
// Copyright (C) 2009 Char Software Inc., DBA Localytics
|
||||
//
|
||||
// This code is provided under the Localytics Modified BSD License.
|
||||
// A copy of this license has been distributed in a file called LICENSE
|
||||
// with this source code.
|
||||
//
|
||||
// Please visit www.localytics.com for more information.
|
||||
|
||||
#import "UploaderThread.h"
|
||||
#import "LocalyticsSession.h"
|
||||
#import "LocalyticsDatabase.h"
|
||||
#import <zlib.h>
|
||||
|
||||
#define LOCALYTICS_URL @"http://analytics.localytics.com/api/v2/applications/%@/uploads" // url to send the
|
||||
|
||||
static UploaderThread *_sharedUploaderThread = nil;
|
||||
|
||||
@interface UploaderThread ()
|
||||
- (void)complete;
|
||||
- (NSData *)gzipDeflatedDataWithData:(NSData *)data;
|
||||
- (void)logMessage:(NSString *)message;
|
||||
@end
|
||||
|
||||
@implementation UploaderThread
|
||||
|
||||
@synthesize uploadConnection = _uploadConnection;
|
||||
@synthesize isUploading = _isUploading;
|
||||
|
||||
#pragma mark Singleton Class
|
||||
+ (UploaderThread *)sharedUploaderThread {
|
||||
@synchronized(self) {
|
||||
if (_sharedUploaderThread == nil)
|
||||
{
|
||||
_sharedUploaderThread = [[self alloc] init];
|
||||
}
|
||||
}
|
||||
return _sharedUploaderThread;
|
||||
}
|
||||
|
||||
#pragma mark Class Methods
|
||||
- (void)uploaderThreadwithApplicationKey:(NSString *)localyticsApplicationKey {
|
||||
|
||||
// Do nothing if already uploading.
|
||||
if (self.uploadConnection != nil || self.isUploading == true)
|
||||
{
|
||||
[self logMessage:@"Upload already in progress. Aborting."];
|
||||
return;
|
||||
}
|
||||
|
||||
[self logMessage:@"Beginning upload process"];
|
||||
self.isUploading = true;
|
||||
|
||||
// Prepare the data for upload. The upload could take a long time, so some effort has to be made to be sure that events
|
||||
// which get written while the upload is taking place don't get lost or duplicated. To achieve this, the logic is:
|
||||
// 1) Append every header row blob string and and those of its associated events to the upload string.
|
||||
// 2) Deflate and upload the data.
|
||||
// 3) On success, delete all blob headers and staged events. Events added while an upload is in process are not
|
||||
// deleted because they are not associated a header (and cannot be until the upload completes).
|
||||
|
||||
// Step 1
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
LocalyticsDatabase *db = [LocalyticsDatabase sharedLocalyticsDatabase];
|
||||
NSString *blobString = [db uploadBlobString];
|
||||
|
||||
if ([blobString length] == 0) {
|
||||
// There is nothing outstanding to upload.
|
||||
[self logMessage:@"Abandoning upload. There are no new events."];
|
||||
|
||||
[pool drain];
|
||||
[self complete];
|
||||
return;
|
||||
}
|
||||
|
||||
NSData *requestData = [blobString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSString *myString = [[[NSString alloc] initWithData:requestData encoding:NSUTF8StringEncoding] autorelease];
|
||||
[self logMessage:@"Upload data:"];
|
||||
[self logMessage:myString];
|
||||
|
||||
// Step 2
|
||||
NSData *deflatedRequestData = [[self gzipDeflatedDataWithData:requestData] retain];
|
||||
|
||||
[pool drain];
|
||||
|
||||
NSString *apiUrlString = [NSString stringWithFormat:LOCALYTICS_URL, [localyticsApplicationKey stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
|
||||
NSMutableURLRequest *submitRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:apiUrlString]
|
||||
cachePolicy:NSURLRequestReloadIgnoringCacheData
|
||||
timeoutInterval:60.0];
|
||||
[submitRequest setHTTPMethod:@"POST"];
|
||||
[submitRequest setValue:@"application/x-gzip" forHTTPHeaderField:@"Content-Type"];
|
||||
[submitRequest setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"];
|
||||
[submitRequest setValue:[NSString stringWithFormat:@"%d", [deflatedRequestData length]] forHTTPHeaderField:@"Content-Length"];
|
||||
[submitRequest setHTTPBody:deflatedRequestData];
|
||||
[deflatedRequestData release];
|
||||
|
||||
// The NSURLConnection Object automatically spawns its own thread as a default behavior.
|
||||
@try
|
||||
{
|
||||
[self logMessage:@"Spawning new thread for upload"];
|
||||
self.uploadConnection = [NSURLConnection connectionWithRequest:submitRequest delegate:self];
|
||||
|
||||
// Step 3 is handled by connectionDidFinishLoading.
|
||||
}
|
||||
@catch (NSException * e)
|
||||
{
|
||||
[self complete];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark **** NSURLConnection FUNCTIONS ****
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
|
||||
// Used to gather response data from server - Not utilized in this version
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
|
||||
// Could receive multiple response callbacks, likely due to redirection.
|
||||
// Record status and act only when connection completes load.
|
||||
_responseStatusCode = [(NSHTTPURLResponse *)response statusCode];
|
||||
}
|
||||
|
||||
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
|
||||
// If the connection finished loading, the files should be deleted. While response status codes in the 5xx range
|
||||
// leave upload rows intact, the default case is to delete.
|
||||
if (_responseStatusCode >= 500 && _responseStatusCode < 600)
|
||||
{
|
||||
[self logMessage:[NSString stringWithFormat:@"Upload failed with response status code %d", _responseStatusCode]];
|
||||
} else
|
||||
{
|
||||
// The connection finished loading and uploaded data should be deleted. Because only one instance of the
|
||||
// uploader can be running at a time it should not be possible for new upload rows to appear so there is no
|
||||
// fear of deleting data which has not yet been uploaded.
|
||||
[self logMessage:[NSString stringWithFormat:@"Upload completed successfully. Response code %d", _responseStatusCode]];
|
||||
[[LocalyticsDatabase sharedLocalyticsDatabase] deleteUploadData];
|
||||
}
|
||||
|
||||
// Close upload session
|
||||
[self complete];
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
|
||||
// On error, simply print the error and close the uploader. We have to assume the data was not transmited
|
||||
// so it is not deleted. In the event that we accidently store data which was succesfully uploaded, the
|
||||
// duplicate data will be ignored by the server when it is next uploaded.
|
||||
[self logMessage:[NSString stringWithFormat:
|
||||
@"Error Uploading. Code: %d, Description: %s",
|
||||
[error code],
|
||||
[error localizedDescription]]];
|
||||
|
||||
[self complete];
|
||||
}
|
||||
|
||||
/*!
|
||||
@method complete
|
||||
@abstract closes the upload connection and reports back to the session that the upload is complete
|
||||
*/
|
||||
- (void)complete {
|
||||
_responseStatusCode = 0;
|
||||
self.uploadConnection = nil;
|
||||
self.isUploading = false;
|
||||
}
|
||||
|
||||
/*!
|
||||
@method gzipDeflatedDataWithData
|
||||
@abstract Deflates the provided data using gzip at the default compression level (6). Complete NSData gzip category available on CocoaDev. http://www.cocoadev.com/index.pl?NSDataCategory.
|
||||
@return the deflated data
|
||||
*/
|
||||
- (NSData *)gzipDeflatedDataWithData:(NSData *)data
|
||||
{
|
||||
if ([data length] == 0) return data;
|
||||
|
||||
z_stream strm;
|
||||
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
strm.total_out = 0;
|
||||
strm.next_in=(Bytef *)[data bytes];
|
||||
strm.avail_in = [data length];
|
||||
|
||||
// Compresssion Levels:
|
||||
// Z_NO_COMPRESSION
|
||||
// Z_BEST_SPEED
|
||||
// Z_BEST_COMPRESSION
|
||||
// Z_DEFAULT_COMPRESSION
|
||||
|
||||
if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY) != Z_OK) return nil;
|
||||
|
||||
NSMutableData *compressed = [NSMutableData dataWithLength:16384]; // 16K chunks for expansion
|
||||
|
||||
do {
|
||||
|
||||
if (strm.total_out >= [compressed length])
|
||||
[compressed increaseLengthBy: 16384];
|
||||
|
||||
strm.next_out = [compressed mutableBytes] + strm.total_out;
|
||||
strm.avail_out = [compressed length] - strm.total_out;
|
||||
|
||||
deflate(&strm, Z_FINISH);
|
||||
|
||||
} while (strm.avail_out == 0);
|
||||
|
||||
deflateEnd(&strm);
|
||||
|
||||
[compressed setLength: strm.total_out];
|
||||
return [NSData dataWithData:compressed];
|
||||
}
|
||||
|
||||
/*!
|
||||
@method logMessage
|
||||
@abstract Logs a message with (localytics uploader) prepended to it
|
||||
@param message The message to log
|
||||
*/
|
||||
- (void) logMessage:(NSString *)message {
|
||||
if(DO_LOCALYTICS_LOGGING) {
|
||||
NSLog(@"(localytics uploader) %s\n", [message UTF8String]);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark System Functions
|
||||
+ (id)allocWithZone:(NSZone *)zone {
|
||||
@synchronized(self) {
|
||||
if (_sharedUploaderThread == nil) {
|
||||
_sharedUploaderThread = [super allocWithZone:zone];
|
||||
return _sharedUploaderThread;
|
||||
}
|
||||
}
|
||||
// returns nil on subsequent allocations
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)retain {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (unsigned)retainCount {
|
||||
// maximum value of an unsigned int - prevents additional retains for the class
|
||||
return UINT_MAX;
|
||||
}
|
||||
|
||||
- (oneway void)release {
|
||||
// ignore release commands
|
||||
}
|
||||
|
||||
- (id)autorelease {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[_uploadConnection release];
|
||||
[_sharedUploaderThread release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,5 +1,5 @@
|
||||
// WebserviceConstants.h
|
||||
// Copyright (C) 2009 Char Software Inc., DBA Localytics
|
||||
// Copyright (C) 2012 Char Software Inc., DBA Localytics
|
||||
//
|
||||
// This code is provided under the Localytics Modified BSD License.
|
||||
// A copy of this license has been distributed in a file called LICENSE
|
||||
@@ -11,6 +11,12 @@
|
||||
// To save disk space and network bandwidth all the keywords have been
|
||||
// abbreviated and are exploded by the server.
|
||||
|
||||
/*****************
|
||||
* Upload Header *
|
||||
*****************/
|
||||
#define HEADER_CLIENT_TIME @"x-upload-time"
|
||||
#define HEADER_INSTALL_ID @"x-install-id"
|
||||
|
||||
/*********************
|
||||
* Shared Attributes *
|
||||
*********************/
|
||||
@@ -22,6 +28,7 @@
|
||||
#define PARAM_SESSION_UUID @"su" // UUID for an existing session
|
||||
#define PARAM_NEW_SESSION_UUID @"u" // UUID for a new session
|
||||
#define PARAM_ATTRIBUTES @"attrs" // Attributes (dictionary)
|
||||
#define PARAM_SESSION_ELAPSE_TIME @"sl" // Number of seconds since the previous session start
|
||||
|
||||
/***************
|
||||
* Blob Header *
|
||||
@@ -42,9 +49,8 @@
|
||||
|
||||
// PARAM_DATA_TYPE
|
||||
#define PARAM_APP_KEY @"au" // Localytics Application ID
|
||||
#define PARAM_DEVICE_UUID @"du" // Device UUID
|
||||
#define PARAM_DEVICE_UUID_HASHED @"udid" // Hashed version of the UUID
|
||||
#define PARAM_DEVICE_MAC @"wmac" // Hashed version of the device Mac
|
||||
#define PARAM_DEVICE_ADID @"adid" // Advertising Identifier
|
||||
#define PARAM_INSTALL_ID @"iu" // Install ID
|
||||
#define PARAM_JAILBROKEN @"j" // Jailbroken (boolean)
|
||||
#define PARAM_LIBRARY_VERSION @"lv" // Client Version
|
||||
@@ -52,14 +58,11 @@
|
||||
#define PARAM_DEVICE_PLATFORM @"dp" // Device Platform
|
||||
#define PARAM_LOCALE_LANGUAGE @"dll" // Locale Language
|
||||
#define PARAM_LOCALE_COUNTRY @"dlc" // Locale Country
|
||||
#define PARAM_NETWORK_COUNTRY @"nc" // Network Country (iso code) // ???: Never used on iPhone.
|
||||
#define PARAM_DEVICE_COUNTRY @"dc" // Device Country (iso code)
|
||||
#define PARAM_DEVICE_MANUFACTURER @"dma" // Device Manufacturer // ???: Never used on iPhone. Used to be "Device Make".
|
||||
#define PARAM_DEVICE_MODEL @"dmo" // Device Model
|
||||
#define PARAM_DEVICE_OS_VERSION @"dov" // Device OS Version
|
||||
#define PARAM_NETWORK_CARRIER @"nca" // Network Carrier
|
||||
#define PARAM_DATA_CONNECTION @"dac" // Data Connection Type // ???: Never used on iPhone.
|
||||
#define PARAM_OPT_VALUE @"optin" // Opt In (boolean)
|
||||
#define PARAM_OPT_VALUE @"out" // Opt Out (boolean)
|
||||
#define PARAM_DEVICE_MEMORY @"dmem" // Device Memory
|
||||
|
||||
/*****************
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
DA350A0715F11F9400C14A8E /* pull-down@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA350A0315F11F9400C14A8E /* pull-down@2x.png */; };
|
||||
DA350A0815F11F9400C14A8E /* pull-up.png in Resources */ = {isa = PBXBuildFile; fileRef = DA350A0415F11F9400C14A8E /* pull-up.png */; };
|
||||
DA350A0915F11F9400C14A8E /* pull-up@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA350A0515F11F9400C14A8E /* pull-up@2x.png */; };
|
||||
DA3EE946160145C700C68F6D /* Default-568h.png in Resources */ = {isa = PBXBuildFile; fileRef = DA3EE944160145C700C68F6D /* Default-568h.png */; };
|
||||
DA3EE947160145C700C68F6D /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA3EE945160145C700C68F6D /* Default-568h@2x.png */; };
|
||||
DA3EF17B15A47744003ABF4E /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA3EF17A15A47744003ABF4E /* SenTestingKit.framework */; };
|
||||
DA3EF17C15A47744003ABF4E /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA48147E415C00F98B1E /* UIKit.framework */; };
|
||||
DA3EF17D15A47744003ABF4E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
|
||||
@@ -120,6 +122,13 @@
|
||||
DA6061D715F20C0900097266 /* page-deblock@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA6061D115F20C0900097266 /* page-deblock@2x.png */; };
|
||||
DA6061D815F20C0900097266 /* page-gorillas.png in Resources */ = {isa = PBXBuildFile; fileRef = DA6061D215F20C0900097266 /* page-gorillas.png */; };
|
||||
DA6061D915F20C0900097266 /* page-gorillas@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA6061D315F20C0900097266 /* page-gorillas@2x.png */; };
|
||||
DA609F581600CE980030AE31 /* LocalyticsDatabase.h in Headers */ = {isa = PBXBuildFile; fileRef = DA609F511600CE980030AE31 /* LocalyticsDatabase.h */; };
|
||||
DA609F591600CE980030AE31 /* LocalyticsDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = DA609F521600CE980030AE31 /* LocalyticsDatabase.m */; };
|
||||
DA609F5A1600CE980030AE31 /* LocalyticsSession.h in Headers */ = {isa = PBXBuildFile; fileRef = DA609F531600CE980030AE31 /* LocalyticsSession.h */; };
|
||||
DA609F5B1600CE980030AE31 /* LocalyticsSession.m in Sources */ = {isa = PBXBuildFile; fileRef = DA609F541600CE980030AE31 /* LocalyticsSession.m */; };
|
||||
DA609F5C1600CE980030AE31 /* LocalyticsUploader.h in Headers */ = {isa = PBXBuildFile; fileRef = DA609F551600CE980030AE31 /* LocalyticsUploader.h */; };
|
||||
DA609F5D1600CE980030AE31 /* LocalyticsUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = DA609F561600CE980030AE31 /* LocalyticsUploader.m */; };
|
||||
DA609F5E1600CE980030AE31 /* WebserviceConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = DA609F571600CE980030AE31 /* WebserviceConstants.h */; };
|
||||
DA672D2F14F92C6B004A189C /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DA672D2E14F92C6B004A189C /* libz.dylib */; };
|
||||
DA672D3014F9413D004A189C /* libPearl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC77CAD148291A600BCF976 /* libPearl.a */; };
|
||||
DA829E52159847E0002417D3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
|
||||
@@ -770,20 +779,11 @@
|
||||
DACABB8D1572A4A5008BA211 /* tip_basic_black_top_right@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DACABB8B1572A4A4008BA211 /* tip_basic_black_top_right@2x.png */; };
|
||||
DACABB901572B76A008BA211 /* tip_basic_black_top.png in Resources */ = {isa = PBXBuildFile; fileRef = DACABB8E1572B769008BA211 /* tip_basic_black_top.png */; };
|
||||
DACABB911572B76A008BA211 /* tip_basic_black_top@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DACABB8F1572B769008BA211 /* tip_basic_black_top@2x.png */; };
|
||||
DAD18EF4160A760600D15088 /* libTestFlight.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAD3126215528C9C00A3F9ED /* libTestFlight.a */; };
|
||||
DAD3126715528C9C00A3F9ED /* Crashlytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAD3125F15528C9C00A3F9ED /* Crashlytics.framework */; };
|
||||
DAD3126815528C9C00A3F9ED /* Crashlytics.plist in Resources */ = {isa = PBXBuildFile; fileRef = DAD3126015528C9C00A3F9ED /* Crashlytics.plist */; };
|
||||
DAD3126915528C9C00A3F9ED /* libTestFlight.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAD3126215528C9C00A3F9ED /* libTestFlight.a */; };
|
||||
DAD3126C15528C9C00A3F9ED /* TestFlight.plist in Resources */ = {isa = PBXBuildFile; fileRef = DAD3126615528C9C00A3F9ED /* TestFlight.plist */; };
|
||||
DAD3127215528CD200A3F9ED /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
|
||||
DAD3128715528D0F00A3F9ED /* LocalyticsDatabase.h in Headers */ = {isa = PBXBuildFile; fileRef = DAD3127E15528D0F00A3F9ED /* LocalyticsDatabase.h */; };
|
||||
DAD3128815528D0F00A3F9ED /* LocalyticsDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = DAD3127F15528D0F00A3F9ED /* LocalyticsDatabase.m */; };
|
||||
DAD3128915528D0F00A3F9ED /* LocalyticsSession.h in Headers */ = {isa = PBXBuildFile; fileRef = DAD3128015528D0F00A3F9ED /* LocalyticsSession.h */; };
|
||||
DAD3128A15528D0F00A3F9ED /* LocalyticsSession.m in Sources */ = {isa = PBXBuildFile; fileRef = DAD3128115528D0F00A3F9ED /* LocalyticsSession.m */; };
|
||||
DAD3128B15528D0F00A3F9ED /* LocalyticsUploader.h in Headers */ = {isa = PBXBuildFile; fileRef = DAD3128215528D0F00A3F9ED /* LocalyticsUploader.h */; };
|
||||
DAD3128C15528D0F00A3F9ED /* LocalyticsUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = DAD3128315528D0F00A3F9ED /* LocalyticsUploader.m */; };
|
||||
DAD3128D15528D0F00A3F9ED /* UploaderThread.h in Headers */ = {isa = PBXBuildFile; fileRef = DAD3128415528D0F00A3F9ED /* UploaderThread.h */; };
|
||||
DAD3128E15528D0F00A3F9ED /* UploaderThread.m in Sources */ = {isa = PBXBuildFile; fileRef = DAD3128515528D0F00A3F9ED /* UploaderThread.m */; };
|
||||
DAD3128F15528D0F00A3F9ED /* WebserviceConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = DAD3128615528D0F00A3F9ED /* WebserviceConstants.h */; };
|
||||
DAD3129015528D1600A3F9ED /* Localytics.plist in Resources */ = {isa = PBXBuildFile; fileRef = DAD3127D15528D0F00A3F9ED /* Localytics.plist */; };
|
||||
DAD312BB1552977200A3F9ED /* UIColor+Expanded.h in Headers */ = {isa = PBXBuildFile; fileRef = DAD312B71552977200A3F9ED /* UIColor+Expanded.h */; };
|
||||
DAD312BC1552977200A3F9ED /* UIColor+Expanded.m in Sources */ = {isa = PBXBuildFile; fileRef = DAD312B81552977200A3F9ED /* UIColor+Expanded.m */; };
|
||||
@@ -1022,6 +1022,8 @@
|
||||
DA350A0315F11F9400C14A8E /* pull-down@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "pull-down@2x.png"; sourceTree = "<group>"; };
|
||||
DA350A0415F11F9400C14A8E /* pull-up.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "pull-up.png"; sourceTree = "<group>"; };
|
||||
DA350A0515F11F9400C14A8E /* pull-up@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "pull-up@2x.png"; sourceTree = "<group>"; };
|
||||
DA3EE944160145C700C68F6D /* Default-568h.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h.png"; sourceTree = "<group>"; };
|
||||
DA3EE945160145C700C68F6D /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = "<group>"; };
|
||||
DA3EF17915A47744003ABF4E /* Tests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.octest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DA3EF17A15A47744003ABF4E /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; };
|
||||
DA3EF18015A47744003ABF4E /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = "<group>"; };
|
||||
@@ -1180,6 +1182,13 @@
|
||||
DA6061D115F20C0900097266 /* page-deblock@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "page-deblock@2x.png"; sourceTree = "<group>"; };
|
||||
DA6061D215F20C0900097266 /* page-gorillas.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "page-gorillas.png"; sourceTree = "<group>"; };
|
||||
DA6061D315F20C0900097266 /* page-gorillas@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "page-gorillas@2x.png"; sourceTree = "<group>"; };
|
||||
DA609F511600CE980030AE31 /* LocalyticsDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LocalyticsDatabase.h; sourceTree = "<group>"; };
|
||||
DA609F521600CE980030AE31 /* LocalyticsDatabase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LocalyticsDatabase.m; sourceTree = "<group>"; };
|
||||
DA609F531600CE980030AE31 /* LocalyticsSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LocalyticsSession.h; sourceTree = "<group>"; };
|
||||
DA609F541600CE980030AE31 /* LocalyticsSession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LocalyticsSession.m; sourceTree = "<group>"; };
|
||||
DA609F551600CE980030AE31 /* LocalyticsUploader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LocalyticsUploader.h; sourceTree = "<group>"; };
|
||||
DA609F561600CE980030AE31 /* LocalyticsUploader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LocalyticsUploader.m; sourceTree = "<group>"; };
|
||||
DA609F571600CE980030AE31 /* WebserviceConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebserviceConstants.h; sourceTree = "<group>"; };
|
||||
DA672D2E14F92C6B004A189C /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
|
||||
DA79A9BB1557DB6F00BAA07A /* libscryptenc-ios.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libscryptenc-ios.a"; sourceTree = "<group>"; };
|
||||
DA79A9BD1557DDC700BAA07A /* scrypt.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = scrypt.xcodeproj; path = External/Pearl/External/iOSPorts/ports/security/scrypt/scrypt.xcodeproj; sourceTree = "<group>"; };
|
||||
@@ -1919,15 +1928,6 @@
|
||||
DAD3126615528C9C00A3F9ED /* TestFlight.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = TestFlight.plist; sourceTree = "<group>"; };
|
||||
DAD3127115528CD200A3F9ED /* libLocalytics.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libLocalytics.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DAD3127D15528D0F00A3F9ED /* Localytics.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Localytics.plist; sourceTree = "<group>"; };
|
||||
DAD3127E15528D0F00A3F9ED /* LocalyticsDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LocalyticsDatabase.h; sourceTree = "<group>"; };
|
||||
DAD3127F15528D0F00A3F9ED /* LocalyticsDatabase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LocalyticsDatabase.m; sourceTree = "<group>"; };
|
||||
DAD3128015528D0F00A3F9ED /* LocalyticsSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LocalyticsSession.h; sourceTree = "<group>"; };
|
||||
DAD3128115528D0F00A3F9ED /* LocalyticsSession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LocalyticsSession.m; sourceTree = "<group>"; };
|
||||
DAD3128215528D0F00A3F9ED /* LocalyticsUploader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LocalyticsUploader.h; sourceTree = "<group>"; };
|
||||
DAD3128315528D0F00A3F9ED /* LocalyticsUploader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LocalyticsUploader.m; sourceTree = "<group>"; };
|
||||
DAD3128415528D0F00A3F9ED /* UploaderThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UploaderThread.h; sourceTree = "<group>"; };
|
||||
DAD3128515528D0F00A3F9ED /* UploaderThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UploaderThread.m; sourceTree = "<group>"; };
|
||||
DAD3128615528D0F00A3F9ED /* WebserviceConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebserviceConstants.h; sourceTree = "<group>"; };
|
||||
DAD312B71552977200A3F9ED /* UIColor+Expanded.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIColor+Expanded.h"; path = "External/Pearl/External/uicolor-utilities/UIColor+Expanded.h"; sourceTree = SOURCE_ROOT; };
|
||||
DAD312B81552977200A3F9ED /* UIColor+Expanded.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIColor+Expanded.m"; path = "External/Pearl/External/uicolor-utilities/UIColor+Expanded.m"; sourceTree = SOURCE_ROOT; };
|
||||
DAD312B91552977200A3F9ED /* UIColor+HSV.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIColor+HSV.h"; path = "External/Pearl/External/uicolor-utilities/UIColor+HSV.h"; sourceTree = SOURCE_ROOT; };
|
||||
@@ -2108,9 +2108,9 @@
|
||||
DA5BFA4D147E415C00F98B1E /* CoreGraphics.framework in Frameworks */,
|
||||
DA5BFA4F147E415C00F98B1E /* CoreData.framework in Frameworks */,
|
||||
DAD3126715528C9C00A3F9ED /* Crashlytics.framework in Frameworks */,
|
||||
DAD3126915528C9C00A3F9ED /* libTestFlight.a in Frameworks */,
|
||||
93D399433EA75E50656040CB /* Twitter.framework in Frameworks */,
|
||||
DA55878015E82C0300860B4F /* FacebookSDK.framework in Frameworks */,
|
||||
DAD18EF4160A760600D15088 /* libTestFlight.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -2168,6 +2168,16 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
DA3EE95B1601C06000C68F6D /* Facebook */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA5587B715E838CD00860B4F /* DeprecatedHeaders */,
|
||||
DA55878115E82C2B00860B4F /* FacebookSDKResources.bundle */,
|
||||
DA55877F15E82C0300860B4F /* FacebookSDK.framework */,
|
||||
);
|
||||
name = Facebook;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DA3EF17E15A47744003ABF4E /* Tests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -2377,6 +2387,7 @@
|
||||
DAD3126115528C9C00A3F9ED /* TestFlight */,
|
||||
DAD3127315528CD200A3F9ED /* Localytics */,
|
||||
DA5587F715E8B7B200860B4F /* Google+ */,
|
||||
DA3EE95B1601C06000C68F6D /* Facebook */,
|
||||
DA4425D71557BF260052177D /* iCloudStoreManager */,
|
||||
DA829E5D15984812002417D3 /* FontReplacer */,
|
||||
DA3EF17E15A47744003ABF4E /* Tests */,
|
||||
@@ -2405,9 +2416,6 @@
|
||||
DA5BFA47147E415C00F98B1E /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA5587B715E838CD00860B4F /* DeprecatedHeaders */,
|
||||
DA55878115E82C2B00860B4F /* FacebookSDKResources.bundle */,
|
||||
DA55877F15E82C0300860B4F /* FacebookSDK.framework */,
|
||||
93D394077F8FAB8167647187 /* Twitter.framework */,
|
||||
DAAC35DD156BD77D00C5FD93 /* CoreTelephony.framework */,
|
||||
DAD312C01552A20800A3F9ED /* libsqlite3.dylib */,
|
||||
@@ -2639,6 +2647,8 @@
|
||||
DAB8D46F15036BF600CED3BC /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA3EE944160145C700C68F6D /* Default-568h.png */,
|
||||
DA3EE945160145C700C68F6D /* Default-568h@2x.png */,
|
||||
DA6061CE15F20C0900097266 /* book.png */,
|
||||
DA6061CF15F20C0900097266 /* book@2x.png */,
|
||||
DA6061D015F20C0900097266 /* page-deblock.png */,
|
||||
@@ -3473,16 +3483,14 @@
|
||||
DAD3127315528CD200A3F9ED /* Localytics */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA609F511600CE980030AE31 /* LocalyticsDatabase.h */,
|
||||
DA609F521600CE980030AE31 /* LocalyticsDatabase.m */,
|
||||
DA609F531600CE980030AE31 /* LocalyticsSession.h */,
|
||||
DA609F541600CE980030AE31 /* LocalyticsSession.m */,
|
||||
DA609F551600CE980030AE31 /* LocalyticsUploader.h */,
|
||||
DA609F561600CE980030AE31 /* LocalyticsUploader.m */,
|
||||
DA609F571600CE980030AE31 /* WebserviceConstants.h */,
|
||||
DAD3127D15528D0F00A3F9ED /* Localytics.plist */,
|
||||
DAD3127E15528D0F00A3F9ED /* LocalyticsDatabase.h */,
|
||||
DAD3127F15528D0F00A3F9ED /* LocalyticsDatabase.m */,
|
||||
DAD3128015528D0F00A3F9ED /* LocalyticsSession.h */,
|
||||
DAD3128115528D0F00A3F9ED /* LocalyticsSession.m */,
|
||||
DAD3128215528D0F00A3F9ED /* LocalyticsUploader.h */,
|
||||
DAD3128315528D0F00A3F9ED /* LocalyticsUploader.m */,
|
||||
DAD3128415528D0F00A3F9ED /* UploaderThread.h */,
|
||||
DAD3128515528D0F00A3F9ED /* UploaderThread.m */,
|
||||
DAD3128615528D0F00A3F9ED /* WebserviceConstants.h */,
|
||||
);
|
||||
path = Localytics;
|
||||
sourceTree = "<group>";
|
||||
@@ -3743,11 +3751,10 @@
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DAD3128715528D0F00A3F9ED /* LocalyticsDatabase.h in Headers */,
|
||||
DAD3128915528D0F00A3F9ED /* LocalyticsSession.h in Headers */,
|
||||
DAD3128B15528D0F00A3F9ED /* LocalyticsUploader.h in Headers */,
|
||||
DAD3128D15528D0F00A3F9ED /* UploaderThread.h in Headers */,
|
||||
DAD3128F15528D0F00A3F9ED /* WebserviceConstants.h in Headers */,
|
||||
DA609F581600CE980030AE31 /* LocalyticsDatabase.h in Headers */,
|
||||
DA609F5A1600CE980030AE31 /* LocalyticsSession.h in Headers */,
|
||||
DA609F5C1600CE980030AE31 /* LocalyticsUploader.h in Headers */,
|
||||
DA609F5E1600CE980030AE31 /* WebserviceConstants.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -4678,6 +4685,8 @@
|
||||
DA6061D715F20C0900097266 /* page-deblock@2x.png in Resources */,
|
||||
DA6061D815F20C0900097266 /* page-gorillas.png in Resources */,
|
||||
DA6061D915F20C0900097266 /* page-gorillas@2x.png in Resources */,
|
||||
DA3EE946160145C700C68F6D /* Default-568h.png in Resources */,
|
||||
DA3EE947160145C700C68F6D /* Default-568h@2x.png in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -4703,13 +4712,15 @@
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"$(SRCROOT)/External/facebook-ios-sdk/src",
|
||||
);
|
||||
name = "Run Script: FacebookSDK";
|
||||
outputPaths = (
|
||||
"$(SRCROOT)/External/facebook-ios-sdk/build",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "[ -d External/facebook-ios-sdk/build ] || env -i PATH=\"$PATH\" ./External/facebook-ios-sdk/scripts/build_framework.sh";
|
||||
shellScript = "env -i PATH=\"$PATH\" ./External/facebook-ios-sdk/scripts/build_framework.sh";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
DA6556E314D55F3000841C99 /* Run Script: GIT version -> Info.plist */ = {
|
||||
@@ -4935,10 +4946,9 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DAD3128815528D0F00A3F9ED /* LocalyticsDatabase.m in Sources */,
|
||||
DAD3128A15528D0F00A3F9ED /* LocalyticsSession.m in Sources */,
|
||||
DAD3128C15528D0F00A3F9ED /* LocalyticsUploader.m in Sources */,
|
||||
DAD3128E15528D0F00A3F9ED /* UploaderThread.m in Sources */,
|
||||
DA609F591600CE980030AE31 /* LocalyticsDatabase.m in Sources */,
|
||||
DA609F5B1600CE980030AE31 /* LocalyticsSession.m in Sources */,
|
||||
DA609F5D1600CE980030AE31 /* LocalyticsUploader.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -5138,7 +5148,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
|
||||
ARCHS = armv7;
|
||||
CLANG_WARN_CXX0X_EXTENSIONS = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
|
||||
@@ -5199,7 +5209,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
|
||||
ARCHS = armv7;
|
||||
CLANG_WARN_CXX0X_EXTENSIONS = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
|
||||
@@ -5208,7 +5218,7 @@
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: Maarten Billemont";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
COPY_PHASE_STRIP = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
@@ -5248,7 +5258,7 @@
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "${TARGET_NAME}";
|
||||
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "1C557CB8-083F-4EFE-A88E-41171ADF7611";
|
||||
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
@@ -5285,6 +5295,7 @@
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = MasterPassword/iOS/MasterPassword.entitlements;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: Maarten Billemont";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(SRCROOT)/Crashlytics\"",
|
||||
@@ -5299,6 +5310,7 @@
|
||||
"\"$(SRCROOT)/External/Pearl/Pearl-Crypto\"",
|
||||
"\"$(SRCROOT)/External/google-plus-ios-sdk/lib\"",
|
||||
);
|
||||
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "DC0CD5E6-3ADC-40FF-8A6D-02BA6D8F5039";
|
||||
SKIP_INSTALL = NO;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
};
|
||||
@@ -5351,7 +5363,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
|
||||
ARCHS = armv7;
|
||||
CLANG_WARN_CXX0X_EXTENSIONS = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
|
||||
@@ -5360,7 +5372,7 @@
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: Maarten Billemont";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
COPY_PHASE_STRIP = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
@@ -5400,7 +5412,7 @@
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "${TARGET_NAME}";
|
||||
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "1C557CB8-083F-4EFE-A88E-41171ADF7611";
|
||||
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
@@ -5413,6 +5425,7 @@
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = MasterPassword/iOS/MasterPassword.entitlements;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: Maarten Billemont";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(SRCROOT)/Crashlytics\"",
|
||||
@@ -5427,6 +5440,7 @@
|
||||
"\"$(SRCROOT)/External/Pearl/Pearl-Crypto\"",
|
||||
"\"$(SRCROOT)/External/google-plus-ios-sdk/lib\"",
|
||||
);
|
||||
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "3EF104A5-610A-429E-ACF0-232A2C8A7E1C";
|
||||
SKIP_INSTALL = NO;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
};
|
||||
@@ -5572,6 +5586,7 @@
|
||||
DAD3127A15528CD200A3F9ED /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_WARN_OBJCPP_ARC_ABI = NO;
|
||||
GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
|
||||
};
|
||||
name = Debug;
|
||||
@@ -5579,6 +5594,7 @@
|
||||
DAD3127B15528CD200A3F9ED /* AdHoc */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_WARN_OBJCPP_ARC_ABI = NO;
|
||||
GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
|
||||
};
|
||||
name = AdHoc;
|
||||
@@ -5586,6 +5602,7 @@
|
||||
DAD3127C15528CD200A3F9ED /* AppStore */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_WARN_OBJCPP_ARC_ABI = NO;
|
||||
GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
|
||||
};
|
||||
name = AppStore;
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Uncomment this to set your user name. Master Password will no longer ask you for a user name.
|
||||
# export MP_USERNAME="Robert Lee Mitchell"
|
||||
# Uncomment this to hardcode your master password. Make sure this file's permissions are tight. Master Password will not ask you for your master password anymore. This is probably not a good idea.
|
||||
# export MP_PASSWORD="banana colored duckling"
|
||||
|
||||
cd "${BASH_SOURCE[0]%/*}"
|
||||
java -jar masterpassword-cli-GIT-SNAPSHOT.jar "$@"
|
||||
|
||||
@@ -16,9 +16,11 @@
|
||||
static NSDictionary *keyQuery(MPUserEntity *user) {
|
||||
|
||||
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword
|
||||
attributes:@{(__bridge id)kSecAttrService: @"Saved Master Password",
|
||||
(__bridge id)kSecAttrAccount: user.name}
|
||||
matches:nil];
|
||||
attributes:@{
|
||||
(__bridge id)kSecAttrService: @"Saved Master Password",
|
||||
(__bridge id)kSecAttrAccount: IfNotNilElse(user.name, @"")
|
||||
}
|
||||
matches:nil];
|
||||
}
|
||||
|
||||
- (MPKey *)loadSavedKeyFor:(MPUserEntity *)user {
|
||||
@@ -28,7 +30,9 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
||||
inf(@"Found key in keychain for: %@", user.userID);
|
||||
|
||||
else {
|
||||
user.saveKey = NO;
|
||||
[user.managedObjectContext performBlockAndWait:^{
|
||||
user.saveKey = NO;
|
||||
}];
|
||||
inf(@"No key found in keychain for: %@", user.userID);
|
||||
}
|
||||
|
||||
@@ -58,7 +62,9 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
||||
|
||||
OSStatus result = [PearlKeyChain deleteItemForQuery:keyQuery(user)];
|
||||
if (result == noErr || result == errSecItemNotFound) {
|
||||
user.saveKey = NO;
|
||||
[user.managedObjectContext performBlockAndWait:^{
|
||||
user.saveKey = NO;
|
||||
}];
|
||||
|
||||
if (result == noErr) {
|
||||
inf(@"Removed key from keychain for: %@", user.userID);
|
||||
@@ -82,14 +88,68 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
||||
|
||||
- (BOOL)signInAsUser:(MPUserEntity *)user usingMasterPassword:(NSString *)password {
|
||||
|
||||
assert(!password || ![NSThread isMainThread]); // If we need to computing a key, this operation shouldn't be on the main thread.
|
||||
MPKey *tryKey = nil;
|
||||
|
||||
// Method 1: When the user has no keyID set, set a new key from the given master password.
|
||||
if (!user.keyID) {
|
||||
if ([password length])
|
||||
if ((tryKey = [MPAlgorithmDefault keyForPassword:password ofUserNamed:user.name])) {
|
||||
user.keyID = tryKey.keyID;
|
||||
[user.managedObjectContext performBlockAndWait:^{
|
||||
user.keyID = tryKey.keyID;
|
||||
}];
|
||||
|
||||
// Migrate existing elements.
|
||||
MPKey *recoverKey = nil;
|
||||
PearlAlert *activityAlert = [PearlAlert showActivityWithTitle:PearlString(@"Migrating %d sites...", [user.elements count])];
|
||||
|
||||
for (MPElementEntity *element in user.elements) {
|
||||
if (element.type & MPElementTypeClassStored && ![element contentUsingKey:tryKey]) {
|
||||
id content = nil;
|
||||
if (recoverKey)
|
||||
content = [element contentUsingKey:recoverKey];
|
||||
|
||||
while (!content) {
|
||||
__block NSString *masterPassword = nil;
|
||||
dispatch_group_t recoverPasswordGroup = dispatch_group_create();
|
||||
dispatch_group_enter(recoverPasswordGroup);
|
||||
[PearlAlert showAlertWithTitle:@"Enter Old Master Password"
|
||||
message:PearlString(@"Your old master password is required to migrate the stored password for %@", element.name)
|
||||
viewStyle:UIAlertViewStyleSecureTextInput
|
||||
initAlert:nil
|
||||
tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
|
||||
@try {
|
||||
if (buttonIndex_ == [alert_ cancelButtonIndex])
|
||||
// Don't Migrate
|
||||
return;
|
||||
|
||||
masterPassword = [alert_ textFieldAtIndex:0].text;
|
||||
}
|
||||
@finally {
|
||||
dispatch_group_leave(recoverPasswordGroup);
|
||||
}
|
||||
} cancelTitle:@"Don't Migrate" otherTitles:@"Migrate", nil];
|
||||
dispatch_group_wait(recoverPasswordGroup, DISPATCH_TIME_FOREVER);
|
||||
|
||||
if (!masterPassword)
|
||||
// Don't Migrate
|
||||
break;
|
||||
|
||||
recoverKey = [element.algorithm keyForPassword:masterPassword ofUserNamed:user.name];
|
||||
content = [element contentUsingKey:recoverKey];
|
||||
}
|
||||
|
||||
if (!content)
|
||||
// Don't Migrate
|
||||
break;
|
||||
|
||||
[element.managedObjectContext performBlockAndWait:^{
|
||||
[element setContent:content usingKey:tryKey];
|
||||
}];
|
||||
}
|
||||
}
|
||||
[[MPAppDelegate_Shared get] saveContext];
|
||||
[activityAlert dismissAlert];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +187,9 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
||||
if (password) {
|
||||
inf(@"Login failed for: %@", user.userID);
|
||||
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight passCheckpoint:MPCheckpointSignInFailed];
|
||||
#endif
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointSignInFailed attributes:nil];
|
||||
}
|
||||
|
||||
@@ -142,7 +204,9 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
||||
|
||||
@try {
|
||||
if ([[MPiOSConfig get].sendInfo boolValue]) {
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight addCustomEnvironmentInformation:user.userID forKey:@"username"];
|
||||
#endif
|
||||
[Crashlytics setObjectValue:user.userID forKey:@"username"];
|
||||
[Crashlytics setUserName:user.userID];
|
||||
}
|
||||
@@ -152,13 +216,17 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
|
||||
}
|
||||
|
||||
|
||||
user.lastUsed = [NSDate date];
|
||||
self.activeUser = user;
|
||||
self.activeUser.requiresExplicitMigration = NO;
|
||||
[user.managedObjectContext performBlockAndWait:^{
|
||||
user.lastUsed = [NSDate date];
|
||||
self.activeUser = user;
|
||||
self.activeUser.requiresExplicitMigration = NO;
|
||||
}];
|
||||
[[MPAppDelegate_Shared get] saveContext];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationSignedIn object:self];
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight passCheckpoint:MPCheckpointSignedIn];
|
||||
#endif
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointSignedIn attributes:nil];
|
||||
|
||||
return YES;
|
||||
|
||||
@@ -43,14 +43,11 @@
|
||||
[managedObjectContext performBlockAndWait:^{
|
||||
managedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
|
||||
managedObjectContext.undoManager = [NSUndoManager new];
|
||||
managedObjectContext.persistentStoreCoordinator = self.storeManager.persistentStoreCoordinator;
|
||||
}];
|
||||
}
|
||||
|
||||
if (![managedObjectContext.persistentStoreCoordinator.persistentStores count])
|
||||
[managedObjectContext performBlockAndWait:^{
|
||||
managedObjectContext.persistentStoreCoordinator = [self storeManager].persistentStoreCoordinator;
|
||||
}];
|
||||
|
||||
[[self storeManager] persistentStoreCoordinator];
|
||||
if (![self storeManager].isReady)
|
||||
return nil;
|
||||
|
||||
@@ -136,7 +133,9 @@
|
||||
iCloudEnabled = manager.iCloudEnabled;
|
||||
inf(@"Using iCloud? %@", iCloudEnabled? @"YES": @"NO");
|
||||
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight passCheckpoint:iCloudEnabled? MPCheckpointCloudEnabled: MPCheckpointCloudDisabled];
|
||||
#endif
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointCloud attributes:@{
|
||||
@"enabled": iCloudEnabled? @"YES": @"NO"
|
||||
}];
|
||||
@@ -149,7 +148,9 @@
|
||||
|
||||
err(@"StoreManager: cause=%d, context=%@, error=%@", cause, context, error);
|
||||
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight passCheckpoint:PearlString(MPCheckpointMPErrorUbiquity @"_%d", cause)];
|
||||
#endif
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointMPErrorUbiquity attributes:@{
|
||||
@"cause": @(cause),
|
||||
@"error.domain": error.domain,
|
||||
@@ -168,7 +169,9 @@
|
||||
if (error.code == NSMigrationMissingSourceModelError) {
|
||||
wrn(@"Resetting the local store.");
|
||||
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight passCheckpoint:MPCheckpointLocalStoreReset];
|
||||
#endif
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointLocalStoreReset attributes:nil];
|
||||
manager.hardResetEnabled = YES;
|
||||
[manager hardResetLocalStorage];
|
||||
@@ -184,7 +187,9 @@
|
||||
if (error.code == NSMigrationMissingSourceModelError) {
|
||||
wrn(@"Resetting the iCloud store.");
|
||||
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight passCheckpoint:MPCheckpointCloudStoreReset];
|
||||
#endif
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointCloudStoreReset attributes:nil];
|
||||
manager.hardResetEnabled = YES;
|
||||
[manager hardResetCloudStorage];
|
||||
@@ -416,7 +421,9 @@
|
||||
[self saveContext];
|
||||
success = YES;
|
||||
inf(@"Import completed successfully.");
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight passCheckpoint:MPCheckpointSitesImported];
|
||||
#endif
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointSitesImported attributes:nil];
|
||||
|
||||
return MPImportResultSuccess;
|
||||
@@ -479,7 +486,9 @@
|
||||
? content: @""];
|
||||
}
|
||||
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight passCheckpoint:MPCheckpointSitesExported];
|
||||
#endif
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointSitesExported attributes:nil];
|
||||
|
||||
return export;
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
@property (assign) BOOL requiresExplicitMigration;
|
||||
@property (readonly) id<MPAlgorithm> algorithm;
|
||||
|
||||
- (id)contentUsingKey:(MPKey *)key;
|
||||
- (void)setContent:(id)content usingKey:(MPKey *)key;
|
||||
|
||||
- (NSUInteger)use;
|
||||
- (NSString *)exportContent;
|
||||
- (void)importProtectedContent:(NSString *)protectedContent protectedByKey:(MPKey *)contentProtectionKey usingKey:(MPKey *)key2;
|
||||
|
||||
@@ -84,7 +84,32 @@
|
||||
|
||||
- (id)content {
|
||||
|
||||
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Content implementation missing." userInfo:nil];
|
||||
MPKey *key = [MPAppDelegate get].key;
|
||||
if (!key)
|
||||
return nil;
|
||||
|
||||
assert([key.keyID isEqualToData:self.user.keyID]);
|
||||
return [self contentUsingKey:key];
|
||||
}
|
||||
|
||||
- (void)setContent:(id)content {
|
||||
|
||||
MPKey *key = [MPAppDelegate get].key;
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
assert([key.keyID isEqualToData:self.user.keyID]);
|
||||
[self setContent:content usingKey:key];
|
||||
}
|
||||
|
||||
- (id)contentUsingKey:(MPKey *)key {
|
||||
|
||||
Throw(@"Content retrieval implementation missing for: %@", [self class]);
|
||||
}
|
||||
|
||||
- (void)setContent:(id)content usingKey:(MPKey *)key {
|
||||
|
||||
Throw(@"Content assignment implementation missing for: %@", [self class]);
|
||||
}
|
||||
|
||||
- (NSString *)exportContent {
|
||||
@@ -139,11 +164,7 @@
|
||||
self.counter_ = @(aCounter);
|
||||
}
|
||||
|
||||
- (id)content {
|
||||
|
||||
MPKey *key = [MPAppDelegate get].key;
|
||||
if (!key)
|
||||
return nil;
|
||||
- (id)contentUsingKey:(MPKey *)key {
|
||||
|
||||
if (!(self.type & MPElementTypeClassGenerated)) {
|
||||
err(@"Corrupt element: %@, type: %d is not in MPElementTypeClassGenerated", self.name, self.type);
|
||||
@@ -156,6 +177,7 @@
|
||||
return [self.algorithm generateContentForElement:self usingKey:key];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPElementStoredEntity (MP)
|
||||
@@ -168,28 +190,9 @@
|
||||
matches:nil];
|
||||
}
|
||||
|
||||
- (id)content {
|
||||
|
||||
MPKey *key = [MPAppDelegate get].key;
|
||||
if (!key)
|
||||
return nil;
|
||||
|
||||
return [self contentUsingKey:key];
|
||||
}
|
||||
|
||||
- (void)setContent:(id)content {
|
||||
|
||||
MPKey *key = [MPAppDelegate get].key;
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
[self setContent:content usingKey:key];
|
||||
}
|
||||
|
||||
- (id)contentUsingKey:(MPKey *)key {
|
||||
|
||||
assert(self.type & MPElementTypeClassStored);
|
||||
assert([key.keyID isEqualToData:self.user.keyID]);
|
||||
|
||||
NSData *encryptedContent;
|
||||
if (self.type & MPElementFeatureDevicePrivate)
|
||||
@@ -201,6 +204,9 @@
|
||||
if ([encryptedContent length])
|
||||
decryptedContent = [self decryptContent:encryptedContent usingKey:key];
|
||||
|
||||
if (!decryptedContent)
|
||||
return nil;
|
||||
|
||||
return [[NSString alloc] initWithBytes:decryptedContent.bytes length:decryptedContent.length encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1487" systemVersion="12B19" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
|
||||
<model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1810" systemVersion="12B19" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
|
||||
<entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" syncable="YES">
|
||||
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
|
||||
<attribute name="lastUsed" attributeType="Date" indexed="YES" syncable="YES"/>
|
||||
<attribute name="loginName" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="loginName" optional="YES" attributeType="String" elementID="A1B9F981-D33C-4BFE-9F94-C9D3E1F78E51" syncable="YES"/>
|
||||
<attribute name="name" attributeType="String" minValueString="1" indexed="YES" syncable="YES"/>
|
||||
<attribute name="requiresExplicitMigration_" attributeType="Boolean" defaultValueString="NO">
|
||||
<userInfo/>
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
// Copyright (c) 2011 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wnewline-eof"
|
||||
#import <FacebookSDK/FacebookSDK.h>
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
#import "MPAppDelegate.h"
|
||||
#import "MPAppDelegate_Key.h"
|
||||
@@ -51,19 +54,22 @@
|
||||
[[[NSBundle mainBundle] mutableInfoDictionary] setObject:@"Master Password" forKey:@"CFBundleDisplayName"];
|
||||
[[[NSBundle mainBundle] mutableLocalizedInfoDictionary] setObject:@"Master Password" forKey:@"CFBundleDisplayName"];
|
||||
|
||||
#ifdef ADHOC
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
@try {
|
||||
NSString *testFlightToken = [self testFlightToken];
|
||||
if ([testFlightToken length]) {
|
||||
inf(@"Initializing TestFlight");
|
||||
#ifdef ADHOC
|
||||
[TestFlight setDeviceIdentifier:[(id)[UIDevice currentDevice] uniqueIdentifier]];
|
||||
//[TestFlight setDeviceIdentifier[PearlKeyChain deviceIdentifier]];
|
||||
#else
|
||||
[TestFlight setDeviceIdentifier:[PearlKeyChain deviceIdentifier]];
|
||||
#endif
|
||||
[TestFlight addCustomEnvironmentInformation:@"Anonymous" forKey:@"username"];
|
||||
[TestFlight addCustomEnvironmentInformation:[PearlKeyChain deviceIdentifier] forKey:@"deviceIdentifier"];
|
||||
[TestFlight setOptions:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSNumber numberWithBool:NO], @"logToConsole",
|
||||
[NSNumber numberWithBool:NO], @"logToSTDERR",
|
||||
nil]];
|
||||
[NSNumber numberWithBool:NO], @"logToConsole",
|
||||
[NSNumber numberWithBool:NO], @"logToSTDERR",
|
||||
nil]];
|
||||
[TestFlight takeOff:testFlightToken];
|
||||
[[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) {
|
||||
PearlLogLevel level = PearlLogLevelWarn;
|
||||
@@ -116,7 +122,10 @@
|
||||
NSString *localyticsKey = [self localyticsKey];
|
||||
if ([localyticsKey length]) {
|
||||
inf(@"Initializing Localytics");
|
||||
[[LocalyticsSession sharedLocalyticsSession] startSession:localyticsKey];
|
||||
[[LocalyticsSession sharedLocalyticsSession] LocalyticsSession:localyticsKey];
|
||||
[[LocalyticsSession sharedLocalyticsSession] open];
|
||||
[LocalyticsSession sharedLocalyticsSession].enableHTTPS = YES;
|
||||
[[LocalyticsSession sharedLocalyticsSession] upload];
|
||||
[[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) {
|
||||
if (message.level >= PearlLogLevelWarn)
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:@"Problem"
|
||||
@@ -241,15 +250,7 @@
|
||||
if (!importedSitesData)
|
||||
return;
|
||||
|
||||
PearlAlert *activityAlert = [PearlAlert showAlertWithTitle:@"Importing" message:@"\n\n"
|
||||
viewStyle:UIAlertViewStyleDefault initAlert:
|
||||
^(UIAlertView *alert, UITextField *firstField) {
|
||||
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
|
||||
activityIndicator.center = CGPointMake(140, 90);
|
||||
[activityIndicator startAnimating];
|
||||
[alert addSubview:activityIndicator];
|
||||
}
|
||||
tappedButtonBlock:nil cancelTitle:nil otherTitles:nil];
|
||||
PearlAlert *activityAlert = [PearlAlert showActivityWithTitle:@"Importing"];
|
||||
|
||||
NSString *importedSitesString = [[NSString alloc] initWithData:importedSitesData encoding:NSUTF8StringEncoding];
|
||||
MPImportResult result = [self importSites:importedSitesString askImportPassword:^NSString *(NSString *userName) {
|
||||
@@ -329,23 +330,11 @@
|
||||
|
||||
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
|
||||
|
||||
wrn(@"Received memory warning.");
|
||||
inf(@"Received memory warning.");
|
||||
|
||||
[super applicationDidReceiveMemoryWarning:application];
|
||||
}
|
||||
|
||||
- (void)applicationDidBecomeActive:(UIApplication *)application {
|
||||
|
||||
inf(@"Re-activated");
|
||||
[[MPAppDelegate get] checkConfig];
|
||||
|
||||
if (FBSession.activeSession.state == FBSessionStateCreatedOpening)
|
||||
// An old Facebook Login session that wasn't finished. Clean it up.
|
||||
[FBSession.activeSession close];
|
||||
|
||||
[super applicationDidBecomeActive:application];
|
||||
}
|
||||
|
||||
- (void)applicationDidEnterBackground:(UIApplication *)application {
|
||||
|
||||
[[LocalyticsSession sharedLocalyticsSession] close];
|
||||
@@ -377,10 +366,31 @@
|
||||
- (void)applicationWillResignActive:(UIApplication *)application {
|
||||
|
||||
inf(@"Will deactivate");
|
||||
|
||||
[self saveContext];
|
||||
|
||||
if (![[MPiOSConfig get].rememberLogin boolValue])
|
||||
[self signOutAnimated:NO];
|
||||
|
||||
[[LocalyticsSession sharedLocalyticsSession] close];
|
||||
[[LocalyticsSession sharedLocalyticsSession] upload];
|
||||
|
||||
[super applicationWillResignActive:application];
|
||||
}
|
||||
|
||||
- (void)applicationDidBecomeActive:(UIApplication *)application {
|
||||
|
||||
inf(@"Re-activated");
|
||||
[[MPAppDelegate get] checkConfig];
|
||||
|
||||
if (FBSession.activeSession.state == FBSessionStateCreatedOpening)
|
||||
// An old Facebook Login session that wasn't finished. Clean it up.
|
||||
[FBSession.activeSession close];
|
||||
|
||||
[[LocalyticsSession sharedLocalyticsSession] resume];
|
||||
[[LocalyticsSession sharedLocalyticsSession] upload];
|
||||
|
||||
[super applicationDidBecomeActive:application];
|
||||
}
|
||||
|
||||
#pragma mark - Behavior
|
||||
@@ -405,6 +415,7 @@
|
||||
[[Crashlytics sharedInstance] setIntValue:[[PearlConfig get].reviewAfterLaunches intValue] forKey:@"reviewAfterLaunches"];
|
||||
[[Crashlytics sharedInstance] setObjectValue:[PearlConfig get].reviewedVersion forKey:@"reviewedVersion"];
|
||||
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight addCustomEnvironmentInformation:[[MPConfig get].rememberLogin boolValue]? @"YES": @"NO" forKey:@"rememberLogin"];
|
||||
[TestFlight addCustomEnvironmentInformation:[[MPConfig get].iCloud boolValue]? @"YES": @"NO" forKey:@"iCloud"];
|
||||
[TestFlight addCustomEnvironmentInformation:[[MPConfig get].iCloudDecided boolValue]? @"YES": @"NO" forKey:@"iCloudDecided"];
|
||||
@@ -418,28 +429,20 @@
|
||||
[TestFlight addCustomEnvironmentInformation:[PearlConfig get].reviewedVersion forKey:@"reviewedVersion"];
|
||||
|
||||
[TestFlight passCheckpoint:MPCheckpointConfig];
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointConfig attributes:
|
||||
@{
|
||||
@"rememberLogin": [[MPConfig get].rememberLogin boolValue]
|
||||
? @"YES": @"NO",
|
||||
@"iCloud": [[MPConfig get].iCloud boolValue]? @"YES"
|
||||
: @"NO",
|
||||
@"iCloudDecided": [[MPConfig get].iCloudDecided boolValue]
|
||||
? @"YES": @"NO",
|
||||
@"sendInfo": [[MPiOSConfig get].sendInfo boolValue]
|
||||
? @"YES": @"NO",
|
||||
@"helpHidden": [[MPiOSConfig get].helpHidden boolValue]
|
||||
? @"YES": @"NO",
|
||||
@"showQuickStart": [[MPiOSConfig get].showQuickStart boolValue]
|
||||
? @"YES": @"NO",
|
||||
@"firstRun": [[PearlConfig get].firstRun boolValue]
|
||||
? @"YES": @"NO",
|
||||
@"launchCount": [[PearlConfig get].launchCount description],
|
||||
@"askForReviews": [[PearlConfig get].askForReviews boolValue]
|
||||
? @"YES": @"NO",
|
||||
@"reviewAfterLaunches": [[PearlConfig get].reviewAfterLaunches description],
|
||||
@"reviewedVersion": [PearlConfig get].reviewedVersion
|
||||
}];
|
||||
#endif
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointConfig attributes:@{
|
||||
@"rememberLogin": [[MPConfig get].rememberLogin boolValue]? @"YES": @"NO",
|
||||
@"iCloud": [[MPConfig get].iCloud boolValue]? @"YES": @"NO",
|
||||
@"iCloudDecided": [[MPConfig get].iCloudDecided boolValue]? @"YES": @"NO",
|
||||
@"sendInfo": [[MPiOSConfig get].sendInfo boolValue]? @"YES": @"NO",
|
||||
@"helpHidden": [[MPiOSConfig get].helpHidden boolValue]? @"YES": @"NO",
|
||||
@"showQuickStart": [[MPiOSConfig get].showQuickStart boolValue]? @"YES": @"NO",
|
||||
@"firstRun": [[PearlConfig get].firstRun boolValue]? @"YES": @"NO",
|
||||
@"launchCount": NilToNSNull([[PearlConfig get].launchCount description]),
|
||||
@"askForReviews": [[PearlConfig get].askForReviews boolValue]? @"YES": @"NO",
|
||||
@"reviewAfterLaunches": NilToNSNull([[PearlConfig get].reviewAfterLaunches description]),
|
||||
@"reviewedVersion": NilToNSNull([PearlConfig get].reviewedVersion)
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,7 +450,9 @@
|
||||
|
||||
[self.navigationController performSegueWithIdentifier:@"MP_Guide" sender:self];
|
||||
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight passCheckpoint:MPCheckpointShowGuide];
|
||||
#endif
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointShowGuide attributes:nil];
|
||||
}
|
||||
|
||||
@@ -458,7 +463,9 @@
|
||||
|
||||
- (void)showReview {
|
||||
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight passCheckpoint:MPCheckpointReview];
|
||||
#endif
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointReview attributes:nil];
|
||||
|
||||
[super showReview];
|
||||
@@ -507,13 +514,13 @@
|
||||
[PearlInfoPlist get].CFBundleShortVersionString,
|
||||
[PearlInfoPlist get].CFBundleVersion)
|
||||
|
||||
attachments:logs
|
||||
? [[PearlEMailAttachment alloc] initWithContent:[[[PearlLogger get] formatMessagesWithLevel:logLevel] dataUsingEncoding:NSUTF8StringEncoding]
|
||||
mimeType:@"text/plain"
|
||||
fileName:PearlString(@"%@-%@.log",
|
||||
[[NSDateFormatter rfc3339DateFormatter] stringFromDate:[NSDate date]],
|
||||
[PearlKeyChain deviceIdentifier])]
|
||||
: nil, nil]
|
||||
attachments:(logs
|
||||
? [[PearlEMailAttachment alloc] initWithContent:[[[PearlLogger get] formatMessagesWithLevel:logLevel] dataUsingEncoding:NSUTF8StringEncoding]
|
||||
mimeType:@"text/plain"
|
||||
fileName:PearlString(@"%@-%@.log",
|
||||
[[NSDateFormatter rfc3339DateFormatter] stringFromDate:[NSDate date]],
|
||||
[PearlKeyChain deviceIdentifier])]
|
||||
: nil), nil]
|
||||
showComposerForVC:viewController];
|
||||
}
|
||||
|
||||
@@ -606,7 +613,9 @@
|
||||
if (didReset)
|
||||
didReset();
|
||||
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight passCheckpoint:MPCheckpointChangeMP];
|
||||
#endif
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointChangeMP attributes:nil];
|
||||
}
|
||||
cancelTitle:[PearlStrings get].commonButtonAbort
|
||||
|
||||
@@ -26,7 +26,9 @@
|
||||
|
||||
- (IBAction)gorillas:(UIButton *)sender {
|
||||
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight passCheckpoint:MPCheckpointAppGorillas];
|
||||
#endif
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointAppGorillas attributes:nil];
|
||||
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"http://itunes.apple.com/app/lyndir/gorillas/id302275459?mt=8"]];
|
||||
@@ -34,7 +36,9 @@
|
||||
|
||||
- (IBAction)deblock:(UIButton *)sender {
|
||||
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight passCheckpoint:MPCheckpointAppDeBlock];
|
||||
#endif
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointAppDeBlock attributes:nil];
|
||||
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"http://itunes.apple.com/app/lyndir/deblock/id325058485?mt=8"]];
|
||||
|
||||
@@ -71,12 +71,21 @@
|
||||
|
||||
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];
|
||||
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight passCheckpoint:MPCheckpointApps];
|
||||
#endif
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointApps attributes:nil];
|
||||
|
||||
[super viewWillAppear:animated];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagScreen:@"Apps"];
|
||||
|
||||
[super viewDidAppear:animated];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
|
||||
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];
|
||||
@@ -96,15 +105,7 @@
|
||||
viewControllerAfterViewController:(UIViewController *)viewController {
|
||||
|
||||
NSUInteger vcIndex = [self.pageVCs indexOfObject:viewController];
|
||||
UIPageViewController *vc = [self.pageVCs objectAtIndex:(vcIndex + 1) % self.pageVCs.count];
|
||||
|
||||
return vc;
|
||||
}
|
||||
|
||||
- (void)viewDidUnload {
|
||||
|
||||
[self setPagePositionView:nil];
|
||||
[super viewDidUnload];
|
||||
return [self.pageVCs objectAtIndex:(vcIndex + 1) % self.pageVCs.count];
|
||||
}
|
||||
|
||||
- (IBAction)exit {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
#import "MPGuideViewController.h"
|
||||
#import "LocalyticsSession.h"
|
||||
|
||||
|
||||
@implementation MPGuideViewController
|
||||
@@ -35,6 +36,8 @@
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagScreen:@"Guide"];
|
||||
|
||||
[super viewDidAppear:animated];
|
||||
}
|
||||
|
||||
@@ -48,13 +51,6 @@
|
||||
[super viewWillDisappear:animated];
|
||||
}
|
||||
|
||||
- (void)viewDidUnload {
|
||||
|
||||
[self setScrollView:nil];
|
||||
[self setPageControl:nil];
|
||||
[super viewDidUnload];
|
||||
}
|
||||
|
||||
- (IBAction)close {
|
||||
|
||||
[self.presentingViewController dismissModalViewControllerAnimated:YES];
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
@property (assign, nonatomic) BOOL siteInfoHidden;
|
||||
@property (strong, nonatomic) MPElementEntity *activeElement;
|
||||
@property (strong, nonatomic) IBOutlet MPSearchDelegate *searchDelegate;
|
||||
@property (strong, nonatomic) IBOutlet UIPanGestureRecognizer *pullDownGesture;
|
||||
@property (strong, nonatomic) IBOutlet UIPanGestureRecognizer *pullUpGesture;
|
||||
@property (weak, nonatomic) IBOutlet UITextField *contentField;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *typeButton;
|
||||
@property (weak, nonatomic) IBOutlet UIWebView *helpView;
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
@synthesize siteInfoHidden = _siteInfoHidden;
|
||||
@synthesize activeElement = _activeElement;
|
||||
@synthesize searchDelegate = _searchDelegate;
|
||||
@synthesize pullDownGesture = _pullDownGesture;
|
||||
@synthesize pullUpGesture = _pullUpGesture;
|
||||
@synthesize typeButton = _typeButton;
|
||||
@synthesize helpView = _helpView;
|
||||
@synthesize siteName = _siteName;
|
||||
@@ -147,7 +149,7 @@
|
||||
inf(@"Main will appear");
|
||||
|
||||
// Sometimes, the search bar gets stuck in some sort of first-responder mode that it can't get out of...
|
||||
[self.searchDisplayController.searchBar resignFirstResponder];
|
||||
[[self.view.window findFirstResponderInHierarchy] resignFirstResponder];
|
||||
|
||||
// Needed for when we appear after a modal VC dismisses:
|
||||
// We can't present until the other modal VC has been fully dismissed and presenting in viewDidAppear will fail.
|
||||
@@ -202,6 +204,8 @@
|
||||
|
||||
}];
|
||||
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagScreen:@"Main"];
|
||||
|
||||
[super viewDidAppear:animated];
|
||||
}
|
||||
|
||||
@@ -211,43 +215,6 @@
|
||||
[super viewWillDisappear:animated];
|
||||
}
|
||||
|
||||
- (void)viewDidUnload {
|
||||
|
||||
[self setContentField:nil];
|
||||
[self setTypeButton:nil];
|
||||
[self setHelpView:nil];
|
||||
[self setSiteName:nil];
|
||||
[self setPasswordCounter:nil];
|
||||
[self setPasswordIncrementer:nil];
|
||||
[self setPasswordEdit:nil];
|
||||
[self setPasswordUpgrade:nil];
|
||||
[self setContentContainer:nil];
|
||||
[self setHelpContainer:nil];
|
||||
[self setContentTipContainer:nil];
|
||||
[self setAlertContainer:nil];
|
||||
[self setAlertTitle:nil];
|
||||
[self setAlertBody:nil];
|
||||
[self setContentTipBody:nil];
|
||||
[self setToolTipEditIcon:nil];
|
||||
[self setSearchTipContainer:nil];
|
||||
[self setActionsTipContainer:nil];
|
||||
[self setTypeTipContainer:nil];
|
||||
[self setToolTipContainer:nil];
|
||||
[self setToolTipBody:nil];
|
||||
[self setDisplayContainer:nil];
|
||||
[self setLoginNameField:nil];
|
||||
[self setLoginNameTipContainer:nil];
|
||||
[self setLoginNameTipBody:nil];
|
||||
[self setLoginNameContainer:nil];
|
||||
[self setPasswordUser:nil];
|
||||
[self setOutdatedAlertContainer:nil];
|
||||
[self setOutdatedAlertCloseButton:nil];
|
||||
[self setOutdatedAlertBack:nil];
|
||||
[self setPullUpView:nil];
|
||||
[self setPullDownView:nil];
|
||||
[super viewDidUnload];
|
||||
}
|
||||
|
||||
- (void)updateAnimated:(BOOL)animated {
|
||||
|
||||
if (animated) {
|
||||
@@ -371,7 +338,9 @@
|
||||
|
||||
- (void)setHelpChapter:(NSString *)chapter {
|
||||
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight passCheckpoint:PearlString(MPCheckpointHelpChapter @"_%@", chapter)];
|
||||
#endif
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointHelpChapter attributes:@{@"chapter": chapter}];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
@@ -516,15 +485,19 @@
|
||||
|
||||
- (IBAction)copyContent {
|
||||
|
||||
if (!self.activeElement)
|
||||
id content = self.activeElement.content;
|
||||
if (!content)
|
||||
// Nothing to copy.
|
||||
return;
|
||||
|
||||
inf(@"Copying password for: %@", self.activeElement.name);
|
||||
[UIPasteboard generalPasteboard].string = [self.activeElement.content description];
|
||||
[UIPasteboard generalPasteboard].string = [content description];
|
||||
|
||||
[self showContentTip:@"Copied!" withIcon:nil];
|
||||
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight passCheckpoint:MPCheckpointCopyToPasteboard];
|
||||
#endif
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointCopyToPasteboard
|
||||
attributes:@{@"type": self.activeElement.typeName,
|
||||
@"version": @(self.activeElement.version)}];
|
||||
@@ -536,11 +509,13 @@
|
||||
return;
|
||||
|
||||
inf(@"Copying user name for: %@", self.activeElement.name);
|
||||
[UIPasteboard generalPasteboard].string = [self.activeElement.content description];
|
||||
[UIPasteboard generalPasteboard].string = self.activeElement.loginName;
|
||||
|
||||
[self showLoginNameTip:@"Copied!"];
|
||||
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight passCheckpoint:MPCheckpointCopyLoginNameToPasteboard];
|
||||
#endif
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointCopyLoginNameToPasteboard
|
||||
attributes:@{@"type": self.activeElement.typeName,
|
||||
@"version": @(self.activeElement.version)}];
|
||||
@@ -561,7 +536,9 @@
|
||||
inf(@"Incrementing password counter for: %@", self.activeElement.name);
|
||||
++((MPElementGeneratedEntity *)self.activeElement).counter;
|
||||
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight passCheckpoint:MPCheckpointIncrementPasswordCounter];
|
||||
#endif
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointIncrementPasswordCounter
|
||||
attributes:@{@"type": self.activeElement.typeName,
|
||||
@"version": @(self.activeElement.version)}];
|
||||
@@ -588,7 +565,9 @@
|
||||
inf(@"Resetting password counter for: %@", self.activeElement.name);
|
||||
((MPElementGeneratedEntity *)self.activeElement).counter = 1;
|
||||
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight passCheckpoint:MPCheckpointResetPasswordCounter];
|
||||
#endif
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointResetPasswordCounter
|
||||
attributes:@{@"type": self.activeElement.typeName,
|
||||
@"version": @(self.activeElement.version)}];
|
||||
@@ -607,7 +586,9 @@
|
||||
self.loginNameField.enabled = YES;
|
||||
[self.loginNameField becomeFirstResponder];
|
||||
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight passCheckpoint:MPCheckpointEditLoginName];
|
||||
#endif
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointEditLoginName attributes:@{@"type": self.activeElement.typeName,
|
||||
@"version": @(self.activeElement.version)}];
|
||||
}
|
||||
@@ -649,7 +630,9 @@
|
||||
self.contentField.enabled = YES;
|
||||
[self.contentField becomeFirstResponder];
|
||||
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight passCheckpoint:MPCheckpointEditPassword];
|
||||
#endif
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointEditPassword
|
||||
attributes:@{@"type": self.activeElement.typeName,
|
||||
@"version": @(self.activeElement.version)}];
|
||||
@@ -670,7 +653,9 @@
|
||||
inf(@"Explicitly migrating element: %@", self.activeElement);
|
||||
[self.activeElement migrateExplicitly:YES];
|
||||
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight passCheckpoint:MPCheckpointExplicitMigration];
|
||||
#endif
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointExplicitMigration
|
||||
attributes:@{@"type": self.activeElement.typeName,
|
||||
@"version": @(self.activeElement.version)}];
|
||||
@@ -711,7 +696,7 @@
|
||||
|
||||
- (IBAction)action:(id)sender {
|
||||
|
||||
[PearlSheet showSheetWithTitle:nil message:nil viewStyle:UIActionSheetStyleAutomatic
|
||||
[PearlSheet showSheetWithTitle:nil viewStyle:UIActionSheetStyleAutomatic
|
||||
initSheet:nil
|
||||
tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
|
||||
if (buttonIndex == [sheet cancelButtonIndex])
|
||||
@@ -739,7 +724,7 @@
|
||||
[self performSegueWithIdentifier:@"OtherApps" sender:self];
|
||||
break;
|
||||
}
|
||||
#ifdef ADHOC
|
||||
#if defined(ADHOC) && defined(TESTFLIGHT_SDK_VERSION)
|
||||
case 4: {
|
||||
inf(@"Action: Feedback via TestFlight");
|
||||
[TestFlight openFeedbackView];
|
||||
@@ -840,7 +825,9 @@
|
||||
}];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPNotificationElementUpdated object:self.activeElement];
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight passCheckpoint:PearlString(MPCheckpointUseType @"_%@", self.activeElement.typeShortName)];
|
||||
#endif
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointUseType attributes:@{@"type": self.activeElement.typeName,
|
||||
@"version": @(self.activeElement.version)}];
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#import "MPAppDelegate.h"
|
||||
#import "MPAppDelegate_Key.h"
|
||||
#import "MPAppDelegate_Store.h"
|
||||
#import "LocalyticsSession.h"
|
||||
|
||||
@interface MPPreferencesViewController ()
|
||||
|
||||
@@ -80,6 +81,13 @@
|
||||
[super viewWillAppear:animated];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagScreen:@"Preferences"];
|
||||
|
||||
[super viewDidAppear:animated];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
|
||||
inf(@"Preferences will disappear");
|
||||
@@ -91,18 +99,6 @@
|
||||
return (interfaceOrientation == UIInterfaceOrientationPortrait);
|
||||
}
|
||||
|
||||
- (void)viewDidUnload {
|
||||
|
||||
[self setAvatarsView:nil];
|
||||
[self setAvatarTemplate:nil];
|
||||
[self setAvatarsView:nil];
|
||||
[self setSavePasswordSwitch:nil];
|
||||
[self setExportCell:nil];
|
||||
[self setChangeMPCell:nil];
|
||||
[self setDefaultTypeLabel:nil];
|
||||
[super viewDidUnload];
|
||||
}
|
||||
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
||||
|
||||
if ([[segue identifier] isEqualToString:@"MP_ChooseType"])
|
||||
@@ -164,6 +160,7 @@
|
||||
vc.showDoneButton = NO;
|
||||
|
||||
[self.navigationController pushViewController:vc animated:YES];
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagScreen:@"Settings"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
- (id)init {
|
||||
|
||||
if (!([super init]))
|
||||
if (!(self = [super init]))
|
||||
return nil;
|
||||
|
||||
self.dateFormatter = [NSDateFormatter new];
|
||||
@@ -406,7 +406,9 @@ forRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
inf(@"Deleting element: %@", element.name);
|
||||
[self.fetchedResultsController.managedObjectContext deleteObject:element];
|
||||
|
||||
#ifdef TESTFLIGHT_SDK_VERSION
|
||||
[TestFlight passCheckpoint:MPCheckpointDeleteElement];
|
||||
#endif
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointDeleteElement
|
||||
attributes:@{@"type": element.typeName,
|
||||
@"version": @(element.version)}];
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
#import "MPTypeViewController.h"
|
||||
#import "LocalyticsSession.h"
|
||||
|
||||
|
||||
@interface MPTypeViewController ()
|
||||
@@ -44,6 +45,8 @@
|
||||
}
|
||||
}];
|
||||
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagScreen:@"Type Selection"];
|
||||
|
||||
[super viewDidAppear:animated];
|
||||
}
|
||||
|
||||
@@ -140,9 +143,4 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewDidUnload {
|
||||
|
||||
[self setRecommendedTipContainer:nil];
|
||||
[super viewDidUnload];
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -9,13 +9,17 @@
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import <Twitter/Twitter.h>
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wnewline-eof"
|
||||
#import "Facebook.h"
|
||||
#pragma clang diagnostic pop
|
||||
#import "GooglePlusShare.h"
|
||||
|
||||
#import "MPUnlockViewController.h"
|
||||
#import "MPAppDelegate.h"
|
||||
#import "MPAppDelegate_Key.h"
|
||||
#import "MPAppDelegate_Store.h"
|
||||
#import "LocalyticsSession.h"
|
||||
|
||||
@interface MPUnlockViewController ()
|
||||
|
||||
@@ -85,7 +89,9 @@
|
||||
} options:0];
|
||||
[avatar onSelect:^(BOOL selected) {
|
||||
if (selected)
|
||||
user.avatar = (unsigned)avatar.tag;
|
||||
[user.managedObjectContext performBlock:^{
|
||||
user.avatar = (unsigned)avatar.tag;
|
||||
}];
|
||||
} options:0];
|
||||
avatar.selected = (a == user.avatar);
|
||||
if (avatar.selected)
|
||||
@@ -133,6 +139,7 @@
|
||||
self.fbOperationQueue = [NSOperationQueue new];
|
||||
[self.fbOperationQueue setSuspended:YES];
|
||||
|
||||
[self.avatarsView addGestureRecognizer:self.targetedUserActionGesture];
|
||||
self.avatarsView.decelerationRate = UIScrollViewDecelerationRateFast;
|
||||
self.avatarsView.clipsToBounds = NO;
|
||||
self.nameLabel.layer.cornerRadius = 5;
|
||||
@@ -169,26 +176,6 @@
|
||||
[super viewDidLoad];
|
||||
}
|
||||
|
||||
- (void)viewDidUnload {
|
||||
|
||||
[self setSpinner:nil];
|
||||
[self setPasswordField:nil];
|
||||
[self setPasswordView:nil];
|
||||
[self setAvatarsView:nil];
|
||||
[self setNameLabel:nil];
|
||||
[self setAvatarTemplate:nil];
|
||||
[self setTip:nil];
|
||||
[self setPasswordTipView:nil];
|
||||
[self setPasswordTipLabel:nil];
|
||||
[self setTargetedUserActionGesture:nil];
|
||||
[self setWordWall:nil];
|
||||
[self setCreatePasswordTipView:nil];
|
||||
[self setPasswordFieldLabel:nil];
|
||||
[self setLoadingUsersIndicator:nil];
|
||||
[self setUiContainer:nil];
|
||||
[super viewDidUnload];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
|
||||
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];
|
||||
@@ -209,10 +196,12 @@
|
||||
else
|
||||
[self updateLayoutAnimated:YES allowScroll:YES completion:nil];
|
||||
|
||||
[UIView animateWithDuration:0.3 animations:^{
|
||||
[UIView animateWithDuration:0.5 animations:^{
|
||||
self.uiContainer.alpha = 1;
|
||||
}];
|
||||
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagScreen:@"Unlock"];
|
||||
|
||||
[super viewDidAppear:animated];
|
||||
}
|
||||
|
||||
@@ -254,9 +243,11 @@
|
||||
[self.avatarToUser removeAllObjects];
|
||||
|
||||
// Create avatars.
|
||||
for (MPUserEntity *user in users)
|
||||
[self setupAvatar:[self.avatarTemplate clone] forUser:user];
|
||||
[self setupAvatar:[self.avatarTemplate clone] forUser:nil];
|
||||
[moc performBlockAndWait:^{
|
||||
for (MPUserEntity *user in users)
|
||||
[self setupAvatar:[self.avatarTemplate clone] forUser:user];
|
||||
[self setupAvatar:[self.avatarTemplate clone] forUser:nil];
|
||||
}];
|
||||
|
||||
// Scroll view's content changed, update its content size.
|
||||
[self.avatarsView autoSizeContentIgnoreHidden:YES ignoreInvisible:YES limitPadding:NO ignoreSubviews:nil];
|
||||
@@ -365,7 +356,9 @@
|
||||
}
|
||||
|
||||
// Save
|
||||
newUser.name = [alert textFieldAtIndex:0].text;
|
||||
[newUser.managedObjectContext performBlock:^{
|
||||
newUser.name = [alert textFieldAtIndex:0].text;
|
||||
}];
|
||||
[self showNewUserAvatarAlertFor:newUser completion:completion];
|
||||
}
|
||||
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonSave, nil];
|
||||
@@ -562,6 +555,10 @@
|
||||
|
||||
- (void)tryMasterPassword {
|
||||
|
||||
if (!self.selectedUser)
|
||||
// No user selected, can't try sign-in.
|
||||
return;
|
||||
|
||||
[self setSpinnerActive:YES];
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
@@ -769,7 +766,7 @@
|
||||
return;
|
||||
|
||||
[PearlSheet showSheetWithTitle:targetedUser.name
|
||||
message:nil viewStyle:UIActionSheetStyleBlackTranslucent
|
||||
viewStyle:UIActionSheetStyleBlackTranslucent
|
||||
initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
|
||||
if (buttonIndex == [sheet cancelButtonIndex])
|
||||
return;
|
||||
@@ -843,7 +840,7 @@
|
||||
|
||||
- (IBAction)add:(UIButton *)sender {
|
||||
|
||||
[PearlSheet showSheetWithTitle:@"Follow Master Password" message:nil viewStyle:UIActionSheetStyleBlackTranslucent
|
||||
[PearlSheet showSheetWithTitle:@"Follow Master Password" viewStyle:UIActionSheetStyleBlackTranslucent
|
||||
initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
|
||||
if (buttonIndex == [sheet cancelButtonIndex])
|
||||
return;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="2.0" toolsVersion="2549" systemVersion="12B19" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" initialViewController="KZF-fe-y9n">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="2.0" toolsVersion="2840" systemVersion="12B19" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" initialViewController="KZF-fe-y9n">
|
||||
<dependencies>
|
||||
<deployment defaultVersion="1296" identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="1498"/>
|
||||
<deployment defaultVersion="1536" identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="1926"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Type View Controller - Type-->
|
||||
@@ -419,7 +419,7 @@ Your passwords will be AES-encrypted with your master password.</string>
|
||||
<rect key="frame" x="0.0" y="64" width="320" height="416"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="bottom" image="background.png" id="0hY-LL-ITu">
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="center" image="background.png" id="0hY-LL-ITu">
|
||||
<rect key="frame" x="0.0" y="44" width="320" height="372"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
</imageView>
|
||||
@@ -475,8 +475,8 @@ Your passwords will be AES-encrypted with your master password.</string>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" hint=""/>
|
||||
<color key="textColor" red="0.47450980390000003" green="0.86666666670000003" blue="0.98431372549999996" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<fontDescription key="fontDescription" name="Copperplate-Bold" family="Copperplate" pointSize="26"/>
|
||||
<textInputTraits key="textInputTraits" autocorrectionType="no"/>
|
||||
<fontDescription key="fontDescription" name="Futura-CondensedExtraBold" family="Futura" pointSize="26"/>
|
||||
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="done"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="PQa-Xl-A3x" id="qOM-gq-c6g"/>
|
||||
</connections>
|
||||
@@ -568,8 +568,8 @@ Your passwords will be AES-encrypted with your master password.</string>
|
||||
<rect key="frame" x="0.0" y="0.0" width="280" height="31"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<fontDescription key="fontDescription" name="Copperplate" family="Copperplate" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" keyboardType="emailAddress"/>
|
||||
<fontDescription key="fontDescription" name="Futura-Medium" family="Futura" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocorrectionType="no" keyboardType="emailAddress" returnKeyType="done"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="PQa-Xl-A3x" id="xG4-np-2j5"/>
|
||||
</connections>
|
||||
@@ -924,7 +924,9 @@ L4m3P4sSw0rD</string>
|
||||
<outlet property="passwordIncrementer" destination="jec-mu-nPt" id="i9B-lX-zzX"/>
|
||||
<outlet property="passwordUpgrade" destination="zbY-EJ-Yiu" id="KAX-Wk-DC4"/>
|
||||
<outlet property="passwordUser" destination="TZ0-1K-Vvw" id="QCC-Ne-qYv"/>
|
||||
<outlet property="pullDownGesture" destination="O19-XO-q6s" id="Hb1-fJ-96l"/>
|
||||
<outlet property="pullDownView" destination="qgs-1p-CRh" id="XWn-9h-aYM"/>
|
||||
<outlet property="pullUpGesture" destination="D0R-Cc-rG7" id="v3L-Nf-bIC"/>
|
||||
<outlet property="pullUpView" destination="dId-fW-MRd" id="JED-fJ-VfH"/>
|
||||
<outlet property="searchDisplayController" destination="P8c-gf-nN3" id="CLs-YI-7NC"/>
|
||||
<outlet property="searchTipContainer" destination="zOR-Du-qRL" id="X7h-Vh-iCE"/>
|
||||
@@ -1164,14 +1166,13 @@ Pink fluffy door frame.</string>
|
||||
<!--App View Controller-->
|
||||
<scene sceneID="aYk-5V-FnG">
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="hkm-U7-Dm7" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
<viewController storyboardIdentifier="MPAppViewController_0" id="yIx-9U-bOF" customClass="MPAppViewController" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Yea-s2-C8N">
|
||||
<rect key="frame" x="0.0" y="0.0" width="305" height="399"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="305" height="400"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="Wrz-tq-o1S">
|
||||
<rect key="frame" x="0.0" y="0.0" width="305" height="399"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="305" height="400"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||
<state key="normal" image="page-gorillas.png">
|
||||
@@ -1186,7 +1187,7 @@ Pink fluffy door frame.</string>
|
||||
</connections>
|
||||
</button>
|
||||
<view contentMode="scaleToFill" id="JN1-cA-6yZ">
|
||||
<rect key="frame" x="0.0" y="330" width="305" height="60"/>
|
||||
<rect key="frame" x="0.0" y="331" width="305" height="60"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Gorillas At Large On Metropolis Rooftops" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" id="Ypg-Yc-UK3">
|
||||
@@ -1212,6 +1213,7 @@ Pink fluffy door frame.</string>
|
||||
<navigationItem key="navigationItem" id="zZZ-QZ-Yur"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="hkm-U7-Dm7" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="463" y="-381"/>
|
||||
</scene>
|
||||
@@ -1223,7 +1225,7 @@ Pink fluffy door frame.</string>
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="480"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="top" image="background.png" id="QWe-Gw-rD3">
|
||||
<imageView userInteractionEnabled="NO" contentMode="center" image="background.png" id="QWe-Gw-rD3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="480"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
</imageView>
|
||||
@@ -1415,7 +1417,6 @@ Pink fluffy door frame.</string>
|
||||
<gestureRecognizers/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="Nbn-Rv-sP1" id="E1h-WA-PYV"/>
|
||||
<outletCollection property="gestureRecognizers" destination="9WS-yS-aqQ" appends="YES" id="B9k-bg-gqA"/>
|
||||
</connections>
|
||||
</scrollView>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.0" contentMode="left" text="Maarten Billemont" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumFontSize="10" id="8s0-nT-Aoq">
|
||||
@@ -1621,7 +1622,6 @@ You could use the word wall for inspiration in finding a memorable master passw
|
||||
<!--Apps View Controller-->
|
||||
<scene sceneID="3cC-Qq-rgU">
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="pv9-cA-JY5" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
<viewController wantsFullScreenLayout="YES" id="2Th-Tb-22a" customClass="MPAppsViewController" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="DHZ-5g-6vT">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="480"/>
|
||||
@@ -1632,7 +1632,7 @@ You could use the word wall for inspiration in finding a memorable master passw
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
</imageView>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="page-gorillas.png" id="QQT-37-azo">
|
||||
<rect key="frame" x="0.0" y="38" width="305" height="399"/>
|
||||
<rect key="frame" x="0.0" y="38" width="305" height="400"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
</imageView>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="drq-47-KK9">
|
||||
@@ -1658,6 +1658,7 @@ You could use the word wall for inspiration in finding a memorable master passw
|
||||
<outlet property="pagePositionView" destination="QQT-37-azo" id="TSf-0y-3VK"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="pv9-cA-JY5" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-85" y="-421"/>
|
||||
</scene>
|
||||
@@ -2018,14 +2019,13 @@ You could use the word wall for inspiration in finding a memorable master passw
|
||||
<!--App View Controller-->
|
||||
<scene sceneID="7oa-Tk-zt7">
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="LHv-Mk-8Kp" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
<viewController storyboardIdentifier="MPAppViewController_1" id="vOg-Xq-hKm" customClass="MPAppViewController" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="jzQ-Oa-Gdj">
|
||||
<rect key="frame" x="0.0" y="0.0" width="305" height="399"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="305" height="400"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="x6i-3e-0Rb">
|
||||
<rect key="frame" x="0.0" y="0.0" width="305" height="399"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="305" height="400"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||
<state key="normal" image="page-deblock.png">
|
||||
@@ -2040,7 +2040,7 @@ You could use the word wall for inspiration in finding a memorable master passw
|
||||
</connections>
|
||||
</button>
|
||||
<view contentMode="scaleToFill" id="AC5-4y-ftd">
|
||||
<rect key="frame" x="0.0" y="330" width="305" height="60"/>
|
||||
<rect key="frame" x="0.0" y="331" width="305" height="60"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Block Destruction Puzzle" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="10" id="11H-1K-20G">
|
||||
@@ -2066,6 +2066,7 @@ You could use the word wall for inspiration in finding a memorable master passw
|
||||
<navigationItem key="navigationItem" id="Pm8-fx-hfM"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="LHv-Mk-8Kp" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1004" y="-381"/>
|
||||
</scene>
|
||||
@@ -2073,7 +2074,7 @@ You could use the word wall for inspiration in finding a memorable master passw
|
||||
<resources>
|
||||
<image name="Square-bottom.png" width="551" height="58"/>
|
||||
<image name="avatar-0.png" width="110" height="110"/>
|
||||
<image name="background.png" width="480" height="480"/>
|
||||
<image name="background.png" width="568" height="568"/>
|
||||
<image name="book.png" width="320" height="480"/>
|
||||
<image name="guide_page_0.png" width="320" height="480"/>
|
||||
<image name="guide_page_1.png" width="320" height="480"/>
|
||||
@@ -2089,8 +2090,8 @@ You could use the word wall for inspiration in finding a memorable master passw
|
||||
<image name="icon_person.png" width="32" height="32"/>
|
||||
<image name="icon_plus.png" width="32" height="32"/>
|
||||
<image name="icon_up.png" width="32" height="32"/>
|
||||
<image name="page-deblock.png" width="305" height="399"/>
|
||||
<image name="page-gorillas.png" width="305" height="399"/>
|
||||
<image name="page-deblock.png" width="305" height="400"/>
|
||||
<image name="page-gorillas.png" width="305" height="400"/>
|
||||
<image name="pull-down.png" width="60" height="10"/>
|
||||
<image name="pull-up.png" width="60" height="20"/>
|
||||
<image name="social-facebook.png" width="44" height="44"/>
|
||||
@@ -2111,136 +2112,6 @@ You could use the word wall for inspiration in finding a memorable master passw
|
||||
<image name="ui_spinner.png" width="75" height="75"/>
|
||||
<image name="ui_textfield.png" width="158" height="34"/>
|
||||
</resources>
|
||||
<classes>
|
||||
<class className="MPAppViewController" superclassName="UIViewController">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPAppViewController.h"/>
|
||||
<relationships>
|
||||
<relationship kind="action" name="deblock:" candidateClass="UIButton"/>
|
||||
<relationship kind="action" name="gorillas:" candidateClass="UIButton"/>
|
||||
</relationships>
|
||||
</class>
|
||||
<class className="MPAppsViewController" superclassName="UIViewController">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPAppsViewController.h"/>
|
||||
<relationships>
|
||||
<relationship kind="action" name="exit"/>
|
||||
<relationship kind="outlet" name="pagePositionView" candidateClass="UIImageView"/>
|
||||
</relationships>
|
||||
</class>
|
||||
<class className="MPGuideViewController" superclassName="UIViewController">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPGuideViewController.h"/>
|
||||
<relationships>
|
||||
<relationship kind="action" name="close"/>
|
||||
<relationship kind="outlet" name="pageControl" candidateClass="UIPageControl"/>
|
||||
<relationship kind="outlet" name="scrollView" candidateClass="UIScrollView"/>
|
||||
</relationships>
|
||||
</class>
|
||||
<class className="MPMainViewController" superclassName="UIViewController">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPMainViewController.h"/>
|
||||
<relationships>
|
||||
<relationship kind="action" name="action:" candidateClass="UIBarButtonItem"/>
|
||||
<relationship kind="action" name="closeAlert"/>
|
||||
<relationship kind="action" name="closeOutdatedAlert"/>
|
||||
<relationship kind="action" name="copyContent"/>
|
||||
<relationship kind="action" name="editLoginName:" candidateClass="UILongPressGestureRecognizer"/>
|
||||
<relationship kind="action" name="editPassword"/>
|
||||
<relationship kind="action" name="incrementPasswordCounter"/>
|
||||
<relationship kind="action" name="infoOutdatedAlert"/>
|
||||
<relationship kind="action" name="panHelpDown:" candidateClass="UIPanGestureRecognizer"/>
|
||||
<relationship kind="action" name="panHelpUp:" candidateClass="UIPanGestureRecognizer"/>
|
||||
<relationship kind="action" name="resetPasswordCounter:" candidateClass="UILongPressGestureRecognizer"/>
|
||||
<relationship kind="action" name="searchOutdatedElements"/>
|
||||
<relationship kind="action" name="toggleUser"/>
|
||||
<relationship kind="action" name="upgradePassword"/>
|
||||
<relationship kind="outlet" name="actionsTipContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="alertBody" candidateClass="UITextView"/>
|
||||
<relationship kind="outlet" name="alertContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="alertTitle" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="contentContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="contentField" candidateClass="UITextField"/>
|
||||
<relationship kind="outlet" name="contentTipBody" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="contentTipContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="displayContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="helpContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="helpView" candidateClass="UIWebView"/>
|
||||
<relationship kind="outlet" name="loginNameContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="loginNameField" candidateClass="UITextField"/>
|
||||
<relationship kind="outlet" name="loginNameTipBody" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="loginNameTipContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="outdatedAlertBack" candidateClass="UIImageView"/>
|
||||
<relationship kind="outlet" name="outdatedAlertCloseButton" candidateClass="UIButton"/>
|
||||
<relationship kind="outlet" name="outdatedAlertContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="passwordCounter" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="passwordEdit" candidateClass="UIButton"/>
|
||||
<relationship kind="outlet" name="passwordIncrementer" candidateClass="UIButton"/>
|
||||
<relationship kind="outlet" name="passwordUpgrade" candidateClass="UIButton"/>
|
||||
<relationship kind="outlet" name="passwordUser" candidateClass="UIButton"/>
|
||||
<relationship kind="outlet" name="pullDownView" candidateClass="UIImageView"/>
|
||||
<relationship kind="outlet" name="pullUpView" candidateClass="UIImageView"/>
|
||||
<relationship kind="outlet" name="searchDelegate" candidateClass="MPSearchDelegate"/>
|
||||
<relationship kind="outlet" name="searchTipContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="siteName" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="toolTipBody" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="toolTipContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="toolTipEditIcon" candidateClass="UIImageView"/>
|
||||
<relationship kind="outlet" name="typeButton" candidateClass="UIButton"/>
|
||||
<relationship kind="outlet" name="typeTipContainer" candidateClass="UIView"/>
|
||||
</relationships>
|
||||
</class>
|
||||
<class className="MPPreferencesViewController" superclassName="UITableViewController">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPPreferencesViewController.h"/>
|
||||
<relationships>
|
||||
<relationship kind="action" name="didToggleSwitch:" candidateClass="UISwitch"/>
|
||||
<relationship kind="action" name="settings:"/>
|
||||
<relationship kind="outlet" name="avatarTemplate" candidateClass="UIButton"/>
|
||||
<relationship kind="outlet" name="avatarsView" candidateClass="UIScrollView"/>
|
||||
<relationship kind="outlet" name="changeMPCell" candidateClass="UITableViewCell"/>
|
||||
<relationship kind="outlet" name="defaultTypeLabel" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="exportCell" candidateClass="UITableViewCell"/>
|
||||
<relationship kind="outlet" name="savePasswordSwitch" candidateClass="UISwitch"/>
|
||||
</relationships>
|
||||
</class>
|
||||
<class className="MPSearchDelegate" superclassName="NSObject">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPSearchDelegate.h"/>
|
||||
<relationships>
|
||||
<relationship kind="outlet" name="delegate"/>
|
||||
<relationship kind="outlet" name="searchDisplayController" candidateClass="UISearchDisplayController"/>
|
||||
<relationship kind="outlet" name="searchTipContainer" candidateClass="UIView"/>
|
||||
</relationships>
|
||||
</class>
|
||||
<class className="MPTypeViewController" superclassName="UITableViewController">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPTypeViewController.h"/>
|
||||
<relationships>
|
||||
<relationship kind="outlet" name="recommendedTipContainer" candidateClass="UIView"/>
|
||||
</relationships>
|
||||
</class>
|
||||
<class className="MPUnlockViewController" superclassName="UIViewController">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPUnlockViewController.h"/>
|
||||
<relationships>
|
||||
<relationship kind="action" name="add:" candidateClass="UIButton"/>
|
||||
<relationship kind="action" name="facebook:" candidateClass="UIButton"/>
|
||||
<relationship kind="action" name="google:" candidateClass="UIButton"/>
|
||||
<relationship kind="action" name="mail:" candidateClass="UIButton"/>
|
||||
<relationship kind="action" name="targetedUserAction:" candidateClass="UILongPressGestureRecognizer"/>
|
||||
<relationship kind="action" name="twitter:" candidateClass="UIButton"/>
|
||||
<relationship kind="outlet" name="avatarTemplate" candidateClass="UIButton"/>
|
||||
<relationship kind="outlet" name="avatarsView" candidateClass="UIScrollView"/>
|
||||
<relationship kind="outlet" name="createPasswordTipView" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="loadingUsersIndicator" candidateClass="UIActivityIndicatorView"/>
|
||||
<relationship kind="outlet" name="nameLabel" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="oldNameLabel" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="passwordField" candidateClass="UITextField"/>
|
||||
<relationship kind="outlet" name="passwordFieldLabel" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="passwordTipLabel" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="passwordTipView" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="passwordView" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="spinner" candidateClass="UIImageView"/>
|
||||
<relationship kind="outlet" name="targetedUserActionGesture" candidateClass="UILongPressGestureRecognizer"/>
|
||||
<relationship kind="outlet" name="tip" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="uiContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="wordWall" candidateClass="UIView"/>
|
||||
</relationships>
|
||||
</class>
|
||||
</classes>
|
||||
<simulatedMetricsContainer key="defaultSimulatedMetrics">
|
||||
<nil key="statusBar"/>
|
||||
<simulatedOrientationMetrics key="orientation"/>
|
||||
|
||||
@@ -19,9 +19,6 @@
|
||||
#import "TestFlight.h"
|
||||
#import <Crashlytics/Crashlytics.h>
|
||||
|
||||
//#define Crashlytics PearlNil
|
||||
//#define TestFlight PearlNil
|
||||
|
||||
#import "MPTypes.h"
|
||||
#import "MPiOSConfig.h"
|
||||
|
||||
|
||||
BIN
Resources/AppStore-iOS-1136.png
Normal file
|
After Width: | Height: | Size: 660 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 235 KiB After Width: | Height: | Size: 247 KiB |
BIN
Resources/Default-568h.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
Resources/Default-568h@2x.png
Normal file
|
After Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 173 KiB After Width: | Height: | Size: 126 KiB |
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 410 KiB After Width: | Height: | Size: 411 KiB |
|
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 162 KiB |
|
Before Width: | Height: | Size: 597 KiB After Width: | Height: | Size: 598 KiB |
BIN
Site/img/objc468x60.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
@@ -155,7 +155,7 @@
|
||||
<!--span class="tip appstore">Great feedback may earn you a free copy for a friend!</span-->
|
||||
<img src="img/appstore.png" />
|
||||
</a><br />
|
||||
<form id="sendtophone">
|
||||
<!--form id="sendtophone">
|
||||
<span class="field">
|
||||
Or send to your phone:<br />
|
||||
<input type="text" name="email" placeholder="E-mail or phone number" />
|
||||
@@ -164,7 +164,7 @@
|
||||
<span class="confirm">
|
||||
Message sent!
|
||||
</span>
|
||||
</form>
|
||||
</form-->
|
||||
</div>
|
||||
<h1><a href="."><img class="logo" src="img/iTunesArtwork-Bare.png" /> Master Password</a></h1>
|
||||
<div class="footnote"><a href="mailto:masterpassword+remove_this@lyndir.com" onclick="_gaq.push(['_trackPageview', '/outbound/mail']);">Contact</a> | <a href="http://www.lyndir.com" onclick="_gaq.push(['_trackPageview', '/outbound/lyndir.com']);">Lyndir</a> | <a href="https://plus.google.com/116256327773442623984/about" rel="publisher" onclick="_gaq.push(['_trackPageview', '/outbound/google+']);">Google+</a></div>
|
||||
@@ -198,7 +198,7 @@
|
||||
<meta itemprop="price" content="5.99" />
|
||||
<meta itemprop="priceCurrency" content="USD" />
|
||||
<a itemprop="url" href="http://itunes.apple.com/app/id510296984">
|
||||
<img itemprop="image" class="badge" src="img/price.png" />
|
||||
<!--img itemprop="image" class="badge" src="img/price.png" /-->
|
||||
</a>
|
||||
</span>
|
||||
<img src="img/frontpage_phone.png" />
|
||||
@@ -341,6 +341,8 @@
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<a href="http://www.jetbrains.com/objc/" style="display:inline-block; background:url('img/objc468x60.png') no-repeat 10px 50%; margin:0;padding:0;text-decoration:none;text-indent:0;letter-spacing:-0.001em; width:466px; height:58px" alt="Smarter and faster IDE for iOS and Mac OS applications development" title="Smarter and faster IDE for iOS and Mac OS applications development"><span style="margin: 2px 0 0 65px;padding: 0;float: left;font-size: 12px;cursor:pointer; background-image:none;border:0;color: #435570; font-family: trebuchet ms,arial,sans-serif;font-weight: normal;text-align:left;">Developed with</span><span style="margin:0 0 0 200px;padding:18px 0 2px 0; line-height:14px;font-size:15px;cursor:pointer; background-image:none;border:0;display:block; width:265px; color:#435570; font-family: trebuchet ms,arial,sans-serif;font-weight: normal;text-align:left;">Smarter and faster IDE for iOS and<br/>Mac OS applications development</span></a>
|
||||
<br /> <br />
|
||||
Master Password is a security and productivity product by <a href="http://www.lyndir.com" rel="author" onclick="_gaq.push(['_trackPageview', '/outbound/lyndir.com']);">Lyndir</a>, © 2011.
|
||||
</footer>
|
||||
</body>
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
set -e
|
||||
|
||||
cd "${BASH_SOURCE[0]%/*}"
|
||||
s3cmd sync . s3://masterpassword.lyndir.com/
|
||||
s3cmd sync . s3://www.masterpasswordapp.com/
|
||||
nice rsync --partial --progress --delete --sparse --archive --no-owner --no-perms --no-group --chmod=ugo=rwX \
|
||||
--exclude rate-limit \
|
||||
. masterpassword.lyndir.com:/usr/local/www/masterpassword.lyndir.com/htdocs/
|
||||
. satura.lyndir.com:/usr/local/www/masterpassword.lyndir.com/htdocs/
|
||||
|
||||