Compare commits
1 Commits
2.7-java-1
...
1.0.0-r1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
54db3d3199 |
@@ -1,28 +0,0 @@
|
||||
# OS-Specific junk.
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# IntelliJ
|
||||
.idea
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
# Xcode IDE
|
||||
xcuserdata/
|
||||
DerivedData/
|
||||
|
||||
# Generated
|
||||
/platform-darwin/Resources/Media/Images.xcassets/
|
||||
/platform-darwin/Podfile.lock
|
||||
/platform-darwin/Pods/
|
||||
|
||||
# Gradle
|
||||
build
|
||||
.gradle
|
||||
local.properties
|
||||
/builds
|
||||
/platform-android/.externalNativeBuild
|
||||
|
||||
# Git
|
||||
.git
|
||||
38
.gitignore
vendored
@@ -1,29 +1,25 @@
|
||||
# OS-Specific junk.
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
*~
|
||||
|
||||
# IntelliJ
|
||||
.idea
|
||||
/.idea/*
|
||||
!/.idea/encodings.xml
|
||||
!/.idea/inspectionProfiles
|
||||
!/.idea/projectCodeStyle.xml
|
||||
!/.idea/validation.xml
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
out
|
||||
/*.ipr
|
||||
/*.iws
|
||||
|
||||
# Xcode IDE
|
||||
xcuserdata/
|
||||
DerivedData/
|
||||
|
||||
# Generated
|
||||
/platform-darwin/Resources/Media/Images.xcassets/
|
||||
/platform-darwin/Podfile.lock
|
||||
/platform-darwin/Pods/
|
||||
|
||||
# Gradle
|
||||
build
|
||||
.gradle
|
||||
local.properties
|
||||
/builds
|
||||
/platform-android/.externalNativeBuild
|
||||
.cxx
|
||||
/*.xcodeproj/*
|
||||
!/*.xcodeproj/*.pbxproj
|
||||
!/*.xcodeproj/xcshareddata
|
||||
!/*.xcodeproj/xcshareddata/*
|
||||
!/*.xcodeproj/project.xcworkspace
|
||||
!/*.xcodeproj/project.xcworkspace/*
|
||||
/*.xcodeproj/project.xcworkspace/xcuserdata
|
||||
|
||||
# IPA
|
||||
/sendipa/*
|
||||
!/sendipa/sendipa.conf
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
variables:
|
||||
GIT_DEPTH: 3
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
|
||||
build_project:
|
||||
stage: build
|
||||
script:
|
||||
- "( brew bundle )"
|
||||
- "( ./lib/bin/build_libsodium-macos clean && ./lib/bin/build_libsodium-macos )"
|
||||
- "( ./lib/bin/build_libjson-c-macos clean && ./lib/bin/build_libjson-c-macos )"
|
||||
- "( cd ./platform-independent/c/cli && ./clean && targets=all ./build && ./mpw-tests && ./mpw-cli-tests )"
|
||||
- "( ./gradlew --stacktrace --info clean test )"
|
||||
- "( cd platform-darwin && pod install )"
|
||||
- "( xcodebuild -workspace platform-darwin/MasterPassword.xcworkspace -configuration 'Release' -scheme 'MasterPassword iOS' -sdk iphonesimulator clean build )"
|
||||
- "( xcodebuild -workspace platform-darwin/MasterPassword.xcworkspace -configuration 'Release' -scheme 'MasterPassword macOS' clean build )"
|
||||
tags:
|
||||
- brew
|
||||
- java
|
||||
- cocoapods
|
||||
- xcode
|
||||
24
.gitmodules
vendored
@@ -1,20 +1,6 @@
|
||||
[submodule "External/Pearl"]
|
||||
path = platform-darwin/External/Pearl
|
||||
url = git://github.com/Lyndir/Pearl.git
|
||||
[submodule "External/AttributedMarkdown"]
|
||||
path = platform-darwin/External/AttributedMarkdown
|
||||
url = https://github.com/dreamwieber/AttributedMarkdown.git
|
||||
[submodule "MasterPassword/Web/js/mpw-js"]
|
||||
path = platform-independent/web/js/mpw-js
|
||||
url = https://github.com/tmthrgd/mpw-js.git
|
||||
[submodule "lib/libsodium"]
|
||||
path = lib/libsodium
|
||||
url = https://github.com/jedisct1/libsodium.git
|
||||
[submodule "lib/libjson-c"]
|
||||
path = lib/libjson-c
|
||||
url = https://github.com/json-c/json-c.git
|
||||
[submodule "public/site"]
|
||||
path = public/site
|
||||
url = https://gitlab.com/MasterPassword/MasterPassword.git
|
||||
branch = gh-pages
|
||||
shallow = true
|
||||
path = External/Pearl
|
||||
url = git@github.com:Lyndir/Pearl.git
|
||||
[submodule "External/InAppSettingsKit"]
|
||||
path = External/InAppSettingsKit
|
||||
url = git://github.com/futuretap/InAppSettingsKit.git
|
||||
|
||||
5
.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
|
||||
</project>
|
||||
|
||||
9
.idea/projectCodeStyle.xml
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CodeStyleSettingsManager">
|
||||
<option name="PER_PROJECT_SETTINGS">
|
||||
<value />
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
|
||||
6
Brewfile
@@ -1,6 +0,0 @@
|
||||
brew "libsodium"
|
||||
brew "json-c"
|
||||
|
||||
brew "libtool"
|
||||
brew "automake"
|
||||
brew "autoconf"
|
||||
BIN
Default.png
Normal file
|
After Width: | Height: | Size: 130 KiB |
BIN
Default@2x.png
Normal file
|
After Width: | Height: | Size: 502 KiB |
12
Dockerfile
@@ -1,12 +0,0 @@
|
||||
# Set up a container for doing gradle cross-compiling.
|
||||
#
|
||||
# docker build -t lhunath/mp-gradle --file Dockerfile /var/empty
|
||||
FROM debian:stable-slim
|
||||
|
||||
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=863199
|
||||
RUN mkdir -p /usr/share/man/man1
|
||||
|
||||
RUN apt-get update && apt-get install -y default-jdk-headless git-core bash libtool automake autoconf make g++-multilib
|
||||
RUN git clone --depth=3 $(: --shallow-submodules) --recurse-submodules https://gitlab.com/MasterPassword/MasterPassword.git /mpw
|
||||
RUN cd /mpw && ./gradlew -i clean
|
||||
RUN cd /mpw && git pull && git log -1 && ./gradlew -i check
|
||||
1
External/InAppSettingsKit
vendored
Submodule
1
External/Pearl
vendored
Submodule
199
External/TestFlightSDK/README.txt
vendored
Normal file
@@ -0,0 +1,199 @@
|
||||
Thanks for downloading the TestFlight SDK 0.8.2!
|
||||
|
||||
This document is also available on the web at https://www.testflightapp.com/sdk/doc
|
||||
|
||||
1. Why use the TestFlight SDK?
|
||||
2. Considerations
|
||||
3. How do I integrate the SDK into my project?
|
||||
4. Using the Checkpoint API
|
||||
5. Using the Feedback API
|
||||
6. Upload your build
|
||||
7. Questions API
|
||||
8. View your results
|
||||
9. Advanced Exception Handling
|
||||
10. Remote Logging
|
||||
|
||||
START
|
||||
|
||||
|
||||
1. Why use the TestFlight SDK?
|
||||
|
||||
The TestFlight SDK allows you to track how beta testers are testing your application. Out of the box we track simple usage information, such as which tester is using your application, their device model/OS, how long they used the application, logs of their test session, and automatic recording of any crashes they encounter.
|
||||
|
||||
To get the most out of the SDK we have provided the Checkpoint API.
|
||||
|
||||
The Checkpoint API is used to help you track exactly how your testers are using your application. Curious about which users passed level 5 in your game, or posted their high score to Twitter, or found that obscure feature? With a single line of code you can finally gather all this information. Wondering how many times your app has crashed? Wondering who your power testers are? We've got you covered. See more information on the Checkpoint API in section 4.
|
||||
|
||||
Alongside the Checkpoint API is the Questions interface. The Questions interface is managed on a per build basis on the TestFlight website. Find out more about the Questions Interface in section 6.
|
||||
|
||||
For more detailed debugging we have a remote logging solution. Find out more about our logging system with TFLog in the Remote Logging section.
|
||||
|
||||
2. Considerations
|
||||
|
||||
|
||||
Information gathered by the SDK is sent to the website in real time. When an application is put into the background (iOS 4.x) or terminated (iOS 3.x) we try to send the finalizing information for the session during the time allowed for finalizing the application. Should all of the data not get sent the remaining data will be sent the next time the application is launched. As such, to get the most out of the SDK we recommend your application support iOS 4.0 and higher.
|
||||
|
||||
This SDK can be run from both the iPhone Simulator and Device and has been tested using Xcode 4.0.
|
||||
|
||||
3. How do I integrate the SDK into my project?
|
||||
|
||||
|
||||
1. Add the files to your project: File -> Add Files to "<your project name>"
|
||||
|
||||
1. Find and select the folder that contains the SDK
|
||||
2. Make sure that "Copy items into destination folder (if needed)" is checked
|
||||
3. Set Folders to "Create groups for any added folders"
|
||||
4. Select all targets that you want to add the SDK to
|
||||
|
||||
2. Verify that libTestFlight.a has been added to the Link Binary With Libraries Build Phase for the targets you want to use the SDK with
|
||||
|
||||
1. Select your Project in the Project Navigator
|
||||
2. Select the target you want to enable the SDK for
|
||||
3. Select the Build Phases tab
|
||||
4. Open the Link Binary With Libraries Phase
|
||||
5. If libTestFlight.a is not listed, drag and drop the library from your Project Navigator to the Link Binary With Libraries area
|
||||
6. Repeat Steps 2 - 5 until all targets you want to use the SDK with have the SDK linked
|
||||
|
||||
3. In your Application Delegate:
|
||||
|
||||
1. Import TestFlight: `#import "TestFlight.h"`
|
||||
NOTE: If you do not want to import TestFlight.h in every file you may add the above line into you pre-compiled header (`<projectname>_Prefix.pch`) file inside of the
|
||||
|
||||
#ifdef __OBJC__ 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 edit.
|
||||
|
||||
|
||||
3. Launch TestFlight with your Team Token
|
||||
|
||||
-(BOOL)application:(UIApplication *)application
|
||||
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||
// start of your application:didFinishLaunchingWithOptions
|
||||
// ...
|
||||
[TestFlight takeOff:@"Insert your Team 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.
|
||||
|
||||
4. 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
|
||||
|
||||
4. Use the Checkpoint API to create important checkpoints throughout your application.
|
||||
|
||||
When a tester does something you care about in your app you can pass a checkpoint. For example completing a level, adding a todo item, etc. The checkpoint progress is used to provide insight into how your testers are testing your apps. The passed checkpoints are also attached to crashes, which can help when creating steps to replicate.
|
||||
|
||||
`[TestFlight passCheckpoint:@"CHECKPOINT_NAME"];`
|
||||
Use `passCheckpoint:` to track when a user performs certain tasks in your application. This can be useful for making sure testers are hitting all parts of your application, as well as tracking which testers are being thorough.
|
||||
|
||||
5. Using the Feedback API
|
||||
|
||||
To launch unguided feedback call the `openFeedbackView` method. We recommend that you call this from a GUI element.
|
||||
|
||||
-(IBAction)launchFeedback {
|
||||
[TestFlight openFeedbackView];
|
||||
}
|
||||
|
||||
Once users have submitted feedback from inside of the application you can view it in the feedback area of your build page.
|
||||
|
||||
6. Upload your build.
|
||||
|
||||
After you have integrated the SDK into your application you need to upload your build to TestFlight. You can upload from your dashboard or or using the Upload API, full documentation at <https://testflightapp.com/api/doc/>
|
||||
|
||||
7. Add Questions to Checkpoints
|
||||
|
||||
In order to ask a question, you'll need to associate it with a checkpoint. Make sure your checkpoints are initialized by running your app and hitting them all yourself before you start adding questions.
|
||||
|
||||
There are three question types available: Yes/No, Multiple Choice, and Long Answer.
|
||||
|
||||
To create questions, visit your builds Questions page and click on 'Add Question'. If you choose Multiple Choice, you'll need to enter a list of possible answers for your testers to choose from — otherwise, you'll only need to enter your question's, well, question. If your build has no questions, you can also choose to migrate questions from another build (because seriously — who wants to do all that typing again)?
|
||||
|
||||
After restarting your application on an approved device, when you pass the checkpoint associated with your questions a TestFlight modal question form will appear on the screen asking the beta tester to answer your question.
|
||||
|
||||
After you upload a new build to TestFlight you will need to associate questions once again. However if your checkpoints and questions have remained the same you can choose "copy questions from an older build" and choose which build to copy the questions from.
|
||||
|
||||
8. View your results.
|
||||
|
||||
As testers install your build and start to test it you will see their session data on the web on the build report page for the build you've uploaded.
|
||||
|
||||
9. Advanced Exception Handling
|
||||
|
||||
An uncaught exception means that your application is in an unknown state and there is not much that you can do but try and exit gracefully. Our SDK does its best to get the data we collect in this situation to you while it is crashing, but it is designed in such a way that the important act of saving the data occurs in as safe way a way as possible before trying to send anything. If you do use uncaught exception or signal handlers install your handlers before calling `takeOff`. Our SDK will then call your handler while ours is running. For example:
|
||||
|
||||
/*
|
||||
My Apps Custom uncaught exception catcher, we do special stuff here, and TestFlight takes care of the rest
|
||||
**/
|
||||
void HandleExceptions(NSException *exception) {
|
||||
NSLog(@"This is where we save the application data during a exception");
|
||||
// Save application data on crash
|
||||
}
|
||||
/*
|
||||
My Apps Custom signal catcher, we do special stuff here, and TestFlight takes care of the rest
|
||||
**/
|
||||
void SignalHandler(int sig) {
|
||||
NSLog(@"This is where we save the application data during a signal");
|
||||
// Save application data on crash
|
||||
}
|
||||
|
||||
-(BOOL)application:(UIApplication *)application
|
||||
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||
// installs HandleExceptions as the Uncaught Exception Handler
|
||||
NSSetUncaughtExceptionHandler(&HandleExceptions);
|
||||
// create the signal action structure
|
||||
struct sigaction newSignalAction;
|
||||
// initialize the signal action structure
|
||||
memset(&newSignalAction, 0, sizeof(newSignalAction));
|
||||
// set SignalHandler as the handler in the signal action structure
|
||||
newSignalAction.sa_handler = &SignalHandler;
|
||||
// set SignalHandler as the handlers for SIGABRT, SIGILL and SIGBUS
|
||||
sigaction(SIGABRT, &newSignalAction, NULL);
|
||||
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"];
|
||||
// continue with your application initialization
|
||||
}
|
||||
|
||||
You do not need to add the above code if your application does not use exception handling already.
|
||||
|
||||
10. Remote Logging
|
||||
|
||||
To perform remote logging you can use the TFLog method which logs in a few different methods described below. In order to make the transition from NSLog to TFLog easy we have used the same method signature for TFLog as NSLog. You can easily switch over to TFLog by adding the following macro to your header
|
||||
|
||||
#define NSLog TFLog
|
||||
|
||||
That will do a switch from NSLog to TFLog, if you want more information, such as file name and line number you can use a macro like
|
||||
|
||||
#define NSLog(__FORMAT__, ...) TFLog((@"%s [Line %d] " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
|
||||
Which will produce output that looks like
|
||||
|
||||
-[HTFCheckpointsController showYesNoQuestion:] [Line 45] Pressed YES/NO
|
||||
|
||||
We have implemented three different loggers.
|
||||
|
||||
1. TestFlight logger
|
||||
2. Apple System Log logger
|
||||
3. STDERR logger
|
||||
|
||||
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:@"logsToConsole"]];
|
||||
|
||||
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.
|
||||
|
||||
END
|
||||
|
||||
Please contact us if you have any questions.
|
||||
|
||||
The TestFlight Team
|
||||
|
||||
w. http://www.testflightapp.com
|
||||
e. beta@testflightapp.com
|
||||
71
External/TestFlightSDK/TestFlight.h
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// TestFlight.h
|
||||
// libTestFlight
|
||||
//
|
||||
// Created by Jonathan Janzen on 06/11/11.
|
||||
// Copyright 2011 TestFlight. All rights reserved.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#define TESTFLIGHT_SDK_VERSION @"0.8.2"
|
||||
#undef TFLog
|
||||
|
||||
#if __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void TFLog(NSString *format, ...);
|
||||
#if __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* TestFlight object
|
||||
* All methods are class level
|
||||
*/
|
||||
@interface TestFlight : NSObject {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add custom environment information
|
||||
* If you want to track custom information such as a user name from your application you can add it here
|
||||
*
|
||||
* @param information A string containing the environment you are storing
|
||||
* @param key The key to store the information with
|
||||
*/
|
||||
+ (void)addCustomEnvironmentInformation:(NSString *)information forKey:(NSString*)key;
|
||||
|
||||
/**
|
||||
* Starts a TestFlight session
|
||||
*
|
||||
* @param teamToken Will be your team token obtained from https://testflightapp.com/dashboard/team/edit/
|
||||
*/
|
||||
+ (void)takeOff:(NSString *)teamToken;
|
||||
|
||||
/**
|
||||
* Sets custom options
|
||||
*
|
||||
* @param options NSDictionary containing the options you want to set available options are described below
|
||||
*
|
||||
* 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
|
||||
* 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
|
||||
*/
|
||||
+ (void)setOptions:(NSDictionary*)options;
|
||||
|
||||
/**
|
||||
* Track when a user has passed a checkpoint after the flight has taken off. Eg. passed level 1, posted high score
|
||||
*
|
||||
* @param checkpointName The name of the checkpoint, this should be a static string
|
||||
*/
|
||||
+ (void)passCheckpoint:(NSString *)checkpointName;
|
||||
|
||||
/**
|
||||
* Opens a feedback window that is not attached to a checkpoint
|
||||
*/
|
||||
+ (void)openFeedbackView;
|
||||
|
||||
@end
|
||||
BIN
External/TestFlightSDK/libTestFlight.a
vendored
Normal file
94
External/TestFlightSDK/release_notes.txt
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
0.8.2 - December 20, 2011
|
||||
Promoted 0.8.2 BETA 4 to stable
|
||||
|
||||
Known Issues:
|
||||
Under some circumstances Session End events may not be sent until the next launch.
|
||||
With large log files Session End events may take a long time to show up.
|
||||
|
||||
Tested compiled library with
|
||||
Xcode 4.3
|
||||
Xcode 4.2
|
||||
Xcode 4.1
|
||||
Xcode 3.2.6
|
||||
|
||||
0.8.2 BETA 4 - December 12, 2011
|
||||
Prevented "The string argument is NULL" from occuring during finishedHandshake in rare cases
|
||||
Resolved issue where background data may not be sent
|
||||
|
||||
0.8.2 BETA 3 - December 8, 2011
|
||||
Added auto-release pools to background setup and tear down
|
||||
Added C++ specific code to TFLog declaration
|
||||
|
||||
0.8.2 BETA 2 - December 5, 2011
|
||||
Fixed the "pointer being freed was not allocated" bug
|
||||
|
||||
0.8.1 - November 18, 2011
|
||||
Implemented TFLog logging system, see README for more information
|
||||
Fixed an issue where Session End events may not be sent until next launch
|
||||
Fixed an issue where duplicate events could be sent
|
||||
Fixed an issue with Session End events not being sent from some iPod touch models
|
||||
|
||||
Tested compiled library with
|
||||
Xcode 4.2
|
||||
Xcode 4.1
|
||||
Xcode 3.2.6
|
||||
|
||||
0.8 - November 8, 2011
|
||||
Added SIGTRAP as a signal type that we catch
|
||||
Removed all Objective-c from crash reporting
|
||||
Removed the use of non signal safe functions from signal handling
|
||||
Created a signal safe way to get symbols from a stack trace
|
||||
Changed the keyboardType for Long Answer Questions and Feedback to allow for international character input
|
||||
Changed TESTFLIGHT_SDK_VERSION string to be an NSString
|
||||
Changed cache folder from Library/Caches/TestFlight to Library/Caches/com.testflight.testflightsdk
|
||||
Fixed issue with saving data when device is offline
|
||||
Fixed compability issues with iOS 3
|
||||
Added calling into the rootViewController shouldAutorotateToInterfaceOrientation if a rootViewController is set
|
||||
Made the comments in TestFlight.h compatible with Appledoc
|
||||
|
||||
Tested compiled library with
|
||||
Xcode 4.2
|
||||
Xcode 4.1
|
||||
Xcode 3.2
|
||||
|
||||
0.7.2 - September 29, 2011
|
||||
Changed TESTFLIGHT_SDK_VERSION string to be an NSString
|
||||
Fixed an issue where exiting an application while the SDK is active caused modal views to be dismissed
|
||||
0.7.1 - September 22, 2011
|
||||
Internal release
|
||||
Refactoring
|
||||
0.7 - September 21, 2011
|
||||
Moved TestFlight images and data to the Library/Caches folder
|
||||
Resolved an issue where sometimes the rootViewController could not be found and feedback, questions and upgrade views would not be displayed
|
||||
In application upgrade changed to allow skipping until the next version is installed and allows upgrades to be forced
|
||||
Fixed a memory leak when launching questions
|
||||
0.6 - September 2, 2011
|
||||
Renamed base64_encode to testflight_base64_encode to remove a conflict with other third party libraries
|
||||
Added ability to reinstall crash handlers when they are overwritten using the setOptions API
|
||||
Fixed an issue where crash reports might not get sent under certain circumstances
|
||||
Fixed a deadlock when the application is put in the background and then resumed before all information can be sent
|
||||
Fixed an issue when attempting to un-install all signal handlers during a signal
|
||||
Added support for landscape mode on the iPad to the Questions and Feedback views
|
||||
Crash reporting now works in versions of Xcode earlier than 4.2
|
||||
Fixed a memory leak during handshake
|
||||
0.5 - August 19, 2011
|
||||
Feedback that is not attached to a checkpoint [TestFlight openFeedbackView]
|
||||
Usability changes to question views
|
||||
Removed pause and resume sessions, replaced with sessions being stopped and started
|
||||
Added text auto correction to the Long Answer question type
|
||||
Crash reports now send on crash instead of next launch
|
||||
0.4 - August 15, 2011
|
||||
In Application Feedback with Questions
|
||||
In application updates
|
||||
Custom Environment Information added
|
||||
Networking stack reimplementation
|
||||
Exception handling fixes
|
||||
0.3 - June 15, 2011
|
||||
Removed all mention of JSONKit from the README
|
||||
Added support for using both the Bundle Version and the Bundle Short Version string
|
||||
0.2 - June 14, 2011
|
||||
Removed all categories this allows users to use the SDK without having to set -ObjC and -load_all
|
||||
Prefixed JSONKit for use in TestFlight to remove reported issues where some users were already using JSONKit
|
||||
Added support for armv6 again
|
||||
0.1 - June 11, 2011
|
||||
Initial Version
|
||||
BIN
MasterPassword.sketch/Data
Normal file
BIN
MasterPassword.sketch/QuickLook/Preview.pdf
Normal file
BIN
MasterPassword.sketch/QuickLook/Thumbnail.jpg
Normal file
|
After Width: | Height: | Size: 43 KiB |
3750
MasterPassword.xcodeproj/project.pbxproj
Normal file
@@ -2,6 +2,6 @@
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:MasterPassword-iOS.xcodeproj">
|
||||
location = "self:MasterPassword.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -1,13 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1140"
|
||||
version = "1.7">
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "NO"
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
@@ -16,93 +15,70 @@
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
|
||||
BuildableName = "MasterPassword.app"
|
||||
BlueprintName = "MasterPassword-iOS"
|
||||
ReferencedContainer = "container:MasterPassword-iOS.xcodeproj">
|
||||
BlueprintName = "MasterPassword"
|
||||
ReferencedContainer = "container:MasterPassword.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Production">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
|
||||
BuildableName = "MasterPassword.app"
|
||||
BlueprintName = "MasterPassword-iOS"
|
||||
ReferencedContainer = "container:MasterPassword-iOS.xcodeproj">
|
||||
BlueprintName = "MasterPassword"
|
||||
ReferencedContainer = "container:MasterPassword.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
buildConfiguration = "Production"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
|
||||
BuildableName = "MasterPassword.app"
|
||||
BlueprintName = "MasterPassword-iOS"
|
||||
ReferencedContainer = "container:MasterPassword-iOS.xcodeproj">
|
||||
BlueprintName = "MasterPassword"
|
||||
ReferencedContainer = "container:MasterPassword.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<CommandLineArguments>
|
||||
<CommandLineArgument
|
||||
argument = "-com.apple.coredata.ubiquity.logLevel"
|
||||
isEnabled = "YES">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "0"
|
||||
isEnabled = "YES">
|
||||
</CommandLineArgument>
|
||||
</CommandLineArguments>
|
||||
<LocationScenarioReference
|
||||
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
|
||||
referenceType = "1">
|
||||
</LocationScenarioReference>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "TERM"
|
||||
value = "color"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Debug"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Production"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
|
||||
BuildableName = "MasterPassword.app"
|
||||
BlueprintName = "MasterPassword-iOS"
|
||||
ReferencedContainer = "container:MasterPassword-iOS.xcodeproj">
|
||||
BlueprintName = "MasterPassword"
|
||||
ReferencedContainer = "container:MasterPassword.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Release">
|
||||
buildConfiguration = "Production">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
buildConfiguration = "Production"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1200"
|
||||
version = "1.7">
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
@@ -15,72 +14,68 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
|
||||
BuildableName = "Master Password.app"
|
||||
BlueprintName = "MasterPassword-macOS"
|
||||
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
|
||||
BuildableName = "MasterPassword.app"
|
||||
BlueprintName = "MasterPassword"
|
||||
ReferencedContainer = "container:MasterPassword.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
|
||||
BuildableName = "Master Password.app"
|
||||
BlueprintName = "MasterPassword-macOS"
|
||||
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
|
||||
BuildableName = "MasterPassword.app"
|
||||
BlueprintName = "MasterPassword"
|
||||
ReferencedContainer = "container:MasterPassword.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Debug"
|
||||
debugDocumentVersioning = "YES"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
|
||||
BuildableName = "MasterPassword.app"
|
||||
BlueprintName = "MasterPassword"
|
||||
ReferencedContainer = "container:MasterPassword.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "TERM"
|
||||
value = "color"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
<LocationScenarioReference
|
||||
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
|
||||
referenceType = "1">
|
||||
</LocationScenarioReference>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Debug"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Release"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA5BFA43147E415C00F98B1E"
|
||||
BuildableName = "Master Password.app"
|
||||
BlueprintName = "MasterPassword-macOS"
|
||||
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
|
||||
BuildableName = "MasterPassword.app"
|
||||
BlueprintName = "MasterPassword"
|
||||
ReferencedContainer = "container:MasterPassword.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Release">
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
BIN
MasterPassword/.OPMainViewController.h.swk
Normal file
BIN
MasterPassword/.OPMainViewController.h.swl
Normal file
BIN
MasterPassword/.OPMainViewController.h.swm
Normal file
BIN
MasterPassword/.OPMainViewController.h.swn
Normal file
BIN
MasterPassword/.OPMainViewController.h.swo
Normal file
BIN
MasterPassword/.OPTypes.m.swn
Normal file
BIN
MasterPassword/.OPTypes.m.swo
Normal file
30
MasterPassword/MPAppDelegate.h
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// MPAppDelegate.h
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 24/11/11.
|
||||
// Copyright (c) 2011 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface MPAppDelegate : AbstractAppDelegate
|
||||
|
||||
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
|
||||
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
|
||||
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
|
||||
@property (strong, nonatomic) NSString *keyPhrase;
|
||||
@property (strong, nonatomic) NSData *keyPhraseHash;
|
||||
@property (strong, nonatomic) NSString *keyPhraseHashHex;
|
||||
|
||||
+ (MPAppDelegate *)get;
|
||||
+ (NSManagedObjectModel *)managedObjectModel;
|
||||
+ (NSManagedObjectContext *)managedObjectContext;
|
||||
|
||||
- (void)saveContext;
|
||||
- (NSURL *)applicationDocumentsDirectory;
|
||||
|
||||
- (void)showGuide;
|
||||
- (void)loadKeyPhrase;
|
||||
|
||||
@end
|
||||
492
MasterPassword/MPAppDelegate.m
Normal file
@@ -0,0 +1,492 @@
|
||||
//
|
||||
// MPAppDelegate.m
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 24/11/11.
|
||||
// Copyright (c) 2011 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPAppDelegate.h"
|
||||
|
||||
#import "MPMainViewController.h"
|
||||
#import "IASKSettingsReader.h"
|
||||
|
||||
@interface MPAppDelegate ()
|
||||
|
||||
+ (NSDictionary *)keyPhraseQuery;
|
||||
+ (NSDictionary *)keyPhraseHashQuery;
|
||||
|
||||
- (void)forgetKeyPhrase;
|
||||
- (void)loadStoredKeyPhrase;
|
||||
- (void)askKeyPhrase;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPAppDelegate
|
||||
|
||||
@synthesize managedObjectModel = __managedObjectModel;
|
||||
@synthesize managedObjectContext = __managedObjectContext;
|
||||
@synthesize persistentStoreCoordinator = __persistentStoreCoordinator;
|
||||
|
||||
@synthesize keyPhrase = _keyPhrase;
|
||||
@synthesize keyPhraseHash = _keyPhraseHash;
|
||||
@synthesize keyPhraseHashHex = _keyPhraseHashHex;
|
||||
|
||||
+ (void)initialize {
|
||||
|
||||
#ifdef DEBUG
|
||||
[Logger get].autoprintLevel = LogLevelTrace;
|
||||
[NSClassFromString(@"WebView") performSelector:@selector(_enableRemoteInspector)];
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (NSDictionary *)keyPhraseQuery {
|
||||
|
||||
static NSDictionary *MPKeyPhraseQuery = nil;
|
||||
if (!MPKeyPhraseQuery)
|
||||
MPKeyPhraseQuery = [KeyChain createQueryForClass:kSecClassGenericPassword
|
||||
attributes:[NSDictionary dictionaryWithObject:@"MasterPassword"
|
||||
forKey:(__bridge id)kSecAttrService]
|
||||
matches:nil];
|
||||
|
||||
return MPKeyPhraseQuery;
|
||||
}
|
||||
|
||||
+ (NSDictionary *)keyPhraseHashQuery {
|
||||
|
||||
static NSDictionary *MPKeyPhraseHashQuery = nil;
|
||||
if (!MPKeyPhraseHashQuery)
|
||||
MPKeyPhraseHashQuery = [KeyChain createQueryForClass:kSecClassGenericPassword
|
||||
attributes:[NSDictionary dictionaryWithObject:@"MasterPasswordHash"
|
||||
forKey:(__bridge id)kSecAttrService]
|
||||
matches:nil];
|
||||
|
||||
return MPKeyPhraseHashQuery;
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||
|
||||
#ifndef PRODUCTION
|
||||
[TestFlight takeOff:@"bd44885deee7adce0645ce8e5498d80a_NDQ5NDQyMDExLTEyLTAyIDExOjM1OjQ4LjQ2NjM4NA"];
|
||||
[TestFlight setOptions:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSNumber numberWithBool:YES], @"logToConsole",
|
||||
nil]];
|
||||
[TestFlight passCheckpoint:MPTestFlightCheckpointLaunched];
|
||||
#endif
|
||||
|
||||
UIImage *navBarImage = [[UIImage imageNamed:@"ui_navbar_container"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 5)];
|
||||
[[UINavigationBar appearance] setBackgroundImage:navBarImage forBarMetrics:UIBarMetricsDefault];
|
||||
[[UINavigationBar appearance] setBackgroundImage:navBarImage forBarMetrics:UIBarMetricsLandscapePhone];
|
||||
[[UINavigationBar appearance] setTitleTextAttributes:
|
||||
[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[UIColor colorWithRed:255.0/255.0 green:255.0/255.0 blue:255.0/255.0 alpha:1.0], UITextAttributeTextColor,
|
||||
[UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.8], UITextAttributeTextShadowColor,
|
||||
[NSValue valueWithUIOffset:UIOffsetMake(0, -1)], UITextAttributeTextShadowOffset,
|
||||
[UIFont fontWithName:@"Helvetica-Neue" size:0.0], UITextAttributeFont,
|
||||
nil]];
|
||||
|
||||
UIImage *navBarButton = [[UIImage imageNamed:@"ui_navbar_button"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 5)];
|
||||
UIImage *navBarBack = [[UIImage imageNamed:@"ui_navbar_back"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 13, 0, 5)];
|
||||
[[UIBarButtonItem appearance] setBackgroundImage:navBarButton forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
|
||||
[[UIBarButtonItem appearance] setBackgroundImage:nil forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];
|
||||
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:navBarBack forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
|
||||
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:nil forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];
|
||||
[[UIBarButtonItem appearance] setTitleTextAttributes:
|
||||
[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0], UITextAttributeTextColor,
|
||||
[UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.5], UITextAttributeTextShadowColor,
|
||||
[NSValue valueWithUIOffset:UIOffsetMake(0, 1)], UITextAttributeTextShadowOffset,
|
||||
[UIFont fontWithName:@"Helvetica-Neue" size:0.0], UITextAttributeFont,
|
||||
nil]
|
||||
forState:UIControlStateNormal];
|
||||
|
||||
UIImage *toolBarImage = [[UIImage imageNamed:@"ui_toolbar_container"] resizableImageWithCapInsets:UIEdgeInsetsMake(25, 5, 5, 5)];
|
||||
[[UISearchBar appearance] setBackgroundImage:toolBarImage];
|
||||
[[UIToolbar appearance] setBackgroundImage:toolBarImage forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsDefault];
|
||||
|
||||
/*
|
||||
UIImage *minImage = [[UIImage imageNamed:@"slider-minimum.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 0)];
|
||||
UIImage *maxImage = [[UIImage imageNamed:@"slider-maximum.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 0)];
|
||||
UIImage *thumbImage = [UIImage imageNamed:@"slider-handle.png"];
|
||||
|
||||
[[UISlider appearance] setMaximumTrackImage:maxImage forState:UIControlStateNormal];
|
||||
[[UISlider appearance] setMinimumTrackImage:minImage forState:UIControlStateNormal];
|
||||
[[UISlider appearance] setThumbImage:thumbImage forState:UIControlStateNormal];
|
||||
|
||||
UIImage *segmentSelected = [[UIImage imageNamed:@"segcontrol_sel.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 4, 0, 4)];
|
||||
UIImage *segmentUnselected = [[UIImage imageNamed:@"segcontrol_uns.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 15, 0, 15)];
|
||||
UIImage *segmentSelectedUnselected = [UIImage imageNamed:@"segcontrol_sel-uns.png"];
|
||||
UIImage *segUnselectedSelected = [UIImage imageNamed:@"segcontrol_uns-sel.png"];
|
||||
UIImage *segmentUnselectedUnselected = [UIImage imageNamed:@"segcontrol_uns-uns.png"];
|
||||
|
||||
[[UISegmentedControl appearance] setBackgroundImage:segmentUnselected forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
|
||||
[[UISegmentedControl appearance] setBackgroundImage:segmentSelected forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];
|
||||
|
||||
[[UISegmentedControl appearance] setDividerImage:segmentUnselectedUnselected forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
|
||||
[[UISegmentedControl appearance] setDividerImage:segmentSelectedUnselected forLeftSegmentState:UIControlStateSelected rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
|
||||
[[UISegmentedControl appearance] setDividerImage:segUnselectedSelected forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateSelected barMetrics:UIBarMetricsDefault];*/
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:kIASKAppSettingChanged object:nil queue:nil
|
||||
usingBlock:^(NSNotification *note) {
|
||||
if ([NSStringFromSelector(@selector(storeKeyPhrase))
|
||||
isEqualToString:[note.object description]]) {
|
||||
self.keyPhrase = self.keyPhrase;
|
||||
[self loadKeyPhrase];
|
||||
}
|
||||
if ([NSStringFromSelector(@selector(forgetKeyPhrase))
|
||||
isEqualToString:[note.object description]])
|
||||
[self loadKeyPhrase];
|
||||
}];
|
||||
|
||||
return [super application:application didFinishLaunchingWithOptions:launchOptions];
|
||||
}
|
||||
- (void)applicationDidBecomeActive:(UIApplication *)application {
|
||||
|
||||
if ([[MPConfig get].showQuickStart boolValue])
|
||||
[self showGuide];
|
||||
else
|
||||
[self loadKeyPhrase];
|
||||
|
||||
#ifndef PRODUCTION
|
||||
[TestFlight passCheckpoint:MPTestFlightCheckpointActivated];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)showGuide {
|
||||
|
||||
[self.navigationController performSegueWithIdentifier:@"MP_Guide" sender:self];
|
||||
|
||||
#ifndef PRODUCTION
|
||||
[TestFlight passCheckpoint:MPTestFlightCheckpointShowGuide];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)loadKeyPhrase {
|
||||
|
||||
if ([[MPConfig get].forgetKeyPhrase boolValue]) {
|
||||
[self forgetKeyPhrase];
|
||||
return;
|
||||
}
|
||||
|
||||
[self loadStoredKeyPhrase];
|
||||
if (!self.keyPhrase) {
|
||||
// Key phrase is not known. Ask user to set/specify it.
|
||||
dbg(@"Key phrase not known. Will ask user.");
|
||||
[self askKeyPhrase];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)forgetKeyPhrase {
|
||||
|
||||
dbg(@"Forgetting key phrase.");
|
||||
[AlertViewController showAlertWithTitle:@"Changing Master Password"
|
||||
message:
|
||||
@"You've requested to change your master password.\n\n"
|
||||
@"If you continue, your current sites and passwords will become unavailable.\n\n"
|
||||
@"You can always change back to the old master password later.\n"
|
||||
@"Your old sites and passwords will then become available again."
|
||||
viewStyle:UIAlertViewStyleDefault
|
||||
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
|
||||
if (buttonIndex != [alert cancelButtonIndex]) {
|
||||
// Key phrase reset. Delete it.
|
||||
dbg(@"Deleting master key phrase and hash from key chain.");
|
||||
[KeyChain deleteItemForQuery:[MPAppDelegate keyPhraseQuery]];
|
||||
[KeyChain deleteItemForQuery:[MPAppDelegate keyPhraseHashQuery]];
|
||||
}
|
||||
|
||||
[self loadKeyPhrase];
|
||||
|
||||
#ifndef PRODUCTION
|
||||
[TestFlight passCheckpoint:MPTestFlightCheckpointMPChanged];
|
||||
#endif
|
||||
}
|
||||
cancelTitle:[PearlStrings get].commonButtonAbort
|
||||
otherTitles:[PearlStrings get].commonButtonContinue, nil];
|
||||
[MPConfig get].forgetKeyPhrase = [NSNumber numberWithBool:NO];
|
||||
}
|
||||
|
||||
- (void)loadStoredKeyPhrase {
|
||||
|
||||
if ([[MPConfig get].storeKeyPhrase boolValue]) {
|
||||
// Key phrase is stored in keychain. Load it.
|
||||
dbg(@"Loading master key phrase from key chain.");
|
||||
NSData *keyPhraseData = [KeyChain dataOfItemForQuery:[MPAppDelegate keyPhraseQuery]];
|
||||
dbg(@" -> Master key phrase %@.", keyPhraseData? @"found": @"NOT found");
|
||||
|
||||
self.keyPhrase = keyPhraseData? [[NSString alloc] initWithBytes:keyPhraseData.bytes length:keyPhraseData.length
|
||||
encoding:NSUTF8StringEncoding]: nil;
|
||||
} else {
|
||||
// Key phrase should not be stored in keychain. Delete it.
|
||||
dbg(@"Deleting master key phrase from key chain.");
|
||||
[KeyChain deleteItemForQuery:[MPAppDelegate keyPhraseQuery]];
|
||||
#ifndef PRODUCTION
|
||||
[TestFlight passCheckpoint:MPTestFlightCheckpointMPUnstored];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
- (void)askKeyPhrase {
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSData *keyPhraseHash = [KeyChain dataOfItemForQuery:[MPAppDelegate keyPhraseHashQuery]];
|
||||
dbg(@"Key phrase hash %@.", keyPhraseHash? @"known": @"NOT known");
|
||||
|
||||
[AlertViewController showAlertWithTitle:@"Master Password"
|
||||
message:keyPhraseHash? @"Unlock with your master password:": @"Choose your master password:"
|
||||
viewStyle:UIAlertViewStyleSecureTextInput
|
||||
tappedButtonBlock:
|
||||
^(UIAlertView *alert, NSInteger buttonIndex) {
|
||||
if (buttonIndex == [alert cancelButtonIndex])
|
||||
exit(0);
|
||||
|
||||
NSString *answer = [alert textFieldAtIndex:0].text;
|
||||
if (![answer length]) {
|
||||
// User didn't enter a key phrase.
|
||||
[AlertViewController showAlertWithTitle:[PearlStrings get].commonTitleError
|
||||
message:@"No master password entered."
|
||||
viewStyle:UIAlertViewStyleDefault
|
||||
tappedButtonBlock:
|
||||
^(UIAlertView *alert, NSInteger buttonIndex) {
|
||||
exit(0);
|
||||
} cancelTitle:@"Quit" otherTitles:nil];
|
||||
}
|
||||
|
||||
NSData *answerHash = [answer hashWith:PearlDigestSHA512];
|
||||
if (keyPhraseHash)
|
||||
// A key phrase hash is known -> a key phrase is set.
|
||||
// Make sure the user's entered key phrase matches it.
|
||||
if (![keyPhraseHash isEqual:answerHash]) {
|
||||
dbg(@"Key phrase hash mismatch. Expected: %@, answer: %@.", keyPhraseHash, answerHash);
|
||||
|
||||
#ifndef PRODUCTION
|
||||
[TestFlight passCheckpoint:MPTestFlightCheckpointMPMismatch];
|
||||
#endif
|
||||
[AlertViewController showAlertWithTitle:[PearlStrings get].commonTitleError
|
||||
message:
|
||||
@"Incorrect master password.\n\n"
|
||||
@"If you are trying to use the app with a different master password, "
|
||||
@"flip the 'Change my password' option in Settings."
|
||||
viewStyle:UIAlertViewStyleDefault
|
||||
tappedButtonBlock:
|
||||
^(UIAlertView *alert, NSInteger buttonIndex) {
|
||||
exit(0);
|
||||
} cancelTitle:@"Quit" otherTitles:nil];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef PRODUCTION
|
||||
[TestFlight passCheckpoint:MPTestFlightCheckpointMPAsked];
|
||||
#endif
|
||||
|
||||
self.keyPhrase = answer;
|
||||
} cancelTitle:@"Quit" otherTitles:@"Unlock", nil];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive:(UIApplication *)application {
|
||||
|
||||
[self saveContext];
|
||||
|
||||
if (![[MPConfig get].rememberKeyPhrase boolValue])
|
||||
self.keyPhrase = nil;
|
||||
|
||||
#ifndef PRODUCTION
|
||||
[TestFlight passCheckpoint:MPTestFlightCheckpointDeactivated];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(UIApplication *)application {
|
||||
|
||||
[self saveContext];
|
||||
|
||||
#ifndef PRODUCTION
|
||||
[TestFlight passCheckpoint:MPTestFlightCheckpointTerminated];
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (MPAppDelegate *)get {
|
||||
|
||||
return (MPAppDelegate *)[super get];
|
||||
}
|
||||
|
||||
+ (NSManagedObjectContext *)managedObjectContext {
|
||||
|
||||
return [(MPAppDelegate *)[UIApplication sharedApplication].delegate managedObjectContext];
|
||||
}
|
||||
|
||||
+ (NSManagedObjectModel *)managedObjectModel {
|
||||
|
||||
return [(MPAppDelegate *)[UIApplication sharedApplication].delegate managedObjectModel];
|
||||
}
|
||||
|
||||
- (void)saveContext {
|
||||
|
||||
[self.managedObjectContext performBlock:^{
|
||||
NSError *error = nil;
|
||||
if ([self.managedObjectContext hasChanges] && ![self.managedObjectContext save:&error])
|
||||
err(@"Unresolved error %@", error);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setKeyPhrase:(NSString *)keyPhrase {
|
||||
|
||||
_keyPhrase = keyPhrase;
|
||||
|
||||
if (keyPhrase) {
|
||||
self.keyPhraseHash = [keyPhrase hashWith:PearlDigestSHA512];
|
||||
self.keyPhraseHashHex = [self.keyPhraseHash encodeHex];
|
||||
|
||||
dbg(@"Updating master key phrase hash to: %@.", self.keyPhraseHashHex);
|
||||
[KeyChain addOrUpdateItemForQuery:[MPAppDelegate keyPhraseHashQuery]
|
||||
withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
self.keyPhraseHash, (__bridge id)kSecValueData,
|
||||
kSecAttrAccessibleWhenUnlocked, (__bridge id)kSecAttrAccessible,
|
||||
nil]];
|
||||
if ([[MPConfig get].storeKeyPhrase boolValue]) {
|
||||
dbg(@"Storing master key phrase in key chain.");
|
||||
[KeyChain addOrUpdateItemForQuery:[MPAppDelegate keyPhraseQuery]
|
||||
withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[keyPhrase dataUsingEncoding:NSUTF8StringEncoding], (__bridge id)kSecValueData,
|
||||
kSecAttrAccessibleWhenUnlocked, (__bridge id)kSecAttrAccessible,
|
||||
nil]];
|
||||
}
|
||||
|
||||
#ifndef PRODUCTION
|
||||
[TestFlight passCheckpoint:[NSString stringWithFormat:MPTestFlightCheckpointSetKeyphraseLength, _keyPhrase.length]];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Core Data stack
|
||||
|
||||
/**
|
||||
Returns the managed object context for the application.
|
||||
If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
|
||||
*/
|
||||
- (NSManagedObjectContext *)managedObjectContext
|
||||
{
|
||||
if (__managedObjectContext)
|
||||
return __managedObjectContext;
|
||||
|
||||
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
|
||||
if (coordinator) {
|
||||
__managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
|
||||
__managedObjectContext.persistentStoreCoordinator = coordinator;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:NSPersistentStoreDidImportUbiquitousContentChangesNotification
|
||||
object:coordinator
|
||||
queue:nil
|
||||
usingBlock:^(NSNotification *note) {
|
||||
dbg(@"Ubiquitous content change: %@", note);
|
||||
|
||||
[__managedObjectContext performBlock:^{
|
||||
[__managedObjectContext mergeChangesFromContextDidSaveNotification:note];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotification:
|
||||
[NSNotification notificationWithName:UIScreenModeDidChangeNotification
|
||||
object:self userInfo:[note userInfo]]];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
return __managedObjectContext;
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the managed object model for the application.
|
||||
If the model doesn't already exist, it is created from the application's model.
|
||||
*/
|
||||
- (NSManagedObjectModel *)managedObjectModel
|
||||
{
|
||||
if (__managedObjectModel)
|
||||
return __managedObjectModel;
|
||||
|
||||
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"MasterPassword" withExtension:@"momd"];
|
||||
return __managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the persistent store coordinator for the application.
|
||||
If the coordinator doesn't already exist, it is created and the application's store added to it.
|
||||
*/
|
||||
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
|
||||
{
|
||||
if (__persistentStoreCoordinator)
|
||||
return __persistentStoreCoordinator;
|
||||
|
||||
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"MasterPassword.sqlite"];
|
||||
|
||||
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
|
||||
[__persistentStoreCoordinator lock];
|
||||
NSError *error = nil;
|
||||
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL
|
||||
options:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,
|
||||
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
|
||||
@"MasterPassword.store", NSPersistentStoreUbiquitousContentNameKey,
|
||||
[[[NSFileManager defaultManager]
|
||||
URLForUbiquityContainerIdentifier:nil]
|
||||
URLByAppendingPathComponent:@"store"
|
||||
isDirectory:YES], NSPersistentStoreUbiquitousContentURLKey,
|
||||
nil]
|
||||
error:&error])
|
||||
{
|
||||
/*
|
||||
Replace this implementation with code to handle the error appropriately.
|
||||
|
||||
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
|
||||
|
||||
Typical reasons for an error here include:
|
||||
* The persistent store is not accessible;
|
||||
* The schema for the persistent store is incompatible with current managed object model.
|
||||
Check the error message to determine what the actual problem was.
|
||||
|
||||
|
||||
If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory.
|
||||
|
||||
If you encounter schema incompatibility errors during development, you can reduce their frequency by:
|
||||
* Simply deleting the existing store:
|
||||
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]
|
||||
|
||||
* Performing automatic lightweight migration by passing the following dictionary as the options parameter:
|
||||
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
|
||||
|
||||
Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.
|
||||
|
||||
*/
|
||||
err(@"Unresolved error %@, %@", error, [error userInfo]);
|
||||
#if DEBUG
|
||||
wrn(@"Deleted datastore: %@", storeURL);
|
||||
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil];
|
||||
#endif
|
||||
|
||||
#ifndef PRODUCTION
|
||||
[TestFlight passCheckpoint:MPTestFlightCheckpointStoreIncompatible];
|
||||
#endif
|
||||
|
||||
@throw [NSException exceptionWithName:error.domain reason:error.localizedDescription
|
||||
userInfo:[NSDictionary dictionaryWithObject:error forKey:@"cause"]];
|
||||
}
|
||||
|
||||
if (![[NSFileManager defaultManager] setAttributes:[NSDictionary dictionaryWithObject:NSFileProtectionComplete
|
||||
forKey:NSFileProtectionKey]
|
||||
ofItemAtPath:storeURL.path error:&error])
|
||||
err(@"Unresolved error %@, %@", error, [error userInfo]);
|
||||
[__persistentStoreCoordinator unlock];
|
||||
|
||||
return __persistentStoreCoordinator;
|
||||
}
|
||||
|
||||
#pragma mark - Application's Documents directory
|
||||
|
||||
/**
|
||||
Returns the URL to the application's Documents directory.
|
||||
*/
|
||||
- (NSURL *)applicationDocumentsDirectory
|
||||
{
|
||||
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
|
||||
}
|
||||
|
||||
@end
|
||||
20
MasterPassword/MPConfig.h
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// MPConfig.h
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 02/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
@interface MPConfig : Config
|
||||
|
||||
@property (nonatomic, retain) NSNumber *dataStoreError;
|
||||
@property (nonatomic, retain) NSNumber *storeKeyPhrase;
|
||||
@property (nonatomic, retain) NSNumber *rememberKeyPhrase;
|
||||
@property (nonatomic, retain) NSNumber *forgetKeyPhrase;
|
||||
@property (nonatomic, retain) NSNumber *helpHidden;
|
||||
@property (nonatomic, retain) NSNumber *showQuickStart;
|
||||
|
||||
+ (MPConfig *)get;
|
||||
|
||||
@end
|
||||
38
MasterPassword/MPConfig.m
Normal file
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// MPConfig.m
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 02/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPConfig.h"
|
||||
|
||||
@implementation MPConfig
|
||||
|
||||
@dynamic dataStoreError, storeKeyPhrase, rememberKeyPhrase, forgetKeyPhrase, helpHidden, showQuickStart;
|
||||
|
||||
|
||||
- (id)init {
|
||||
|
||||
if(!(self = [super init]))
|
||||
return self;
|
||||
|
||||
[self.defaults registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSNumber numberWithBool:NO], NSStringFromSelector(@selector(dataStoreError)),
|
||||
[NSNumber numberWithBool:NO], NSStringFromSelector(@selector(storeKeyPhrase)),
|
||||
[NSNumber numberWithBool:YES], NSStringFromSelector(@selector(rememberKeyPhrase)),
|
||||
[NSNumber numberWithBool:NO], NSStringFromSelector(@selector(forgetKeyPhrase)),
|
||||
[NSNumber numberWithBool:NO], NSStringFromSelector(@selector(helpHidden)),
|
||||
[NSNumber numberWithBool:YES], NSStringFromSelector(@selector(showQuickStart)),
|
||||
nil]];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (MPConfig *)get {
|
||||
|
||||
return (MPConfig *)[super get];
|
||||
}
|
||||
|
||||
@end
|
||||
24
MasterPassword/MPElementEntity.h
Normal file
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// MPElementEntity.h
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 02/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
|
||||
|
||||
@interface MPElementEntity : NSManagedObject
|
||||
|
||||
@property (nonatomic, retain) NSString *name;
|
||||
@property (nonatomic, retain) NSString *mpHashHex;
|
||||
@property (nonatomic, assign) int16_t type;
|
||||
@property (nonatomic, assign) int16_t uses;
|
||||
@property (nonatomic, assign) NSTimeInterval lastUsed;
|
||||
@property (nonatomic, retain, readonly) id content;
|
||||
|
||||
- (void)use;
|
||||
|
||||
@end
|
||||
42
MasterPassword/MPElementEntity.m
Normal file
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// MPElementEntity.m
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 02/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPElementEntity.h"
|
||||
|
||||
|
||||
@implementation MPElementEntity
|
||||
|
||||
@dynamic name;
|
||||
@dynamic mpHashHex;
|
||||
@dynamic type;
|
||||
@dynamic uses;
|
||||
@dynamic lastUsed;
|
||||
|
||||
- (void)use {
|
||||
|
||||
++self.uses;
|
||||
self.lastUsed = [[NSDate date] timeIntervalSinceReferenceDate];
|
||||
}
|
||||
|
||||
- (id)content {
|
||||
|
||||
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Content implementation missing." userInfo:nil];
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
|
||||
return [[self content] description];
|
||||
}
|
||||
|
||||
- (NSString *)debugDescription {
|
||||
|
||||
return [NSString stringWithFormat:@"{%@: name=%@, mpHashHex=%@, type=%d, uses=%d, lastUsed=%@}",
|
||||
NSStringFromClass([self class]), self.name, self.mpHashHex, self.type, self.uses, self.lastUsed];
|
||||
}
|
||||
|
||||
@end
|
||||
18
MasterPassword/MPElementGeneratedEntity.h
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// MPElementGeneratedEntity.h
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 16/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
#import "MPElementEntity.h"
|
||||
|
||||
|
||||
@interface MPElementGeneratedEntity : MPElementEntity
|
||||
|
||||
@property (nonatomic, assign) int16_t counter;
|
||||
|
||||
@end
|
||||
31
MasterPassword/MPElementGeneratedEntity.m
Normal file
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// MPElementGeneratedEntity.m
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 16/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPElementGeneratedEntity.h"
|
||||
#import "MPAppDelegate.h"
|
||||
|
||||
|
||||
@implementation MPElementGeneratedEntity
|
||||
|
||||
@dynamic counter;
|
||||
|
||||
- (id)content {
|
||||
|
||||
assert(self.type & MPElementTypeClassCalculated);
|
||||
|
||||
if (![self.name length])
|
||||
return nil;
|
||||
|
||||
if (self.type & MPElementTypeClassCalculated)
|
||||
return MPCalculateContent(self.type, self.name, [MPAppDelegate get].keyPhrase, self.counter);
|
||||
|
||||
@throw [NSException exceptionWithName:NSInternalInconsistencyException
|
||||
reason:[NSString stringWithFormat:@"Unsupported type: %d", self.type] userInfo:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
18
MasterPassword/MPElementStoredEntity.h
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// MPElementStoredEntity.h
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 02/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
#import "MPElementEntity.h"
|
||||
|
||||
|
||||
@interface MPElementStoredEntity : MPElementEntity
|
||||
|
||||
@property (nonatomic, retain, readwrite) id content;
|
||||
|
||||
@end
|
||||
65
MasterPassword/MPElementStoredEntity.m
Normal file
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// MPElementStoredEntity.m
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 02/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPElementStoredEntity.h"
|
||||
#import "MPAppDelegate.h"
|
||||
|
||||
@interface MPElementStoredEntity ()
|
||||
|
||||
@property (nonatomic, retain, readwrite) id contentObject;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPElementStoredEntity
|
||||
|
||||
@dynamic contentObject;
|
||||
|
||||
+ (NSDictionary *)queryForDevicePrivateElementNamed:(NSString *)name {
|
||||
|
||||
return [KeyChain createQueryForClass:kSecClassGenericPassword
|
||||
attributes:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
@"DevicePrivate", (__bridge id)kSecAttrService,
|
||||
name, (__bridge id)kSecAttrAccount,
|
||||
nil]
|
||||
matches:nil];
|
||||
}
|
||||
|
||||
- (id)content {
|
||||
|
||||
assert(self.type & MPElementTypeClassStored);
|
||||
|
||||
NSData *encryptedContent;
|
||||
if (self.type == MPElementTypeStoredDevicePrivate)
|
||||
encryptedContent = [KeyChain dataOfItemForQuery:[MPElementStoredEntity queryForDevicePrivateElementNamed:self.name]];
|
||||
else
|
||||
encryptedContent = self.contentObject;
|
||||
|
||||
NSData *decryptedContent = [encryptedContent decryptWithSymmetricKey:[[MPAppDelegate get].keyPhrase
|
||||
dataUsingEncoding:NSUTF8StringEncoding]
|
||||
usePadding:YES];
|
||||
return [[NSString alloc] initWithBytes:decryptedContent.bytes length:decryptedContent.length encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
- (void)setContent:(id)content {
|
||||
|
||||
NSData *encryptedContent = [[content description] encryptWithSymmetricKey:[[MPAppDelegate get].keyPhrase
|
||||
dataUsingEncoding:NSUTF8StringEncoding]
|
||||
usePadding:YES];
|
||||
|
||||
if (self.type == MPElementTypeStoredDevicePrivate) {
|
||||
[KeyChain addOrUpdateItemForQuery:[MPElementStoredEntity queryForDevicePrivateElementNamed:self.name]
|
||||
withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
encryptedContent, (__bridge id)kSecValueData,
|
||||
kSecAttrAccessibleWhenUnlockedThisDeviceOnly, (__bridge id)kSecAttrAccessible,
|
||||
nil]];
|
||||
self.contentObject = nil;
|
||||
} else
|
||||
self.contentObject = encryptedContent;
|
||||
}
|
||||
|
||||
@end
|
||||
17
MasterPassword/MPGuideViewController.h
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// MPGuideViewController.h
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 30/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface MPGuideViewController : UIViewController
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
|
||||
|
||||
- (IBAction)close;
|
||||
|
||||
@end
|
||||
46
MasterPassword/MPGuideViewController.m
Normal file
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// MPGuideViewController.m
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 30/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPGuideViewController.h"
|
||||
#import "MPAppDelegate.h"
|
||||
|
||||
@implementation MPGuideViewController
|
||||
@synthesize scrollView;
|
||||
|
||||
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
|
||||
|
||||
return (interfaceOrientation == UIInterfaceOrientationPortrait);
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
|
||||
[super viewDidLoad];
|
||||
|
||||
[UIUtils autoSizeContent:self.scrollView];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
[MPConfig get].showQuickStart = [NSNumber numberWithBool:NO];
|
||||
[[MPAppDelegate get] loadKeyPhrase];
|
||||
}
|
||||
|
||||
- (void)viewDidUnload {
|
||||
|
||||
[self setScrollView:nil];
|
||||
[super viewDidUnload];
|
||||
}
|
||||
|
||||
- (IBAction)close {
|
||||
|
||||
[self.presentingViewController dismissModalViewControllerAnimated:YES];
|
||||
}
|
||||
|
||||
@end
|
||||
46
MasterPassword/MPMainViewController.h
Normal file
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// MPMainViewController.h
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 24/11/11.
|
||||
// Copyright (c) 2011 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPTypeViewController.h"
|
||||
#import "MPElementEntity.h"
|
||||
#import "MPSearchDelegate.h"
|
||||
#import "IASKAppSettingsViewController.h"
|
||||
|
||||
@interface MPMainViewController : UIViewController <MPTypeDelegate, UITextFieldDelegate, UISearchBarDelegate, MPSearchResultsDelegate, UIWebViewDelegate, IASKSettingsDelegate>
|
||||
|
||||
@property (strong, nonatomic) MPElementEntity *activeElement;
|
||||
@property (strong, nonatomic) IBOutlet MPSearchDelegate *searchResultsController;
|
||||
@property (weak, nonatomic) IBOutlet UITextField *contentField;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *typeButton;
|
||||
@property (weak, nonatomic) IBOutlet UIWebView *helpView;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *siteName;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *passwordCounter;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *passwordIncrementer;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *passwordEdit;
|
||||
@property (weak, nonatomic) IBOutlet UIView *contentContainer;
|
||||
@property (weak, nonatomic) IBOutlet UIView *helpContainer;
|
||||
@property (weak, nonatomic) IBOutlet UIView *contentTipContainer;
|
||||
@property (weak, nonatomic) IBOutlet UIView *alertContainer;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *alertTitle;
|
||||
@property (weak, nonatomic) IBOutlet UITextView *alertBody;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *contentTipBody;
|
||||
@property (weak, nonatomic) IBOutlet UIImageView *contentTipEditIcon;
|
||||
@property (weak, nonatomic) IBOutlet UIView *searchTipContainer;
|
||||
|
||||
- (IBAction)copyContent;
|
||||
- (IBAction)incrementPasswordCounter;
|
||||
- (IBAction)editPassword;
|
||||
- (IBAction)closeAlert;
|
||||
- (IBAction)action:(UIBarButtonItem *)sender;
|
||||
|
||||
- (BOOL)isHelpVisible;
|
||||
- (void)toggleHelpAnimated:(BOOL)animated;
|
||||
- (void)setHelpHidden:(BOOL)hidden animated:(BOOL)animated;
|
||||
- (void)setHelpChapter:(NSString *)chapter;
|
||||
|
||||
@end
|
||||
478
MasterPassword/MPMainViewController.m
Normal file
@@ -0,0 +1,478 @@
|
||||
//
|
||||
// MPMainViewController.m
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 24/11/11.
|
||||
// Copyright (c) 2011 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPMainViewController.h"
|
||||
#import "MPAppDelegate.h"
|
||||
#import "MPElementGeneratedEntity.h"
|
||||
#import "MPElementStoredEntity.h"
|
||||
#import "IASKAppSettingsViewController.h"
|
||||
|
||||
#import <MobileCoreServices/MobileCoreServices.h>
|
||||
|
||||
|
||||
@interface MPMainViewController (Private)
|
||||
|
||||
- (void)updateAnimated:(BOOL)animated;
|
||||
- (void)updateWasAnimated:(BOOL)animated;
|
||||
- (void)showContentTip:(NSString *)message withIcon:(UIImageView *)icon;
|
||||
- (void)showAlertWithTitle:(NSString *)title message:(NSString *)message;
|
||||
- (void)updateElement:(void (^)(void))updateElement;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPMainViewController
|
||||
@synthesize activeElement = _activeElement;
|
||||
@synthesize searchResultsController = _searchResultsController;
|
||||
@synthesize typeButton = _typeButton;
|
||||
@synthesize helpView = _helpView;
|
||||
@synthesize siteName = _siteName;
|
||||
@synthesize passwordCounter = _passwordCounter;
|
||||
@synthesize passwordIncrementer = _passwordIncrementer;
|
||||
@synthesize passwordEdit = _passwordEdit;
|
||||
@synthesize contentContainer = _contentContainer;
|
||||
@synthesize helpContainer = _helpContainer;
|
||||
@synthesize contentTipContainer = _copiedContainer;
|
||||
@synthesize alertContainer = _alertContainer;
|
||||
@synthesize alertTitle = _alertTitle;
|
||||
@synthesize alertBody = _alertBody;
|
||||
@synthesize contentTipBody = _contentTipBody;
|
||||
@synthesize contentTipEditIcon = _contentTipEditIcon;
|
||||
@synthesize searchTipContainer = _searchTip;
|
||||
@synthesize contentField = _contentField;
|
||||
|
||||
#pragma mark - View lifecycle
|
||||
|
||||
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
|
||||
|
||||
return [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad || interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown;
|
||||
}
|
||||
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
||||
|
||||
if ([[segue identifier] isEqualToString:@"MP_Main_ChooseType"])
|
||||
[[segue destinationViewController] setDelegate:self];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
self.searchTipContainer.hidden = NO;
|
||||
|
||||
if (!self.activeElement.name)
|
||||
[UIView animateWithDuration:animated? 0.2f: 0 animations:^{
|
||||
self.searchTipContainer.alpha = 1;
|
||||
}];
|
||||
|
||||
[self setHelpHidden:[[MPConfig get].helpHidden boolValue] animated:animated];
|
||||
[self updateAnimated:animated];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
self.searchTipContainer.hidden = YES;
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
|
||||
[super viewDidAppear:animated];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
|
||||
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"ui_background"]];
|
||||
|
||||
// Put the search tip on the window so it's above the nav bar.
|
||||
if (![self.searchTipContainer.superview isEqual:self.navigationController.navigationBar.superview]) {
|
||||
CGRect frameInWindow = [self.searchTipContainer.window convertRect:self.searchTipContainer.frame
|
||||
fromView:self.searchTipContainer.superview];
|
||||
[self.searchTipContainer removeFromSuperview];
|
||||
[self.navigationController.navigationBar.superview addSubview:self.searchTipContainer];
|
||||
self.searchTipContainer.frame = [self.searchTipContainer.window convertRect:frameInWindow
|
||||
toView:self.searchTipContainer.superview];
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification object:nil queue:[NSOperationQueue mainQueue]
|
||||
usingBlock:^(NSNotification *note) {
|
||||
if (![MPAppDelegate get].keyPhrase) {
|
||||
self.activeElement = nil;
|
||||
[self updateAnimated:NO];
|
||||
}
|
||||
}];
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidBecomeActiveNotification object:nil queue:[NSOperationQueue mainQueue]
|
||||
usingBlock:^(NSNotification *note) {
|
||||
if (![MPAppDelegate get].keyPhrase) {
|
||||
self.activeElement = nil;
|
||||
[self updateAnimated:NO];
|
||||
}
|
||||
}];
|
||||
|
||||
[self closeAlert];
|
||||
|
||||
[super viewDidLoad];
|
||||
}
|
||||
|
||||
- (void)viewDidUnload {
|
||||
|
||||
[self setContentField:nil];
|
||||
[self setTypeButton:nil];
|
||||
[self setSearchResultsController:nil];
|
||||
[self setHelpView:nil];
|
||||
[self setSiteName:nil];
|
||||
[self setPasswordCounter:nil];
|
||||
[self setPasswordIncrementer:nil];
|
||||
[self setPasswordEdit:nil];
|
||||
[self setContentContainer:nil];
|
||||
[self setHelpContainer:nil];
|
||||
[self setContentTipContainer:nil];
|
||||
[self setAlertContainer:nil];
|
||||
[self setAlertTitle:nil];
|
||||
[self setAlertBody:nil];
|
||||
[self setContentTipBody:nil];
|
||||
[self setContentTipEditIcon:nil];
|
||||
[self setSearchTipContainer:nil];
|
||||
[super viewDidUnload];
|
||||
}
|
||||
|
||||
- (void)updateAnimated:(BOOL)animated {
|
||||
|
||||
[[MPAppDelegate get] saveContext];
|
||||
|
||||
if (animated)
|
||||
[UIView animateWithDuration:0.2 animations:^{
|
||||
[self updateWasAnimated:YES];
|
||||
}];
|
||||
else
|
||||
[self updateWasAnimated:NO];
|
||||
}
|
||||
|
||||
- (void)updateWasAnimated:(BOOL)animated {
|
||||
|
||||
[self setHelpChapter:self.activeElement? @"2": @"1"];
|
||||
self.siteName.text = self.activeElement.name;
|
||||
|
||||
self.passwordCounter.alpha = self.activeElement.type & MPElementTypeClassCalculated? 0.5f: 0;
|
||||
self.passwordIncrementer.alpha = self.activeElement.type & MPElementTypeClassCalculated? 0.5f: 0;
|
||||
self.passwordEdit.alpha = self.activeElement.type & MPElementTypeClassStored? 0.5f: 0;
|
||||
|
||||
[self.typeButton setTitle:NSStringFromMPElementType(self.activeElement.type)
|
||||
forState:UIControlStateNormal];
|
||||
self.typeButton.alpha = NSStringFromMPElementType(self.activeElement.type).length? 1: 0;
|
||||
|
||||
self.contentField.enabled = NO;
|
||||
|
||||
if ([self.activeElement isKindOfClass:[MPElementGeneratedEntity class]])
|
||||
self.passwordCounter.text = [NSString stringWithFormat:@"%d", ((MPElementGeneratedEntity *) self.activeElement).counter];
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
|
||||
NSString *description = self.activeElement.description;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self.contentField.text = description;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
- (BOOL)isHelpVisible {
|
||||
|
||||
return self.helpContainer.frame.origin.y < 400;
|
||||
}
|
||||
|
||||
- (void)toggleHelpAnimated:(BOOL)animated {
|
||||
|
||||
[self setHelpHidden:[self isHelpVisible] animated:animated];
|
||||
}
|
||||
|
||||
- (void)setHelpHidden:(BOOL)hidden animated:(BOOL)animated {
|
||||
|
||||
[UIView animateWithDuration:animated? 0.3f: 0 animations:^{
|
||||
|
||||
if (hidden) {
|
||||
self.contentContainer.frame = CGRectSetHeight(self.contentContainer.frame, 373);
|
||||
self.helpContainer.frame = CGRectSetY(self.helpContainer.frame, 415);
|
||||
[MPConfig get].helpHidden = [NSNumber numberWithBool:YES];
|
||||
} else {
|
||||
self.contentContainer.frame = CGRectSetHeight(self.contentContainer.frame, 175);
|
||||
self.helpContainer.frame = CGRectSetY(self.helpContainer.frame, 216);
|
||||
[MPConfig get].helpHidden = [NSNumber numberWithBool:NO];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setHelpChapter:(NSString *)chapter {
|
||||
|
||||
#ifndef PRODUCTION
|
||||
[TestFlight passCheckpoint:[NSString stringWithFormat:MPTestFlightCheckpointHelpChapter, chapter]];
|
||||
#endif
|
||||
|
||||
[self.helpView loadRequest:
|
||||
[NSURLRequest requestWithURL:
|
||||
[NSURL URLWithString:[NSString stringWithFormat:@"#%@", chapter] relativeToURL:
|
||||
[[NSBundle mainBundle] URLForResource:@"help" withExtension:@"html"]]]];
|
||||
[self.helpView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"setClass('%@');",
|
||||
ClassNameFromMPElementType(self.activeElement.type)]];
|
||||
}
|
||||
|
||||
- (void)showContentTip:(NSString *)message withIcon:(UIImageView *)icon {
|
||||
|
||||
self.contentTipBody.text = message;
|
||||
|
||||
icon.hidden = NO;
|
||||
[UIView animateWithDuration:0.2f animations:^{
|
||||
self.contentTipContainer.alpha = 1;
|
||||
} completion:^(BOOL finished) {
|
||||
if (!finished) {
|
||||
icon.hidden = YES;
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 5.0f * NSEC_PER_SEC);
|
||||
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
|
||||
[UIView animateWithDuration:0.2f animations:^{
|
||||
self.contentTipContainer.alpha = 0;
|
||||
} completion:^(BOOL finished) {
|
||||
icon.hidden = YES;
|
||||
}];
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)showAlertWithTitle:(NSString *)title message:(NSString *)message {
|
||||
|
||||
self.alertTitle.text = title;
|
||||
NSRange scrollRange = NSMakeRange(self.alertBody.text.length, message.length);
|
||||
if ([self.alertBody.text length])
|
||||
self.alertBody.text = [NSString stringWithFormat:@"%@\n\n---\n\n%@", self.alertBody.text, message];
|
||||
else
|
||||
self.alertBody.text = message;
|
||||
[self.alertBody scrollRangeToVisible:scrollRange];
|
||||
|
||||
[UIView animateWithDuration:0.2f animations:^{
|
||||
self.alertContainer.alpha = 1;
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Protocols
|
||||
|
||||
- (IBAction)copyContent {
|
||||
|
||||
if (!self.activeElement)
|
||||
return;
|
||||
|
||||
[[UIPasteboard generalPasteboard] setValue:self.activeElement.content
|
||||
forPasteboardType:(id)kUTTypeUTF8PlainText];
|
||||
|
||||
[self showContentTip:@"Copied!" withIcon:nil];
|
||||
|
||||
#ifndef PRODUCTION
|
||||
[TestFlight passCheckpoint:MPTestFlightCheckpointCopyToPasteboard];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (IBAction)incrementPasswordCounter {
|
||||
|
||||
if (![self.activeElement isKindOfClass:[MPElementGeneratedEntity class]])
|
||||
// Not of a type that supports a password counter;
|
||||
return;
|
||||
|
||||
[self updateElement:^{
|
||||
++((MPElementGeneratedEntity *) self.activeElement).counter;
|
||||
}];
|
||||
|
||||
#ifndef PRODUCTION
|
||||
[TestFlight passCheckpoint:MPTestFlightCheckpointIncrementPasswordCounter];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)updateElement:(void (^)(void))updateElement {
|
||||
|
||||
// Update password counter.
|
||||
NSString *oldPassword = self.activeElement.description;
|
||||
updateElement();
|
||||
NSString *newPassword = self.activeElement.description;
|
||||
[self updateAnimated:YES];
|
||||
|
||||
// Show new and old password.
|
||||
if (oldPassword && ![oldPassword isEqualToString:newPassword])
|
||||
[self showAlertWithTitle:@"Password Changed!" message:l(@"The password for %@ has changed.\n\n"
|
||||
@"Don't forget to update the site with your new password! "
|
||||
@"Your old password was:\n"
|
||||
@"%@", self.activeElement.name, oldPassword)];
|
||||
}
|
||||
|
||||
- (IBAction)editPassword {
|
||||
|
||||
if (self.activeElement.type & MPElementTypeClassStored) {
|
||||
self.contentField.enabled = YES;
|
||||
[self.contentField becomeFirstResponder];
|
||||
}
|
||||
|
||||
#ifndef PRODUCTION
|
||||
[TestFlight passCheckpoint:MPTestFlightCheckpointEditPassword];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (IBAction)closeAlert {
|
||||
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
self.alertContainer.alpha = 0;
|
||||
} completion:^(BOOL finished) {
|
||||
self.alertBody.text = nil;
|
||||
}];
|
||||
|
||||
#ifndef PRODUCTION
|
||||
[TestFlight passCheckpoint:MPTestFlightCheckpointCloseAlert];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (IBAction)action:(id)sender {
|
||||
|
||||
[SheetViewController showSheetWithTitle:nil message:nil viewStyle:UIActionSheetStyleAutomatic
|
||||
tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
|
||||
if (buttonIndex == [sheet cancelButtonIndex])
|
||||
return;
|
||||
|
||||
switch (buttonIndex - [sheet firstOtherButtonIndex]) {
|
||||
case 0:
|
||||
[self toggleHelpAnimated:YES];
|
||||
break;
|
||||
case 1:
|
||||
[self setHelpChapter:@"faq"];
|
||||
[self setHelpHidden:NO animated:YES];
|
||||
break;
|
||||
case 2:
|
||||
[[MPAppDelegate get] showGuide];
|
||||
break;
|
||||
case 3: {
|
||||
IASKAppSettingsViewController *settingsVC = [IASKAppSettingsViewController new];
|
||||
settingsVC.delegate = self;
|
||||
[self.navigationController pushViewController:settingsVC animated:YES];
|
||||
break;
|
||||
}
|
||||
#ifndef PRODUCTION
|
||||
case 4:
|
||||
[TestFlight openFeedbackView];
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef PRODUCTION
|
||||
[TestFlight passCheckpoint:MPTestFlightCheckpointAction];
|
||||
#endif
|
||||
} cancelTitle:[PearlStrings get].commonButtonCancel destructiveTitle:nil
|
||||
otherTitles:
|
||||
[self isHelpVisible]? @"Hide Help": @"Show Help", @"FAQ", @"Tutorial", @"Settings",
|
||||
#ifndef PRODUCTION
|
||||
@"Feedback",
|
||||
#endif
|
||||
nil];
|
||||
}
|
||||
|
||||
- (void)didSelectType:(MPElementType)type {
|
||||
|
||||
[self updateElement:^{
|
||||
// Update password type.
|
||||
if (ClassFromMPElementType(type) != ClassFromMPElementType(self.activeElement.type))
|
||||
// Type requires a different class of element. Recreate the element.
|
||||
[[MPAppDelegate managedObjectContext] performBlockAndWait:^{
|
||||
MPElementEntity *newElement = [NSEntityDescription insertNewObjectForEntityForName:ClassNameFromMPElementType(type)
|
||||
inManagedObjectContext:[MPAppDelegate managedObjectContext]];
|
||||
newElement.name = self.activeElement.name;
|
||||
newElement.mpHashHex = self.activeElement.mpHashHex;
|
||||
newElement.uses = self.activeElement.uses;
|
||||
newElement.lastUsed = self.activeElement.lastUsed;
|
||||
|
||||
[[MPAppDelegate managedObjectContext] deleteObject:self.activeElement];
|
||||
self.activeElement = newElement;
|
||||
}];
|
||||
|
||||
self.activeElement.type = type;
|
||||
|
||||
#ifndef PRODUCTION
|
||||
[TestFlight passCheckpoint:[NSString stringWithFormat:MPTestFlightCheckpointSelectType, NSStringFromMPElementType(type)]];
|
||||
#endif
|
||||
|
||||
if (type & MPElementTypeClassStored && ![self.activeElement.description length])
|
||||
[self showContentTip:@"Tap to set a password." withIcon:self.contentTipEditIcon];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)didSelectElement:(MPElementEntity *)element {
|
||||
|
||||
self.activeElement = element;
|
||||
[self.activeElement use];
|
||||
|
||||
[self.searchDisplayController setActive:NO animated:YES];
|
||||
self.searchDisplayController.searchBar.text = self.activeElement.name;
|
||||
|
||||
#ifndef PRODUCTION
|
||||
[TestFlight passCheckpoint:MPTestFlightCheckpointSelectElement];
|
||||
#endif
|
||||
|
||||
[self updateAnimated:YES];
|
||||
}
|
||||
|
||||
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
|
||||
|
||||
#ifndef PRODUCTION
|
||||
[TestFlight passCheckpoint:MPTestFlightCheckpointCancelSearch];
|
||||
#endif
|
||||
|
||||
[self updateAnimated:YES];
|
||||
}
|
||||
|
||||
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
|
||||
|
||||
if (textField == self.contentField)
|
||||
[self.contentField resignFirstResponder];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)textFieldDidEndEditing:(UITextField *)textField {
|
||||
|
||||
if (textField == self.contentField) {
|
||||
self.contentField.enabled = NO;
|
||||
if (![self.activeElement isKindOfClass:[MPElementStoredEntity class]])
|
||||
// Not of a type whose content can be edited.
|
||||
return;
|
||||
|
||||
if ([((MPElementStoredEntity *) self.activeElement).content isEqual:self.contentField.text])
|
||||
// Content hasn't changed.
|
||||
return;
|
||||
|
||||
[self updateElement:^{
|
||||
((MPElementStoredEntity *) self.activeElement).content = self.contentField.text;
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
|
||||
navigationType:(UIWebViewNavigationType)navigationType {
|
||||
|
||||
if (navigationType == UIWebViewNavigationTypeLinkClicked) {
|
||||
#ifndef PRODUCTION
|
||||
[TestFlight passCheckpoint:MPTestFlightCheckpointExternalLink];
|
||||
#endif
|
||||
|
||||
[[UIApplication sharedApplication] openURL:[request URL]];
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)settingsViewControllerDidEnd:(IASKAppSettingsViewController *)sender {
|
||||
|
||||
while ([self.navigationController.viewControllers containsObject:sender])
|
||||
[self.navigationController popViewControllerAnimated:YES];
|
||||
}
|
||||
|
||||
@end
|
||||
26
MasterPassword/MPSearchDelegate.h
Normal file
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// MPSearchDelegate.h
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 04/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MPElementEntity.h"
|
||||
|
||||
@protocol MPSearchResultsDelegate <NSObject>
|
||||
|
||||
- (void)didSelectElement:(MPElementEntity *)element;
|
||||
|
||||
@end
|
||||
|
||||
@interface MPSearchDelegate : NSObject <UITableViewDelegate, UITableViewDataSource, UISearchDisplayDelegate, NSFetchedResultsControllerDelegate>
|
||||
|
||||
@property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet id<MPSearchResultsDelegate> delegate;
|
||||
@property (weak, nonatomic) IBOutlet UISearchDisplayController *searchDisplayController;
|
||||
@property (weak, nonatomic) IBOutlet UIView *searchTipContainer;
|
||||
|
||||
@end
|
||||
263
MasterPassword/MPSearchDelegate.m
Normal file
@@ -0,0 +1,263 @@
|
||||
//
|
||||
// MPSearchDelegate.m
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 04/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPSearchDelegate.h"
|
||||
#import "MPAppDelegate.h"
|
||||
#import "MPElementGeneratedEntity.h"
|
||||
|
||||
@interface MPSearchDelegate (Private)
|
||||
|
||||
- (void)configureCell:(UITableViewCell *)cell inTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath;
|
||||
- (void)update;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPSearchDelegate
|
||||
@synthesize fetchedResultsController;
|
||||
@synthesize delegate;
|
||||
@synthesize searchDisplayController;
|
||||
@synthesize searchTipContainer;
|
||||
|
||||
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
|
||||
|
||||
self.searchDisplayController.searchBar.text = @"";
|
||||
self.searchDisplayController.searchBar.prompt = @"Enter the site's domain name (eg. apple.com):";
|
||||
|
||||
[UIView animateWithDuration:0.2f animations:^{
|
||||
self.searchTipContainer.alpha = 0;
|
||||
}];
|
||||
|
||||
[self update];
|
||||
}
|
||||
|
||||
- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller {
|
||||
|
||||
self.searchDisplayController.searchBar.prompt = nil;
|
||||
}
|
||||
|
||||
- (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView {
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:UIScreenModeDidChangeNotification
|
||||
object:nil queue:nil usingBlock:^(NSNotification *note) {
|
||||
NSError *error;
|
||||
if (![self.fetchedResultsController performFetch:&error])
|
||||
err(@"Couldn't fetch elements: %@", error);
|
||||
}];
|
||||
|
||||
tableView.backgroundColor = [UIColor blackColor];
|
||||
tableView.rowHeight = 34.0f;
|
||||
tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||
}
|
||||
|
||||
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
|
||||
|
||||
[self update];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)update {
|
||||
|
||||
NSString *query = self.searchDisplayController.searchBar.text;
|
||||
if (!query)
|
||||
query = @"";
|
||||
|
||||
NSFetchRequest *fetchRequest = [[MPAppDelegate get].managedObjectModel
|
||||
fetchRequestFromTemplateWithName:@"MPSearchElement"
|
||||
substitutionVariables:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
query, @"query",
|
||||
[MPAppDelegate get].keyPhraseHashHex, @"mpHashHex",
|
||||
nil]];
|
||||
[fetchRequest setSortDescriptors:
|
||||
[NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"uses" ascending:NO]]];
|
||||
|
||||
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
|
||||
managedObjectContext:[MPAppDelegate managedObjectContext]
|
||||
sectionNameKeyPath:nil cacheName:nil];
|
||||
self.fetchedResultsController.delegate = self;
|
||||
|
||||
NSError *error;
|
||||
if (![self.fetchedResultsController performFetch:&error])
|
||||
err(@"Couldn't fetch elements: %@", error);
|
||||
}
|
||||
|
||||
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
|
||||
|
||||
[self.searchDisplayController.searchResultsTableView beginUpdates];
|
||||
}
|
||||
|
||||
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
|
||||
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
|
||||
|
||||
UITableView *tableView = self.searchDisplayController.searchResultsTableView;
|
||||
switch(type) {
|
||||
|
||||
case NSFetchedResultsChangeInsert:
|
||||
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
|
||||
break;
|
||||
|
||||
case NSFetchedResultsChangeDelete:
|
||||
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
|
||||
break;
|
||||
|
||||
case NSFetchedResultsChangeUpdate:
|
||||
[self configureCell:[tableView cellForRowAtIndexPath:indexPath]
|
||||
inTableView:tableView
|
||||
atIndexPath:indexPath];
|
||||
break;
|
||||
|
||||
case NSFetchedResultsChangeMove:
|
||||
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
|
||||
withRowAnimation:UITableViewRowAnimationFade];
|
||||
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
|
||||
withRowAnimation:UITableViewRowAnimationFade];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
|
||||
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
|
||||
|
||||
UITableView *tableView = self.searchDisplayController.searchResultsTableView;
|
||||
switch(type) {
|
||||
|
||||
case NSFetchedResultsChangeInsert:
|
||||
[tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
|
||||
withRowAnimation:UITableViewRowAnimationFade];
|
||||
break;
|
||||
|
||||
case NSFetchedResultsChangeDelete:
|
||||
[tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
|
||||
withRowAnimation:UITableViewRowAnimationFade];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
|
||||
|
||||
[self.searchDisplayController.searchResultsTableView endUpdates];
|
||||
}
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
||||
|
||||
return [[self.fetchedResultsController sections] count] + 1;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
|
||||
if (section == [self numberOfSectionsInTableView:tableView] - 1)
|
||||
return 1;
|
||||
|
||||
return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
|
||||
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MPElementSearch"];
|
||||
if (!cell) {
|
||||
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"MPElementSearch"];
|
||||
UIImageView *backgroundImageView = [[UIImageView alloc] initWithImage:[[UIImage imageNamed:@"ui_list_middle"] resizableImageWithCapInsets:UIEdgeInsetsMake(5, 5, 5, 5)]];
|
||||
backgroundImageView.frame = CGRectMake(-5, 0, 330, 34);
|
||||
UIView *backgroundView = [[UIView alloc] initWithFrame:cell.frame];
|
||||
[backgroundView addSubview:backgroundImageView];
|
||||
|
||||
cell.backgroundView = backgroundView;
|
||||
cell.textLabel.backgroundColor = [UIColor clearColor];
|
||||
cell.textLabel.textColor = [UIColor whiteColor];
|
||||
cell.detailTextLabel.backgroundColor = [UIColor clearColor];
|
||||
cell.detailTextLabel.textColor = [UIColor lightGrayColor];
|
||||
}
|
||||
|
||||
[self configureCell:cell inTableView:tableView atIndexPath:indexPath];
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)configureCell:(UITableViewCell *)cell inTableView:(UITableView *)tableView atIndexPath:(NSIndexPath *)indexPath {
|
||||
|
||||
if (indexPath.section < [[self.fetchedResultsController sections] count]) {
|
||||
MPElementEntity *element = [self.fetchedResultsController objectAtIndexPath:indexPath];
|
||||
|
||||
cell.textLabel.text = element.name;
|
||||
cell.detailTextLabel.text = [NSString stringWithFormat:@"%d", element.uses];
|
||||
} else {
|
||||
// "New" section
|
||||
cell.textLabel.text = self.searchDisplayController.searchBar.text;
|
||||
cell.detailTextLabel.text = @"New";
|
||||
}
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
|
||||
if (indexPath.section < [[self.fetchedResultsController sections] count])
|
||||
[self.delegate didSelectElement:[self.fetchedResultsController objectAtIndexPath:indexPath]];
|
||||
|
||||
else {
|
||||
// "New" section.
|
||||
NSString *siteName = self.searchDisplayController.searchBar.text;
|
||||
[AlertViewController showAlertWithTitle:@"New Site"
|
||||
message:l(@"Do you want to create a new site named:\n%@", siteName)
|
||||
viewStyle:UIAlertViewStyleDefault
|
||||
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
|
||||
if (buttonIndex == [alert cancelButtonIndex])
|
||||
return;
|
||||
|
||||
[self.fetchedResultsController.managedObjectContext performBlock:^{
|
||||
MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([MPElementGeneratedEntity class])
|
||||
inManagedObjectContext:self.fetchedResultsController.managedObjectContext];
|
||||
assert([element isKindOfClass:ClassFromMPElementType(element.type)]);
|
||||
|
||||
element.name = siteName;
|
||||
element.mpHashHex = [MPAppDelegate get].keyPhraseHashHex;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.delegate didSelectElement:element];
|
||||
});
|
||||
}];
|
||||
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonYes, nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
|
||||
|
||||
if (--section == -1)
|
||||
return @"";
|
||||
|
||||
return [[[self.fetchedResultsController sections] objectAtIndex:section] name];
|
||||
}
|
||||
|
||||
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
|
||||
|
||||
return [self.fetchedResultsController sectionIndexTitles];
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
|
||||
|
||||
return [self.fetchedResultsController sectionForSectionIndexTitle:title atIndex:index];
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
|
||||
if (indexPath.section == [[self.fetchedResultsController sections] count])
|
||||
// "New" section.
|
||||
return;
|
||||
|
||||
if (editingStyle == UITableViewCellEditingStyleDelete)
|
||||
[self.fetchedResultsController.managedObjectContext performBlock:^{
|
||||
MPElementEntity *element = [self.fetchedResultsController objectAtIndexPath:indexPath];
|
||||
[self.fetchedResultsController.managedObjectContext deleteObject:element];
|
||||
|
||||
#ifndef PRODUCTION
|
||||
[TestFlight passCheckpoint:MPTestFlightCheckpointDeleteElement];
|
||||
#endif
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
21
MasterPassword/MPTypeViewController.h
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// MPTypeViewController.h
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 27/11/11.
|
||||
// Copyright (c) 2011 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@protocol MPTypeDelegate <NSObject>
|
||||
|
||||
- (void)didSelectType:(MPElementType)type;
|
||||
|
||||
@end
|
||||
|
||||
@interface MPTypeViewController : UITableViewController
|
||||
|
||||
@property (nonatomic, weak) id<MPTypeDelegate> delegate;
|
||||
|
||||
@end
|
||||
86
MasterPassword/MPTypeViewController.m
Normal file
@@ -0,0 +1,86 @@
|
||||
//
|
||||
// MPTypeViewController.m
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 27/11/11.
|
||||
// Copyright (c) 2011 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPTypeViewController.h"
|
||||
|
||||
@implementation MPTypeViewController
|
||||
@synthesize delegate;
|
||||
|
||||
#pragma mark - View lifecycle
|
||||
|
||||
- (void)viewDidLoad {
|
||||
|
||||
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"ui_background"]];
|
||||
|
||||
[super viewDidLoad];
|
||||
}
|
||||
|
||||
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
|
||||
assert(self.navigationController.topViewController == self);
|
||||
|
||||
MPElementType type;
|
||||
switch (indexPath.section) {
|
||||
case 0: {
|
||||
// Calculated
|
||||
switch (indexPath.row) {
|
||||
case 0:
|
||||
type = MPElementTypeCalculatedLong;
|
||||
break;
|
||||
case 1:
|
||||
type = MPElementTypeCalculatedMedium;
|
||||
break;
|
||||
case 2:
|
||||
type = MPElementTypeCalculatedShort;
|
||||
break;
|
||||
case 3:
|
||||
type = MPElementTypeCalculatedBasic;
|
||||
break;
|
||||
case 4:
|
||||
type = MPElementTypeCalculatedPIN;
|
||||
break;
|
||||
|
||||
default:
|
||||
[NSException raise:NSInternalInconsistencyException
|
||||
format:@"Unsupported row: %d, when selecting calculated element type.", indexPath.row];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 1: {
|
||||
// Stored
|
||||
switch (indexPath.row) {
|
||||
case 0:
|
||||
type = MPElementTypeStoredPersonal;
|
||||
break;
|
||||
case 1:
|
||||
type = MPElementTypeStoredDevicePrivate;
|
||||
break;
|
||||
|
||||
default:
|
||||
[NSException raise:NSInternalInconsistencyException
|
||||
format:@"Unsupported row: %d, when selecting stored element type.", indexPath.row];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
[NSException raise:NSInternalInconsistencyException
|
||||
format:@"Unsupported section: %d, when selecting element type.", indexPath.section];
|
||||
}
|
||||
|
||||
[delegate didSelectType:type];
|
||||
[self.navigationController popViewControllerAnimated:YES];
|
||||
}
|
||||
|
||||
@end
|
||||
63
MasterPassword/MPTypes.h
Normal file
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// MPTypes.h
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 02/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#define MPPersistentStoreDidChangeNotification @"MPPersistentStoreDidChange"
|
||||
|
||||
typedef enum {
|
||||
MPElementContentTypePassword,
|
||||
MPElementContentTypeNote,
|
||||
MPElementContentTypePicture,
|
||||
} MPElementContentType;
|
||||
|
||||
typedef enum {
|
||||
MPElementTypeClassCalculated = 2 << 7,
|
||||
MPElementTypeClassStored = 2 << 8,
|
||||
} MPElementTypeClass;
|
||||
|
||||
typedef enum {
|
||||
MPElementTypeCalculatedLong = MPElementTypeClassCalculated | 0x01,
|
||||
MPElementTypeCalculatedMedium = MPElementTypeClassCalculated | 0x02,
|
||||
MPElementTypeCalculatedShort = MPElementTypeClassCalculated | 0x03,
|
||||
MPElementTypeCalculatedBasic = MPElementTypeClassCalculated | 0x04,
|
||||
MPElementTypeCalculatedPIN = MPElementTypeClassCalculated | 0x05,
|
||||
|
||||
MPElementTypeStoredPersonal = MPElementTypeClassStored | 0x01,
|
||||
MPElementTypeStoredDevicePrivate = MPElementTypeClassStored | 0x02,
|
||||
} MPElementType;
|
||||
|
||||
#ifndef PRODUCTION
|
||||
#define MPTestFlightCheckpointAction @"MPTestFlightCheckpointAction"
|
||||
#define MPTestFlightCheckpointHelpChapter @"MPTestFlightCheckpointHelpChapter_%@"
|
||||
#define MPTestFlightCheckpointCopyToPasteboard @"MPTestFlightCheckpointCopyToPasteboard"
|
||||
#define MPTestFlightCheckpointIncrementPasswordCounter @"MPTestFlightCheckpointIncrementPasswordCounter"
|
||||
#define MPTestFlightCheckpointEditPassword @"MPTestFlightCheckpointEditPassword"
|
||||
#define MPTestFlightCheckpointCloseAlert @"MPTestFlightCheckpointCloseAlert"
|
||||
#define MPTestFlightCheckpointSelectType @"MPTestFlightCheckpointSelectType_%@"
|
||||
#define MPTestFlightCheckpointSelectElement @"MPTestFlightCheckpointSelectElement"
|
||||
#define MPTestFlightCheckpointDeleteElement @"MPTestFlightCheckpointDeleteElement"
|
||||
#define MPTestFlightCheckpointCancelSearch @"MPTestFlightCheckpointCancelSearch"
|
||||
#define MPTestFlightCheckpointExternalLink @"MPTestFlightCheckpointExternalLink"
|
||||
#define MPTestFlightCheckpointLaunched @"MPTestFlightCheckpointLaunched"
|
||||
#define MPTestFlightCheckpointActivated @"MPTestFlightCheckpointActivated"
|
||||
#define MPTestFlightCheckpointDeactivated @"MPTestFlightCheckpointDeactivated"
|
||||
#define MPTestFlightCheckpointTerminated @"MPTestFlightCheckpointTerminated"
|
||||
#define MPTestFlightCheckpointShowGuide @"MPTestFlightCheckpointShowGuide"
|
||||
#define MPTestFlightCheckpointMPChanged @"MPTestFlightCheckpointMPChanged"
|
||||
#define MPTestFlightCheckpointMPUnstored @"MPTestFlightCheckpointMPUnstored"
|
||||
#define MPTestFlightCheckpointMPMismatch @"MPTestFlightCheckpointMPMismatch"
|
||||
#define MPTestFlightCheckpointMPAsked @"MPTestFlightCheckpointMPAsked"
|
||||
#define MPTestFlightCheckpointStoreIncompatible @"MPTestFlightCheckpointStoreIncompatible"
|
||||
#define MPTestFlightCheckpointSetKeyphraseLength @"MPTestFlightCheckpointSetKeyphraseLength_%d"
|
||||
#endif
|
||||
|
||||
NSString *NSStringFromMPElementType(MPElementType type);
|
||||
NSString *ClassNameFromMPElementType(MPElementType type);
|
||||
Class ClassFromMPElementType(MPElementType type);
|
||||
NSString *MPCalculateContent(MPElementType type, NSString *name, NSString *keyPhrase, int counter);
|
||||
115
MasterPassword/MPTypes.m
Normal file
@@ -0,0 +1,115 @@
|
||||
//
|
||||
// MPTypes.m
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 02/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPTypes.h"
|
||||
#import "MPElementGeneratedEntity.h"
|
||||
#import "MPElementStoredEntity.h"
|
||||
|
||||
|
||||
NSString *NSStringFromMPElementType(MPElementType type) {
|
||||
|
||||
if (!type)
|
||||
return nil;
|
||||
|
||||
switch (type) {
|
||||
case MPElementTypeCalculatedLong:
|
||||
return @"Long Password";
|
||||
|
||||
case MPElementTypeCalculatedMedium:
|
||||
return @"Medium Password";
|
||||
|
||||
case MPElementTypeCalculatedShort:
|
||||
return @"Short Password";
|
||||
|
||||
case MPElementTypeCalculatedBasic:
|
||||
return @"Basic Password";
|
||||
|
||||
case MPElementTypeCalculatedPIN:
|
||||
return @"PIN";
|
||||
|
||||
case MPElementTypeStoredPersonal:
|
||||
return @"Personal Password";
|
||||
|
||||
case MPElementTypeStoredDevicePrivate:
|
||||
return @"Device Private Password";
|
||||
|
||||
default:
|
||||
[NSException raise:NSInternalInconsistencyException format:@"Type not supported: %d", type];
|
||||
}
|
||||
}
|
||||
|
||||
Class ClassFromMPElementType(MPElementType type) {
|
||||
|
||||
if (!type)
|
||||
return nil;
|
||||
|
||||
switch (type) {
|
||||
case MPElementTypeCalculatedLong:
|
||||
return [MPElementGeneratedEntity class];
|
||||
|
||||
case MPElementTypeCalculatedMedium:
|
||||
return [MPElementGeneratedEntity class];
|
||||
|
||||
case MPElementTypeCalculatedShort:
|
||||
return [MPElementGeneratedEntity class];
|
||||
|
||||
case MPElementTypeCalculatedBasic:
|
||||
return [MPElementGeneratedEntity class];
|
||||
|
||||
case MPElementTypeCalculatedPIN:
|
||||
return [MPElementGeneratedEntity class];
|
||||
|
||||
case MPElementTypeStoredPersonal:
|
||||
return [MPElementStoredEntity class];
|
||||
|
||||
case MPElementTypeStoredDevicePrivate:
|
||||
return [MPElementStoredEntity class];
|
||||
|
||||
default:
|
||||
[NSException raise:NSInternalInconsistencyException format:@"Type not supported: %d", type];
|
||||
}
|
||||
}
|
||||
|
||||
NSString *ClassNameFromMPElementType(MPElementType type) {
|
||||
|
||||
return NSStringFromClass(ClassFromMPElementType(type));
|
||||
}
|
||||
|
||||
static NSDictionary *MPTypes_ciphers = nil;
|
||||
NSString *MPCalculateContent(MPElementType type, NSString *name, NSString *keyPhrase, int counter) {
|
||||
|
||||
assert(type & MPElementTypeClassCalculated);
|
||||
|
||||
if (MPTypes_ciphers == nil)
|
||||
MPTypes_ciphers = [NSDictionary dictionaryWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"ciphers"
|
||||
withExtension:@"plist"]];
|
||||
|
||||
// Determine the hash whose bytes will be used for calculating a password: md4(name-keyPhrase)
|
||||
assert(name && keyPhrase);
|
||||
NSData *keyHash = [[NSString stringWithFormat:@"%@-%@-%d", name, keyPhrase, counter] hashWith:PearlDigestSHA1];
|
||||
const char *keyBytes = keyHash.bytes;
|
||||
|
||||
// Determine the cipher from the first hash byte.
|
||||
assert([keyHash length]);
|
||||
NSArray *typeCiphers = [[MPTypes_ciphers valueForKey:ClassNameFromMPElementType(type)]
|
||||
valueForKey:NSStringFromMPElementType(type)];
|
||||
NSString *cipher = [typeCiphers objectAtIndex:keyBytes[0] % [typeCiphers count]];
|
||||
|
||||
// Encode the content, character by character, using subsequent hash bytes and the cipher.
|
||||
assert([keyHash length] >= [cipher length] + 1);
|
||||
NSMutableString *content = [NSMutableString stringWithCapacity:[cipher length]];
|
||||
for (NSUInteger c = 0; c < [cipher length]; ++c) {
|
||||
const char keyByte = keyBytes[c + 1];
|
||||
NSString *cipherClass = [cipher substringWithRange:NSMakeRange(c, 1)];
|
||||
NSString *cipherClassCharacters = [[MPTypes_ciphers valueForKey:@"MPCharacterClasses"] valueForKey:cipherClass];
|
||||
|
||||
[content appendString:[cipherClassCharacters substringWithRange:NSMakeRange(keyByte % [cipherClassCharacters length], 1)]];
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
720
MasterPassword/MainStoryboard_iPhone.storyboard
Normal file
@@ -0,0 +1,720 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="1.0" toolsVersion="1938" systemVersion="11D50" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" initialViewController="KZF-fe-y9n">
|
||||
<dependencies>
|
||||
<development defaultVersion="4200" identifier="xcode"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="933"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<scene sceneID="WkW-SR-cr2">
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="jZj-N1-rhF" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
<tableViewController id="NKe-nv-566" customClass="MPTypeViewController" sceneMemberID="viewController">
|
||||
<tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="plain" separatorStyle="none" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="btl-G4-V0S">
|
||||
<rect key="frame" x="0.0" y="64" width="320" height="416"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" red="0.12549020350000001" green="0.1411764771" blue="0.14901961389999999" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<view key="tableFooterView" contentMode="scaleToFill" id="aNa-wb-cYK">
|
||||
<rect key="frame" x="0.0" y="588" width="320" height="20"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<sections>
|
||||
<tableViewSection id="gUv-FU-O36">
|
||||
<cells>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" rowHeight="204" id="NCB-rX-weq">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="204"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="203"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Generated" lineBreakMode="tailTruncation" minimumFontSize="10" id="azh-GA-KA1">
|
||||
<rect key="frame" x="20" y="20" width="280" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" name="GillSans-Bold" family="Gill Sans" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<color key="shadowColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<size key="shadowOffset" width="0.0" height="1"/>
|
||||
</label>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="ui_list_first.png" id="WXs-ZQ-JuJ">
|
||||
<rect key="frame" x="10" y="160" width="300" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<rect key="contentStretch" x="0.10000000000000001" y="0.20000000000000001" width="0.79999999999999982" height="0.59999999999999964"/>
|
||||
</imageView>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Long Password" lineBreakMode="tailTruncation" minimumFontSize="10" id="QIm-RX-xY7">
|
||||
<rect key="frame" x="20" y="160" width="280" height="24"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<fontDescription key="fontDescription" name="GillSans" family="Gill Sans" pointSize="18"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<color key="shadowColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<size key="shadowOffset" width="0.0" height="1"/>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Word-like, 14 characters, contains symbols." lineBreakMode="tailTruncation" minimumFontSize="10" id="6iu-aM-lJA">
|
||||
<rect key="frame" x="20" y="184" width="280" height="20"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<fontDescription key="fontDescription" name="GillSans-Light" family="Gill Sans" pointSize="14"/>
|
||||
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" lineBreakMode="tailTruncation" numberOfLines="0" minimumFontSize="10" id="E4a-XS-Qlz">
|
||||
<rect key="frame" x="23" y="49" width="280" height="112"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<string key="text">These types create a safe password for your site.
|
||||
The passwords aren't saved anywhere. This is a major advantage: if you loose your device, your passwords cannot be stolen. You can use any other device to get all your passwords back, no need for restoring backups or any other pains.</string>
|
||||
<fontDescription key="fontDescription" name="GillSans-LightItalic" family="Gill Sans" pointSize="14"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<color key="shadowColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<size key="shadowOffset" width="0.0" height="1"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</tableViewCell>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" id="re0-ZT-bnP">
|
||||
<rect key="frame" x="0.0" y="204" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="ui_list_middle.png" id="EWy-Eb-L5p">
|
||||
<rect key="frame" x="10" y="0.0" width="300" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<rect key="contentStretch" x="0.10000000000000001" y="0.20000000000000001" width="0.79999999999999982" height="0.59999999999999964"/>
|
||||
</imageView>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Medium Password" lineBreakMode="tailTruncation" minimumFontSize="10" id="5NA-XX-th4">
|
||||
<rect key="frame" x="20" y="0.0" width="280" height="24"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<fontDescription key="fontDescription" name="GillSans" family="Gill Sans" pointSize="18"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<color key="shadowColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<size key="shadowOffset" width="0.0" height="1"/>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Word-like, 8 characters, contains symbols." lineBreakMode="tailTruncation" minimumFontSize="10" id="MI2-JM-j4b">
|
||||
<rect key="frame" x="20" y="24" width="280" height="20"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<fontDescription key="fontDescription" name="GillSans-Light" family="Gill Sans" pointSize="14"/>
|
||||
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</tableViewCell>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" id="4pb-c9-i2D">
|
||||
<rect key="frame" x="0.0" y="248" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="ui_list_middle.png" id="Jqw-h1-g2X">
|
||||
<rect key="frame" x="10" y="0.0" width="300" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<rect key="contentStretch" x="0.10000000000000001" y="0.20000000000000001" width="0.79999999999999982" height="0.59999999999999964"/>
|
||||
</imageView>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Short Password" lineBreakMode="tailTruncation" minimumFontSize="10" id="iqW-mf-H1R">
|
||||
<rect key="frame" x="20" y="0.0" width="280" height="24"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<fontDescription key="fontDescription" name="GillSans" family="Gill Sans" pointSize="18"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<color key="shadowColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<size key="shadowOffset" width="0.0" height="1"/>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Word-like, 4 characters." lineBreakMode="tailTruncation" minimumFontSize="10" id="Zi3-26-3iq">
|
||||
<rect key="frame" x="20" y="24" width="280" height="20"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<fontDescription key="fontDescription" name="GillSans-Light" family="Gill Sans" pointSize="14"/>
|
||||
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</tableViewCell>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" id="ywW-4w-Wej">
|
||||
<rect key="frame" x="0.0" y="292" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="ui_list_middle.png" id="Son-EL-Drx">
|
||||
<rect key="frame" x="10" y="0.0" width="300" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<rect key="contentStretch" x="0.10000000000000001" y="0.20000000000000001" width="0.79999999999999982" height="0.59999999999999964"/>
|
||||
</imageView>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Basic Password" lineBreakMode="tailTruncation" minimumFontSize="10" id="Pim-A8-aib">
|
||||
<rect key="frame" x="20" y="0.0" width="280" height="24"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<fontDescription key="fontDescription" name="GillSans" family="Gill Sans" pointSize="18"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<color key="shadowColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<size key="shadowOffset" width="0.0" height="1"/>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="8 characters." lineBreakMode="tailTruncation" minimumFontSize="10" id="CYQ-D8-vNS">
|
||||
<rect key="frame" x="20" y="24" width="280" height="20"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<fontDescription key="fontDescription" name="GillSans-Light" family="Gill Sans" pointSize="14"/>
|
||||
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</tableViewCell>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" id="5p5-OD-zpn">
|
||||
<rect key="frame" x="0.0" y="336" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="ui_list_last.png" id="5nI-rL-CoO">
|
||||
<rect key="frame" x="10" y="0.0" width="300" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<rect key="contentStretch" x="0.10000000000000001" y="0.20000000000000001" width="0.79999999999999982" height="0.59999999999999964"/>
|
||||
</imageView>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="PIN" lineBreakMode="tailTruncation" minimumFontSize="10" id="Yu2-5k-AwW">
|
||||
<rect key="frame" x="20" y="0.0" width="280" height="24"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<fontDescription key="fontDescription" name="GillSans" family="Gill Sans" pointSize="18"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<color key="shadowColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<size key="shadowOffset" width="0.0" height="1"/>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="4 numbers." lineBreakMode="tailTruncation" minimumFontSize="10" id="5Zm-AH-bAe">
|
||||
<rect key="frame" x="20" y="24" width="280" height="20"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<fontDescription key="fontDescription" name="GillSans-Light" family="Gill Sans" pointSize="14"/>
|
||||
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection id="Jqk-Jl-vVm">
|
||||
<cells>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" rowHeight="164" id="OK2-fa-fDw">
|
||||
<rect key="frame" x="0.0" y="380" width="320" height="164"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="163"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="User-specified" lineBreakMode="tailTruncation" minimumFontSize="10" id="146-3h-mdD">
|
||||
<rect key="frame" x="20" y="20" width="280" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" name="GillSans-Bold" family="Gill Sans" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<color key="shadowColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<size key="shadowOffset" width="0.0" height="1"/>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" lineBreakMode="tailTruncation" numberOfLines="0" minimumFontSize="10" id="Bbk-lL-BeB">
|
||||
<rect key="frame" x="20" y="49" width="280" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<string key="text">If you need to specify a custom password for your site, use one of these types. The site's password will be encrypted using your master password.</string>
|
||||
<fontDescription key="fontDescription" name="GillSans-LightItalic" family="Gill Sans" pointSize="14"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<color key="shadowColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<size key="shadowOffset" width="0.0" height="1"/>
|
||||
</label>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="ui_list_first.png" id="fxL-ee-UVP">
|
||||
<rect key="frame" x="10" y="120" width="300" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<rect key="contentStretch" x="0.10000000000000001" y="0.20000000000000001" width="0.79999999999999982" height="0.59999999999999964"/>
|
||||
</imageView>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Personal Password" lineBreakMode="tailTruncation" minimumFontSize="10" id="kzc-rQ-74F">
|
||||
<rect key="frame" x="20" y="120" width="280" height="24"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<fontDescription key="fontDescription" name="GillSans" family="Gill Sans" pointSize="18"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<color key="shadowColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<size key="shadowOffset" width="0.0" height="1"/>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="AES-encrypted, iTunes backup, iCloud sync." lineBreakMode="tailTruncation" minimumFontSize="10" id="vNa-Yq-XIJ">
|
||||
<rect key="frame" x="20" y="144" width="280" height="20"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<fontDescription key="fontDescription" name="GillSans-Light" family="Gill Sans" pointSize="14"/>
|
||||
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</tableViewCell>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" id="ePk-a9-aNi">
|
||||
<rect key="frame" x="0.0" y="544" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="ui_list_last.png" id="PUy-Qk-CeF">
|
||||
<rect key="frame" x="10" y="0.0" width="300" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<rect key="contentStretch" x="0.10000000000000001" y="0.20000000000000001" width="0.79999999999999982" height="0.59999999999999964"/>
|
||||
</imageView>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Device Private Password" lineBreakMode="tailTruncation" minimumFontSize="10" id="2kB-0P-FeR">
|
||||
<rect key="frame" x="20" y="0.0" width="280" height="24"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<fontDescription key="fontDescription" name="GillSans" family="Gill Sans" pointSize="18"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<color key="shadowColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<size key="shadowOffset" width="0.0" height="1"/>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="AES-encrypted, stays on this device only." lineBreakMode="tailTruncation" minimumFontSize="10" id="6S8-9Y-pzj">
|
||||
<rect key="frame" x="20" y="24" width="280" height="20"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<fontDescription key="fontDescription" name="GillSans-Light" family="Gill Sans" pointSize="14"/>
|
||||
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="NKe-nv-566" id="5yH-j4-f9c"/>
|
||||
<outlet property="delegate" destination="NKe-nv-566" id="O5k-hI-7Tf"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="Type" id="rak-Td-wu1"/>
|
||||
</tableViewController>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="996" y="182"/>
|
||||
</scene>
|
||||
<scene sceneID="U26-Zf-euQ">
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="mK2-p1-3zC" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
<customObject id="0QO-2P-OhD" customClass="MPSearchDelegate">
|
||||
<connections>
|
||||
<outlet property="delegate" destination="PQa-Xl-A3x" id="O2f-mW-ab8"/>
|
||||
<outlet property="searchDisplayController" destination="P8c-gf-nN3" id="exk-dS-Ui3"/>
|
||||
<outlet property="searchTipContainer" destination="zOR-Du-qRL" id="lk6-2X-lIb"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<searchDisplayController id="P8c-gf-nN3">
|
||||
<connections>
|
||||
<outlet property="delegate" destination="0QO-2P-OhD" id="gst-Hm-5ja"/>
|
||||
<outlet property="searchBar" destination="qeo-n2-WVh" id="bFO-FC-Xdj"/>
|
||||
<outlet property="searchContentsController" destination="PQa-Xl-A3x" id="iEu-t3-hJY"/>
|
||||
<outlet property="searchResultsDataSource" destination="0QO-2P-OhD" id="XJh-rC-kId"/>
|
||||
<outlet property="searchResultsDelegate" destination="0QO-2P-OhD" id="Bm8-Q3-lLA"/>
|
||||
</connections>
|
||||
</searchDisplayController>
|
||||
<viewController id="PQa-Xl-A3x" customClass="MPMainViewController" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Ep0-Hn-5TR">
|
||||
<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="top" image="Content-Backdrop.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>
|
||||
<view contentMode="scaleToFill" id="g9q-4d-ZgJ">
|
||||
<rect key="frame" x="0.0" y="43" width="320" height="175"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="top" image="Content-Backdrop.png" id="enb-OH-DVZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="175"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
</imageView>
|
||||
<imageView userInteractionEnabled="NO" alpha="0.80000000000000004" contentMode="scaleToFill" image="ui_panel_container.png" id="0Yh-Py-lB6">
|
||||
<rect key="frame" x="11" y="20" width="298" height="87"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
<rect key="contentStretch" x="0.050000000000000003" y="0.10000000000000001" width="0.89999999999999991" height="0.79999999999999982"/>
|
||||
</imageView>
|
||||
<imageView userInteractionEnabled="NO" alpha="0.80000000000000004" contentMode="scaleToFill" image="ui_panel_display.png" id="cw7-HD-Wht">
|
||||
<rect key="frame" x="11" y="20" width="298" height="86"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
<rect key="contentStretch" x="0.050000000000000003" y="0.10000000000000001" width="0.89999999999999991" height="0.79999999999999982"/>
|
||||
</imageView>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" id="Zji-Vg-VwX">
|
||||
<rect key="frame" x="11" y="20" width="298" height="86"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" size="button"/>
|
||||
<state key="normal">
|
||||
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<state key="highlighted">
|
||||
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="copyContent" destination="PQa-Xl-A3x" eventType="touchUpInside" id="9tS-J5-wlx"/>
|
||||
</connections>
|
||||
</button>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.50000000000000011" contentMode="left" text="apple.com" lineBreakMode="tailTruncation" minimumFontSize="10" id="gSK-aB-wNI">
|
||||
<rect key="frame" x="25" y="20" width="270" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<color key="shadowColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<size key="shadowOffset" width="0.0" height="1"/>
|
||||
</label>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="S3cre7^Pa$swcrD" textAlignment="center" minimumFontSize="17" clearButtonMode="whileEditing" id="fiX-10-fif">
|
||||
<rect key="frame" x="20" y="46" width="280" height="60"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<color key="textColor" red="0.47450980390000003" green="0.86666666670000003" blue="0.98431372549999996" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<fontDescription key="fontDescription" name="Optima-ExtraBlack" family="Optima" pointSize="26"/>
|
||||
<textInputTraits key="textInputTraits" autocorrectionType="no"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="PQa-Xl-A3x" id="qOM-gq-c6g"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.50000000000000011" contentMode="left" text="1" textAlignment="right" lineBreakMode="tailTruncation" minimumFontSize="10" id="Iuf-np-e9C">
|
||||
<rect key="frame" x="240" y="27" width="40" height="20"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" name="Optima-ExtraBlack" family="Optima" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<color key="shadowColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<size key="shadowOffset" width="0.0" height="1"/>
|
||||
</label>
|
||||
<button opaque="NO" alpha="0.50000000000000011" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="jec-mu-nPt">
|
||||
<rect key="frame" x="274.5" y="18.5" width="37" height="37"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||
<inset key="contentEdgeInsets" minX="5" minY="5" maxX="5" maxY="5"/>
|
||||
<state key="normal" image="icon_plus.png">
|
||||
<color key="titleColor" red="0.19607843459999999" green="0.30980393290000002" blue="0.52156865600000002" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<state key="highlighted">
|
||||
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="incrementPasswordCounter" destination="PQa-Xl-A3x" eventType="touchUpInside" id="hMc-kb-yFA"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="9FS-fS-xH6">
|
||||
<rect key="frame" x="275" y="18.5" width="37" height="37"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||
<inset key="contentEdgeInsets" minX="5" minY="5" maxX="5" maxY="5"/>
|
||||
<state key="normal" image="icon_edit.png">
|
||||
<color key="titleColor" red="0.19607843459999999" green="0.30980393290000002" blue="0.52156865600000002" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<state key="highlighted">
|
||||
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="editPassword" destination="PQa-Xl-A3x" eventType="touchUpInside" id="jZC-Jm-ZZ0"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" reversesTitleShadowWhenHighlighted="YES" lineBreakMode="middleTruncation" id="Cei-5z-uWE">
|
||||
<rect key="frame" x="10" y="115" width="300" height="46"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||
<size key="titleShadowOffset" width="0.0" height="1"/>
|
||||
<state key="normal" title="Long" backgroundImage="ui_button_standard_large.png">
|
||||
<color key="titleColor" cocoaTouchSystemColor="lightTextColor"/>
|
||||
<color key="titleShadowColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<state key="highlighted">
|
||||
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<connections>
|
||||
<segue destination="NKe-nv-566" kind="push" identifier="MP_Main_ChooseType" id="vw4-Vd-O6q"/>
|
||||
</connections>
|
||||
</button>
|
||||
<view userInteractionEnabled="NO" alpha="0.0" contentMode="scaleToFill" id="v2m-Gf-pEV">
|
||||
<rect key="frame" x="55" y="8" width="210" height="57"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="tip_basic_black.png" id="fdm-NG-GFC">
|
||||
<rect key="frame" x="0.0" y="-1" width="210" height="60"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
</imageView>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Tap to set a password." textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" id="ieN-QQ-PyR">
|
||||
<rect key="frame" x="20" y="0.0" width="170" height="37"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" image="icon_edit.png" id="KEn-n3-qhX">
|
||||
<rect key="frame" x="48.5" y="6.5" width="24" height="24"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<view clipsSubviews="YES" alpha="0.60000000000000009" contentMode="scaleToFill" id="61G-By-qLB">
|
||||
<rect key="frame" x="0.0" y="216" width="320" height="200"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="bottom" image="Square-bottom.png" id="lbm-A0-3PK">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="58"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
</imageView>
|
||||
<webView opaque="NO" contentMode="scaleToFill" id="8FQ-x4-lR9">
|
||||
<rect key="frame" x="0.0" y="1" width="320" height="199"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="PQa-Xl-A3x" id="H1z-xP-1Pm"/>
|
||||
</connections>
|
||||
</webView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="scrollViewTexturedBackgroundColor"/>
|
||||
</view>
|
||||
<view alpha="0.0" contentMode="scaleToFill" id="yRY-qt-gz8">
|
||||
<rect key="frame" x="10" y="230" width="300" height="180"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="tip_alert_black.png" id="TUv-Tl-xTc">
|
||||
<rect key="frame" x="0.0" y="0.0" width="300" height="180"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<rect key="contentStretch" x="0.25" y="0.6499999999999998" width="0.64999999999999969" height="0.10000000000000002"/>
|
||||
</imageView>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Password Changed!" lineBreakMode="tailTruncation" minimumFontSize="10" id="ZdH-we-KcW">
|
||||
<rect key="frame" x="70" y="20" width="210" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
|
||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" editable="NO" id="jCg-t7-RuK">
|
||||
<rect key="frame" x="20" y="60" width="260" height="100"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<string key="text">The password for apple.com has changed.
|
||||
|
||||
Don't forget to update the site with your new password! Your old password was:
|
||||
L4m3P4sSw0rD</string>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="cX2-vD-vjc">
|
||||
<rect key="frame" x="259" y="5" width="32" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||
<state key="normal" image="icon_cancel.png">
|
||||
<color key="titleColor" red="0.19607843459999999" green="0.30980393290000002" blue="0.52156865600000002" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<state key="highlighted">
|
||||
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="closeAlert" destination="PQa-Xl-A3x" eventType="touchUpInside" id="oJt-DU-l38"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<searchBar contentMode="redraw" barStyle="blackOpaque" placeholder="Site" id="qeo-n2-WVh">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
<gestureRecognizers/>
|
||||
<textInputTraits key="textInputTraits" autocorrectionType="no" keyboardType="URL"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="PQa-Xl-A3x" id="oXp-0r-Gkl"/>
|
||||
</connections>
|
||||
</searchBar>
|
||||
<view userInteractionEnabled="NO" contentMode="scaleToFill" id="zOR-Du-qRL">
|
||||
<rect key="frame" x="55" y="-27" width="210" height="57"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="tip_basic_black.png" id="ORD-Xv-bOQ">
|
||||
<rect key="frame" x="0.0" y="-1" width="210" height="60"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
</imageView>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Enter the site's name." textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" id="21b-bH-lR9">
|
||||
<rect key="frame" x="20" y="0.0" width="170" height="37"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" image="icon_edit.png" id="4Xf-mC-295">
|
||||
<rect key="frame" x="49" y="6" width="24" height="24"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.14901960784313725" green="0.16470588235294117" blue="0.1803921568627451" alpha="1" colorSpace="calibratedRGB"/>
|
||||
</view>
|
||||
<navigationItem key="navigationItem" title="Master Password" id="hB9-S6-KHC">
|
||||
<barButtonItem key="rightBarButtonItem" systemItem="action" id="8Y7-Bf-FHa">
|
||||
<connections>
|
||||
<action selector="action:" destination="PQa-Xl-A3x" id="mhz-Ey-IOC"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="alertBody" destination="jCg-t7-RuK" id="Aru-Nr-DY0"/>
|
||||
<outlet property="alertContainer" destination="yRY-qt-gz8" id="gZx-90-Zgr"/>
|
||||
<outlet property="alertTitle" destination="ZdH-we-KcW" id="VWi-aS-HRw"/>
|
||||
<outlet property="contentContainer" destination="g9q-4d-ZgJ" id="Hgw-Hp-FHB"/>
|
||||
<outlet property="contentField" destination="fiX-10-fif" id="b2c-gv-qMp"/>
|
||||
<outlet property="contentTipBody" destination="ieN-QQ-PyR" id="flJ-gF-wbI"/>
|
||||
<outlet property="contentTipContainer" destination="v2m-Gf-pEV" id="9zL-GR-kyH"/>
|
||||
<outlet property="contentTipEditIcon" destination="KEn-n3-qhX" id="dDG-Nt-OLM"/>
|
||||
<outlet property="helpContainer" destination="61G-By-qLB" id="Jwt-0d-ZAV"/>
|
||||
<outlet property="helpView" destination="8FQ-x4-lR9" id="MOg-5s-kvK"/>
|
||||
<outlet property="passwordCounter" destination="Iuf-np-e9C" id="CIm-Mk-nJh"/>
|
||||
<outlet property="passwordEdit" destination="9FS-fS-xH6" id="YeB-HF-ZPk"/>
|
||||
<outlet property="passwordIncrementer" destination="jec-mu-nPt" id="i9B-lX-zzX"/>
|
||||
<outlet property="searchDisplayController" destination="P8c-gf-nN3" id="CLs-YI-7NC"/>
|
||||
<outlet property="searchResultsController" destination="0QO-2P-OhD" id="xEC-gV-lHp"/>
|
||||
<outlet property="searchTipContainer" destination="zOR-Du-qRL" id="X7h-Vh-iCE"/>
|
||||
<outlet property="siteName" destination="gSK-aB-wNI" id="IIe-z8-zy8"/>
|
||||
<outlet property="typeButton" destination="Cei-5z-uWE" id="4M1-d7-5Bh"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="455" y="182"/>
|
||||
</scene>
|
||||
<scene sceneID="Rt1-b4-sUB">
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="7yf-G7-kVy" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
<viewController id="qz3-eG-aEi" customClass="MPGuideViewController" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="cwa-ct-2DY">
|
||||
<rect key="frame" x="0.0" y="20" width="320" height="460"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" pagingEnabled="YES" showsVerticalScrollIndicator="NO" id="Nr4-hi-8b2">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="460"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="bottom" image="guide_page_0.png" id="eJP-cS-VRU">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="460"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
</imageView>
|
||||
<imageView userInteractionEnabled="NO" contentMode="bottom" image="guide_page_1.png" id="1J9-z9-h96">
|
||||
<rect key="frame" x="320" y="0.0" width="320" height="460"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
</imageView>
|
||||
<imageView userInteractionEnabled="NO" contentMode="bottom" image="guide_page_2.png" id="8kf-Wm-F3L">
|
||||
<rect key="frame" x="640" y="0.0" width="320" height="460"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
</imageView>
|
||||
<imageView userInteractionEnabled="NO" contentMode="bottom" image="guide_page_3.png" id="H5W-P4-o7O">
|
||||
<rect key="frame" x="960" y="0.0" width="320" height="460"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
</imageView>
|
||||
<imageView userInteractionEnabled="NO" contentMode="bottom" image="guide_page_4.png" id="IXz-T9-Umi">
|
||||
<rect key="frame" x="1280" y="0.0" width="320" height="460"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
</imageView>
|
||||
<imageView userInteractionEnabled="NO" contentMode="bottom" image="guide_page_5.png" id="Hd0-Or-naq">
|
||||
<rect key="frame" x="1280" y="0.0" width="320" height="460"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
</imageView>
|
||||
<imageView userInteractionEnabled="NO" contentMode="bottom" image="guide_page_6.png" id="1jq-Ic-GVZ">
|
||||
<rect key="frame" x="1600" y="0.0" width="320" height="460"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
</imageView>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" reversesTitleShadowWhenHighlighted="YES" lineBreakMode="middleTruncation" id="q93-J1-auj">
|
||||
<rect key="frame" x="1610" y="394" width="300" height="46"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
|
||||
<size key="titleShadowOffset" width="0.0" height="1"/>
|
||||
<state key="normal" title="Get started" backgroundImage="ui_button_green_large.png">
|
||||
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<color key="titleShadowColor" red="0.194560533886145" green="0.30086678832116787" blue="0.1263640535337863" alpha="1" colorSpace="deviceRGB"/>
|
||||
</state>
|
||||
<state key="highlighted">
|
||||
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="close" destination="qz3-eG-aEi" eventType="touchUpInside" id="uYR-EJ-U6H"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="scrollView" destination="Nr4-hi-8b2" id="8QD-sv-vu6"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="455" y="785"/>
|
||||
</scene>
|
||||
<scene sceneID="8r0-wA-Zre">
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="Lcz-JH-B5B" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
<navigationController id="KZF-fe-y9n" sceneMemberID="viewController">
|
||||
<toolbarItems/>
|
||||
<simulatedStatusBarMetrics key="simulatedStatusBarMetrics" statusBarStyle="blackTranslucent"/>
|
||||
<navigationBar key="navigationBar" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" barStyle="blackOpaque" id="sif-x3-Fol">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<nil name="viewControllers"/>
|
||||
<connections>
|
||||
<segue destination="PQa-Xl-A3x" kind="relationship" relationship="rootViewController" id="LUg-eF-JQd"/>
|
||||
<segue destination="qz3-eG-aEi" kind="modal" identifier="MP_Guide" id="vyG-wN-8hU"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-85" y="182"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="Content-Backdrop.png" width="480" height="480"/>
|
||||
<image name="Square-bottom.png" width="551" height="58"/>
|
||||
<image name="guide_page_0.png" width="320" height="480"/>
|
||||
<image name="guide_page_1.png" width="320" height="480"/>
|
||||
<image name="guide_page_2.png" width="320" height="480"/>
|
||||
<image name="guide_page_3.png" width="320" height="480"/>
|
||||
<image name="guide_page_4.png" width="320" height="480"/>
|
||||
<image name="guide_page_5.png" width="320" height="480"/>
|
||||
<image name="guide_page_6.png" width="320" height="480"/>
|
||||
<image name="icon_cancel.png" width="32" height="32"/>
|
||||
<image name="icon_edit.png" width="32" height="32"/>
|
||||
<image name="icon_plus.png" width="32" height="32"/>
|
||||
<image name="tip_alert_black.png" width="235" height="81"/>
|
||||
<image name="tip_basic_black.png" width="210" height="60"/>
|
||||
<image name="ui_button_green_large.png" width="300" height="46"/>
|
||||
<image name="ui_button_standard_large.png" width="300" height="46"/>
|
||||
<image name="ui_list_first.png" width="300" height="35"/>
|
||||
<image name="ui_list_last.png" width="300" height="35"/>
|
||||
<image name="ui_list_middle.png" width="300" height="34"/>
|
||||
<image name="ui_panel_container.png" width="300" height="87"/>
|
||||
<image name="ui_panel_display.png" width="300" height="86"/>
|
||||
</resources>
|
||||
<simulatedMetricsContainer key="defaultSimulatedMetrics">
|
||||
<simulatedStatusBarMetrics key="statusBar" statusBarStyle="blackTranslucent"/>
|
||||
<simulatedOrientationMetrics key="orientation"/>
|
||||
<simulatedScreenMetrics key="destination"/>
|
||||
</simulatedMetricsContainer>
|
||||
</document>
|
||||
75
MasterPassword/MasterPassword-Info.plist
Normal file
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Passwords</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIconFiles</key>
|
||||
<array>
|
||||
<string>Icon.png</string>
|
||||
<string>Icon@2x.png</string>
|
||||
<string>Icon-72.png</string>
|
||||
</array>
|
||||
<key>CFBundleIcons</key>
|
||||
<dict>
|
||||
<key>CFBundlePrimaryIcon</key>
|
||||
<dict>
|
||||
<key>CFBundleIconFiles</key>
|
||||
<array>
|
||||
<string>Icon.png</string>
|
||||
<string>Icon@2x.png</string>
|
||||
<string>Icon-72.png</string>
|
||||
</array>
|
||||
<key>UIPrerenderedIcon</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.lyndir.lhunath.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>[auto]</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>[auto]</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>© 2011-2012, Lyndir</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>MainStoryboard_iPhone</string>
|
||||
<key>UIMainStoryboardFile~ipad</key>
|
||||
<string>MainStoryboard_iPad</string>
|
||||
<key>UIPrerenderedIcon</key>
|
||||
<true/>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UIStatusBarStyle</key>
|
||||
<string>UIStatusBarStyleBlackOpaque</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
30
MasterPassword/MasterPassword-Prefix.pch
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Prefix header for all source files of the 'MasterPassword' target in the 'MasterPassword' project
|
||||
//
|
||||
|
||||
#import <Availability.h>
|
||||
|
||||
#ifndef __IPHONE_5_0
|
||||
#warning "This project uses features only available in iOS SDK 5.0 and later."
|
||||
#endif
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
|
||||
#ifndef PRODUCTION
|
||||
#import "TestFlight.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define PEARL
|
||||
#define PEARL_CRYPTO
|
||||
#define PEARL_UIKIT
|
||||
|
||||
#import "Pearl.h"
|
||||
#import "Pearl-Crypto.h"
|
||||
#import "Pearl-UIKit.h"
|
||||
|
||||
#import "MPTypes.h"
|
||||
#import "MPConfig.h"
|
||||
16
MasterPassword/MasterPassword.entitlements
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.ubiquity-container-identifiers</key>
|
||||
<array>
|
||||
<string>$(TeamIdentifierPrefix)com.lyndir.lhunath.MasterPassword</string>
|
||||
</array>
|
||||
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
|
||||
<string>$(TeamIdentifierPrefix)com.lyndir.lhunath.MasterPassword</string>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)com.lyndir.lhunath.MasterPassword</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -3,6 +3,6 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>MasterPassword 9.xcdatamodel</string>
|
||||
<string>MasterPassword.xcdatamodel</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model name="" userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="872" systemVersion="11C74" minimumToolsVersion="Automatic" macOSVersion="Automatic" iOSVersion="Automatic">
|
||||
<entity name="MPElementEntity" representedClassName="MPElementEntity" isAbstract="YES" syncable="YES">
|
||||
<attribute name="lastUsed" attributeType="Date" syncable="YES"/>
|
||||
<attribute name="mpHashHex" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="name" attributeType="String" indexed="YES" syncable="YES"/>
|
||||
<attribute name="type" attributeType="Integer 16" defaultValueString="257" syncable="YES"/>
|
||||
<attribute name="uses" attributeType="Integer 16" defaultValueString="0" syncable="YES"/>
|
||||
</entity>
|
||||
<entity name="MPElementGeneratedEntity" representedClassName="MPElementGeneratedEntity" parentEntity="MPElementEntity" syncable="YES">
|
||||
<attribute name="counter" optional="YES" attributeType="Integer 16" defaultValueString="1" syncable="YES"/>
|
||||
</entity>
|
||||
<entity name="MPElementStoredEntity" representedClassName="MPElementStoredEntity" parentEntity="MPElementEntity" syncable="YES">
|
||||
<attribute name="contentObject" optional="YES" attributeType="Transformable" storedInTruthFile="YES" syncable="YES"/>
|
||||
</entity>
|
||||
<fetchRequest name="MPSearchElement" entity="MPElementEntity" predicateString="name BEGINSWITH[cd] $query AND mpHashHex == $mpHashHex"/>
|
||||
<elements>
|
||||
<element name="MPElementEntity" positionX="160" positionY="192" width="128" height="135"/>
|
||||
<element name="MPElementGeneratedEntity" positionX="160" positionY="192" width="128" height="60"/>
|
||||
<element name="MPElementStoredEntity" positionX="160" positionY="192" width="128" height="60"/>
|
||||
</elements>
|
||||
</model>
|
||||
BIN
MasterPassword/Resources/Automaton/ui_background.png
Normal file
|
After Width: | Height: | Size: 1023 B |
BIN
MasterPassword/Resources/Automaton/ui_background@2x.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
MasterPassword/Resources/Automaton/ui_box_checked.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
MasterPassword/Resources/Automaton/ui_box_checked@2x.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
MasterPassword/Resources/Automaton/ui_box_empty.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
MasterPassword/Resources/Automaton/ui_box_empty@2x.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
MasterPassword/Resources/Automaton/ui_box_filled.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
MasterPassword/Resources/Automaton/ui_box_filled@2x.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
MasterPassword/Resources/Automaton/ui_button_green_large.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
MasterPassword/Resources/Automaton/ui_button_green_large@2x.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
MasterPassword/Resources/Automaton/ui_button_green_small.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
MasterPassword/Resources/Automaton/ui_button_green_small@2x.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
MasterPassword/Resources/Automaton/ui_button_red_large.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
BIN
MasterPassword/Resources/Automaton/ui_button_red_small.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
BIN
MasterPassword/Resources/Automaton/ui_button_standard_small.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
BIN
MasterPassword/Resources/Automaton/ui_dropdown.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
BIN
MasterPassword/Resources/Automaton/ui_list_arrow.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
MasterPassword/Resources/Automaton/ui_list_arrow@2x.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
MasterPassword/Resources/Automaton/ui_list_arrow_active.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
MasterPassword/Resources/Automaton/ui_list_arrow_active@2x.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
BIN
MasterPassword/Resources/Automaton/ui_navbar_back.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
MasterPassword/Resources/Automaton/ui_navbar_back@2x.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
BIN
MasterPassword/Resources/Automaton/ui_navbar_container.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
MasterPassword/Resources/Automaton/ui_navbar_container@2x.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
BIN
MasterPassword/Resources/Automaton/ui_panel_container@2x.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
BIN
MasterPassword/Resources/Automaton/ui_panel_display@2x.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |