Threading fixes for Mac, spinner fix for iOS.
[UPDATED] TestFlight. [FIXED] Mac: References to MPAppDelegate that should be OS-independant now use MPAppDelegate_Shared. [FIXED] Mac: Threading and content UI updates. [FIXED] iOS: Spinner was showing when going back to unlock VC.
This commit is contained in:
parent
291b408995
commit
5d5e9395b3
32
External/TestFlight/README.md
vendored
32
External/TestFlight/README.md
vendored
@ -52,37 +52,33 @@ This SDK can be run from both the iPhone Simulator and Device and has been teste
|
||||
|
||||
section. This will give you access to the SDK across all files.
|
||||
|
||||
2. Get your Team Token which you can find at [http://testflightapp.com/dashboard/team/](http://testflightapp.com/dashboard/team/) select the team you are using from the team selection drop down list on the top of the page and then select Team Info.
|
||||
2. Get your Application Token which you can find at [http://testflightapp.com/dashboard/applications/](http://testflightapp.com/dashboard/applications/) select the application you are using from the list choose the SDK option and the application token for this application will be there. To ensure that your testers do not show up as anonymous place the call to setDeviceIdentifer before calling takeOff. Remove #define TESTING 1 before building your release build for the App Store.
|
||||
|
||||
3. Launch TestFlight with your Team Token
|
||||
3. Launch TestFlight with your Application Token
|
||||
|
||||
-(BOOL)application:(UIApplication *)application
|
||||
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||
// start of your application:didFinishLaunchingWithOptions
|
||||
// ...
|
||||
[TestFlight takeOff:@"Insert your Team Token here"];
|
||||
|
||||
// !!!: Use the next line only during beta
|
||||
// [TestFlight setDeviceIdentifier:[[UIDevice currentDevice] uniqueIdentifier]];
|
||||
|
||||
[TestFlight takeOff:@"Insert your Application Token here"];
|
||||
// The rest of your application:didFinishLaunchingWithOptions method
|
||||
// ...
|
||||
}
|
||||
|
||||
4. To report crashes to you we install our own uncaught exception handler. If you are not currently using an exception handler of your own then all you need to do is go to the next step. If you currently use an Exception Handler, or you use another framework that does please go to the section on advanced exception handling.
|
||||
|
||||
5. To enable the best crash reporting possible we recommend setting the following project build settings in Xcode to NO for all targets that you want to have live crash reporting for. You can find build settings by opening the Project Navigator (default command+1 or command+shift+j) then clicking on the project you are configuring (usually the first selection in the list). From there you can choose to either change the global project settings or settings on an individual project basis. All settings below are in the Deployment Section.
|
||||
|
||||
1. Deployment Post Processing
|
||||
2. Strip Debug Symbols During Copy
|
||||
3. Strip Linked Product
|
||||
|
||||
##Beta Testing and Release Differentiation
|
||||
|
||||
In order to provide more information about your testers while beta testing you will need to provide the device's unique identifier. This identifier is not something that the SDK will collect from the device and we do not recommend using this in production. To send the device identifier to us put the following code before your call to takeOff.
|
||||
In order to provide more information about your testers while beta testing you will need to provide the device's unique identifier. This identifier is not something that the SDK will collect from the device and we do not recommend using this in production. To send the device identifier to us put the following code **before your call to takeOff**.
|
||||
|
||||
#define TESTING 1
|
||||
#ifdef TESTING
|
||||
[TestFlight setDeviceIdentifier:[[UIDevice currentDevice] uniqueIdentifier]];
|
||||
#endif
|
||||
[TestFlight setDeviceIdentifier:[[UIDevice currentDevice] uniqueIdentifier]];
|
||||
[TestFlight takeOff:@"Insert your Application Token here"];
|
||||
|
||||
This will allow you to have the best possible information during testing, but disable getting and sending of the device unique identifier when you release your application. When it is time to release simply comment out #define TESTING 1. If you decide to not include the device's unique identifier during your testing phase TestFlight will still collect all of the information that you send but it may be anonymized.
|
||||
This will allow you to have the best possible information during testing. **When it is time to submit to the App Store comment this line out**. Apple may reject your app if you leave this line in. If you decide to not include the device's unique identifier during your testing phase TestFlight will still collect all of the information that you send but it may be anonymized.
|
||||
|
||||
|
||||
##Checkpoint API
|
||||
@ -165,7 +161,7 @@ An uncaught exception means that your application is in an unknown state and the
|
||||
sigaction(SIGILL, &newSignalAction, NULL);
|
||||
sigaction(SIGBUS, &newSignalAction, NULL);
|
||||
// Call takeOff after install your own unhandled exception and signal handlers
|
||||
[TestFlight takeOff:@"Insert your Team Token here"];
|
||||
[TestFlight takeOff:@"Insert your Application Token here"];
|
||||
// continue with your application initialization
|
||||
}
|
||||
|
||||
@ -193,13 +189,13 @@ We have implemented three different loggers.
|
||||
|
||||
Each of the loggers log asynchronously and all TFLog calls are non blocking. The TestFlight logger writes its data to a file which is then sent to our servers on Session End events. The Apple System Logger sends its messages to the Apple System Log and are viewable using the Organizer in Xcode when the device is attached to your computer. The ASL logger can be disabled by turning it off in your TestFlight options
|
||||
|
||||
[TestFlight setOptions:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO] forKey:@"logToConsole"]];
|
||||
[TestFlight setOptions:{ TFOptionLogToConsole : @NO }];
|
||||
|
||||
The default option is YES.
|
||||
|
||||
The STDERR logger sends log messages to STDERR so that you can see your log statements while debugging. The STDERR logger is only active when a debugger is attached to your application. If you do not wish to use the STDERR logger you can disable it by turning it off in your TestFlight options
|
||||
|
||||
[TestFlight setOptions:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO] forKey:@"logToSTDERR"]];
|
||||
[TestFlight setOptions:{ TFOptionLogToSTDERR : @NO }];
|
||||
|
||||
The default option is YES.
|
||||
|
||||
|
63
External/TestFlight/TestFlight.h
vendored
63
External/TestFlight/TestFlight.h
vendored
@ -6,7 +6,7 @@
|
||||
// Copyright 2011 TestFlight. All rights reserved.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#define TESTFLIGHT_SDK_VERSION @"1.2beta2"
|
||||
#define TESTFLIGHT_SDK_VERSION @"1.2.4"
|
||||
#undef TFLog
|
||||
|
||||
#if __cplusplus
|
||||
@ -35,31 +35,22 @@ extern "C" {
|
||||
*/
|
||||
+ (void)addCustomEnvironmentInformation:(NSString *)information forKey:(NSString*)key;
|
||||
|
||||
|
||||
/**
|
||||
* Starts a TestFlight session
|
||||
* Starts a TestFlight session using the Application Token for this Application
|
||||
*
|
||||
* @param teamToken Will be your team token obtained from https://testflightapp.com/dashboard/team/edit/
|
||||
* @param applicationToken Will be the application token for the current application.
|
||||
* The token for this application can be retrieved by going to https://testflightapp.com/dashboard/applications/
|
||||
* selecting this application from the list then selecting SDK.
|
||||
*/
|
||||
+ (void)takeOff:(NSString *)teamToken;
|
||||
|
||||
+ (void)takeOff:(NSString *)applicationToken;
|
||||
|
||||
/**
|
||||
* Sets custom options
|
||||
*
|
||||
* @param options NSDictionary containing the options you want to set available options are described below
|
||||
* @param options NSDictionary containing the options you want to set. Available options are described below at "TestFlight Option Keys"
|
||||
*
|
||||
* Option Accepted Values Description
|
||||
* reinstallCrashHandlers [ NSNumber numberWithBool:YES ] Reinstalls crash handlers, to be used if a third party
|
||||
* library installs crash handlers overtop of the TestFlight Crash Handlers
|
||||
* logToConsole [ NSNumber numberWithBool:YES ] YES - default, sends log statements to Apple System Log and TestFlight log
|
||||
* NO - sends log statements to TestFlight log only
|
||||
* logToSTDERR [ NSNumber numberWithBool:YES ] YES - default, sends log statements to STDERR when debugger is attached
|
||||
* NO - sends log statements to TestFlight log only
|
||||
* sendLogOnlyOnCrash [ NSNumber numberWithBool:YES ] NO - default, sends logs to TestFlight at the end of every session
|
||||
* YES - sends logs statements to TestFlight only if there was a crash
|
||||
* attachBacktraceToFeedback [ NSNumber numberWithBool:YES ] NO - default, feedback is sent exactly as the user enters it
|
||||
* YES - attaches the current backtrace, with symbols, to the feedback.
|
||||
* disableInAppUpdates [ NSNumber numberWithBool:YES ] NO - default, in application updates are allowed
|
||||
* YES - the in application update screen will not be displayed
|
||||
*/
|
||||
+ (void)setOptions:(NSDictionary*)options;
|
||||
|
||||
@ -84,18 +75,40 @@ extern "C" {
|
||||
+ (void)submitFeedback:(NSString*)feedback;
|
||||
|
||||
/**
|
||||
* Sets the Device Identifier.
|
||||
* The SDK no longer obtains the device unique identifier. This method should only be used during testing so that you can
|
||||
* identify a testers test data with them. If you do not provide the identifier you will still see all session data, with checkpoints
|
||||
* Sets the Device Identifier.
|
||||
*
|
||||
* !! DO NOT CALL IN SUBMITTED APP STORE APP.
|
||||
*
|
||||
* !! MUST BE CALLED BEFORE +takeOff:
|
||||
*
|
||||
* This method should only be used during testing so that you can identify a testers test data with them.
|
||||
* If you do not provide the identifier you will still see all session data, with checkpoints
|
||||
* and logs, but the data will be anonymized.
|
||||
* It is recommended that you only use this method during testing. We also recommended that you wrap this method with a pre-processor
|
||||
* directive that is only active for non-app store builds.
|
||||
* #ifndef RELEASE
|
||||
*
|
||||
* It is recommended that you only use this method during testing.
|
||||
* Apple may reject your app if left in a submitted app.
|
||||
*
|
||||
* Use:
|
||||
* Only use this with the Apple device UDID. DO NOT use Open ID or your own identifier.
|
||||
* [TestFlight setDeviceIdentifier:[[UIDevice currentDevice] uniqueIdentifier]];
|
||||
* #endif
|
||||
*
|
||||
* @param deviceIdentifer The current devices device identifier
|
||||
*/
|
||||
+ (void)setDeviceIdentifier:(NSString*)deviceIdentifer;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/**
|
||||
* TestFlight Option Keys
|
||||
*
|
||||
* Pass these as keys to the dictionary you pass to +`[TestFlight setOptions:]`.
|
||||
* The values should be NSNumber BOOLs (`[NSNumber numberWithBool:YES]` or `@YES`)
|
||||
*/
|
||||
extern NSString *const TFOptionAttachBacktraceToFeedback; // Defaults to @NO. Setting to @YES attaches the current backtrace, with symbols, to the feedback.
|
||||
extern NSString *const TFOptionDisableInAppUpdates; // Defaults to @NO. Setting to @YES, disables the in app update screen shown in BETA apps when there is a new version available on TestFlight.
|
||||
extern NSString *const TFOptionLogToConsole; // Defaults to @YES. Prints remote logs to Apple System Log.
|
||||
extern NSString *const TFOptionLogToSTDERR; // Defaults to @YES. Sends remote logs to STDERR when debugger is attached.
|
||||
extern NSString *const TFOptionReinstallCrashHandlers; // If set to @YES: Reinstalls crash handlers, to be used if a third party library installs crash handlers overtop of the TestFlight Crash Handlers.
|
||||
extern NSString *const TFOptionSendLogOnlyOnCrash; // Defaults to @NO. Setting to @YES stops remote logs from being sent when sessions end. They would only be sent in the event of a crash.
|
||||
|
||||
|
BIN
External/TestFlight/libTestFlight.a
vendored
BIN
External/TestFlight/libTestFlight.a
vendored
Binary file not shown.
31
External/TestFlight/release_notes.md
vendored
31
External/TestFlight/release_notes.md
vendored
@ -1,3 +1,34 @@
|
||||
## 1.2.4 - February 19, 2013
|
||||
|
||||
- Fixed bug that caused crash reports to sometimes not send immediately (they would be resent later)
|
||||
|
||||
## 1.2.3 - January 8, 2013
|
||||
|
||||
- Fixed typos in readme
|
||||
- Fixed bug where logs not sent on crash
|
||||
- Fixed bug where empty crash files were created (but not sent)
|
||||
- Cache cache path
|
||||
- Use consts for `setOptions:`
|
||||
- Updated `setDeviceIdentifier:` comments to make them clearer
|
||||
- Remove potentially conflicting function name `UIColorFromRGB`
|
||||
- Fixed crash on bad in app update data
|
||||
|
||||
## 1.2.2 - December 26, 2012
|
||||
|
||||
- Fix typo in app token error message
|
||||
|
||||
## 1.2.1 - December 26, 2012
|
||||
|
||||
- The max number of concurrent network connections has been reduced from 4 to 2.
|
||||
|
||||
##1.2 - November 12, 2012
|
||||
|
||||
* Removed Team Token support. As of version 1.2 takeOff must be called with the Application Token, https://testflightapp.com/dashboard/applications/, choose your application, select SDK, get the Token for this Application.
|
||||
|
||||
##1.2 BETA 3 - October 11, 2012
|
||||
|
||||
* Added application token support. Application Tokens are currently optional if you do not have one you do not need one
|
||||
|
||||
##1.2 BETA 2 - October 9, 2012
|
||||
|
||||
* Resolved an instance of close_file being called on a bad file descriptor
|
||||
|
@ -0,0 +1 @@
|
||||
../../../../../../MasterPassword/Resources/Data/ciphers.plist
|
@ -1 +0,0 @@
|
||||
../../../../../../Resources/ciphers.plist
|
@ -6,7 +6,7 @@
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPAppDelegate.h"
|
||||
#import "MPAppDelegate_Shared.h"
|
||||
|
||||
@implementation MPConfig
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
NSStringFromSelector( @selector(iCloudDecided) ) : @NO
|
||||
}];
|
||||
|
||||
self.delegate = [MPAppDelegate get];
|
||||
self.delegate = [MPAppDelegate_Shared get];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
//
|
||||
|
||||
#import "MPEntities.h"
|
||||
#import "MPAppDelegate.h"
|
||||
#import "MPAppDelegate_Shared.h"
|
||||
|
||||
@implementation NSManagedObjectContext(MP)
|
||||
|
||||
@ -112,7 +112,7 @@
|
||||
|
||||
- (id)content {
|
||||
|
||||
MPKey *key = [MPAppDelegate get].key;
|
||||
MPKey *key = [MPAppDelegate_Shared get].key;
|
||||
if (!key)
|
||||
return nil;
|
||||
|
||||
@ -122,7 +122,7 @@
|
||||
|
||||
- (void)setContent:(id)content {
|
||||
|
||||
MPKey *key = [MPAppDelegate get].key;
|
||||
MPKey *key = [MPAppDelegate_Shared get].key;
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
|
@ -8,12 +8,7 @@
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface MPPasswordWindowController : NSWindowController<NSTextFieldDelegate> {
|
||||
|
||||
NSString *_content;
|
||||
}
|
||||
|
||||
@property(nonatomic, strong) NSString *content;
|
||||
@interface MPPasswordWindowController : NSWindowController<NSTextFieldDelegate>
|
||||
|
||||
@property(nonatomic, weak) IBOutlet NSTextField *siteField;
|
||||
@property(nonatomic, weak) IBOutlet NSTextField *contentField;
|
||||
|
@ -16,13 +16,14 @@
|
||||
|
||||
@interface MPPasswordWindowController()
|
||||
|
||||
@property(nonatomic, strong) NSArray /* MPElementEntity */ *siteResults;
|
||||
@property(nonatomic) BOOL inProgress;
|
||||
@property(nonatomic) BOOL siteFieldPreventCompletion;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPPasswordWindowController
|
||||
@implementation MPPasswordWindowController {
|
||||
NSManagedObjectID *_activeElementOID;
|
||||
}
|
||||
|
||||
- (void)windowDidLoad {
|
||||
|
||||
@ -30,7 +31,7 @@
|
||||
self.window.styleMask = NSHUDWindowMask | NSTitledWindowMask | NSUtilityWindowMask | NSClosableWindowMask;
|
||||
else
|
||||
self.window.styleMask = NSTexturedBackgroundWindowMask | NSResizableWindowMask | NSTitledWindowMask | NSClosableWindowMask;
|
||||
|
||||
|
||||
[self setContent:@""];
|
||||
[self.tipField setStringValue:@""];
|
||||
|
||||
@ -110,19 +111,23 @@
|
||||
switch (returnCode) {
|
||||
case NSAlertAlternateReturn:
|
||||
// "Change" button.
|
||||
if ([[NSAlert alertWithMessageText:@"Changing Master Password"
|
||||
defaultButton:nil alternateButton:[PearlStrings get].commonButtonCancel otherButton:nil
|
||||
informativeTextWithFormat:
|
||||
@"This will allow you to log in with a different master password.\n\n"
|
||||
@"Note that you will only see the sites and passwords for the master password you log in with.\n"
|
||||
@"If you log in with a different master password, your current sites will be unavailable.\n\n"
|
||||
@"You can always change back to your current master password later.\n"
|
||||
@"Your current sites and passwords will then become available again."] runModal]
|
||||
== 1) {
|
||||
{
|
||||
NSInteger returnCode_ = [[NSAlert
|
||||
alertWithMessageText:@"Changing Master Password" defaultButton:nil
|
||||
alternateButton:[PearlStrings get].commonButtonCancel otherButton:nil informativeTextWithFormat:
|
||||
@"This will allow you to log in with a different master password.\n\n"
|
||||
@"Note that you will only see the sites and passwords for the master password you log in with.\n"
|
||||
@"If you log in with a different master password, your current sites will be unavailable.\n\n"
|
||||
@"You can always change back to your current master password later.\n"
|
||||
@"Your current sites and passwords will then become available again."]
|
||||
runModal];
|
||||
|
||||
if (returnCode_ == NSAlertDefaultReturn) {
|
||||
activeUser.keyID = nil;
|
||||
[[MPMacAppDelegate get] forgetSavedKeyFor:activeUser];
|
||||
[[MPMacAppDelegate get] signOutAnimated:YES];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NSAlertOtherReturn:
|
||||
@ -137,7 +142,7 @@
|
||||
self.inProgress = YES;
|
||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0 ), ^{
|
||||
BOOL success = [[MPMacAppDelegate get] signInAsUser:activeUser
|
||||
usingMasterPassword:[(NSSecureTextField *)alert.accessoryView stringValue]];
|
||||
usingMasterPassword:[(NSSecureTextField *)alert.accessoryView stringValue]];
|
||||
self.inProgress = NO;
|
||||
|
||||
dispatch_async( dispatch_get_main_queue(), ^{
|
||||
@ -171,29 +176,31 @@
|
||||
if (![query length] || ![MPMacAppDelegate get].key)
|
||||
return nil;
|
||||
|
||||
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
|
||||
fetchRequest.sortDescriptors = [NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"uses_" ascending:NO]];
|
||||
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(name BEGINSWITH[cd] %@) AND user == %@",
|
||||
query, [[MPMacAppDelegate get] activeUserForThread]];
|
||||
__block NSMutableArray *mutableResults = [NSMutableArray array];
|
||||
[MPMacAppDelegate managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
|
||||
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
|
||||
fetchRequest.sortDescriptors = [NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"lastUsed" ascending:NO]];
|
||||
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(name BEGINSWITH[cd] %@) AND user == %@",
|
||||
query, [[MPMacAppDelegate get] activeUserInContext:context]];
|
||||
|
||||
NSError *error = nil;
|
||||
self.siteResults = [[MPMacAppDelegate managedObjectContextForThreadIfReady] executeFetchRequest:fetchRequest error:&error];
|
||||
if (error)
|
||||
err(@"While fetching elements for completion: %@", error);
|
||||
NSError *error = nil;
|
||||
NSArray *siteResults = [context executeFetchRequest:fetchRequest error:&error];
|
||||
if (error)
|
||||
err(@"While fetching elements for completion: %@", error);
|
||||
|
||||
if ([self.siteResults count] == 1) {
|
||||
[textView setString:[(MPElementEntity *)[self.siteResults objectAtIndex:0] name]];
|
||||
[textView setSelectedRange:NSMakeRange( [query length], [[textView string] length] - [query length] )];
|
||||
if ([self trySite])
|
||||
return nil;
|
||||
if (siteResults) {
|
||||
for (MPElementEntity *element in siteResults)
|
||||
[mutableResults addObject:element.name];
|
||||
//[mutableResults addObject:query]; // For when the app should be able to create new sites.
|
||||
}
|
||||
}];
|
||||
|
||||
if ([mutableResults count] == 1) {
|
||||
//[textView setString:[(MPElementEntity *)[siteResults objectAtIndex:0] name]];
|
||||
//[textView setSelectedRange:NSMakeRange( [query length], [[textView string] length] - [query length] )];
|
||||
[self trySiteAndCopyContent:NO];
|
||||
}
|
||||
|
||||
NSMutableArray *mutableResults = [NSMutableArray arrayWithCapacity:[self.siteResults count] + 1];
|
||||
if (self.siteResults)
|
||||
for (MPElementEntity *element in self.siteResults)
|
||||
[mutableResults addObject:element.name];
|
||||
// [mutableResults addObject:query]; // For when the app should be able to create new sites.
|
||||
|
||||
return mutableResults;
|
||||
}
|
||||
|
||||
@ -205,9 +212,8 @@
|
||||
}
|
||||
if ((self.siteFieldPreventCompletion = [NSStringFromSelector( commandSelector ) hasPrefix:@"delete"]))
|
||||
return NO;
|
||||
if (commandSelector == @selector(insertNewline:) && [self.content length]) {
|
||||
if ([self trySite])
|
||||
[self copyContents];
|
||||
if (commandSelector == @selector(insertNewline:)) {
|
||||
[self trySiteAndCopyContent:YES];
|
||||
return YES;
|
||||
}
|
||||
|
||||
@ -215,26 +221,6 @@
|
||||
}
|
||||
|
||||
- (void)copyContents {
|
||||
|
||||
[[NSPasteboard generalPasteboard] declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
|
||||
if (![[NSPasteboard generalPasteboard] setString:self.content forType:NSPasteboardTypeString]) {
|
||||
wrn(@"Couldn't copy password to pasteboard.");
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch_async( dispatch_get_main_queue(), ^{
|
||||
self.tipField.alphaValue = 1;
|
||||
[self.tipField setStringValue:@"Copied! Hit ⎋ (ESC) to close window."];
|
||||
dispatch_time_t popTime = dispatch_time( DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC) );
|
||||
dispatch_after( popTime, dispatch_get_main_queue(), ^{
|
||||
[NSAnimationContext beginGrouping];
|
||||
[[NSAnimationContext currentContext] setDuration:0.2f];
|
||||
[self.tipField.animator setAlphaValue:0];
|
||||
[NSAnimationContext endGrouping];
|
||||
} );
|
||||
} );
|
||||
|
||||
[[self findElement] use];
|
||||
}
|
||||
|
||||
- (void)controlTextDidEndEditing:(NSNotification *)note {
|
||||
@ -242,7 +228,7 @@
|
||||
if (note.object != self.siteField)
|
||||
return;
|
||||
|
||||
[self trySite];
|
||||
[self trySiteAndCopyContent:NO];
|
||||
}
|
||||
|
||||
- (void)controlTextDidChange:(NSNotification *)note {
|
||||
@ -251,13 +237,12 @@
|
||||
return;
|
||||
|
||||
// Update the site content as the site name changes.
|
||||
BOOL hasValidSite = [self trySite];
|
||||
BOOL enterPressed = [[NSApp currentEvent] type] == NSKeyDown &&
|
||||
[[[NSApp currentEvent] charactersIgnoringModifiers] isEqualToString:@"\r"];
|
||||
[self trySiteAndCopyContent:enterPressed];
|
||||
|
||||
if ([[NSApp currentEvent] type] == NSKeyDown && [[[NSApp currentEvent] charactersIgnoringModifiers] isEqualToString:@"\r"]) {
|
||||
if (hasValidSite)
|
||||
[self copyContents];
|
||||
if (enterPressed)
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.siteFieldPreventCompletion) {
|
||||
self.siteFieldPreventCompletion = NO;
|
||||
@ -269,43 +254,70 @@
|
||||
self.siteFieldPreventCompletion = NO;
|
||||
}
|
||||
|
||||
- (NSString *)content {
|
||||
- (MPElementEntity *)activeElementForThread {
|
||||
|
||||
return _content;
|
||||
if (!_activeElementOID)
|
||||
return nil;
|
||||
|
||||
NSManagedObjectContext *moc = [MPMacAppDelegate managedObjectContextForThreadIfReady];
|
||||
if (!moc)
|
||||
return nil;
|
||||
|
||||
NSError *error;
|
||||
MPElementEntity *activeElement = (MPElementEntity *)[moc existingObjectWithID:_activeElementOID error:&error];
|
||||
if (!activeElement)
|
||||
err(@"Couldn't retrieve active element: %@", error);
|
||||
|
||||
return activeElement;
|
||||
}
|
||||
|
||||
- (void)setContent:(NSString *)content {
|
||||
|
||||
_content = content;
|
||||
|
||||
NSMutableParagraphStyle *paragraph = [NSMutableParagraphStyle new];
|
||||
paragraph.alignment = NSCenterTextAlignment;
|
||||
|
||||
[self.contentField setAttributedStringValue:
|
||||
[[NSAttributedString alloc] initWithString:_content
|
||||
attributes:[[NSMutableDictionary alloc] initWithObjectsAndKeys:
|
||||
paragraph, NSParagraphStyleAttributeName,
|
||||
nil]]];
|
||||
[self.contentField setAttributedStringValue:[[NSAttributedString alloc] initWithString:content attributes:@{
|
||||
NSParagraphStyleAttributeName : paragraph
|
||||
}]];
|
||||
}
|
||||
|
||||
- (BOOL)trySite {
|
||||
- (void)trySiteAndCopyContent:(BOOL)copyContent {
|
||||
|
||||
MPElementEntity *result = [self findElement];
|
||||
if (!result) {
|
||||
[self setContent:@""];
|
||||
[self.tipField setStringValue:@""];
|
||||
return NO;
|
||||
}
|
||||
[self setContent:@""];
|
||||
[self.tipField setStringValue:@"Generating..."];
|
||||
|
||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0 ), ^{
|
||||
NSString *description = [result.content description];
|
||||
if (!description)
|
||||
description = @"";
|
||||
NSString *content = [[self activeElementForThread].content description];
|
||||
if (!content)
|
||||
content = @"";
|
||||
if (copyContent) {
|
||||
[[NSPasteboard generalPasteboard] declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
|
||||
if (![[NSPasteboard generalPasteboard] setString:content forType:NSPasteboardTypeString]) {
|
||||
wrn(@"Couldn't copy password to pasteboard.");
|
||||
return;
|
||||
}
|
||||
|
||||
MPElementEntity *activeElement = [self activeElementForThread];
|
||||
[activeElement use];
|
||||
[activeElement.managedObjectContext saveToStore];
|
||||
}
|
||||
|
||||
dispatch_async( dispatch_get_main_queue(), ^{
|
||||
[self setContent:description];
|
||||
[self.tipField setStringValue:@"Hit ⌤ (ENTER) to copy the password."];
|
||||
[self setContent:content];
|
||||
|
||||
self.tipField.alphaValue = 1;
|
||||
if (!copyContent)
|
||||
[self.tipField setStringValue:@"Hit ⌤ (ENTER) to copy the password."];
|
||||
else {
|
||||
[self.tipField setStringValue:@"Copied! Hit ⎋ (ESC) to close window."];
|
||||
dispatch_time_t popTime = dispatch_time( DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC) );
|
||||
dispatch_after( popTime, dispatch_get_main_queue(), ^{
|
||||
[NSAnimationContext beginGrouping];
|
||||
[[NSAnimationContext currentContext] setDuration:0.2f];
|
||||
[self.tipField.animator setAlphaValue:0];
|
||||
[NSAnimationContext endGrouping];
|
||||
} );
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
||||
@ -329,17 +341,6 @@
|
||||
});
|
||||
}];
|
||||
*/
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (MPElementEntity *)findElement {
|
||||
|
||||
for (MPElementEntity *element in self.siteResults)
|
||||
if ([element.name isEqualToString:[self.siteField stringValue]])
|
||||
return element;
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -126,7 +126,6 @@
|
||||
self.tip.text = @"";
|
||||
self.nameLabel.layer.cornerRadius = 5;
|
||||
self.avatarTemplate.hidden = YES;
|
||||
self.spinner.alpha = 0;
|
||||
self.passwordTipView.hidden = NO;
|
||||
self.createPasswordTipView.hidden = NO;
|
||||
[self.emergencyPassword setTitle:@"" forState:UIControlStateNormal];
|
||||
@ -200,13 +199,14 @@
|
||||
[self updateUsers];
|
||||
|
||||
self.uiContainer.alpha = 0;
|
||||
self.spinner.alpha = 0;
|
||||
|
||||
[super viewWillAppear:animated];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
|
||||
if (!animated)
|
||||
if (!animated && !self.navigationController.presentedViewController)
|
||||
[[self findTargetedAvatar] setSelected:YES];
|
||||
else
|
||||
[self updateLayoutAnimated:YES allowScroll:YES completion:nil];
|
||||
|
@ -611,47 +611,6 @@
|
||||
postNotificationName:MPCheckConfigNotification object:NSStringFromSelector( configKey ) userInfo:nil];
|
||||
}
|
||||
|
||||
#pragma mark - UbiquityStoreManagerDelegate
|
||||
- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager willLoadStoreIsCloud:(BOOL)isCloudStore {
|
||||
|
||||
[super ubiquityStoreManager:manager willLoadStoreIsCloud:isCloudStore];
|
||||
|
||||
if (!isCloudStore && ![[MPConfig get].iCloudDecided boolValue])
|
||||
[self alertCloudDisabledForManager:manager];
|
||||
}
|
||||
|
||||
- (void)alertCloudDisabledForManager:(UbiquityStoreManager *)manager {
|
||||
|
||||
[PearlAlert showAlertWithTitle:@"iCloud" message:
|
||||
@"iCloud is now disabled.\n\n"
|
||||
@"It is highly recommended you enable iCloud."
|
||||
viewStyle:UIAlertViewStyleDefault initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
|
||||
if (buttonIndex == [alert firstOtherButtonIndex] + 0) {
|
||||
[PearlAlert showAlertWithTitle:@"About iCloud" message:
|
||||
@"iCloud is Apple's solution for saving your data in \"the cloud\" "
|
||||
@"and making sure your other iPhones, iPads and Macs are in sync.\n\n"
|
||||
@"For Master Password, that means your sites are available on all your "
|
||||
@"Apple devices, and you always have a backup of them in case "
|
||||
@"you lose one or need to restore.\n\n"
|
||||
@"Thanks to the way Master Password works, it doesn't need to send your "
|
||||
@"site's passwords to Apple for the backup to work: Only their names are "
|
||||
@"saved. If you set a custom password it will be sent to iCloud after "
|
||||
@"being encrypted with your master password.\n\n"
|
||||
@"Apple can never see any of your passwords."
|
||||
viewStyle:UIAlertViewStyleDefault
|
||||
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
|
||||
[self alertCloudDisabledForManager:manager];
|
||||
}
|
||||
cancelTitle:[PearlStrings get].commonButtonThanks otherTitles:nil];
|
||||
return;
|
||||
}
|
||||
|
||||
[MPConfig get].iCloudDecided = @YES;
|
||||
if (buttonIndex == [alert firstOtherButtonIndex] + 1)
|
||||
manager.cloudEnabled = YES;
|
||||
} cancelTitle:@"Leave Off" otherTitles:@"Explain?", @"Enable iCloud", nil];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Google+
|
||||
|
||||
|
@ -2810,6 +2810,190 @@ However, it means that anyone who finds your device unlocked can do the same.</s
|
||||
<image name="ui_textfield.png" width="158" height="34"/>
|
||||
<image name="unlocked.png" width="84" height="80"/>
|
||||
</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="MPElementListAllViewController" superclassName="MPElementListController">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPElementListAllViewController.h"/>
|
||||
<relationships>
|
||||
<relationship kind="action" name="add:"/>
|
||||
<relationship kind="action" name="close:"/>
|
||||
<relationship kind="outlet" name="navigationBar" candidateClass="UINavigationBar"/>
|
||||
</relationships>
|
||||
</class>
|
||||
<class className="MPElementListController" superclassName="UITableViewController">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPElementListController.h"/>
|
||||
<relationships>
|
||||
<relationship kind="outlet" name="delegate"/>
|
||||
</relationships>
|
||||
</class>
|
||||
<class className="MPElementListSearchController" superclassName="MPElementListController">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPElementListSearchController.h"/>
|
||||
<relationships>
|
||||
<relationship kind="outlet" name="searchDisplayController" candidateClass="UISearchDisplayController"/>
|
||||
<relationship kind="outlet" name="searchTipContainer" candidateClass="UIView"/>
|
||||
</relationships>
|
||||
</class>
|
||||
<class className="MPGuideViewController" superclassName="UIViewController">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPGuideViewController.h"/>
|
||||
<relationships>
|
||||
<relationship kind="action" name="close"/>
|
||||
<relationship kind="action" name="play"/>
|
||||
<relationship kind="action" name="toggleVolume"/>
|
||||
<relationship kind="outlet" name="content" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="contentButton" candidateClass="UIButton"/>
|
||||
<relationship kind="outlet" name="contentText" candidateClass="UITextField"/>
|
||||
<relationship kind="outlet" name="contentTip" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="contentTipText" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="largePlayButton" candidateClass="UIButton"/>
|
||||
<relationship kind="outlet" name="progress" candidateClass="UIProgressView"/>
|
||||
<relationship kind="outlet" name="siteNameTip" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="smallPlayButton" candidateClass="UIButton"/>
|
||||
<relationship kind="outlet" name="toolButton" candidateClass="UIButton"/>
|
||||
<relationship kind="outlet" name="toolTip" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="typeButton" candidateClass="UIButton"/>
|
||||
<relationship kind="outlet" name="typeTip" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="usernameButton" candidateClass="UIButton"/>
|
||||
<relationship kind="outlet" name="usernameTip" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="volumeButton" candidateClass="UIButton"/>
|
||||
</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="pullDownGesture" candidateClass="UIPanGestureRecognizer"/>
|
||||
<relationship kind="outlet" name="pullDownView" candidateClass="UIImageView"/>
|
||||
<relationship kind="outlet" name="pullUpGesture" candidateClass="UIPanGestureRecognizer"/>
|
||||
<relationship kind="outlet" name="pullUpView" candidateClass="UIImageView"/>
|
||||
<relationship kind="outlet" name="searchDelegate" candidateClass="MPElementListSearchController"/>
|
||||
<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="MPSetupViewController" superclassName="UIViewController">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/MPSetupViewController.h"/>
|
||||
<relationships>
|
||||
<relationship kind="action" name="close:" candidateClass="UIBarButtonItem"/>
|
||||
<relationship kind="outlet" name="cloudSwitch" candidateClass="UISwitch"/>
|
||||
<relationship kind="outlet" name="rememberLoginSwitch" candidateClass="UISwitch"/>
|
||||
</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="emergencyClose:" candidateClass="UIButton"/>
|
||||
<relationship kind="action" name="emergencyCopy:" 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="emergencyActivity" candidateClass="UIActivityIndicatorView"/>
|
||||
<relationship kind="outlet" name="emergencyContentTipContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="emergencyCounter" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="emergencyCounterStepper" candidateClass="UIStepper"/>
|
||||
<relationship kind="outlet" name="emergencyGeneratorContainer" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="emergencyMasterPassword" candidateClass="UITextField"/>
|
||||
<relationship kind="outlet" name="emergencyName" candidateClass="UITextField"/>
|
||||
<relationship kind="outlet" name="emergencyPassword" candidateClass="UIButton"/>
|
||||
<relationship kind="outlet" name="emergencySite" candidateClass="UITextField"/>
|
||||
<relationship kind="outlet" name="emergencyTypeControl" candidateClass="UISegmentedControl"/>
|
||||
<relationship kind="outlet" name="nameLabel" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="newsView" candidateClass="UIWebView"/>
|
||||
<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>
|
||||
<class className="PearlNavigationController" superclassName="UINavigationController">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/PearlNavigationController.h"/>
|
||||
</class>
|
||||
</classes>
|
||||
<simulatedMetricsContainer key="defaultSimulatedMetrics">
|
||||
<nil key="statusBar"/>
|
||||
<simulatedOrientationMetrics key="orientation"/>
|
||||
|
Loading…
Reference in New Issue
Block a user