├── .github
└── ISSUE_TEMPLATE
│ └── bug_report.md
├── .gitignore
├── LICENSE
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── beautycoder
│ │ └── applicationlockscreenexample
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── beautycoder
│ │ │ └── applicationlockscreenexample
│ │ │ ├── MainActivity.java
│ │ │ ├── MainFragment.java
│ │ │ ├── PreferencesSettings.java
│ │ │ └── TestPFPinCodeHelperImpl.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable-xxxhdpi
│ │ ├── delete_lockscreen.png
│ │ └── fingerprint_lockscreen.png
│ │ ├── drawable
│ │ ├── code_selector.xml
│ │ ├── empty_code.xml
│ │ ├── filled_code.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── key_foreground.xml
│ │ └── screen_background.xml
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ └── fragment_main.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── beautycoder
│ └── applicationlockscreenexample
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── pflockscreen
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── beautycoder
│ │ └── pflockscreen
│ │ ├── ExampleInstrumentedTest.java
│ │ ├── PFCodeViewTest.java
│ │ ├── PFLockScreenFragmentAuthTest.java
│ │ ├── PFLockScreenFragmentCreateTest.java
│ │ ├── PFSecurityUtilsTest.java
│ │ ├── actions
│ │ ├── PFCodeViewActionDelete.java
│ │ └── PFCodeViewActionInput.java
│ │ ├── matchers
│ │ └── PFViewMatchers.java
│ │ └── rules
│ │ ├── FragmentTestRule.java
│ │ └── ViewTestRule.java
│ ├── debug
│ ├── AndroidManifest.xml
│ └── java
│ │ └── com
│ │ └── beautycoder
│ │ └── pflockscreen
│ │ └── activities
│ │ └── TestActivity.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── beautycoder
│ │ │ └── pflockscreen
│ │ │ ├── PFFLockScreenConfiguration.java
│ │ │ ├── fragments
│ │ │ ├── PFFingerprintAuthDialogFragment.java
│ │ │ ├── PFFingerprintAuthListener.java
│ │ │ ├── PFFingerprintUIHelper.java
│ │ │ └── PFLockScreenFragment.java
│ │ │ ├── security
│ │ │ ├── IPFPinCodeHelper.java
│ │ │ ├── IPFSecurityUtils.java
│ │ │ ├── PFFingerprintPinCodeHelper.java
│ │ │ ├── PFResult.java
│ │ │ ├── PFSecurityError.java
│ │ │ ├── PFSecurityException.java
│ │ │ ├── PFSecurityManager.java
│ │ │ ├── PFSecurityUtils.java
│ │ │ ├── PFSecurityUtilsErrorCodes.java
│ │ │ ├── PFSecurityUtilsFactory.java
│ │ │ ├── PFSecurityUtilsOld.java
│ │ │ ├── callbacks
│ │ │ │ └── PFPinCodeHelperCallback.java
│ │ │ └── livedata
│ │ │ │ └── PFLiveData.java
│ │ │ ├── viewmodels
│ │ │ └── PFPinCodeViewModel.java
│ │ │ └── views
│ │ │ ├── PFCodeView.java
│ │ │ └── PFKeyButton.java
│ └── res
│ │ ├── anim
│ │ ├── cycle_7_pf.xml
│ │ └── shake_pf.xml
│ │ ├── drawable-hdpi
│ │ ├── ic_fp_40px_pf.png
│ │ └── tile.9.png
│ │ ├── drawable-mdpi
│ │ └── ic_fp_40px_pf.png
│ │ ├── drawable-v21
│ │ ├── ripple_selector_key_pf.xml
│ │ ├── ripple_selector_side_pf.xml
│ │ └── side_button_background_pf.xml
│ │ ├── drawable-xhdpi
│ │ └── ic_fp_40px_pf.png
│ │ ├── drawable-xxhdpi
│ │ └── ic_fp_40px_pf.png
│ │ ├── drawable-xxxhdpi
│ │ ├── delete_lockscreen_pf.png
│ │ ├── fingerprint_lockscreen_pf.png
│ │ └── ic_fp_40px_pf.png
│ │ ├── drawable
│ │ ├── background_pf.xml
│ │ ├── circle_background_pf_lock_screen.xml
│ │ ├── circle_code_empty_pf_lockscreen.xml
│ │ ├── circle_code_fill_pf_lockscreen.xml
│ │ ├── circle_key_selector_pf.xml
│ │ ├── code_selector_pf.xml
│ │ ├── ic_fingerprint_error_pf.xml
│ │ ├── ic_fingerprint_success_pf.xml
│ │ ├── side_button_background_pf.xml
│ │ └── touch_selector_pf.xml
│ │ ├── layout-land
│ │ └── fragment_lock_screen_pf.xml
│ │ ├── layout
│ │ ├── fragment_lock_screen_pf.xml
│ │ ├── view_code_pf_lockscreen.xml
│ │ ├── view_pf_code_checkbox.xml
│ │ ├── view_pf_fingerprint_dialog_container.xml
│ │ └── view_pf_fingerprint_dialog_content.xml
│ │ ├── values-hdpi
│ │ └── dimens.xml
│ │ ├── values-land-hdpi
│ │ └── dimen.xml
│ │ ├── values-land
│ │ └── dimen.xml
│ │ ├── values-ru
│ │ └── strings.xml
│ │ ├── values-v21
│ │ └── styles.xml
│ │ ├── values-xhdpi
│ │ └── dimens.xml
│ │ └── values
│ │ ├── attrs.xml
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── beautycoder
│ └── pflockscreen
│ └── ExampleUnitTest.java
└── settings.gradle
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 | **Describe the bug**
10 | A clear and concise description of what the bug is.
11 |
12 | **To Reproduce**
13 | Steps to reproduce the behavior:
14 | 1. Go to '...'
15 | 2. Click on '....'
16 | 3. Scroll down to '....'
17 | 4. See error
18 |
19 | **Expected behavior**
20 | A clear and concise description of what you expected to happen.
21 |
22 | **Screenshots**
23 | If applicable, add screenshots to help explain your problem.
24 |
25 | **Smartphone (please complete the following information):**
26 | - Device: [e.g. Samsung Galaxy S10]
27 | - OS: [e.g. Android 8.1]
28 | - Library version Version [e.g. 22]
29 |
30 | **Can you reproduce it with example project.**
31 | Some errors can happen because of different use of library or themes/style.
32 | This bugs is hard to reproduce. Do you have the same error if you build example project.
33 | What the difference between example project and your code. If you can help me to find edge cases
34 | this would be very helpful.
35 |
36 | **Additional context**
37 | Add any other context about the problem here.
38 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea
5 | /.idea/workspace.xml
6 | /.idea/libraries
7 | .DS_Store
8 | /build
9 | /captures
10 | .externalNativeBuild
11 | app/release/app-release.apk
12 | app/release/output.json
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PFLockScreen-Android
2 |
3 | [](https://jitpack.io/#thealeksandr/PFLockScreen-Android)
4 |
5 | **Feel free to ask questions add issues.** If I don't respond to an issue or PR, feel free to ping me or send me **DIRECT Message** on twitter @thealeksandr. If you create issue it would be nice if you **SUBSCRIBE FOR UPDATES**. So we can discuss it.
6 |
7 | **Please support with a Star :D**
8 |
9 | ##
10 | 7 Update - Bug fixing (November 3, 2019)
11 | * Added code validation for CREATE mode.
12 |
13 | ## Min SDK Version - 15
14 |
15 | PFLockScreen - Lock Screen Library for Android Application. Library support **pin code** and **fingerprint** authorization for API level 23+.
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | ## Add library to your project
24 | ```gradle
25 | allprojects {
26 | repositories {
27 | ...
28 | maven { url 'https://jitpack.io' }
29 | }
30 | }
31 | ```
32 |
33 | ```gradle
34 | dependencies {
35 | compile 'com.github.thealeksandr:PFLockScreen-Android:1.0.0-beta7'
36 | }
37 | ```
38 |
39 | ## Create pin code
40 |
41 | Creating lock screen fragment in **create mode**.
42 |
43 | ```java
44 | PFLockScreenFragment fragment = new PFLockScreenFragment();
45 | PFFLockScreenConfiguration.Builder builder = new PFFLockScreenConfiguration.Builder(this)
46 | .setMode(PFFLockScreenConfiguration.MODE_CREATE);
47 | fragment.setConfiguration(builder.build());
48 | fragment.setCodeCreateListener(new PFLockScreenFragment.OnPFLockScreenCodeCreateListener() {
49 | @Override
50 | public void onCodeCreated(String encodedCode) {
51 | //TODO: save somewhere;
52 | }
53 | });
54 | //TODO: show fragment;
55 | ```
56 |
57 |
58 | After user created pin code. The library will encode it using Android Key Store and return an encoded string in **PFLockScreenFragment.OnPFLockScreenCodeCreateListener**. All you have to do is save encoded string somewhere - database, SharedPreferences, Android Account etc.
59 |
60 |
61 | ## Show authorization screen
62 |
63 | Creating lock screen fragment in *authorization mode* is same as in *creation mode*, but instead of MODE_CREATE use MODE_AUTH.
64 |
65 | ```java
66 | PFFLockScreenConfiguration.Builder(this).setMode(PFFLockScreenConfiguration.MODE_AUTH);
67 | ```
68 |
69 |
70 | ## Configure screen
71 |
72 | ```java
73 | PFFLockScreenConfiguration.Builder builder = new PFFLockScreenConfiguration.Builder(this)
74 | .setTitle("Unlock")
75 | .setUseFingerprint(true).
76 | .setMode(PFFLockScreenConfiguration.MODE_AUTH)
77 | .setCodeLength(6)
78 | .setLeftButton("Can't remeber",
79 | new View.OnClickListener() {
80 | @Override
81 | public void onClick(View v) {
82 |
83 | }
84 | });
85 |
86 | ```
87 |
88 |
89 | *setTitle(String)* - set custom string on the top of the screen.
90 | *setUseFingerprint(boolean)* - by default fingerprint button will be shown for all device 23+ with a fingerprint sensor. If you don't want use fingerprint at all set *false*.
91 | *setMode(PFLockScreenMode)* - MODE_CREATE or MODE_AUTH. See details above.
92 | *setCodeLength(int)* - set the length of the pin code. By default, length is 4. Minimum length is 4.
93 | *setLeftButton(String, View.OnClickListener)* - set string for the left button and ClickListener.
94 |
95 |
96 |
97 | ## Check if pin code encryption key exist
98 |
99 | ```java
100 | boolean isExist = PFSecurityManager.getInstance().getPinCodeHelper().isPinCodeEncryptionKeyExist();
101 | ```
102 |
103 | An encryption key is needed to encode/decode pin code and stored in Android KeyStore.
104 |
105 |
106 |
107 | ## Delete pin code encryption key.
108 | You need to delete encryption key if you delete/reset pin code.
109 |
110 | ```java
111 | PFSecurityManager.getInstance().getPinCodeHelper().delete();
112 | ```
113 |
114 | **Don't use PFFingerprintPinCodeHelper.getInstance().isPinCodeExist() directly.**
115 |
116 | ## PFPinCodeViewModel
117 |
118 | Also you can use PFPinCodeViewModel() for the same methods. ViewModel wrapper around PinCodeHelper that returns LiveData objects.
119 |
120 | ## Custom encryption. **NEW!** **NEW!** **NEW!**
121 | Now you can create your custom encryption if for some reasons the one I have doesn't meet your app requarements:
122 |
123 | You need to override ```java IPFPinCodeHelper``` interface. That has four methods:
124 | ```java
125 | void encodePin(Context context, String pin, PFPinCodeHelperCallback callBack);
126 | void checkPin(Context context, String encodedPin, String pin, PFPinCodeHelperCallback callback);
127 | void delete(PFPinCodeHelperCallback callback);
128 | void isPinCodeEncryptionKeyExist(PFPinCodeHelperCallback callback);
129 | ```
130 | *encodePin* - method where you encode pin.
131 | *checkPin* - to check if pin is valid.
132 | *delete* - deletePinCode and all related stuff.
133 | *isPinCodeEncryptionKeyExist* - if you have any encryprion keys or something else you're using to encrpt your key
134 | here you check if all keys you need are exists. Haven't beed deleted or anyting. This method is only for your own logic.
135 | Basically to check if you're code can be decrypted.
136 |
137 | All methods take callback as a parameter in case if you want implement something async like server side or whatever.
138 |
139 |
140 | ## UI Customization
141 |
142 | You can customize buttons, backgrounds, etc. To do that, use attributes in your activity theme:
143 |
144 | *pf_key_button* - style object for key buttons (0-9)
145 | *pf_lock_screen* - style object for the background. Use it to set custom background.
146 | *pf_fingerprint_button* - style object for fingerprint button. You can set custom drawable, paddings, etc.
147 | *pf_delete_button* - style object for delete/backspace button. You can set custom drawable, paddings, etc.
148 | *pf_code_view* - style object to customize code view. (The view from the top of the screen). The view itself is set of check boxes. To customize it use a custom selector with checked states.
149 | *pf_title* (**NEW**) - style object for title.
150 | *pf_next* (**NEW**) - style object for next button.
151 | *pf_hint* (**NEW**) - style object for hint button (Can't remember).
152 |
153 |
154 | Examples:
155 | ```xml
156 |
164 |
165 |
166 |
169 |
170 |
175 |
176 |
179 |
180 |
183 |
184 |
187 |
188 |
194 |
195 |
198 |
199 |
202 | ```
203 |
204 | **If you want just a bit correct an existing styles you can override:**
205 | ```xml
206 | PFLockScreenStyle
207 | PFLockScreenButtonStyle
208 | PFLockScreenFingerPrintButtonStyle
209 | PFLockScreenDeleteButtonStyle
210 | PFCheckBox
211 | PFLockScreenCodeStyle
212 | PFLockScreenNextTextStyle
213 | PFLockScreenHintTextStyle
214 | PFLockScreenTitleTextStyle
215 | ```
216 |
217 | The only important thing you can't change right now is the size of keys. But it's coming.
218 |
219 | **Feel free to ask questions add issues.** If I don't respond to an issue or PR, feel free to ping me or send me DM on twitter @thealeksandr. If you create issue it would be nice if you **subscribe** for updates. So we can discuss it.
220 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 | defaultConfig {
6 | applicationId "com.beautycoder.applicationlockscreenexample"
7 | minSdkVersion 15
8 | targetSdkVersion 28
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | implementation fileTree(include: ['*.jar'], dir: 'libs')
23 | implementation 'androidx.appcompat:appcompat:1.1.0-alpha01'
24 | implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha3'
25 | testImplementation 'junit:junit:4.12'
26 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.2-alpha01'
27 | androidTestImplementation 'androidx.test:runner:1.1.2-alpha01'
28 | androidTestImplementation 'androidx.test:rules:1.1.2-alpha01'
29 | implementation project(':pflockscreen')
30 | }
31 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/beautycoder/applicationlockscreenexample/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.applicationlockscreenexample;
2 |
3 | import android.content.Context;
4 | import androidx.test.InstrumentationRegistry;
5 | import androidx.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.beautycoder.applicationlockscreenexample", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/beautycoder/applicationlockscreenexample/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.applicationlockscreenexample;
2 |
3 | import androidx.lifecycle.Observer;
4 | import androidx.annotation.Nullable;
5 | import androidx.appcompat.app.AppCompatActivity;
6 | import android.os.Bundle;
7 | import android.view.View;
8 | import android.widget.Toast;
9 |
10 | import com.beautycoder.pflockscreen.PFFLockScreenConfiguration;
11 | import com.beautycoder.pflockscreen.fragments.PFLockScreenFragment;
12 | import com.beautycoder.pflockscreen.security.PFResult;
13 | import com.beautycoder.pflockscreen.security.PFSecurityManager;
14 | import com.beautycoder.pflockscreen.viewmodels.PFPinCodeViewModel;
15 |
16 | public class MainActivity extends AppCompatActivity {
17 |
18 | @Override
19 | protected void onCreate(Bundle savedInstanceState) {
20 | super.onCreate(savedInstanceState);
21 | setContentView(R.layout.activity_main);
22 | showLockScreenFragment();
23 | //PFSecurityManager.getInstance().setPinCodeHelper(new TestPFPinCodeHelperImpl());
24 | }
25 |
26 | private final PFLockScreenFragment.OnPFLockScreenCodeCreateListener mCodeCreateListener =
27 | new PFLockScreenFragment.OnPFLockScreenCodeCreateListener() {
28 | @Override
29 | public void onCodeCreated(String encodedCode) {
30 | Toast.makeText(MainActivity.this, "Code created", Toast.LENGTH_SHORT).show();
31 | PreferencesSettings.saveToPref(MainActivity.this, encodedCode);
32 | }
33 |
34 | @Override
35 | public void onNewCodeValidationFailed() {
36 | Toast.makeText(MainActivity.this, "Code validation error", Toast.LENGTH_SHORT).show();
37 | }
38 | };
39 |
40 | private final PFLockScreenFragment.OnPFLockScreenLoginListener mLoginListener =
41 | new PFLockScreenFragment.OnPFLockScreenLoginListener() {
42 |
43 | @Override
44 | public void onCodeInputSuccessful() {
45 | Toast.makeText(MainActivity.this, "Code successfull", Toast.LENGTH_SHORT).show();
46 | showMainFragment();
47 | }
48 |
49 | @Override
50 | public void onFingerprintSuccessful() {
51 | Toast.makeText(MainActivity.this, "Fingerprint successfull", Toast.LENGTH_SHORT).show();
52 | showMainFragment();
53 | }
54 |
55 | @Override
56 | public void onPinLoginFailed() {
57 | Toast.makeText(MainActivity.this, "Pin failed", Toast.LENGTH_SHORT).show();
58 | }
59 |
60 | @Override
61 | public void onFingerprintLoginFailed() {
62 | Toast.makeText(MainActivity.this, "Fingerprint failed", Toast.LENGTH_SHORT).show();
63 | }
64 | };
65 |
66 | private void showLockScreenFragment() {
67 | new PFPinCodeViewModel().isPinCodeEncryptionKeyExist().observe(
68 | this,
69 | new Observer>() {
70 | @Override
71 | public void onChanged(@Nullable PFResult result) {
72 | if (result == null) {
73 | return;
74 | }
75 | if (result.getError() != null) {
76 | Toast.makeText(MainActivity.this, "Can not get pin code info", Toast.LENGTH_SHORT).show();
77 | return;
78 | }
79 | showLockScreenFragment(result.getResult());
80 | }
81 | }
82 | );
83 | }
84 |
85 | private void showLockScreenFragment(boolean isPinExist) {
86 | final PFFLockScreenConfiguration.Builder builder = new PFFLockScreenConfiguration.Builder(this)
87 | .setTitle(isPinExist ? "Unlock with your pin code or fingerprint" : "Create Code")
88 | .setCodeLength(6)
89 | .setLeftButton("Can't remeber")
90 | .setNewCodeValidation(true)
91 | .setNewCodeValidationTitle("Please input code again")
92 | .setUseFingerprint(true);
93 | final PFLockScreenFragment fragment = new PFLockScreenFragment();
94 |
95 | fragment.setOnLeftButtonClickListener(new View.OnClickListener() {
96 | @Override
97 | public void onClick(View v) {
98 | Toast.makeText(MainActivity.this, "Left button pressed", Toast.LENGTH_LONG).show();
99 | }
100 | });
101 |
102 | builder.setMode(isPinExist
103 | ? PFFLockScreenConfiguration.MODE_AUTH
104 | : PFFLockScreenConfiguration.MODE_CREATE);
105 | if (isPinExist) {
106 | fragment.setEncodedPinCode(PreferencesSettings.getCode(this));
107 | fragment.setLoginListener(mLoginListener);
108 | }
109 |
110 | fragment.setConfiguration(builder.build());
111 | fragment.setCodeCreateListener(mCodeCreateListener);
112 | getSupportFragmentManager().beginTransaction()
113 | .replace(R.id.container_view, fragment).commit();
114 |
115 | }
116 |
117 | private void showMainFragment() {
118 | final MainFragment fragment = new MainFragment();
119 | getSupportFragmentManager().beginTransaction()
120 | .replace(R.id.container_view, fragment).commit();
121 | }
122 |
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/app/src/main/java/com/beautycoder/applicationlockscreenexample/MainFragment.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.applicationlockscreenexample;
2 |
3 | import android.os.Bundle;
4 | import androidx.annotation.Nullable;
5 | import androidx.fragment.app.Fragment;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 |
10 | /**
11 | * Created by aleksandr on 2018/02/14.
12 | */
13 |
14 | public class MainFragment extends Fragment {
15 |
16 | @Nullable
17 | @Override
18 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
19 | @Nullable Bundle savedInstanceState) {
20 | final View view = inflater.inflate(R.layout.fragment_main, container, false);
21 | return view;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/beautycoder/applicationlockscreenexample/PreferencesSettings.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.applicationlockscreenexample;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 |
6 | /**
7 | * Created by aleksandr on 2018/02/09.
8 | */
9 |
10 | public class PreferencesSettings {
11 |
12 | private static final String PREF_FILE = "settings_pref";
13 |
14 | static void saveToPref(Context context, String str) {
15 | final SharedPreferences sharedPref = context.getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
16 | final SharedPreferences.Editor editor = sharedPref.edit();
17 | editor.putString("code", str);
18 | editor.apply();
19 | }
20 |
21 | static String getCode(Context context) {
22 | final SharedPreferences sharedPref = context.getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
23 | final String defaultValue = "";
24 | return sharedPref.getString("code", defaultValue);
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/beautycoder/applicationlockscreenexample/TestPFPinCodeHelperImpl.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.applicationlockscreenexample;
2 |
3 | import android.content.Context;
4 |
5 | import com.beautycoder.pflockscreen.security.IPFPinCodeHelper;
6 | import com.beautycoder.pflockscreen.security.PFResult;
7 | import com.beautycoder.pflockscreen.security.callbacks.PFPinCodeHelperCallback;
8 |
9 | public class TestPFPinCodeHelperImpl implements IPFPinCodeHelper {
10 |
11 | @Override
12 | public void encodePin(Context context, String pin, PFPinCodeHelperCallback callBack) {
13 | //Just an example : D
14 | if (callBack == null) {
15 | return;
16 | }
17 | callBack.onResult(new PFResult(pin + "1111"));
18 | }
19 |
20 | @Override
21 | public void checkPin(
22 | Context context,
23 | String encodedPin,
24 | String pin,
25 | PFPinCodeHelperCallback callback
26 | ) {
27 | if (callback == null) {
28 | return;
29 | }
30 | callback.onResult(new PFResult(encodedPin.equals(pin + "1111")));
31 | }
32 |
33 | @Override
34 | public void delete(PFPinCodeHelperCallback callback) {
35 | //Delete all stuff related to pincode encryption
36 | //Like any additional keys etc;
37 | //Or anything on the server side
38 | }
39 |
40 | @Override
41 | public void isPinCodeEncryptionKeyExist(PFPinCodeHelperCallback callback) {
42 | //If you use anyadditional keys. For encryprion or anything else. Here should be check that
43 | //all that keys or whatever exist and you actually can perform decryption.
44 | //This is necessary for default PFPinCodeHelper with fingerprint and keystore.
45 | //Maybe your won't need it.
46 | callback.onResult(new PFResult(true));
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/delete_lockscreen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thealeksandr/PFLockScreen-Android/ff5b7cd11a7d055d469fed6905d117216356150d/app/src/main/res/drawable-xxxhdpi/delete_lockscreen.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/fingerprint_lockscreen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thealeksandr/PFLockScreen-Android/ff5b7cd11a7d055d469fed6905d117216356150d/app/src/main/res/drawable-xxxhdpi/fingerprint_lockscreen.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/code_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/empty_code.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
11 |
12 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/filled_code.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
11 |
12 |
14 |
15 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/key_foreground.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
9 |
10 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/screen_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thealeksandr/PFLockScreen-Android/ff5b7cd11a7d055d469fed6905d117216356150d/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thealeksandr/PFLockScreen-Android/ff5b7cd11a7d055d469fed6905d117216356150d/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thealeksandr/PFLockScreen-Android/ff5b7cd11a7d055d469fed6905d117216356150d/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thealeksandr/PFLockScreen-Android/ff5b7cd11a7d055d469fed6905d117216356150d/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thealeksandr/PFLockScreen-Android/ff5b7cd11a7d055d469fed6905d117216356150d/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thealeksandr/PFLockScreen-Android/ff5b7cd11a7d055d469fed6905d117216356150d/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thealeksandr/PFLockScreen-Android/ff5b7cd11a7d055d469fed6905d117216356150d/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thealeksandr/PFLockScreen-Android/ff5b7cd11a7d055d469fed6905d117216356150d/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thealeksandr/PFLockScreen-Android/ff5b7cd11a7d055d469fed6905d117216356150d/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thealeksandr/PFLockScreen-Android/ff5b7cd11a7d055d469fed6905d117216356150d/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ApplicationLockScreenExample
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
14 |
15 |
21 |
22 |
25 |
26 |
32 |
33 |
36 |
37 |
40 |
41 |
44 |
45 |
51 |
52 |
55 |
56 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/app/src/test/java/com/beautycoder/applicationlockscreenexample/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.applicationlockscreenexample;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 |
5 | repositories {
6 | google()
7 | jcenter()
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.2.1'
11 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0'
12 |
13 | // NOTE: Do not place your application dependencies here; they belong
14 | // in the individual module build.gradle files
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | google()
21 | jcenter()
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | android.enableJetifier=true
13 | android.useAndroidX=true
14 | org.gradle.jvmargs=-Xmx1536m
15 |
16 | # When configured, Gradle will run in incubating parallel mode.
17 | # This option should only be used with decoupled projects. More details, visit
18 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
19 | # org.gradle.parallel=true
20 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thealeksandr/PFLockScreen-Android/ff5b7cd11a7d055d469fed6905d117216356150d/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Jul 19 17:50:04 BRT 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/pflockscreen/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/pflockscreen/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.github.dcendents.android-maven'
3 |
4 |
5 | group='com.github.thealeksandr'
6 |
7 | final def lifecycle_version = "2.0.0"
8 |
9 | android {
10 | compileSdkVersion 28
11 |
12 | defaultConfig {
13 | minSdkVersion 15
14 | targetSdkVersion 28
15 | versionCode 1
16 | versionName "1.0.0-beta6"
17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
18 |
19 | }
20 |
21 | buildTypes {
22 | release {
23 | minifyEnabled false
24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
25 | }
26 | }
27 |
28 | }
29 |
30 | dependencies {
31 | implementation fileTree(dir: 'libs', include: ['*.jar'])
32 |
33 | implementation 'androidx.appcompat:appcompat:1.1.0-alpha01'
34 |
35 | //Livecycles & ViewModel
36 | implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
37 |
38 | testImplementation 'junit:junit:4.12'
39 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.2-alpha01'
40 | androidTestImplementation 'androidx.test:runner:1.1.2-alpha01'
41 | androidTestImplementation 'androidx.test:rules:1.1.2-alpha01'
42 | }
43 |
--------------------------------------------------------------------------------
/pflockscreen/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/pflockscreen/src/androidTest/java/com/beautycoder/pflockscreen/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen;
2 |
3 | import android.content.Context;
4 | import androidx.test.InstrumentationRegistry;
5 | import androidx.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 | assertEquals("com.beautycoder.pflockscreen.test", appContext.getPackageName());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/pflockscreen/src/androidTest/java/com/beautycoder/pflockscreen/PFCodeViewTest.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen;
2 |
3 | import android.content.Context;
4 | import androidx.test.espresso.Espresso;
5 | import androidx.test.espresso.ViewAssertion;
6 | import androidx.test.espresso.assertion.ViewAssertions;
7 | import androidx.test.runner.AndroidJUnit4;
8 |
9 | import com.beautycoder.pflockscreen.actions.PFCodeViewActionDelete;
10 | import com.beautycoder.pflockscreen.actions.PFCodeViewActionInput;
11 | import com.beautycoder.pflockscreen.matchers.PFViewMatchers;
12 | import com.beautycoder.pflockscreen.rules.ViewTestRule;
13 | import com.beautycoder.pflockscreen.views.PFCodeView;
14 |
15 | import org.junit.Rule;
16 | import org.junit.Test;
17 | import org.junit.runner.RunWith;
18 |
19 | import static androidx.test.espresso.matcher.ViewMatchers.withId;
20 |
21 | /**
22 | * Created by Aleksandr Nikiforov on 2018/03/11.
23 | */
24 | @RunWith(AndroidJUnit4.class)
25 | public class PFCodeViewTest {
26 |
27 | private static final int CODE_LENGTH = 5;
28 |
29 | @Rule
30 | public ViewTestRule mViewTestRule
31 | = new ViewTestRule(PFCodeView.class) {
32 | @Override
33 | public PFCodeView getView(Context context) {
34 | PFCodeView view = new PFCodeView(context);
35 | view.setCodeLength(CODE_LENGTH);
36 | view.setId(R.id.code_view);
37 | return view;
38 | }
39 | };
40 |
41 | @Test
42 | public void pfCodeViewTest() {
43 |
44 | mViewTestRule.launchActivity(null);
45 |
46 | Espresso.onView(withId(R.id.code_view)).check(ViewAssertions.matches(
47 | PFViewMatchers.withCodeValue("")));
48 |
49 | for (int i = 0; i < CODE_LENGTH * 2; i ++) {
50 | Espresso.onView(withId(R.id.code_view)).perform(new PFCodeViewActionInput(i));
51 | }
52 |
53 | Espresso.onView(withId(R.id.code_view)).check(ViewAssertions.matches(
54 | PFViewMatchers.withCodeValue("01234")));
55 |
56 | Espresso.onView(withId(R.id.code_view)).check(ViewAssertions.matches(
57 | PFViewMatchers.withCodeLength(CODE_LENGTH)));
58 |
59 | Espresso.onView(withId(R.id.code_view)).perform(new PFCodeViewActionDelete());
60 |
61 | Espresso.onView(withId(R.id.code_view)).check(ViewAssertions.matches(
62 | PFViewMatchers.withCodeValue("0123")));
63 |
64 | Espresso.onView(withId(R.id.code_view)).check(ViewAssertions.matches(
65 | PFViewMatchers.withCodeLength(CODE_LENGTH - 1)));
66 |
67 | for (int i = 0; i < CODE_LENGTH * 3; i ++) {
68 | Espresso.onView(withId(R.id.code_view)).perform(new PFCodeViewActionDelete());
69 | }
70 |
71 | Espresso.onView(withId(R.id.code_view)).check(ViewAssertions.matches(
72 | PFViewMatchers.withCodeLength(0)));
73 |
74 | Espresso.onView(withId(R.id.code_view)).check(ViewAssertions.matches(
75 | PFViewMatchers.withCodeValue("")));
76 |
77 |
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/pflockscreen/src/androidTest/java/com/beautycoder/pflockscreen/PFLockScreenFragmentAuthTest.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen;
2 |
3 | import android.content.Context;
4 | import androidx.test.espresso.Espresso;
5 | import androidx.test.espresso.matcher.ViewMatchers;
6 | import androidx.test.runner.AndroidJUnit4;
7 | import android.view.View;
8 |
9 | import com.beautycoder.pflockscreen.fragments.PFLockScreenFragment;
10 | import com.beautycoder.pflockscreen.rules.FragmentTestRule;
11 |
12 | import org.junit.Rule;
13 | import org.junit.Test;
14 | import org.junit.runner.RunWith;
15 |
16 | import static androidx.test.espresso.action.ViewActions.click;
17 | import static androidx.test.espresso.assertion.ViewAssertions.matches;
18 | import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
19 | import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
20 | import static androidx.test.espresso.matcher.ViewMatchers.withId;
21 | import static androidx.test.espresso.matcher.ViewMatchers.withText;
22 | import static org.hamcrest.Matchers.not;
23 | import static org.junit.Assert.assertFalse;
24 | import static org.junit.Assert.assertTrue;
25 |
26 | /**
27 | * Created by aleksandr on 2018/03/10.
28 | */
29 | @RunWith(AndroidJUnit4.class)
30 | public class PFLockScreenFragmentAuthTest {
31 |
32 | private static final String LEFT_BUTTON = "Can't remember";
33 | private static final int CODE_LENGTH = 5;
34 |
35 | @Rule
36 | public FragmentTestRule mFragmentTestRule
37 | = new FragmentTestRule(PFLockScreenFragment.class) {
38 | @Override
39 | public PFLockScreenFragment getInstance(Context context) {
40 | final PFLockScreenFragment fragment = new PFLockScreenFragment();
41 | final PFFLockScreenConfiguration.Builder builder =
42 | new PFFLockScreenConfiguration.Builder(context)
43 | .setCodeLength(CODE_LENGTH)
44 | .setLeftButton(LEFT_BUTTON, new View.OnClickListener() {
45 | @Override
46 | public void onClick(View v) {
47 |
48 | }
49 | })
50 | .setUseFingerprint(true)
51 | .setMode(PFFLockScreenConfiguration.MODE_AUTH);
52 | fragment.setConfiguration(builder.build());
53 | return fragment;
54 | }
55 | };
56 |
57 |
58 | @Test
59 | public void fragment_can_be_instantiated() {
60 |
61 | // Launch the activity to make the fragment visible
62 | mFragmentTestRule.launchActivity(null);
63 |
64 | // Then use Espresso to test the Fragment
65 | Espresso.onView(withId(R.id.fragment_pf)).check(matches(isDisplayed()));
66 | Espresso.onView(withId(R.id.button_finger_print)).check(matches(isDisplayed()));
67 | Espresso.onView(withId(R.id.button_delete)).check(matches(not(isDisplayed())));
68 | Espresso.onView(withId(R.id.button_left)).check(matches(isDisplayed()));
69 | Espresso.onView(withId(R.id.button_next)).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.INVISIBLE)));
70 |
71 | Espresso.onView(withId(R.id.button_left)).check(matches(withText(LEFT_BUTTON)));
72 |
73 |
74 | for (int i = 0; i < CODE_LENGTH; i++) {
75 | Espresso.onView(withId(R.id.button_1)).perform(click());
76 | Espresso.onView(withId(R.id.button_finger_print)).check(matches(not(isDisplayed())));
77 | Espresso.onView(withId(R.id.button_delete)).check(matches(isDisplayed()));
78 | }
79 |
80 | for (int i = 0; i < CODE_LENGTH - 1; i++) {
81 | Espresso.onView(withId(R.id.button_delete)).perform(click());
82 | Espresso.onView(withId(R.id.button_delete)).check(matches(isDisplayed()));
83 | Espresso.onView(withId(R.id.button_finger_print)).check(matches(not(isDisplayed())));
84 | }
85 |
86 | Espresso.onView(withId(R.id.button_delete)).perform(click());
87 | Espresso.onView(withId(R.id.button_finger_print)).check(matches(isDisplayed()));
88 | Espresso.onView(withId(R.id.button_delete)).check(matches(not(isDisplayed())));
89 |
90 |
91 | Espresso.onView(withId(R.id.button_next)).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.INVISIBLE)));
92 |
93 |
94 | }
95 |
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/pflockscreen/src/androidTest/java/com/beautycoder/pflockscreen/PFLockScreenFragmentCreateTest.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen;
2 |
3 | import android.content.Context;
4 | import androidx.test.espresso.Espresso;
5 | import androidx.test.espresso.matcher.ViewMatchers;
6 | import androidx.test.runner.AndroidJUnit4;
7 |
8 | import com.beautycoder.pflockscreen.fragments.PFLockScreenFragment;
9 | import com.beautycoder.pflockscreen.rules.FragmentTestRule;
10 | import com.beautycoder.pflockscreen.security.PFFingerprintPinCodeHelper;
11 | import com.beautycoder.pflockscreen.security.PFResult;
12 | import com.beautycoder.pflockscreen.security.callbacks.PFPinCodeHelperCallback;
13 |
14 | import org.junit.Rule;
15 | import org.junit.Test;
16 | import org.junit.runner.RunWith;
17 |
18 | import static androidx.test.espresso.action.ViewActions.click;
19 | import static androidx.test.espresso.assertion.ViewAssertions.matches;
20 | import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
21 | import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
22 | import static androidx.test.espresso.matcher.ViewMatchers.withId;
23 | import static org.hamcrest.Matchers.not;
24 | import static org.junit.Assert.assertFalse;
25 | import static org.junit.Assert.assertNull;
26 | import static org.junit.Assert.assertTrue;
27 |
28 |
29 | /**
30 | * Created by aleksandr on 2018/02/27.
31 | */
32 | @RunWith(AndroidJUnit4.class)
33 | public class PFLockScreenFragmentCreateTest {
34 |
35 | private static final int CODE_LENGTH = 5;
36 |
37 | @Rule
38 | public FragmentTestRule mFragmentTestRule
39 | = new FragmentTestRule(PFLockScreenFragment.class) {
40 | @Override
41 | public PFLockScreenFragment getInstance(Context context) {
42 | PFLockScreenFragment fragment = new PFLockScreenFragment();
43 | PFFLockScreenConfiguration.Builder builder = new PFFLockScreenConfiguration.Builder(context)
44 | .setTitle("Unlock with your pin code or fingerprint")
45 | .setCodeLength(CODE_LENGTH)
46 | .setUseFingerprint(true)
47 | .setMode(PFFLockScreenConfiguration.MODE_CREATE);
48 | fragment.setConfiguration(builder.build());
49 | return fragment;
50 | }
51 | };
52 |
53 |
54 | @Test
55 | public void fragment_can_be_instantiated() {
56 | PFFingerprintPinCodeHelper.getInstance().delete(null);
57 | PFFingerprintPinCodeHelper.getInstance().isPinCodeEncryptionKeyExist(new PFPinCodeHelperCallback() {
58 | @Override
59 | public void onResult(PFResult result) {
60 | assertNull(result.getError());
61 | assertFalse(result.getResult());
62 | }
63 | });
64 |
65 |
66 | // Launch the activity to make the fragment visible
67 | mFragmentTestRule.launchActivity(null);
68 |
69 | // Then use Espresso to test the Fragment
70 | Espresso.onView(withId(R.id.fragment_pf)).check(matches(isDisplayed()));
71 | Espresso.onView(withId(R.id.button_finger_print)).check(matches(not(isDisplayed())));
72 | Espresso.onView(withId(R.id.button_delete)).check(matches(not(isDisplayed())));
73 | Espresso.onView(withId(R.id.button_left)).check(matches(not(isDisplayed())));
74 |
75 | //INPUT CODE_LENGTH
76 | for (int i = 0; i < CODE_LENGTH; i ++) {
77 | Espresso.onView(withId(R.id.button_next)).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.INVISIBLE)));
78 | Espresso.onView(withId(R.id.button_1)).perform(click());
79 | Espresso.onView(withId(R.id.button_delete)).check(matches(isDisplayed()));
80 | }
81 |
82 | //NEXT BUTTON SHOULD BE ON SCREEN
83 | Espresso.onView(withId(R.id.button_next)).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)));
84 |
85 | //DELETE WHOLE CODE
86 | for (int i = 0; i < CODE_LENGTH; i ++) {
87 | Espresso.onView(withId(R.id.button_delete)).check(matches(isDisplayed()));
88 | Espresso.onView(withId(R.id.button_delete)).perform(click());
89 | Espresso.onView(withId(R.id.button_next)).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.INVISIBLE)));
90 | }
91 |
92 | //DELETE BUTTON HAS TO BE HIDDEN
93 | Espresso.onView(withId(R.id.button_delete)).check(matches(not(isDisplayed())));
94 |
95 | //INPUT CODE_LENGTH
96 | for (int i = 0; i < CODE_LENGTH; i ++) {
97 | Espresso.onView(withId(R.id.button_next)).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.INVISIBLE)));
98 | Espresso.onView(withId(R.id.button_1)).perform(click());
99 | Espresso.onView(withId(R.id.button_delete)).check(matches(isDisplayed()));
100 | }
101 |
102 | //NEXT BUTTON SHOULD BE ON SCREEN
103 | Espresso.onView(withId(R.id.button_next)).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)));
104 |
105 |
106 | Espresso.onView(withId(R.id.button_next)).perform(click());
107 |
108 |
109 | PFFingerprintPinCodeHelper.getInstance().isPinCodeEncryptionKeyExist(new PFPinCodeHelperCallback() {
110 | @Override
111 | public void onResult(PFResult result) {
112 | assertNull(result.getError());
113 | assertTrue(result.getResult());
114 | }
115 | });
116 |
117 | }
118 |
119 |
120 | }
121 |
--------------------------------------------------------------------------------
/pflockscreen/src/androidTest/java/com/beautycoder/pflockscreen/PFSecurityUtilsTest.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen;
2 |
3 | import android.content.Context;
4 | import androidx.test.InstrumentationRegistry;
5 | import androidx.test.runner.AndroidJUnit4;
6 |
7 | import com.beautycoder.pflockscreen.security.PFFingerprintPinCodeHelper;
8 | import com.beautycoder.pflockscreen.security.PFResult;
9 | import com.beautycoder.pflockscreen.security.PFSecurityUtils;
10 | import com.beautycoder.pflockscreen.security.PFSecurityUtilsOld;
11 | import com.beautycoder.pflockscreen.security.callbacks.PFPinCodeHelperCallback;
12 |
13 | import org.junit.Test;
14 | import org.junit.runner.RunWith;
15 |
16 | import static org.junit.Assert.assertEquals;
17 | import static org.junit.Assert.assertFalse;
18 | import static org.junit.Assert.assertNotNull;
19 | import static org.junit.Assert.assertNull;
20 | import static org.junit.Assert.assertTrue;
21 |
22 | /**
23 | * Created by aleksandr on 2018/02/26.
24 | */
25 | @RunWith(AndroidJUnit4.class)
26 | public class PFSecurityUtilsTest {
27 |
28 | @Test
29 | public void pfSecurityUtils() throws Exception {
30 | // Context of the app under test.
31 | final String alias = "test_alias";
32 | final String pinCode = "1234";
33 |
34 | final Context appContext = InstrumentationRegistry.getTargetContext();
35 |
36 | PFSecurityUtils.getInstance().deleteKey(alias);
37 |
38 | final boolean isAliasFalse = PFSecurityUtils.getInstance().isKeystoreContainAlias(alias);
39 | assertFalse(isAliasFalse);
40 |
41 | final String encoded = PFSecurityUtils.getInstance().encode(null, alias, pinCode, false);
42 | assertNotNull(encoded);
43 |
44 | final boolean isAliasTrue = PFSecurityUtils.getInstance().isKeystoreContainAlias(alias);
45 | assertTrue(isAliasTrue);
46 |
47 | final String decoded = PFSecurityUtils.getInstance().decode(alias, encoded);
48 | assertEquals(decoded, pinCode);
49 |
50 | PFSecurityUtils.getInstance().deleteKey(alias);
51 | final boolean isAliasFalse2 = PFSecurityUtils.getInstance().isKeystoreContainAlias(alias);
52 | assertFalse(isAliasFalse2);
53 | }
54 |
55 |
56 | @Test
57 | public void pfSecurityUtilsOld() throws Exception {
58 | // Context of the app under test.
59 | final String alias = "test_alias_old";
60 | final String pinCode = "1234";
61 |
62 | final Context appContext = InstrumentationRegistry.getTargetContext();
63 |
64 | PFSecurityUtilsOld.getInstance().deleteKey(alias);
65 |
66 | final boolean isAliasFalse = PFSecurityUtilsOld.getInstance().isKeystoreContainAlias(alias);
67 | assertFalse(isAliasFalse);
68 |
69 | final String encoded = PFSecurityUtilsOld.getInstance().encode(appContext, alias, pinCode, false);
70 | assertNotNull(encoded);
71 |
72 | final boolean isAliasTrue = PFSecurityUtilsOld.getInstance().isKeystoreContainAlias(alias);
73 | assertTrue(isAliasTrue);
74 |
75 | final String decoded = PFSecurityUtilsOld.getInstance().decode(alias, encoded);
76 | assertEquals(decoded, pinCode);
77 |
78 | PFSecurityUtilsOld.getInstance().deleteKey(alias);
79 | final boolean isAliasFalse2 = PFSecurityUtilsOld.getInstance().isKeystoreContainAlias(alias);
80 | assertFalse(isAliasFalse2);
81 | }
82 |
83 | @Test
84 | public void pfFingerPrintPinCodeHelper() throws Exception {
85 | // Context of the app under test.
86 | final String pinCode = "1234";
87 |
88 | final Context appContext = InstrumentationRegistry.getTargetContext();
89 |
90 | PFFingerprintPinCodeHelper.getInstance().delete(new PFPinCodeHelperCallback() {
91 | @Override
92 | public void onResult(PFResult result) {
93 | assertNull(result.getError());
94 | }
95 | });
96 |
97 | PFFingerprintPinCodeHelper.getInstance().isPinCodeEncryptionKeyExist(
98 | new PFPinCodeHelperCallback() {
99 | @Override
100 | public void onResult(PFResult result) {
101 | assertNull(result.getError());
102 | assertFalse(result.getResult());
103 | }
104 | });
105 |
106 | final StringBuilder stringBuilder = new StringBuilder();
107 | PFFingerprintPinCodeHelper.getInstance().encodePin(appContext, pinCode,
108 | new PFPinCodeHelperCallback() {
109 | @Override
110 | public void onResult(PFResult result) {
111 | assertNull(result.getError());
112 | final String encoded = result.getResult();
113 | stringBuilder.append(encoded);
114 | assertNotNull(encoded);
115 | }
116 | });
117 |
118 | PFFingerprintPinCodeHelper.getInstance().isPinCodeEncryptionKeyExist(
119 | new PFPinCodeHelperCallback() {
120 | @Override
121 | public void onResult(PFResult result) {
122 | assertNull(result.getError());
123 | assertTrue(result.getResult());
124 | }
125 | }
126 | );
127 |
128 | final String encoded = stringBuilder.toString();
129 | PFFingerprintPinCodeHelper.getInstance().checkPin(appContext, encoded, pinCode,
130 | new PFPinCodeHelperCallback() {
131 | @Override
132 | public void onResult(PFResult result) {
133 | assertNull(result.getError());
134 | assertTrue(result.getResult());
135 | }
136 | });
137 |
138 |
139 | PFFingerprintPinCodeHelper.getInstance().checkPin(appContext, encoded, "1122",
140 | new PFPinCodeHelperCallback() {
141 | @Override
142 | public void onResult(PFResult result) {
143 | assertNull(result.getError());
144 | assertFalse(result.getResult());
145 | }
146 | });
147 |
148 | PFFingerprintPinCodeHelper.getInstance().delete(new PFPinCodeHelperCallback() {
149 | @Override
150 | public void onResult(PFResult result) {
151 | assertNull(result.getError());
152 | }
153 | });
154 |
155 | PFFingerprintPinCodeHelper.getInstance().isPinCodeEncryptionKeyExist(new PFPinCodeHelperCallback() {
156 | @Override
157 | public void onResult(PFResult result) {
158 | assertNull(result.getError());
159 | assertFalse(result.getResult());
160 | }
161 | });
162 |
163 | }
164 |
165 | }
166 |
--------------------------------------------------------------------------------
/pflockscreen/src/androidTest/java/com/beautycoder/pflockscreen/actions/PFCodeViewActionDelete.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen.actions;
2 |
3 | import androidx.test.espresso.UiController;
4 | import androidx.test.espresso.ViewAction;
5 | import android.view.View;
6 |
7 | import com.beautycoder.pflockscreen.views.PFCodeView;
8 |
9 | import org.hamcrest.Matcher;
10 |
11 | import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom;
12 |
13 | /**
14 | * Created by Aleksandr Nikiforov on 2018/03/19.
15 | */
16 | public class PFCodeViewActionDelete implements ViewAction {
17 |
18 | @Override
19 | public Matcher getConstraints(){
20 | return isAssignableFrom(PFCodeView.class);
21 | }
22 |
23 |
24 | @Override
25 | public String getDescription(){
26 | return "Delete code";
27 | }
28 |
29 | @Override
30 | public void perform(UiController uiController, View view){
31 | PFCodeView codeView = (PFCodeView) view;
32 | codeView.delete();
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/pflockscreen/src/androidTest/java/com/beautycoder/pflockscreen/actions/PFCodeViewActionInput.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen.actions;
2 |
3 | import androidx.test.espresso.UiController;
4 | import androidx.test.espresso.ViewAction;
5 | import android.view.View;
6 |
7 | import com.beautycoder.pflockscreen.views.PFCodeView;
8 |
9 | import org.hamcrest.Matcher;
10 |
11 | import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom;
12 |
13 | /**
14 | * Created by Aleksandr Nikiforov on 2018/03/19.
15 | */
16 |
17 | public class PFCodeViewActionInput implements ViewAction {
18 |
19 | private int mInput;
20 |
21 | public PFCodeViewActionInput(int code) {
22 | mInput = code;
23 | }
24 |
25 | @Override
26 | public Matcher getConstraints(){
27 | return isAssignableFrom(PFCodeView.class);
28 | }
29 |
30 |
31 | @Override
32 | public String getDescription(){
33 | return "Input code " + mInput;
34 | }
35 |
36 | @Override
37 | public void perform(UiController uiController, View view){
38 | PFCodeView codeView = (PFCodeView) view;
39 | codeView.input(String.valueOf(mInput));
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/pflockscreen/src/androidTest/java/com/beautycoder/pflockscreen/matchers/PFViewMatchers.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen.matchers;
2 |
3 | import androidx.test.espresso.matcher.BoundedMatcher;
4 | import android.view.View;
5 |
6 | import com.beautycoder.pflockscreen.views.PFCodeView;
7 |
8 | import org.hamcrest.Description;
9 | import org.hamcrest.Matcher;
10 |
11 | /**
12 | * Created by aleksandr on 2018/03/10.
13 | */
14 |
15 | public class PFViewMatchers {
16 |
17 | public static Matcher withCodeLength(final int expectedLength) {
18 | return new BoundedMatcher(PFCodeView.class) {
19 | @Override
20 | protected boolean matchesSafely(PFCodeView item) {
21 | return item.getInputCodeLength() == expectedLength;
22 | }
23 |
24 | @Override
25 | public void describeTo(Description description) {
26 | description.appendText("Checking the matcher on received view: ");
27 | description.appendText("with expectedLength=" + expectedLength);
28 | }
29 | };
30 | }
31 |
32 |
33 | public static Matcher withCodeValue(final String expectedValue) {
34 | return new BoundedMatcher(PFCodeView.class) {
35 | @Override
36 | protected boolean matchesSafely(PFCodeView item) {
37 | return item.getCode().equals(expectedValue);
38 | }
39 |
40 | @Override
41 | public void describeTo(Description description) {
42 | description.appendText("Checking the matcher on received view: ");
43 | description.appendText("with expectedValue=" + expectedValue);
44 | }
45 | };
46 | }
47 |
48 |
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/pflockscreen/src/androidTest/java/com/beautycoder/pflockscreen/rules/FragmentTestRule.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen.rules;
2 |
3 |
4 | import android.content.Context;
5 | import androidx.test.rule.ActivityTestRule;
6 | import androidx.fragment.app.Fragment;
7 | import androidx.fragment.app.FragmentManager;
8 | import androidx.fragment.app.FragmentTransaction;
9 |
10 | import com.beautycoder.pflockscreen.activities.TestActivity;
11 |
12 | /**
13 | * Created by Aleksandr Nikiforov on 2018/02/27.
14 | */
15 |
16 | public abstract class FragmentTestRule extends ActivityTestRule {
17 |
18 | private final Class mFragmentClass;
19 | private F mFragment;
20 |
21 | public FragmentTestRule(final Class fragmentClass) {
22 | super(TestActivity.class, true, false);
23 | mFragmentClass = fragmentClass;
24 | }
25 |
26 | @Override
27 | protected void afterActivityLaunched() {
28 | super.afterActivityLaunched();
29 |
30 | getActivity().runOnUiThread(new Runnable() {
31 | @Override
32 | public void run() {
33 | //try {
34 | //Instantiate and insert the fragment into the container layout
35 | FragmentManager manager = getActivity().getSupportFragmentManager();
36 | FragmentTransaction transaction = manager.beginTransaction();
37 | mFragment = getInstance(getActivity()); //mFragmentClass.newInstance();
38 | transaction.replace(android.R.id.content, mFragment);
39 | transaction.commit();
40 | /*} catch (InstantiationException | IllegalAccessException e) {
41 | Assert.fail(String.format("%s: Could not insert %s into TestActivity: %s",
42 | getClass().getSimpleName(),
43 | mFragmentClass.getSimpleName(),
44 | e.getMessage()));
45 | }*/
46 | }
47 | });
48 | }
49 |
50 | public abstract F getInstance(Context context);
51 |
52 |
53 | public F getFragment(){
54 | return mFragment;
55 | }
56 |
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/pflockscreen/src/androidTest/java/com/beautycoder/pflockscreen/rules/ViewTestRule.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen.rules;
2 |
3 | import android.content.Context;
4 | import androidx.test.rule.ActivityTestRule;
5 |
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | import com.beautycoder.pflockscreen.activities.TestActivity;
10 |
11 | /**
12 | * Created by aleksandr on 2018/03/11.
13 | */
14 |
15 | public abstract class ViewTestRule extends ActivityTestRule {
16 |
17 | private final Class mViewClass;
18 | private V mView;
19 |
20 | public ViewTestRule(final Class fragmentClass) {
21 | super(TestActivity.class, true, false);
22 | mViewClass = fragmentClass;
23 | }
24 |
25 | @Override
26 | protected void afterActivityLaunched() {
27 | super.afterActivityLaunched();
28 |
29 | getActivity().runOnUiThread(new Runnable() {
30 | @Override
31 | public void run() {
32 | //try {
33 | //Instantiate and insert the fragment into the container layout
34 | //FragmentManager manager = getActivity().getSupportFragmentManager();
35 | //FragmentTransaction transaction = manager.beginTransaction();
36 | mView = getView(getActivity()); //mFragmentClass.newInstance();
37 | ViewGroup viewGroup = getActivity().findViewById(android.R.id.content);
38 | ViewGroup.LayoutParams layoutParams = new ViewGroup.MarginLayoutParams(
39 | ViewGroup.LayoutParams.WRAP_CONTENT,
40 | ViewGroup.LayoutParams.WRAP_CONTENT);
41 | mView.setLayoutParams(layoutParams);
42 | viewGroup.addView(mView);
43 | //transaction.replace(android.R.id.content, mFragment);
44 | //transaction.commit();
45 | /*} catch (InstantiationException | IllegalAccessException e) {
46 | Assert.fail(String.format("%s: Could not insert %s into TestActivity: %s",
47 | getClass().getSimpleName(),
48 | mFragmentClass.getSimpleName(),
49 | e.getMessage()));
50 | }*/
51 | }
52 | });
53 | }
54 |
55 | public abstract V getView(Context context);
56 |
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/pflockscreen/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/pflockscreen/src/debug/java/com/beautycoder/pflockscreen/activities/TestActivity.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen.activities;
2 |
3 | import android.os.Bundle;
4 | import androidx.annotation.Nullable;
5 | import androidx.annotation.VisibleForTesting;
6 | import androidx.appcompat.app.AppCompatActivity;
7 | import android.widget.FrameLayout;
8 |
9 | /**
10 | * Created by aleksandr on 2018/02/27.
11 | */
12 | @VisibleForTesting
13 | public class TestActivity extends AppCompatActivity {
14 |
15 | @Override
16 | protected void onCreate(@Nullable final Bundle savedInstanceState) {
17 | super.onCreate(savedInstanceState);
18 | FrameLayout frameLayout = new FrameLayout(this);
19 | frameLayout.setId(android.R.id.content);
20 | setContentView(frameLayout);
21 | }
22 |
23 | }
24 |
25 |
26 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/java/com/beautycoder/pflockscreen/PFFLockScreenConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen;
2 |
3 | import android.content.Context;
4 | import androidx.annotation.IntDef;
5 |
6 | import java.io.Serializable;
7 | import java.lang.annotation.Retention;
8 |
9 | import static java.lang.annotation.RetentionPolicy.SOURCE;
10 |
11 | /**
12 | * Created by Aleksandr Nikiforov on 2018/02/14.
13 | */
14 | public class PFFLockScreenConfiguration implements Serializable {
15 |
16 | private String mLeftButton = "";
17 | private String mNextButton = "";
18 | private boolean mUseFingerprint = false;
19 | private boolean mAutoShowFingerprint = false;
20 | private String mTitle = "";
21 | private int mMode = MODE_AUTH;
22 | private int mCodeLength = 4;
23 | private boolean mClearCodeOnError = false;
24 | private boolean mErrorVibration = true;
25 | private boolean mErrorAnimation = true;
26 | private boolean mNewCodeValidation = false;
27 | private String mNewCodeValidationTitle = "";
28 |
29 | private PFFLockScreenConfiguration(Builder builder) {
30 | mLeftButton = builder.mLeftButton;
31 | mNextButton = builder.mNextButton;
32 | mUseFingerprint = builder.mUseFingerprint;
33 | mAutoShowFingerprint = builder.mAutoShowFingerprint;
34 | mTitle = builder.mTitle;
35 | mMode = builder.mMode;
36 | mCodeLength = builder.mCodeLength;
37 | mClearCodeOnError = builder.mClearCodeOnError;
38 | mErrorVibration = builder.mErrorVibration;
39 | mErrorAnimation = builder.mErrorAnimation;
40 | mNewCodeValidation = builder.mNewCodeValidation;
41 | mNewCodeValidationTitle = builder.mNewCodeValidationTitle;
42 | }
43 |
44 | public String getLeftButton() {
45 | return mLeftButton;
46 | }
47 |
48 | public String getNextButton() {
49 | return mNextButton;
50 | }
51 |
52 | public boolean isUseFingerprint() {
53 | return mUseFingerprint;
54 | }
55 |
56 | public boolean isAutoShowFingerprint() {
57 | return mAutoShowFingerprint;
58 | }
59 |
60 | public String getTitle() {
61 | return mTitle;
62 | }
63 |
64 | public int getCodeLength() {
65 | return mCodeLength;
66 | }
67 |
68 | public boolean isClearCodeOnError() {
69 | return mClearCodeOnError;
70 | }
71 |
72 | public boolean isErrorVibration() {
73 | return mErrorVibration;
74 | }
75 |
76 | public boolean isErrorAnimation() {
77 | return mErrorAnimation;
78 | }
79 |
80 | public boolean isNewCodeValidation() {
81 | return mNewCodeValidation;
82 | }
83 |
84 | public String getNewCodeValidationTitle() {
85 | return mNewCodeValidationTitle;
86 | }
87 |
88 | @PFLockScreenMode
89 | public int getMode() {
90 | return this.mMode;
91 | }
92 |
93 | public static class Builder {
94 |
95 | private String mLeftButton = "";
96 | private String mNextButton = "";
97 | private boolean mUseFingerprint = false;
98 | private boolean mAutoShowFingerprint = false;
99 | private String mTitle = "";
100 | private int mMode = 0;
101 | private int mCodeLength = 4;
102 | private boolean mClearCodeOnError = false;
103 | private boolean mErrorVibration = true;
104 | private boolean mErrorAnimation = true;
105 | private boolean mNewCodeValidation = false;
106 | private String mNewCodeValidationTitle = "";
107 |
108 |
109 | public Builder(Context context) {
110 | mTitle = context.getResources().getString(R.string.lock_screen_title_pf);
111 | }
112 |
113 | public Builder setTitle(String title) {
114 | mTitle = title;
115 | return this;
116 | }
117 |
118 | public Builder setLeftButton(String leftButton) {
119 | mLeftButton = leftButton;
120 | return this;
121 | }
122 |
123 | public Builder setNextButton(String nextButton) {
124 | mNextButton = nextButton;
125 | return this;
126 | }
127 |
128 | public Builder setUseFingerprint(boolean useFingerprint) {
129 | mUseFingerprint = useFingerprint;
130 | return this;
131 | }
132 |
133 | public Builder setAutoShowFingerprint(boolean autoShowFingerprint) {
134 | mAutoShowFingerprint = autoShowFingerprint;
135 | return this;
136 | }
137 |
138 | public Builder setMode(@PFLockScreenMode int mode) {
139 | mMode = mode;
140 | return this;
141 | }
142 |
143 | public Builder setCodeLength(int codeLength) {
144 | this.mCodeLength = codeLength;
145 | return this;
146 | }
147 |
148 | public Builder setClearCodeOnError(boolean clearCodeOnError) {
149 | mClearCodeOnError = clearCodeOnError;
150 | return this;
151 | }
152 |
153 | public Builder setErrorVibration(boolean errorVibration) {
154 | mErrorVibration = errorVibration;
155 | return this;
156 | }
157 |
158 | public Builder setErrorAnimation(boolean errorAnimation) {
159 | mErrorAnimation = errorAnimation;
160 | return this;
161 | }
162 |
163 | public Builder setNewCodeValidation(boolean newCodeValidation) {
164 | this.mNewCodeValidation = newCodeValidation;
165 | return this;
166 | }
167 |
168 | public Builder setNewCodeValidationTitle(String newCodeValidationTitle) {
169 | this.mNewCodeValidationTitle = newCodeValidationTitle;
170 | return this;
171 | }
172 |
173 | public PFFLockScreenConfiguration build() {
174 | return new PFFLockScreenConfiguration(
175 | this);
176 | }
177 |
178 |
179 | }
180 |
181 | @Retention(SOURCE)
182 | @IntDef({MODE_CREATE, MODE_AUTH})
183 | public @interface PFLockScreenMode {}
184 | public static final int MODE_CREATE = 0;
185 | public static final int MODE_AUTH = 1;
186 |
187 | }
188 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/java/com/beautycoder/pflockscreen/fragments/PFFingerprintAuthDialogFragment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License
15 | */
16 |
17 | package com.beautycoder.pflockscreen.fragments;
18 |
19 | import android.content.Context;
20 | import android.os.Build;
21 | import android.os.Bundle;
22 | import androidx.annotation.RequiresApi;
23 | import androidx.fragment.app.DialogFragment;
24 | import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
25 | import android.view.LayoutInflater;
26 | import android.view.View;
27 | import android.view.ViewGroup;
28 | import android.widget.Button;
29 | import android.widget.ImageView;
30 | import android.widget.TextView;
31 |
32 | import com.beautycoder.pflockscreen.R;
33 |
34 | /**
35 | * A dialog which uses fingerprint APIs to authenticate the user, and falls back to password
36 | * authentication if fingerprint is not available.
37 | */
38 | @RequiresApi(api = Build.VERSION_CODES.M)
39 | public class PFFingerprintAuthDialogFragment extends DialogFragment {
40 |
41 | private Button mCancelButton;
42 | private View mFingerprintContent;
43 |
44 | private Stage mStage = Stage.FINGERPRINT;
45 |
46 | private FingerprintManagerCompat.CryptoObject mCryptoObject;
47 |
48 | private PFFingerprintUIHelper mFingerprintCallback;
49 |
50 | private Context mContext;
51 |
52 | private PFFingerprintAuthListener mAuthListener;
53 |
54 |
55 | @Override
56 | public void onCreate(Bundle savedInstanceState) {
57 | super.onCreate(savedInstanceState);
58 |
59 | // Do not create a new Fragment when the Activity is re-created such as orientation changes.
60 | setRetainInstance(true);
61 | setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Material_Light_Dialog);
62 | }
63 |
64 |
65 | @Override
66 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
67 | Bundle savedInstanceState) {
68 | getDialog().setTitle(getString(R.string.sign_in_pf));
69 | View v = inflater.inflate(R.layout.view_pf_fingerprint_dialog_container, container,
70 | false);
71 | mCancelButton = v.findViewById(R.id.cancel_button);
72 | mCancelButton.setOnClickListener(new View.OnClickListener() {
73 | @Override
74 | public void onClick(View view) {
75 | dismiss();
76 | }
77 | });
78 |
79 | mFingerprintContent = v.findViewById(R.id.fingerprint_container);
80 |
81 |
82 | FingerprintManagerCompat manager = FingerprintManagerCompat.from(getContext());
83 | mFingerprintCallback = new PFFingerprintUIHelper(manager,
84 | (ImageView) v.findViewById(R.id.fingerprint_icon),
85 | (TextView) v.findViewById(R.id.fingerprint_status),
86 | mAuthListener);
87 | updateStage();
88 | return v;
89 | }
90 |
91 | @Override
92 | public void onResume() {
93 | super.onResume();
94 | if (mStage == Stage.FINGERPRINT) {
95 | mFingerprintCallback.startListening(mCryptoObject);
96 | }
97 | }
98 |
99 | public void setStage(Stage stage) {
100 | mStage = stage;
101 | }
102 |
103 | @Override
104 | public void onPause() {
105 | super.onPause();
106 | mFingerprintCallback.stopListening();
107 | }
108 |
109 | @Override
110 | public void onAttach(Context context) {
111 | super.onAttach(context);
112 | mContext = context;
113 | }
114 |
115 | /**
116 | * Sets the crypto object to be passed in when authenticating with fingerprint.
117 | */
118 | /*public void setCryptoObject(FingerprintManagerCompat.CryptoObject cryptoObject) {
119 | mCryptoObject = cryptoObject;
120 | }*/
121 |
122 |
123 | private void updateStage() {
124 | switch (mStage) {
125 | case FINGERPRINT:
126 | mCancelButton.setText(R.string.cancel_pf);
127 | mFingerprintContent.setVisibility(View.VISIBLE);
128 | break;
129 | }
130 | }
131 |
132 |
133 | public void setAuthListener(PFFingerprintAuthListener authListener) {
134 | mAuthListener = authListener;
135 | }
136 |
137 | /**
138 | * Enumeration to indicate which authentication method the user is trying to authenticate with.
139 | */
140 | public enum Stage {
141 | FINGERPRINT
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/java/com/beautycoder/pflockscreen/fragments/PFFingerprintAuthListener.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen.fragments;
2 |
3 | /**
4 | * Created by aleksandr on 2018/02/14.
5 | */
6 |
7 | public interface PFFingerprintAuthListener {
8 |
9 | void onAuthenticated();
10 |
11 | void onError();
12 | }
13 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/java/com/beautycoder/pflockscreen/fragments/PFFingerprintUIHelper.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen.fragments;
2 |
3 | import android.os.Build;
4 | import androidx.annotation.RequiresApi;
5 | import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
6 | import androidx.core.os.CancellationSignal;
7 | import android.widget.ImageView;
8 | import android.widget.TextView;
9 |
10 | import com.beautycoder.pflockscreen.R;
11 |
12 |
13 | /**
14 | * Created by aleksandr on 2018/02/10.
15 | */
16 |
17 | @RequiresApi(api = Build.VERSION_CODES.M)
18 | public class PFFingerprintUIHelper extends FingerprintManagerCompat.AuthenticationCallback {
19 |
20 | private static final long ERROR_TIMEOUT_MILLIS = 1600;
21 | private static final long SUCCESS_DELAY_MILLIS = 200;
22 |
23 | private final FingerprintManagerCompat mFingerprintManager;
24 | private final ImageView mIcon;
25 | private final TextView mErrorTextView;
26 | private final PFFingerprintAuthListener mCallback;
27 | private CancellationSignal mCancellationSignal;
28 |
29 | private boolean mSelfCancelled;
30 |
31 | public PFFingerprintUIHelper(FingerprintManagerCompat fingerprintManager,
32 | ImageView icon, TextView errorTextView,
33 | PFFingerprintAuthListener callback) {
34 | super();
35 | mFingerprintManager = fingerprintManager;
36 | mIcon = icon;
37 | mErrorTextView = errorTextView;
38 | mCallback = callback;
39 | }
40 |
41 | public boolean isFingerprintAuthAvailable() {
42 | // The line below prevents the false positive inspection from Android Studio
43 | // noinspection ResourceType
44 | return mFingerprintManager.isHardwareDetected()
45 | && mFingerprintManager.hasEnrolledFingerprints();
46 | }
47 |
48 | public void startListening(FingerprintManagerCompat.CryptoObject cryptoObject) {
49 | if (!isFingerprintAuthAvailable()) {
50 | return;
51 | }
52 | mCancellationSignal = new CancellationSignal();
53 | mSelfCancelled = false;
54 | // The line below prevents the false positive inspection from Android Studio
55 | // noinspection ResourceType
56 | mFingerprintManager.authenticate(
57 | cryptoObject, 0, mCancellationSignal, this, null);
58 | mIcon.setImageResource(R.drawable.ic_fp_40px_pf);
59 | }
60 |
61 | public void stopListening() {
62 | if (mCancellationSignal != null) {
63 | mSelfCancelled = true;
64 | mCancellationSignal.cancel();
65 | mCancellationSignal = null;
66 | }
67 | }
68 |
69 | @Override
70 | public void onAuthenticationError(int errMsgId, CharSequence errString) {
71 | if (!mSelfCancelled) {
72 | showError(errString);
73 | mIcon.postDelayed(new Runnable() {
74 | @Override
75 | public void run() {
76 | mCallback.onError();
77 | }
78 | }, ERROR_TIMEOUT_MILLIS);
79 | }
80 | }
81 |
82 | @Override
83 | public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
84 | showError(helpString);
85 | }
86 |
87 | @Override
88 | public void onAuthenticationFailed() {
89 | showError(mIcon.getResources().getString(
90 | R.string.fingerprint_not_recognized_pf));
91 | }
92 |
93 | @Override
94 | public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {
95 | mErrorTextView.removeCallbacks(mResetErrorTextRunnable);
96 | mIcon.setImageResource(R.drawable.ic_fingerprint_success_pf);
97 | mErrorTextView.setTextColor(
98 | mErrorTextView.getResources().getColor(R.color.success_color, null));
99 | mErrorTextView.setText(
100 | mErrorTextView.getResources().getString(R.string.fingerprint_success_pf));
101 | mIcon.postDelayed(new Runnable() {
102 | @Override
103 | public void run() {
104 | mCallback.onAuthenticated();
105 | }
106 | }, SUCCESS_DELAY_MILLIS);
107 | }
108 |
109 | private void showError(CharSequence error) {
110 | mIcon.setImageResource(R.drawable.ic_fingerprint_error_pf);
111 | mErrorTextView.setText(error);
112 | mErrorTextView.setTextColor(
113 | mErrorTextView.getResources().getColor(R.color.warning_color, null));
114 | mErrorTextView.removeCallbacks(mResetErrorTextRunnable);
115 | mErrorTextView.postDelayed(mResetErrorTextRunnable, ERROR_TIMEOUT_MILLIS);
116 | }
117 |
118 | private Runnable mResetErrorTextRunnable = new Runnable() {
119 | @Override
120 | public void run() {
121 | mErrorTextView.setTextColor(
122 | mErrorTextView.getResources().getColor(R.color.hint_color, null));
123 | mErrorTextView.setText(
124 | mErrorTextView.getResources().getString(R.string.fingerprint_hint_pf));
125 | mIcon.setImageResource(R.drawable.ic_fp_40px_pf);
126 | }
127 | };
128 |
129 | }
130 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/java/com/beautycoder/pflockscreen/security/IPFPinCodeHelper.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen.security;
2 |
3 | import android.content.Context;
4 |
5 | import com.beautycoder.pflockscreen.security.callbacks.PFPinCodeHelperCallback;
6 |
7 | public interface IPFPinCodeHelper {
8 |
9 | /**
10 | * Encode pin
11 | * @param context any context.
12 | * @param pin pin code string to check.
13 | * @param callback PFPinCodeHelperCallback callback object.
14 | * @return true if pin codes matches.
15 | * @throws PFSecurityException throw exception if something went wrong.
16 | */
17 | void encodePin(Context context, String pin, PFPinCodeHelperCallback callBack);
18 |
19 | /**
20 | * Check if pin code is valid.
21 | * @param context any context.
22 | * @param encodedPin encoded pin code string.
23 | * @param pin pin code string to check.
24 | * @param callback PFPinCodeHelperCallback callback object.
25 | * @return true if pin codes matches.
26 | * @throws PFSecurityException throw exception if something went wrong.
27 | */
28 | void checkPin(Context context, String encodedPin, String pin, PFPinCodeHelperCallback callback);
29 |
30 | /**
31 | * Delete pin code encryption key.
32 | * @param callback PFPinCodeHelperCallback callback object.
33 | * @throws PFSecurityException throw exception if something went wrong.
34 | */
35 | void delete(PFPinCodeHelperCallback callback);
36 |
37 | /**
38 | * Check if pin code encryption key is exist.
39 | * @param callback PFPinCodeHelperCallback callback object.
40 | * @return true if key exist in KeyStore.
41 | * @throws PFSecurityException throw exception if something went wrong.
42 | */
43 | void isPinCodeEncryptionKeyExist(PFPinCodeHelperCallback callback);
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/java/com/beautycoder/pflockscreen/security/IPFSecurityUtils.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen.security;
2 |
3 | import android.content.Context;
4 | import androidx.annotation.NonNull;
5 |
6 | public interface IPFSecurityUtils {
7 |
8 | String encode(@NonNull Context context, String alias, String input, boolean isAuthorizationRequared)
9 | throws PFSecurityException ;
10 |
11 | String decode(String alias, String encodedString) throws PFSecurityException;
12 |
13 | boolean isKeystoreContainAlias(String alias) throws PFSecurityException;
14 |
15 | void deleteKey(String alias) throws PFSecurityException;
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/java/com/beautycoder/pflockscreen/security/PFFingerprintPinCodeHelper.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen.security;
2 |
3 | import android.content.Context;
4 | import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
5 |
6 | import com.beautycoder.pflockscreen.security.callbacks.PFPinCodeHelperCallback;
7 |
8 | /**
9 | * Created by Aleksandr Nikiforov on 2018/02/09.
10 | *
11 | * PFFingerprintPinCodeHelper - helper class to encode/decode pin code string,
12 | * validate pin code etc.
13 | */
14 | public class PFFingerprintPinCodeHelper implements IPFPinCodeHelper {
15 |
16 |
17 | private static final String FINGERPRINT_ALIAS = "fp_fingerprint_lock_screen_key_store";
18 | private static final String PIN_ALIAS = "fp_pin_lock_screen_key_store";
19 |
20 | private static final PFFingerprintPinCodeHelper ourInstance = new PFFingerprintPinCodeHelper();
21 |
22 | public static PFFingerprintPinCodeHelper getInstance() {
23 | return ourInstance;
24 | }
25 |
26 | private final IPFSecurityUtils pfSecurityUtils
27 | = PFSecurityUtilsFactory.getPFSecurityUtilsInstance();
28 |
29 | private PFFingerprintPinCodeHelper() {
30 |
31 | }
32 |
33 | /**
34 | * Encode pin code.
35 | * @param context any context.
36 | * @param pin pin code string.
37 | * @return encoded pin code string.
38 | * @throws PFSecurityException throw exception if something went wrong.
39 | */
40 | @Override
41 | public void encodePin(Context context, String pin, PFPinCodeHelperCallback callback) {
42 | try {
43 | final String encoded = pfSecurityUtils.encode(context, PIN_ALIAS, pin, false);
44 | if (callback != null) {
45 | callback.onResult(new PFResult(encoded));
46 | }
47 | } catch (PFSecurityException e) {
48 | if (callback != null) {
49 | callback.onResult(new PFResult(e.getError()));
50 | }
51 | }
52 | }
53 |
54 | /**
55 | * Check if pin code is valid.
56 | * @param context any context.
57 | * @param encodedPin encoded pin code string.
58 | * @param pin pin code string to check.
59 | * @return true if pin codes matches.
60 | * @throws PFSecurityException throw exception if something went wrong.
61 | */
62 | @Override
63 | public void checkPin(Context context, String encodedPin, String pin, PFPinCodeHelperCallback callback) {
64 | try {
65 | final String pinCode = pfSecurityUtils.decode(PIN_ALIAS, encodedPin);
66 | if (callback != null) {
67 | callback.onResult(new PFResult(pinCode.equals(pin)));
68 | }
69 | } catch (PFSecurityException e) {
70 | if (callback != null) {
71 | callback.onResult(new PFResult(e.getError()));
72 | }
73 | }
74 | }
75 |
76 |
77 | private boolean isFingerPrintAvailable(Context context) {
78 | return FingerprintManagerCompat.from(context).isHardwareDetected();
79 | }
80 |
81 | private boolean isFingerPrintReady(Context context) {
82 | return FingerprintManagerCompat.from(context).hasEnrolledFingerprints();
83 | }
84 |
85 | /**
86 | * Delete pin code encryption key.
87 | * @throws PFSecurityException throw exception if something went wrong.
88 | */
89 | @Override
90 | public void delete(PFPinCodeHelperCallback callback) {
91 | try {
92 | pfSecurityUtils.deleteKey(PIN_ALIAS);
93 | if (callback != null) {
94 | callback.onResult(new PFResult(true));
95 | }
96 | } catch (PFSecurityException e) {
97 | if (callback != null) {
98 | callback.onResult(new PFResult(e.getError()));
99 | }
100 | }
101 | }
102 |
103 | /**
104 | * Check if pin code encryption key is exist.
105 | * @return true if key exist in KeyStore.
106 | * @throws PFSecurityException throw exception if something went wrong.
107 | */
108 | @Override
109 | public void isPinCodeEncryptionKeyExist(PFPinCodeHelperCallback callback) {
110 | try {
111 | final boolean isExist = pfSecurityUtils.isKeystoreContainAlias(PIN_ALIAS);
112 | if (callback != null) {
113 | callback.onResult(new PFResult(isExist));
114 | }
115 | } catch (PFSecurityException e) {
116 | if (callback != null) {
117 | callback.onResult(new PFResult(e.getError()));
118 | }
119 | }
120 | }
121 |
122 | }
123 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/java/com/beautycoder/pflockscreen/security/PFResult.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen.security;
2 |
3 | public class PFResult {
4 |
5 | private PFSecurityError mError = null;
6 | private T mResult = null;
7 |
8 | public PFResult(PFSecurityError mError) {
9 | this.mError = mError;
10 | }
11 |
12 | public PFResult(T result) {
13 | mResult = result;
14 | }
15 |
16 | public PFSecurityError getError() {
17 | return mError;
18 | }
19 |
20 | public T getResult() {
21 | return mResult;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/java/com/beautycoder/pflockscreen/security/PFSecurityError.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen.security;
2 |
3 | public class PFSecurityError {
4 |
5 | private final String mMessage;
6 | private final Integer mCode;
7 |
8 | /**
9 | * Constructor.
10 | * @param message exception message.
11 | * @param code error code.
12 | */
13 | PFSecurityError(String message, Integer code) {
14 | mMessage = message;
15 | mCode = code;
16 | }
17 |
18 | /**
19 | * Get error message.
20 | * @return error message.
21 | */
22 | public String getMessage() {
23 | return mMessage;
24 | }
25 |
26 | /**
27 | * Get error code.
28 | * @return error code.
29 | */
30 | public Integer getCode() {
31 | return mCode;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/java/com/beautycoder/pflockscreen/security/PFSecurityException.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen.security;
2 |
3 | /**
4 | * Created by aleksandr on 2018/02/10.
5 | *
6 | * Exception class for PFSecurityUtils.
7 | */
8 | public class PFSecurityException extends Exception {
9 |
10 | private final Integer mCode;
11 |
12 | /**
13 | * Constructor.
14 | * @param message exception message.
15 | * @param code error code.
16 | */
17 | public PFSecurityException(String message, Integer code) {
18 | super(message);
19 | mCode = code;
20 | }
21 |
22 | /**
23 | * Get error code.
24 | * @return error code.
25 | */
26 | public Integer getCode() {
27 | return mCode;
28 | }
29 |
30 | /**
31 | * Get PFSecurityError object.
32 | * @return PFSecurityError from PFSecurityException message and error code.
33 | */
34 | public PFSecurityError getError() {
35 | return new PFSecurityError(getMessage(), getCode());
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/java/com/beautycoder/pflockscreen/security/PFSecurityManager.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen.security;
2 |
3 | public class PFSecurityManager {
4 | private static final PFSecurityManager ourInstance = new PFSecurityManager();
5 |
6 | public static PFSecurityManager getInstance() {
7 | return ourInstance;
8 | }
9 |
10 | private PFSecurityManager() {
11 | }
12 |
13 | private IPFPinCodeHelper mPinCodeHelper = PFFingerprintPinCodeHelper.getInstance();
14 |
15 | public void setPinCodeHelper(IPFPinCodeHelper pinCodeHelper) {
16 | mPinCodeHelper = pinCodeHelper;
17 | }
18 |
19 | public IPFPinCodeHelper getPinCodeHelper() {
20 | return mPinCodeHelper;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/java/com/beautycoder/pflockscreen/security/PFSecurityUtils.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen.security;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.os.Build;
6 | import android.security.keystore.KeyGenParameterSpec;
7 | import android.security.keystore.KeyProperties;
8 | import androidx.annotation.NonNull;
9 | import android.util.Base64;
10 |
11 | import java.io.IOException;
12 | import java.security.InvalidAlgorithmParameterException;
13 | import java.security.InvalidKeyException;
14 | import java.security.KeyFactory;
15 | import java.security.KeyPairGenerator;
16 | import java.security.KeyStore;
17 | import java.security.KeyStoreException;
18 | import java.security.NoSuchAlgorithmException;
19 | import java.security.NoSuchProviderException;
20 | import java.security.PrivateKey;
21 | import java.security.PublicKey;
22 | import java.security.UnrecoverableKeyException;
23 | import java.security.cert.CertificateException;
24 | import java.security.spec.InvalidKeySpecException;
25 | import java.security.spec.MGF1ParameterSpec;
26 | import java.security.spec.X509EncodedKeySpec;
27 |
28 | import javax.crypto.BadPaddingException;
29 | import javax.crypto.Cipher;
30 | import javax.crypto.IllegalBlockSizeException;
31 | import javax.crypto.NoSuchPaddingException;
32 | import javax.crypto.spec.OAEPParameterSpec;
33 | import javax.crypto.spec.PSource;
34 |
35 | /**
36 | * Created by Aleksandr Nikiforov on 2018/02/07.
37 | *
38 | * Class to work with AndroidKeyStore.
39 | */
40 | public class PFSecurityUtils implements IPFSecurityUtils {
41 |
42 | private static final PFSecurityUtils ourInstance = new PFSecurityUtils();
43 |
44 | public static PFSecurityUtils getInstance() {
45 | return ourInstance;
46 | }
47 |
48 | private PFSecurityUtils() {
49 |
50 | }
51 |
52 | /**
53 | * Load AndroidKeyStore.
54 | * @return true if keystore loaded successfully
55 | */
56 | private KeyStore loadKeyStore() throws PFSecurityException {
57 | try {
58 | final KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
59 | keyStore.load(null);
60 | return keyStore;
61 | } catch (KeyStoreException
62 | | NoSuchAlgorithmException
63 | | CertificateException
64 | | IOException e) {
65 | e.printStackTrace();
66 | throw new PFSecurityException(
67 | "Can not load keystore:" + e.getMessage(),
68 | PFSecurityUtilsErrorCodes.ERROR_LOAD_KEY_STORE
69 | );
70 | }
71 | }
72 |
73 | @Override
74 | public String encode(@NonNull Context context, String alias, String input, boolean isAuthorizationRequared)
75 | throws PFSecurityException {
76 | try {
77 | final Cipher cipher = getEncodeCipher(context, alias, isAuthorizationRequared);
78 | final byte[] bytes = cipher.doFinal(input.getBytes());
79 | return Base64.encodeToString(bytes, Base64.NO_WRAP);
80 | } catch (IllegalBlockSizeException | BadPaddingException e) {
81 | e.printStackTrace();
82 | throw new PFSecurityException(
83 | "Error while encoding : " + e.getMessage(),
84 | PFSecurityUtilsErrorCodes.ERROR_ENCODING
85 | );
86 | }
87 | }
88 |
89 | // More information about this hack
90 | // from https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.html
91 | // from https://code.google.com/p/android/issues/detail?id=197719
92 | private Cipher getEncodeCipher(@NonNull Context context, String alias, boolean isAuthenticationRequired)
93 | throws PFSecurityException {
94 | final Cipher cipher = getCipherInstance();
95 | final KeyStore keyStore = loadKeyStore();
96 | generateKeyIfNecessary(context, keyStore, alias, isAuthenticationRequired);
97 | initEncodeCipher(cipher, alias, keyStore);
98 | return cipher;
99 |
100 | }
101 |
102 | private boolean generateKeyIfNecessary(@NonNull Context context, @NonNull KeyStore keyStore, String alias,
103 | boolean isAuthenticationRequired) {
104 | try {
105 | return keyStore.containsAlias(alias) || generateKey(context, alias, isAuthenticationRequired);
106 | } catch (KeyStoreException e) {
107 | e.printStackTrace();
108 | }
109 | return false;
110 | }
111 |
112 | private boolean generateKey(Context context, String keystoreAlias, boolean isAuthenticationRequired) {
113 | return generateKey(keystoreAlias, isAuthenticationRequired);
114 | }
115 |
116 | private String decode(String encodedString, Cipher cipher) throws PFSecurityException {
117 | try {
118 | final byte[] bytes = Base64.decode(encodedString, Base64.NO_WRAP);
119 | return new String(cipher.doFinal(bytes));
120 | } catch (IllegalBlockSizeException | BadPaddingException e) {
121 | e.printStackTrace();
122 | throw new PFSecurityException(
123 | "Error while decoding: " + e.getMessage(),
124 | PFSecurityUtilsErrorCodes.ERROR_DEENCODING
125 | );
126 | }
127 | }
128 |
129 | @Override
130 | public String decode(String alias, String encodedString) throws PFSecurityException {
131 | try {
132 | final Cipher cipher = getCipherInstance();
133 | initDecodeCipher(cipher, alias);
134 | final byte[] bytes = Base64.decode(encodedString, Base64.NO_WRAP);
135 | return new String(cipher.doFinal(bytes));
136 | } catch (IllegalBlockSizeException | BadPaddingException e) {
137 | e.printStackTrace();
138 | throw new PFSecurityException(
139 | "Error while decoding: " + e.getMessage(),
140 | PFSecurityUtilsErrorCodes.ERROR_DEENCODING
141 | );
142 | }
143 | }
144 |
145 | @TargetApi(Build.VERSION_CODES.M)
146 | private boolean generateKey(String keystoreAlias, boolean isAuthenticationRequired) {
147 | try {
148 | final KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance(
149 | KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
150 | keyGenerator.initialize(
151 | new KeyGenParameterSpec.Builder(keystoreAlias,
152 | KeyProperties.PURPOSE_ENCRYPT |
153 | KeyProperties.PURPOSE_DECRYPT)
154 | .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
155 | .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
156 | .setUserAuthenticationRequired(isAuthenticationRequired)
157 | .build());
158 | keyGenerator.generateKeyPair();
159 | return true;
160 |
161 | } catch ( NoSuchAlgorithmException
162 | | NoSuchProviderException
163 | | InvalidAlgorithmParameterException exc) {
164 | exc.printStackTrace();
165 | return false;
166 | }
167 | }
168 |
169 | private Cipher getCipherInstance() throws PFSecurityException {
170 | try {
171 | final Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
172 | return cipher;
173 | } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
174 | e.printStackTrace();
175 | throw new PFSecurityException(
176 | "Can not get instance of Cipher object" + e.getMessage(),
177 | PFSecurityUtilsErrorCodes.ERROR_GET_CIPHER_INSTANCE
178 | );
179 | }
180 | }
181 |
182 |
183 | private void initDecodeCipher(Cipher cipher, String alias) throws PFSecurityException {
184 | try {
185 | final KeyStore keyStore = loadKeyStore();
186 | final PrivateKey key = (PrivateKey) keyStore.getKey(alias, null);
187 | cipher.init(Cipher.DECRYPT_MODE, key);
188 | } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException
189 | | InvalidKeyException e) {
190 | e.printStackTrace();
191 | throw new PFSecurityException(
192 | "Error init decode Cipher: " + e.getMessage(),
193 | PFSecurityUtilsErrorCodes.ERROR_INIT_DECODE_CIPHER
194 | );
195 | }
196 |
197 | }
198 |
199 | private void initEncodeCipher(Cipher cipher, String alias, KeyStore keyStore)
200 | throws PFSecurityException {
201 | try {
202 | final PublicKey key = keyStore.getCertificate(alias).getPublicKey();
203 | final PublicKey unrestricted = KeyFactory.getInstance(key.getAlgorithm()).generatePublic(
204 | new X509EncodedKeySpec(key.getEncoded()));
205 | final OAEPParameterSpec spec = new OAEPParameterSpec("SHA-256", "MGF1",
206 | MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT);
207 | cipher.init(Cipher.ENCRYPT_MODE, unrestricted, spec);
208 | } catch (KeyStoreException | InvalidKeySpecException |
209 | NoSuchAlgorithmException | InvalidKeyException |
210 | InvalidAlgorithmParameterException e) {
211 | throw new PFSecurityException(
212 | "Can not initialize Encode Cipher:" + e.getMessage(),
213 | PFSecurityUtilsErrorCodes.ERROR_INIT_ENDECODE_CIPHER
214 | );
215 | }
216 | }
217 |
218 | @Override
219 | public boolean isKeystoreContainAlias(String alias) throws PFSecurityException {
220 | final KeyStore keyStore = loadKeyStore();
221 | try {
222 | return keyStore.containsAlias(alias);
223 | } catch (KeyStoreException e) {
224 | e.printStackTrace();
225 | throw new PFSecurityException(
226 | e.getMessage(),
227 | PFSecurityUtilsErrorCodes.ERROR_KEY_STORE
228 | );
229 | }
230 | }
231 |
232 |
233 | /**
234 | * Delete key from KeyStore.
235 | * @param alias KeyStore alias.
236 | * @throws PFSecurityException throw Exception if something went wrong.
237 | */
238 | @Override
239 | public void deleteKey(String alias) throws PFSecurityException {
240 | final KeyStore keyStore = loadKeyStore();
241 | try {
242 | keyStore.deleteEntry(alias);
243 | } catch (KeyStoreException e) {
244 | e.printStackTrace();
245 | throw new PFSecurityException(
246 | "Can not delete key: " + e.getMessage(),
247 | PFSecurityUtilsErrorCodes.ERROR_DELETE_KEY
248 | );
249 | }
250 | }
251 |
252 | }
253 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/java/com/beautycoder/pflockscreen/security/PFSecurityUtilsErrorCodes.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen.security;
2 |
3 | public class PFSecurityUtilsErrorCodes {
4 | public static final int ERROR_LOAD_KEY_STORE = 100101;
5 | public static final int ERROR_KEY_STORE = 100102;
6 | public static final int ERROR_ENCODING = 100103;
7 | public static final int ERROR_DEENCODING = 100104;
8 | public static final int ERROR_INIT_ENDECODE_CIPHER = 100105;
9 | public static final int ERROR_INIT_DECODE_CIPHER = 100106;
10 | public static final int ERROR_GET_CIPHER_INSTANCE = 100107;
11 | public static final int ERROR_DELETE_KEY = 100108;
12 | }
13 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/java/com/beautycoder/pflockscreen/security/PFSecurityUtilsFactory.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen.security;
2 |
3 | public class PFSecurityUtilsFactory {
4 |
5 | public static IPFSecurityUtils getPFSecurityUtilsInstance() {
6 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
7 | return PFSecurityUtils.getInstance();
8 | } else {
9 | return PFSecurityUtilsOld.getInstance();
10 | }
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/java/com/beautycoder/pflockscreen/security/PFSecurityUtilsOld.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen.security;
2 |
3 | import android.content.Context;
4 | import android.security.KeyPairGeneratorSpec;
5 | import android.security.keystore.KeyProperties;
6 | import androidx.annotation.NonNull;
7 | import android.util.Base64;
8 |
9 | import java.io.ByteArrayInputStream;
10 | import java.io.ByteArrayOutputStream;
11 | import java.io.IOException;
12 | import java.math.BigInteger;
13 | import java.security.InvalidAlgorithmParameterException;
14 | import java.security.KeyPairGenerator;
15 | import java.security.KeyStore;
16 | import java.security.KeyStoreException;
17 | import java.security.NoSuchAlgorithmException;
18 | import java.security.NoSuchProviderException;
19 | import java.security.cert.CertificateException;
20 | import java.util.ArrayList;
21 | import java.util.Calendar;
22 |
23 | import javax.crypto.Cipher;
24 | import javax.crypto.CipherInputStream;
25 | import javax.crypto.CipherOutputStream;
26 | import javax.security.auth.x500.X500Principal;
27 |
28 | public class PFSecurityUtilsOld implements IPFSecurityUtils {
29 |
30 | private static final PFSecurityUtilsOld ourInstance = new PFSecurityUtilsOld();
31 |
32 | public static PFSecurityUtilsOld getInstance() {
33 | return ourInstance;
34 | }
35 |
36 | private PFSecurityUtilsOld() {
37 |
38 | }
39 |
40 | private static final String RSA_MODE = "RSA/ECB/PKCS1Padding";
41 | private static final String PROVIDER =
42 | android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M
43 | ? "AndroidKeyStoreBCWorkaround"
44 | : "AndroidOpenSSL";
45 |
46 | /**
47 | * Load AndroidKeyStore.
48 | * @return true if keystore loaded successfully
49 | */
50 | private KeyStore loadKeyStore() throws PFSecurityException {
51 | try {
52 | final KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
53 | keyStore.load(null);
54 | return keyStore;
55 | } catch (KeyStoreException
56 | | NoSuchAlgorithmException
57 | | CertificateException
58 | | IOException e) {
59 | e.printStackTrace();
60 | throw new PFSecurityException(
61 | "Can not load keystore:" + e.getMessage(),
62 | PFSecurityUtilsErrorCodes.ERROR_LOAD_KEY_STORE
63 | );
64 | }
65 | }
66 |
67 | private boolean generateKeyIfNecessary(
68 | @NonNull Context context,
69 | @NonNull KeyStore keyStore,
70 | String alias,
71 | boolean isAuthenticationRequired)
72 | {
73 | try {
74 | return keyStore.containsAlias(alias)
75 | || generateKey(context, alias, isAuthenticationRequired);
76 | } catch (KeyStoreException e) {
77 | e.printStackTrace();
78 | }
79 | return false;
80 | }
81 |
82 | @Override
83 | public String encode(
84 | @NonNull Context context,
85 | String alias,
86 | String input,
87 | boolean isAuthorizationRequared
88 | ) throws PFSecurityException {
89 | try {
90 | final byte[] bytes = rsaEncrypt(context, input.getBytes(), alias);
91 | return Base64.encodeToString(bytes, Base64.NO_WRAP);
92 | } catch (Exception e) {
93 | e.printStackTrace();
94 | throw new PFSecurityException(
95 | "Error while encoding : " + e.getMessage(),
96 | PFSecurityUtilsErrorCodes.ERROR_ENCODING
97 | );
98 | }
99 | }
100 |
101 | private byte[] rsaEncrypt(
102 | @NonNull Context context,
103 | byte[] secret,
104 | String keystoreAlias
105 | ) throws Exception {
106 | final KeyStore keyStore = loadKeyStore();
107 | generateKeyIfNecessary(context, keyStore, keystoreAlias, false);
108 | final KeyStore.PrivateKeyEntry privateKeyEntry
109 | = (KeyStore.PrivateKeyEntry) keyStore.getEntry(keystoreAlias, null);
110 | final Cipher inputCipher = Cipher.getInstance(RSA_MODE, PROVIDER);
111 | inputCipher.init(Cipher.ENCRYPT_MODE, privateKeyEntry.getCertificate().getPublicKey());
112 | final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
113 | final CipherOutputStream cipherOutputStream
114 | = new CipherOutputStream(outputStream, inputCipher);
115 | cipherOutputStream.write(secret);
116 | cipherOutputStream.close();
117 | final byte[] vals = outputStream.toByteArray();
118 | return vals;
119 | }
120 |
121 | @Override
122 | public String decode(
123 | String alias,
124 | String encodedString
125 | ) throws PFSecurityException {
126 | try {
127 | final byte[] bytes = Base64.decode(encodedString, Base64.NO_WRAP);
128 | return new String(rsaDecrypt(bytes, alias));
129 | } catch (Exception e) {
130 | e.printStackTrace();
131 | throw new PFSecurityException(
132 | "Error while decoding: " + e.getMessage(),
133 | PFSecurityUtilsErrorCodes.ERROR_DEENCODING
134 | );
135 | }
136 | }
137 |
138 | private byte[] rsaDecrypt(
139 | byte[] encrypted,
140 | String keystoreAlias
141 | ) throws Exception {
142 | final KeyStore keyStore = loadKeyStore();
143 | final KeyStore.PrivateKeyEntry privateKeyEntry =
144 | (KeyStore.PrivateKeyEntry)keyStore.getEntry(keystoreAlias, null);
145 | final Cipher output = Cipher.getInstance(RSA_MODE, PROVIDER);
146 | output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey());
147 | final CipherInputStream cipherInputStream = new CipherInputStream(
148 | new ByteArrayInputStream(encrypted), output);
149 | final ArrayList values = new ArrayList<>();
150 | int nextByte;
151 | while ((nextByte = cipherInputStream.read()) != -1) {
152 | values.add((byte)nextByte);
153 | }
154 | final byte[] bytes = new byte[values.size()];
155 | for(int i = 0; i < bytes.length; i++) {
156 | bytes[i] = values.get(i).byteValue();
157 | }
158 | return bytes;
159 | }
160 |
161 | private boolean generateKey(
162 | Context context,
163 | String keystoreAlias,
164 | boolean isAuthenticationRequired
165 | ) {
166 | return generateKeyOld(context, keystoreAlias, isAuthenticationRequired);
167 | }
168 |
169 | private boolean generateKeyOld(
170 | Context context,
171 | String keystoreAlias,
172 | boolean isAuthenticationRequired
173 | ) {
174 | try {
175 | final Calendar start = Calendar.getInstance();
176 | final Calendar end = Calendar.getInstance();
177 | end.add(Calendar.YEAR, 25);
178 |
179 | final KeyPairGenerator keyGen = KeyPairGenerator
180 | .getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
181 |
182 | final KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context)
183 | .setAlias(keystoreAlias)
184 | .setSubject(new X500Principal("CN=" + keystoreAlias))
185 | .setSerialNumber(BigInteger.valueOf(Math.abs(keystoreAlias.hashCode())))
186 | .setEndDate(end.getTime())
187 | .setStartDate(start.getTime())
188 | .setSerialNumber(BigInteger.ONE)
189 | .setSubject(new X500Principal(
190 | "CN = Secured Preference Store, O = Devliving Online")
191 | )
192 | .build();
193 |
194 | keyGen.initialize(spec);
195 | keyGen.generateKeyPair();
196 | return true;
197 |
198 | } catch ( NoSuchAlgorithmException
199 | | NoSuchProviderException
200 | | InvalidAlgorithmParameterException exc) {
201 | exc.printStackTrace();
202 | return false;
203 | }
204 | }
205 |
206 | @Override
207 | public boolean isKeystoreContainAlias(String alias) throws PFSecurityException {
208 | final KeyStore keyStore = loadKeyStore();
209 | try {
210 | return keyStore.containsAlias(alias);
211 | } catch (KeyStoreException e) {
212 | e.printStackTrace();
213 | throw new PFSecurityException(
214 | e.getMessage(),
215 | PFSecurityUtilsErrorCodes.ERROR_KEY_STORE
216 | );
217 | }
218 | }
219 |
220 | /**
221 | * Delete key from KeyStore.
222 | * @param alias KeyStore alias.
223 | * @throws PFSecurityException throw Exception if something went wrong.
224 | */
225 | @Override
226 | public void deleteKey(String alias) throws PFSecurityException {
227 | final KeyStore keyStore = loadKeyStore();
228 | try {
229 | keyStore.deleteEntry(alias);
230 | } catch (KeyStoreException e) {
231 | e.printStackTrace();
232 | throw new PFSecurityException(
233 | "Can not delete key: " + e.getMessage(),
234 | PFSecurityUtilsErrorCodes.ERROR_DELETE_KEY
235 | );
236 | }
237 | }
238 |
239 | }
240 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/java/com/beautycoder/pflockscreen/security/callbacks/PFPinCodeHelperCallback.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen.security.callbacks;
2 |
3 | import com.beautycoder.pflockscreen.security.PFResult;
4 |
5 | public interface PFPinCodeHelperCallback {
6 | void onResult(PFResult result);
7 | }
8 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/java/com/beautycoder/pflockscreen/security/livedata/PFLiveData.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen.security.livedata;
2 |
3 | import androidx.lifecycle.LiveData;
4 |
5 | public class PFLiveData extends LiveData {
6 |
7 | public void setData(T data) {
8 | setValue(data);
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/java/com/beautycoder/pflockscreen/viewmodels/PFPinCodeViewModel.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen.viewmodels;
2 |
3 | import androidx.lifecycle.LiveData;
4 | import androidx.lifecycle.ViewModel;
5 |
6 | import android.content.Context;
7 |
8 | import com.beautycoder.pflockscreen.security.PFSecurityManager;
9 | import com.beautycoder.pflockscreen.security.PFResult;
10 | import com.beautycoder.pflockscreen.security.callbacks.PFPinCodeHelperCallback;
11 | import com.beautycoder.pflockscreen.security.livedata.PFLiveData;
12 |
13 |
14 | public class PFPinCodeViewModel extends ViewModel {
15 |
16 | public LiveData> encodePin(Context context, String pin) {
17 | final PFLiveData> liveData = new PFLiveData<>();
18 | PFSecurityManager.getInstance().getPinCodeHelper().encodePin(
19 | context,
20 | pin,
21 | new PFPinCodeHelperCallback() {
22 | @Override
23 | public void onResult(PFResult result) {
24 | liveData.setData(result);
25 | }
26 | }
27 | );
28 | return liveData;
29 | }
30 |
31 | public LiveData> checkPin(Context context, String encodedPin, String pin) {
32 | final PFLiveData> liveData = new PFLiveData<>();
33 | PFSecurityManager.getInstance().getPinCodeHelper().checkPin(
34 | context,
35 | encodedPin,
36 | pin,
37 | new PFPinCodeHelperCallback() {
38 | @Override
39 | public void onResult(PFResult result) {
40 | liveData.setData(result);
41 | }
42 | }
43 | );
44 | return liveData;
45 | }
46 |
47 | public LiveData> delete() {
48 | final PFLiveData> liveData = new PFLiveData<>();
49 | PFSecurityManager.getInstance().getPinCodeHelper().delete(
50 | new PFPinCodeHelperCallback() {
51 | @Override
52 | public void onResult(PFResult result) {
53 | liveData.setData(result);
54 | }
55 | }
56 | );
57 | return liveData;
58 | }
59 |
60 | public LiveData> isPinCodeEncryptionKeyExist() {
61 | final PFLiveData> liveData = new PFLiveData<>();
62 | PFSecurityManager.getInstance().getPinCodeHelper().isPinCodeEncryptionKeyExist(
63 | new PFPinCodeHelperCallback() {
64 | @Override
65 | public void onResult(PFResult result) {
66 | liveData.setData(result);
67 | }
68 | }
69 | );
70 | return liveData;
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/java/com/beautycoder/pflockscreen/views/PFCodeView.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen.views;
2 |
3 | import android.content.Context;
4 | import androidx.annotation.Nullable;
5 | import android.util.AttributeSet;
6 | import android.view.LayoutInflater;
7 | import android.view.ViewGroup;
8 | import android.widget.CheckBox;
9 | import android.widget.LinearLayout;
10 |
11 | import com.beautycoder.pflockscreen.R;
12 |
13 | import java.util.ArrayList;
14 | import java.util.List;
15 |
16 | /**
17 | * Created by Aleksandr Nikiforov on 2018/02/07.
18 | */
19 |
20 | public class PFCodeView extends LinearLayout {
21 |
22 | private static final int DEFAULT_CODE_LENGTH = 4;
23 | List mCodeViews = new ArrayList<>();
24 | private String mCode = "";
25 | private int mCodeLength = DEFAULT_CODE_LENGTH;
26 | private OnPFCodeListener mListener;
27 |
28 |
29 | public PFCodeView(Context context) {
30 | super(context);
31 | init();
32 | }
33 |
34 | public PFCodeView(Context context, @Nullable AttributeSet attrs) {
35 | super(context, attrs);
36 | init();
37 | }
38 |
39 | public PFCodeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
40 | super(context, attrs, defStyleAttr);
41 | init();
42 | }
43 |
44 | private void init() {
45 | inflate(getContext(), R.layout.view_code_pf_lockscreen, this);
46 | setUpCodeViews();
47 | }
48 |
49 | public void setCodeLength(int codeLength) {
50 | mCodeLength = codeLength;
51 | setUpCodeViews();
52 | }
53 |
54 | private void setUpCodeViews() {
55 | removeAllViews();
56 | mCodeViews.clear();
57 | mCode = "";
58 | for (int i = 0; i < mCodeLength; i++) {
59 | LayoutInflater inflater = (LayoutInflater) getContext()
60 | .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
61 | CheckBox view = (CheckBox) inflater.inflate(R.layout.view_pf_code_checkbox, null);
62 |
63 | LinearLayout.LayoutParams layoutParams = new LayoutParams(
64 | ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
65 | int margin = getResources().getDimensionPixelSize(R.dimen.code_fp_margin);
66 | layoutParams.setMargins(margin, margin, margin, margin);
67 | view.setLayoutParams(layoutParams);
68 | view.setChecked(false);
69 | addView(view);
70 | mCodeViews.add(view);
71 | }
72 | if (mListener != null) {
73 | mListener.onCodeNotCompleted("");
74 | }
75 | }
76 |
77 | public int input(String number) {
78 | if (mCode.length() == mCodeLength) {
79 | return mCode.length();
80 | }
81 | mCodeViews.get(mCode.length()).toggle(); //.setChecked(true);
82 | mCode += number;
83 | if (mCode.length() == mCodeLength && mListener != null) {
84 | mListener.onCodeCompleted(mCode);
85 | }
86 | return mCode.length();
87 | }
88 |
89 | public int delete() {
90 | if (mListener != null) {
91 | mListener.onCodeNotCompleted(mCode);
92 | }
93 | if (mCode.length() == 0) {
94 | return mCode.length();
95 | }
96 | mCode = mCode.substring(0, mCode.length() - 1);
97 | mCodeViews.get(mCode.length()).toggle(); //.setChecked(false);
98 | return mCode.length();
99 | }
100 |
101 | public void clearCode() {
102 | if (mListener != null) {
103 | mListener.onCodeNotCompleted(mCode);
104 | }
105 | mCode = "";
106 | for (CheckBox codeView : mCodeViews) {
107 | codeView.setChecked(false);
108 | }
109 | }
110 |
111 | public int getInputCodeLength() {
112 | return mCode.length();
113 | }
114 |
115 | public String getCode() {
116 | return mCode;
117 | }
118 |
119 | public void setListener(OnPFCodeListener listener) {
120 | mListener = listener;
121 | }
122 |
123 | public interface OnPFCodeListener {
124 |
125 | void onCodeCompleted(String code);
126 |
127 | void onCodeNotCompleted(String code);
128 |
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/java/com/beautycoder/pflockscreen/views/PFKeyButton.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen.views;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.appcompat.widget.AppCompatTextView;
6 | import android.util.AttributeSet;
7 |
8 | /**
9 | * Created by aleksandr on 2018/02/14.
10 | */
11 |
12 | public class PFKeyButton extends AppCompatTextView {
13 |
14 | public PFKeyButton(Context context, AttributeSet attrs) {
15 |
16 | super(context, attrs);
17 | }
18 |
19 | public PFKeyButton(Context context, AttributeSet attrs, int defStyleAttr) {
20 | super(context, attrs, defStyleAttr);
21 | }
22 |
23 |
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/anim/cycle_7_pf.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/anim/shake_pf.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/drawable-hdpi/ic_fp_40px_pf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thealeksandr/PFLockScreen-Android/ff5b7cd11a7d055d469fed6905d117216356150d/pflockscreen/src/main/res/drawable-hdpi/ic_fp_40px_pf.png
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/drawable-hdpi/tile.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thealeksandr/PFLockScreen-Android/ff5b7cd11a7d055d469fed6905d117216356150d/pflockscreen/src/main/res/drawable-hdpi/tile.9.png
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/drawable-mdpi/ic_fp_40px_pf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thealeksandr/PFLockScreen-Android/ff5b7cd11a7d055d469fed6905d117216356150d/pflockscreen/src/main/res/drawable-mdpi/ic_fp_40px_pf.png
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/drawable-v21/ripple_selector_key_pf.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | -
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/drawable-v21/ripple_selector_side_pf.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | -
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | -
15 |
16 | -
17 |
18 |
19 |
20 | -
21 |
22 |
23 |
24 | -
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/drawable-v21/side_button_background_pf.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | -
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | -
15 |
16 | -
17 |
18 |
19 |
20 | -
21 |
22 |
23 |
24 | -
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/drawable-xhdpi/ic_fp_40px_pf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thealeksandr/PFLockScreen-Android/ff5b7cd11a7d055d469fed6905d117216356150d/pflockscreen/src/main/res/drawable-xhdpi/ic_fp_40px_pf.png
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/drawable-xxhdpi/ic_fp_40px_pf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thealeksandr/PFLockScreen-Android/ff5b7cd11a7d055d469fed6905d117216356150d/pflockscreen/src/main/res/drawable-xxhdpi/ic_fp_40px_pf.png
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/drawable-xxxhdpi/delete_lockscreen_pf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thealeksandr/PFLockScreen-Android/ff5b7cd11a7d055d469fed6905d117216356150d/pflockscreen/src/main/res/drawable-xxxhdpi/delete_lockscreen_pf.png
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/drawable-xxxhdpi/fingerprint_lockscreen_pf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thealeksandr/PFLockScreen-Android/ff5b7cd11a7d055d469fed6905d117216356150d/pflockscreen/src/main/res/drawable-xxxhdpi/fingerprint_lockscreen_pf.png
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/drawable-xxxhdpi/ic_fp_40px_pf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thealeksandr/PFLockScreen-Android/ff5b7cd11a7d055d469fed6905d117216356150d/pflockscreen/src/main/res/drawable-xxxhdpi/ic_fp_40px_pf.png
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/drawable/background_pf.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/drawable/circle_background_pf_lock_screen.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
9 |
12 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/drawable/circle_code_empty_pf_lockscreen.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
9 |
10 |
13 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/drawable/circle_code_fill_pf_lockscreen.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
9 |
10 |
12 |
13 |
16 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/drawable/circle_key_selector_pf.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/drawable/code_selector_pf.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
9 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/drawable/ic_fingerprint_error_pf.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
22 |
25 |
28 |
29 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/drawable/ic_fingerprint_success_pf.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
22 |
25 |
28 |
29 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/drawable/side_button_background_pf.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
10 |
12 |
14 |
16 |
18 |
19 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/drawable/touch_selector_pf.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
9 |
11 |
13 |
15 |
17 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/layout-land/fragment_lock_screen_pf.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
13 |
14 |
18 |
19 |
28 |
29 |
34 |
35 |
39 |
40 |
49 |
50 |
51 |
55 |
56 |
65 |
66 |
67 |
71 |
72 |
81 |
82 |
83 |
84 |
85 |
90 |
91 |
95 |
96 |
105 |
106 |
107 |
111 |
112 |
121 |
122 |
123 |
127 |
128 |
137 |
138 |
139 |
140 |
141 |
146 |
147 |
151 |
152 |
161 |
162 |
163 |
167 |
168 |
177 |
178 |
179 |
183 |
184 |
193 |
194 |
195 |
196 |
197 |
202 |
203 |
207 |
208 |
220 |
221 |
222 |
226 |
227 |
236 |
237 |
238 |
242 |
243 |
251 |
252 |
263 |
264 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
287 |
288 |
297 |
298 |
304 |
305 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/layout/fragment_lock_screen_pf.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
13 |
14 |
20 |
21 |
30 |
31 |
37 |
38 |
43 |
44 |
48 |
49 |
58 |
59 |
60 |
64 |
65 |
74 |
75 |
76 |
80 |
81 |
90 |
91 |
92 |
93 |
94 |
99 |
100 |
104 |
105 |
114 |
115 |
116 |
120 |
121 |
130 |
131 |
132 |
136 |
137 |
146 |
147 |
148 |
149 |
150 |
155 |
156 |
160 |
161 |
170 |
171 |
172 |
176 |
177 |
186 |
187 |
188 |
192 |
193 |
202 |
203 |
204 |
205 |
206 |
211 |
212 |
216 |
217 |
229 |
230 |
231 |
235 |
236 |
245 |
246 |
247 |
251 |
252 |
260 |
261 |
272 |
273 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
295 |
296 |
304 |
305 |
306 |
307 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/layout/view_code_pf_lockscreen.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/layout/view_pf_code_checkbox.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/layout/view_pf_fingerprint_dialog_container.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 |
14 |
15 |
26 |
27 |
33 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/layout/view_pf_fingerprint_dialog_content.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
20 |
21 |
22 |
32 |
33 |
45 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/values-hdpi/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | !---->
3 | 60dp
4 | 21dp
5 | 5dp
6 |
7 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/values-land-hdpi/dimen.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 50dp
4 |
5 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/values-land/dimen.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 60dp
4 | 15dp
5 |
6 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/values-ru/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/values-xhdpi/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 70dp
4 | 10dp
5 | 5dp
6 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #EE5555
4 | #663333
5 | #ffffff
6 | #66ffffff
7 | #42000000
8 | #f4511e
9 | #009688
10 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 70dp
4 | 25dp
5 | 10dp
6 | 5dp
7 |
8 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | PFLockScreen
3 | Cancel
4 | Use pin
5 | Sign in
6 | OK
7 | Next
8 | Forgot?
9 | Input pin code or use fingerprint
10 | Settings
11 | No fingerprints found. Please add fingerprints in the settings if you want to use this authorization method."
12 | "No fingerprints found"
13 |
14 | Fingerprint not recognized. Try again
15 | Fingerprint recognized
16 | Confirm fingerprint to continue
17 | Touch sensor
18 | Fingerprint icon
19 |
20 |
21 |
--------------------------------------------------------------------------------
/pflockscreen/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
13 |
14 |
18 |
19 |
23 |
24 |
25 |
29 |
30 |
33 |
34 |
39 |
40 |
43 |
44 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/pflockscreen/src/test/java/com/beautycoder/pflockscreen/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.beautycoder.pflockscreen;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':pflockscreen'
2 |
--------------------------------------------------------------------------------