diff --git a/.gitignore b/.gitignore index 2b75303..0dd88b1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,10 @@ *.iml .gradle +gradle.properties +/gradle /local.properties -/.idea/caches +/.idea /.idea/libraries -/.idea/modules.xml -/.idea/workspace.xml -/.idea/navEditor.xml -/.idea/assetWizardSettings.xml .DS_Store /build /captures diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml deleted file mode 100644 index 30aa626..0000000 --- a/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index f43d428..470c9ca 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -3,15 +3,14 @@ - - - + + diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..7bbddb9 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Amirhossein Naghshzan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..65e3c92 --- /dev/null +++ b/README.md @@ -0,0 +1,97 @@ +# Lock Screen + +Simple and beautiful Lock Screen library to set an check pin code. Integrated with fingerprint authentication. + +Easily secure your app with Lock Screen library as easy as starting an intent. + +With great animations for fingerprint authentication. + +Set Pin: + + + +Check Pin: + + + +Lock Screen gets a 4 digit pincode from user at first running time. After that every time that you start the intent, It asks +for pincode. + +Watching this repository will allow GitHub to email you whenever I publish a release. + +--- +# Gradle Dependency + +Add this line to your `build.gradle` project + +```java +compile 'com.amirarcane.lock-screen:lockscreen:2.0.0' +``` +--- +# Usage + + Just add Lock Screen activity to your manifest: + + ```java + + ``` + + Now easily start the Intent: + + ```java + Intent intent = new Intent(getContext(), EnterPinActivity.class); + startActivity(intent); + ``` + + That's it. As easy as piece of cake. + + At first run It checks if you entered pin before or not, If pin was set, It asks for Entering pin else It asks for + setting pincode. If you need to change pin or for any reason you want to set pin again just start the intent like below: + + ```java + Intent intent = EnterPinActivity.getIntent(getContext(), SET_PIN); + startActivity(intent); + ``` + +SET_PIN is boolean. + +If you need to handle back press of Lock Screen activity, just try onActivityResult: + + ```java + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + switch (requestCode) { + case REQUEST_CODE: + if (resultCode == EnterPinActivity.RESULT_BACK_PRESSED) { + Toast.makeText(MainActivity.this, "back pressed", Toast.LENGTH_LONG).show(); + } + break; + } + } + ``` + +See MainActivity class in Sample app for understanding how this library works. + +--- + # Customization + +If you need to set font for this library try like below: + +```java + Intent intent = EnterPinActivity.getIntent(getContext(), FONT_TEXT, FONT_NUMBERS); + startActivity(intent); + ``` + +FONT_TEXT and FONT_NUMBERS are path of your fonts in assets folder like "font/Arial.ttf" + +IF need set pin and changing fonts, do this: + +```java + Intent intent = EnterPinActivity.getIntent(getContext(), SET_PIN, FONT_TEXT, FONT_NUMBERS); + startActivity(intent); + ``` + +I customized PinLockView by andrognito for my Lock Screen view. In case of any further customization, fork the library +and change it. diff --git a/app/build.gradle b/app/build.gradle index 70ae9a0..e041404 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,12 +1,13 @@ apply plugin: 'com.android.application' -apply plugin: 'com.google.gms.google-services' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' android { - compileSdkVersion 28 + compileSdkVersion 27 defaultConfig { applicationId "com.example.capstone1" - minSdkVersion 15 - targetSdkVersion 28 + minSdkVersion 17 + targetSdkVersion 27 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -21,12 +22,11 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'com.android.support:appcompat-v7:28.0.0' + implementation 'com.android.support:appcompat-v7:27.1.1' implementation 'com.android.support.constraint:constraint-layout:1.1.3' - implementation 'com.google.firebase:firebase-database:16.0.4' - implementation 'com.google.firebase:firebase-core:16.0.9' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + compile project(':lockscreen') } -apply plugin: 'com.google.gms.google-services' \ No newline at end of file diff --git a/app/google-services.json b/app/google-services.json deleted file mode 100644 index 22b0d56..0000000 --- a/app/google-services.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "project_info": { - "project_number": "84508744097", - "firebase_url": "https://capstone2-master.firebaseio.com", - "project_id": "capstone2-master", - "storage_bucket": "capstone2-master.appspot.com" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "1:84508744097:android:40757009a34226ff", - "android_client_info": { - "package_name": "com.example.capstone1" - } - }, - "oauth_client": [ - { - "client_id": "84508744097-982jugik6epjonvvp1br822b6ms3hcth.apps.googleusercontent.com", - "client_type": 1, - "android_info": { - "package_name": "com.example.capstone1", - "certificate_hash": "fb92a04a4c94c0ad3003f1fa002823444eee5547" - } - }, - { - "client_id": "84508744097-6na5u3dma6qikva0jt6h9amvtdhn4l9p.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyCOBOBbn9giSQTfZFlK9QKS7qhhPm11et0" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "84508744097-6na5u3dma6qikva0jt6h9amvtdhn4l9p.apps.googleusercontent.com", - "client_type": 3 - } - ] - } - } - } - ], - "configuration_version": "1" -} \ No newline at end of file diff --git a/app/hputty-p0.66-t027-h004-installer.exe b/app/hputty-p0.66-t027-h004-installer.exe deleted file mode 100644 index 498022a..0000000 Binary files a/app/hputty-p0.66-t027-h004-installer.exe and /dev/null differ diff --git a/app/src/androidTest/java/com/example/capstone1/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/example/capstone3/ExampleInstrumentedTest.java similarity index 95% rename from app/src/androidTest/java/com/example/capstone1/ExampleInstrumentedTest.java rename to app/src/androidTest/java/com/example/capstone3/ExampleInstrumentedTest.java index 22d5be3..95a72eb 100644 --- a/app/src/androidTest/java/com/example/capstone1/ExampleInstrumentedTest.java +++ b/app/src/androidTest/java/com/example/capstone3/ExampleInstrumentedTest.java @@ -1,4 +1,4 @@ -package com.example.capstone1; +package com.example.capstone2; import android.content.Context; import android.support.test.InstrumentationRegistry; diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea33bb3..485ffc2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,8 @@ - + package="com.example.capstone3"> + + - - - - - + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/assets/capital.json b/app/src/main/assets/capital.json new file mode 100644 index 0000000..362827a --- /dev/null +++ b/app/src/main/assets/capital.json @@ -0,0 +1,23 @@ +[ + { + "id": 1, + "question": "대한민국의\n수도는?", + "choice1": "서울", + "choice2": "뉴욕", + "answer": "서울" + }, + { + "id": 2, + "question": "네팔의\n수도는?", + "choice1": "딜리", + "choice2": "카트만두", + "answer": "카트만두" + }, + { + "id": 3, + "question": "라오스의\n수도는?", + "choice1": "베이루트", + "choice2": "비엔티안", + "answer": "베이루트" + } +] \ No newline at end of file diff --git a/app/src/main/java/com/example/capstone1/SignUpActivity.java b/app/src/main/java/com/example/capstone1/SignUpActivity.java deleted file mode 100644 index a9f9a01..0000000 --- a/app/src/main/java/com/example/capstone1/SignUpActivity.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.example.capstone1; - -import android.support.annotation.NonNull; -import android.support.v7.app.AppCompatActivity; -import android.os.Bundle; -import android.view.View; -import android.widget.Button; -import android.widget.RadioButton; -import android.widget.RadioGroup; -import android.widget.TextView; -import android.widget.Toast; - -import com.google.firebase.database.DataSnapshot; -import com.google.firebase.database.DatabaseError; -import com.google.firebase.database.DatabaseReference; -import com.google.firebase.database.FirebaseDatabase; -import com.google.firebase.database.ValueEventListener; - -import java.util.Iterator; - -public class SignUpActivity extends AppCompatActivity { - - - private Button btnSignup; - private TextView Phone; - private TextView Partner; - private TextView Password; - private RadioGroup rg; - private int id; - private RadioButton rb; - - - - final FirebaseDatabase database = FirebaseDatabase.getInstance(); - final DatabaseReference databaseReference = database.getReference("users"); - - /* - private ValueEventListener checkRegister = new ValueEventListener() { - @Override - public void onDataChange(@NonNull DataSnapshot dataSnapshot) { - Iterator child = dataSnapshot.getChildren().iterator(); - while(child.hasNext()) { - if (Phone.getText().toString().equals(child.next().getKey())) { - Toast.makeText(getApplicationContext(),"가입된 번호입니다",Toast.LENGTH_LONG).show(); - databaseReference.removeEventListener(this); - return; - } - } - makeNewId(); - } - - @Override - public void onCancelled(@NonNull DatabaseError databaseError) { - - } - - }; - - - void makeNewId() { - btnSignup = (Button)findViewById(R.id.SignUpButton); - Phone = (TextView)findViewById(R.id.PhoneID); - Partner = (TextView)findViewById(R.id.PartnerID); - Password = (TextView)findViewById(R.id.PW1); - rg = (RadioGroup)findViewById(R.id.choice); - id = (int)rg.getCheckedRadioButtonId(); - rb = (RadioButton)findViewById(id); - databaseReference.child(Phone.getText().toString()).child("비밀번호").setValue(Password.getText().toString()); - databaseReference.child(Phone.getText().toString()).child("파트너").setValue(Partner.getText().toString()); - databaseReference.child(Phone.getText().toString()).child("분류").setValue(rb.getText().toString()); - Toast.makeText(getApplicationContext(), "Success", Toast.LENGTH_SHORT).show(); - } - */ - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_sign_up); - btnSignup = findViewById(R.id.SignUpButton); - btnSignup.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - //databaseReference.addListenerForSingleValueEvent(checkRegister); - Phone = (TextView)findViewById(R.id.PhoneID); - Partner = (TextView)findViewById(R.id.PartnerID); - Password = (TextView)findViewById(R.id.PW1); - rg = (RadioGroup)findViewById(R.id.choice); - id = (int)rg.getCheckedRadioButtonId(); - rb = (RadioButton)findViewById(id); - databaseReference.child(Phone.getText().toString()).child("비밀번호").setValue(Password.getText().toString()); - databaseReference.child(Phone.getText().toString()).child("파트너").setValue(Partner.getText().toString()); - databaseReference.child(Phone.getText().toString()).child("분류").setValue(rb.getText().toString()); - Toast.makeText(getApplicationContext(), "Success", Toast.LENGTH_SHORT).show(); - } - }); - - } -} diff --git a/app/src/main/java/com/example/capstone3/BootCompleteReceiver.kt b/app/src/main/java/com/example/capstone3/BootCompleteReceiver.kt new file mode 100644 index 0000000..48e0710 --- /dev/null +++ b/app/src/main/java/com/example/capstone3/BootCompleteReceiver.kt @@ -0,0 +1,34 @@ +package com.example.capstone3 + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.os.Build +import android.preference.PreferenceManager +import android.util.Log + +// BroadcastReceiver 를 상속받는다 +class BootCompleteReceiver : BroadcastReceiver() { + // 브로드캐스트 메세지 수신시 불리는 컬백 함수 + override fun onReceive(context: Context?, intent: Intent?) { + when { + // 부팅이 완료될때의 메세지인지 확인 + intent?.action == Intent.ACTION_BOOT_COMPLETED -> { + Log.d("quizlocker", "부팅이 완료됨") + context?.let { + // 퀴즈잠금화면 설정값이 ON 인지 확인 + val pref = PreferenceManager.getDefaultSharedPreferences(context) + val useLockScreen = pref.getBoolean("useLockScreen", false) + if (useLockScreen) { + // LockScreenService 시작 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + it.startForegroundService(Intent(context, LockScreenService::class.java)) + } else { + it.startService(Intent(context, LockScreenService::class.java)) + } + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/capstone3/FileExActivity.kt b/app/src/main/java/com/example/capstone3/FileExActivity.kt new file mode 100644 index 0000000..5087e29 --- /dev/null +++ b/app/src/main/java/com/example/capstone3/FileExActivity.kt @@ -0,0 +1,191 @@ +package com.example.capstone3 + +import android.Manifest +import android.content.Context +import android.content.pm.PackageManager +import android.os.Build +import android.os.Bundle +import android.os.Environment +import android.support.v4.app.ActivityCompat +import android.support.v4.content.ContextCompat +import android.support.v7.app.AppCompatActivity +import android.text.TextUtils +import android.widget.Toast +import kotlinx.android.synthetic.main.activity_file_ex.* +import java.io.File +import java.io.FileInputStream +import java.io.FileNotFoundException +import java.io.FileOutputStream + +class FileExActivity : AppCompatActivity() { + // 데이터 저장에 사용할 파일이름 + val filename = "data.txt" + // 권한이 있는지 저장하는 변수 + var granted = false + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_file_ex) + // 외부저장소의 권한을 동적으로 체크하는 함수를 호출 + checkPermission() + // 저장 버튼이 클릭된 경우 + saveButton.setOnClickListener { + // textField 의 현재 텍스트를 가져온다. + val text = textField.text.toString() + when { + // 텍스트가 비어있는 경우 오류 메세지를 보여준다. + TextUtils.isEmpty(text) -> { + Toast.makeText(applicationContext, "텍스트가 비어있습니다.", Toast.LENGTH_LONG).show() + } + !isExternalStorageWritable() -> { + Toast.makeText(applicationContext, "외부 저장장치가 없습니다.", Toast.LENGTH_LONG).show() + } + else -> { + // 내부 저장소 파일에 저장하는 함수 호출 + //saveToInnerStorage(text, filename) + // 외부 저장소 파일에 저장하는 함수 호출 + // saveToExternalStorage(text, filename) + + // 외부저장소"/sdcard/data.txt" 에 데이터를 저장 + saveToExternalCustomDirectory(text) + } + } + } + // 불러오기 버튼이 클릭된 경우 + loadButton.setOnClickListener { + try { + // textField 의 텍스트를 불러온 텍스트로 설정한다. + // textField.setText(loadFromInnerStorage(filename)) + // 외부저장소 앱전용 디렉토리의 파일에서 읽어온 데이터로 textField 의 텍스트를 설정 + // textField.setText(loadFromExternalStorage(filename)) + + // 외부저장소 "/sdcard/data.txt" 에서 데이터를 불러온다 + textField.setText(loadFromExternalCustomDirectory()) + } catch (e: FileNotFoundException) { + // 파일이 없는 경우 에러메세지 보여줌 + Toast.makeText(applicationContext, "저장된 텍스트가 없습니다.", Toast.LENGTH_LONG).show() + } + } + } + + // 내부저장소 파일의 텍스트를 저장한다. + fun saveToInnerStorage(text: String, filename: String) { + // 내부 저장소의 전달된 파일이름의 파일 출력 스트림을 가져온다. + val fileOutputStream = openFileOutput(filename, Context.MODE_PRIVATE) + // 파일 출력 스트림에 text 를 바이트로 변환하여 write 한다 + fileOutputStream.write(text.toByteArray()) + // 파일 출력 스트림을 닫는다 + fileOutputStream.close() + } + + // 내부저장소 파일의 텍스트를 불러온다 + fun loadFromInnerStorage(filename: String): String { + // 내부저장소의 전달된 파일이름의 파일 입력 스트림을 가져온다 + val fileInputStream = openFileInput(filename) + // 파일의 저장된 내용을 읽어 String 형태로 불러온다. + return fileInputStream.reader().readText() + } + + // 외부 저장장치를 사용할 수 있고 쓸수 있는지 체크하는 함수 + fun isExternalStorageWritable(): Boolean { + when { + // 외부저장장치 상태가 MEDIA_MONTED 면 사용 가능 + Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED -> return true + else -> return false + } + } + + // 외부저장장치에서 앱 전용데이터로 사용할 파일 객체를 반환하는 함수 + fun getAppDataFileFromExternalStorage(filename: String): File { + // KITKAT 버전 부터는 앱전용 디렉토리의 디렉토리 타입 상수인 Environment.DIRECTORY_DOCUMENTS 를 지원 + val dir = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS) + } else { + // 하위 버전에서는 직접 디렉토리 이름 입력 + File(Environment.getExternalStorageDirectory().absolutePath + "/Documents") + } + // 디렉토리의 경로중 없는 디렉토리가 있다면 생성한다. + dir?.mkdirs() + return File("${dir.absolutePath}${File.separator}${filename}") + } + + // 외부저장소 앱 전용 디렉토리에 파일로 저장하는 함수 + fun saveToExternalStorage(text: String, filename: String) { + val fileOutputStream = FileOutputStream(getAppDataFileFromExternalStorage(filename)) + fileOutputStream.write(text.toByteArray()) + fileOutputStream.close() + } + + // 외부저장소 앱 전용 디렉토리에서 파일 데이터를 불러오는 함수 + fun loadFromExternalStorage(filename: String): String { + return FileInputStream(getAppDataFileFromExternalStorage(filename)).reader().readText() + } + + // 권한요청시 사용할 요청 코드 + val MY_PERMISSION_REQUEST = 999 + + + // 권한 체크및 요청 함수 + fun checkPermission() { + val permissionCheck = ContextCompat.checkSelfPermission(this@FileExActivity, + Manifest.permission.WRITE_EXTERNAL_STORAGE) + when { + permissionCheck != PackageManager.PERMISSION_GRANTED -> { + // 권한을 요청 + ActivityCompat.requestPermissions( + this@FileExActivity, + arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), + MY_PERMISSION_REQUEST + ) + } + } + } + + // 권한요청 결과 컬백 함수 + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: + IntArray) { + when (requestCode) { + MY_PERMISSION_REQUEST -> { + when { + grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED -> { + // 권한 요청 성공 + granted = true + } + else -> { + // 사용자가 권한을 허용하지 않음 + granted = false + } + } + } + } + } + // 임의의 경로의 파일에 데이터를 저장하는 함수 + fun saveToExternalCustomDirectory(text: String, filepath: String = "/sdcard/data.txt") { + when { + // 권한이 있는 경우 + granted -> { + // 파라미터로 전달받은 경로의 파일의 출력 스트림 객체를 생성 + val fileOutputStream = FileOutputStream(File(filepath)) + fileOutputStream.write(text.toByteArray()) + fileOutputStream.close() + } + // 권한이 없는 경우 + else -> { + Toast.makeText(applicationContext, "권한이 없습니다.", Toast.LENGTH_SHORT).show() + } + } + } + // 임의의 경로에 파일에서 데이터를 읽는 함수 + fun loadFromExternalCustomDirectory(filepath: String = "/sdcard/data.txt"): String { + when { + // 권한이 있는 경우 + granted -> { + return FileInputStream(File(filepath)).reader().readText() + } + // 권한이 없는 경우 + else -> { + Toast.makeText(applicationContext, "권한이 없습니다.", Toast.LENGTH_SHORT).show() + return "" + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/capstone3/LockScreenService.kt b/app/src/main/java/com/example/capstone3/LockScreenService.kt new file mode 100644 index 0000000..ca74742 --- /dev/null +++ b/app/src/main/java/com/example/capstone3/LockScreenService.kt @@ -0,0 +1,76 @@ +package com.example.capstone3 + +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.Service +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.graphics.Color +import android.os.Build +import android.os.IBinder + +class LockScreenService : Service() { + // 화면이 꺼질때 브로드캐스트 메세지를 수신하는 리시버 + var receiver: ScreenOffReceiver? = null + private val ANDROID_CHANNEL_ID = "com.example.capstone1" + private val NOTIFICATION_ID = 9999 + // 서비스가 최초 생성될때 컬백 함수 + override fun onCreate() { + super.onCreate() + // 브로드캐스트 리시버가 null 인 경우에만 실행 + if (receiver == null) { + receiver = ScreenOffReceiver() + val filter = IntentFilter(Intent.ACTION_SCREEN_OFF) + registerReceiver(receiver, filter) + } + } + + // 서비스를 호출하는 클라이언트가 startService() 함수를 호출할때마다 불리는 컬백함수 + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + super.onStartCommand(intent, flags, startId) + if (intent != null) { + if (intent.action == null) { + // 서비스가 최초 실행이 아닌 경우 onCreate 가 불리지 않을 수 있음. + // 이 경우 receiver 가 널이면 새로 생성하고 등록한다. + if (receiver == null) { + receiver = ScreenOffReceiver() + val filter = IntentFilter( + Intent.ACTION_SCREEN_OFF) + registerReceiver(receiver, filter) + } + } + } + // 안드로이드 오레오 버전부터 백그라운드 제약이 있기 때문에 포그라운드 서비스를 실행해야함. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + // Notification(상단 알림) 채널 생성 + val chan = NotificationChannel(ANDROID_CHANNEL_ID, "MyService", NotificationManager.IMPORTANCE_NONE) + chan.lightColor = Color.BLUE + chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE + // Notification 서비스 객체를 가져옴 + val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + manager.createNotificationChannel(chan) + // Notification 알림 객체 생성 + val builder = Notification.Builder(this, ANDROID_CHANNEL_ID) + .setContentTitle(getString(R.string.app_name)) + .setContentText("SmartTracker Running") + val notification = builder.build() + // Notification 알림과 함께 포그라운드 서비스 시작 + startForeground(NOTIFICATION_ID, notification) + } + return Service.START_REDELIVER_INTENT + } + + override fun onDestroy() { + super.onDestroy() + // 서비스가 종료될때 브로드캐스트 리시버 등록도 해제 + if (receiver != null) { + unregisterReceiver(receiver) + } + } + + override fun onBind(intent: Intent): IBinder? {4 + return null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/capstone1/LoginActivity.java b/app/src/main/java/com/example/capstone3/LoginActivity.java similarity index 63% rename from app/src/main/java/com/example/capstone1/LoginActivity.java rename to app/src/main/java/com/example/capstone3/LoginActivity.java index 852fd84..45d8e8b 100644 --- a/app/src/main/java/com/example/capstone1/LoginActivity.java +++ b/app/src/main/java/com/example/capstone3/LoginActivity.java @@ -1,4 +1,4 @@ -package com.example.capstone1; +package com.example.capstone3; import android.content.Intent; import android.support.v7.app.AppCompatActivity; @@ -7,6 +7,9 @@ import android.widget.Button; import android.widget.ImageButton; +import com.example.capstone3.MainActivity; +import com.example.capstone3.SignUpActivity; + public class LoginActivity extends AppCompatActivity { @@ -22,25 +25,35 @@ protected void onCreate(Bundle savedInstanceState) { @Override public void onClick(View v) { Intent intent = new Intent( - getApplicationContext(),MainActivity.class); + getApplicationContext(), MainActivity.class); startActivity(intent); } }); + Button button9 = findViewById(R.id.button9); + button9.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent( + getApplicationContext(), Main2Activity.class); + startActivity(intent); + } + }); + Button SignUpButton = findViewById(R.id.SignUpButton); SignUpButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent( - getApplicationContext(),SignUpActivity.class); + getApplicationContext(), SignUpActivity.class); startActivity(intent); } }); - ImageButton FirebaseButton = findViewById(R.id.FirebaseButton); + Button FirebaseButton = findViewById(R.id.FirebaseButton); FirebaseButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent( - getApplicationContext(),ManagementActivity.class); + getApplicationContext(),locker.class); startActivity(intent); } }); diff --git a/app/src/main/java/com/example/capstone3/Main2Activity.kt b/app/src/main/java/com/example/capstone3/Main2Activity.kt new file mode 100644 index 0000000..79ac146 --- /dev/null +++ b/app/src/main/java/com/example/capstone3/Main2Activity.kt @@ -0,0 +1,65 @@ +package com.example.capstone3 +import android.content.Intent +import android.os.Build +import android.os.Bundle +import android.preference.MultiSelectListPreference +import android.preference.PreferenceFragment +import android.preference.SwitchPreference +import android.support.v7.app.AppCompatActivity + +class Main2Activity : AppCompatActivity() { + val fragment = MyPreferenceFragment() + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main2) + // preferenceContent FrameLayout 영역을 PreferenceFragment 로 교체 + fragmentManager.beginTransaction().replace(R.id.preferenceContent, fragment).commit() + } + + class MyPreferenceFragment : PreferenceFragment() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + // 환경설정 리소스 파일 적용 + addPreferencesFromResource(R.xml.pref) + // 퀴즈 종류 요약정보에, 현재 선택된 항목을 보여주는 코드 + val categoryPref = findPreference("category") as MultiSelectListPreference + categoryPref.summary = categoryPref.values.joinToString(", ") + // 환경설정 정보값이 변경될때에도 요약정보를 변경하도록 리스너 등록 + categoryPref.setOnPreferenceChangeListener { preference, newValue -> + // newValue 파라미터가 HashSet 으로 캐스팅이 실패하면 리턴 + val newValueSet = newValue as? HashSet<*> + ?: return@setOnPreferenceChangeListener true + // 선택된 퀴즈종류로 요약정보 보여줌 + categoryPref.summary = newValue.joinToString(", ") + true + } +// 퀴즈 잠금화면 사용 스위치 객체 가져옴 + val useLockScreenPref = findPreference("useLockScreen") as SwitchPreference + // 클릭되었을때의 이벤트 리스너 코드 작성 + useLockScreenPref.setOnPreferenceClickListener { + when { + // 퀴즈 잠금화면 사용이 체크된 경우 LockScreenService 실행 + useLockScreenPref.isChecked -> { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + activity.startForegroundService(Intent(activity, LockScreenService::class.java)) + } else { + activity.startService(Intent(activity, LockScreenService::class.java)) + } + } + // 퀴즈 잠금화면 사용이 체크 해제된 경우 LockScreenService 중단 + else -> activity.stopService(Intent(activity, LockScreenService::class.java)) + } + true + } + // 앱이 시작 되었을때 이미 퀴즈잠금화면 사용이 체크되어있으면 서비스 실행 + if (useLockScreenPref.isChecked) { + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + activity.startForegroundService(Intent(activity, LockScreenService::class.java)) + } else { + activity.startService(Intent(activity, LockScreenService::class.java)) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/capstone1/MainActivity.java b/app/src/main/java/com/example/capstone3/MainActivity.java similarity index 84% rename from app/src/main/java/com/example/capstone1/MainActivity.java rename to app/src/main/java/com/example/capstone3/MainActivity.java index b477405..fce35a0 100644 --- a/app/src/main/java/com/example/capstone1/MainActivity.java +++ b/app/src/main/java/com/example/capstone3/MainActivity.java @@ -1,4 +1,4 @@ -package com.example.capstone1; +package com.example.capstone3; import android.content.Intent; import android.support.v7.app.AppCompatActivity; @@ -6,8 +6,10 @@ import android.view.View; import android.widget.Button; -import com.example.capstone1.MenuActivity; -import com.example.capstone1.R; +import com.example.capstone3.ManagementActivity; +import com.example.capstone3.MenuActivity; +import com.example.capstone3.R; +import com.example.capstone3.subActivity; public class MainActivity extends AppCompatActivity { @@ -22,7 +24,7 @@ protected void onCreate(Bundle savedInstanceState) { button4.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { - Intent intent = new Intent(getApplicationContext(),ManagementActivity.class); + Intent intent = new Intent(getApplicationContext(), ManagementActivity.class); intent.putExtra("name","학생관리"); startActivity(intent); @@ -31,23 +33,20 @@ public void onClick(View v) { button5.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { - Intent intent = new Intent(getApplicationContext(),MenuActivity.class); + Intent intent = new Intent(getApplicationContext(), MenuActivity.class); intent.putExtra("name","미션관리"); startActivity(intent); - finish(); - } }); button6.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { - Intent intent = new Intent(getApplicationContext(),ManagementActivity.class); + Intent intent = new Intent(getApplicationContext(), subActivity.class); intent.putExtra("name","용돈관리"); startActivity(intent); - finish(); } }); diff --git a/app/src/main/java/com/example/capstone1/ManagementActivity.java b/app/src/main/java/com/example/capstone3/ManagementActivity.java similarity index 91% rename from app/src/main/java/com/example/capstone1/ManagementActivity.java rename to app/src/main/java/com/example/capstone3/ManagementActivity.java index e04e20a..b77a49f 100644 --- a/app/src/main/java/com/example/capstone1/ManagementActivity.java +++ b/app/src/main/java/com/example/capstone3/ManagementActivity.java @@ -1,4 +1,4 @@ -package com.example.capstone1; +package com.example.capstone3; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; diff --git a/app/src/main/java/com/example/capstone1/MenuActivity.java b/app/src/main/java/com/example/capstone3/MenuActivity.java similarity index 95% rename from app/src/main/java/com/example/capstone1/MenuActivity.java rename to app/src/main/java/com/example/capstone3/MenuActivity.java index 35736ac..a2cf722 100644 --- a/app/src/main/java/com/example/capstone1/MenuActivity.java +++ b/app/src/main/java/com/example/capstone3/MenuActivity.java @@ -1,4 +1,4 @@ -package com.example.capstone1; +package com.example.capstone3; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; diff --git a/app/src/main/java/com/example/capstone3/ParentActivity.java b/app/src/main/java/com/example/capstone3/ParentActivity.java new file mode 100644 index 0000000..a490892 --- /dev/null +++ b/app/src/main/java/com/example/capstone3/ParentActivity.java @@ -0,0 +1,13 @@ +package com.example.capstone3; + +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; + +public class ParentActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_parent); + } +} diff --git a/app/src/main/java/com/example/capstone3/PrefExActivity.kt b/app/src/main/java/com/example/capstone3/PrefExActivity.kt new file mode 100644 index 0000000..790a458 --- /dev/null +++ b/app/src/main/java/com/example/capstone3/PrefExActivity.kt @@ -0,0 +1,34 @@ +package com.example.capstone3 + +import android.content.Context +import android.os.Bundle +import android.support.v7.app.AppCompatActivity +import kotlinx.android.synthetic.main.activity_pref_ex.* + +class PrefExActivity : AppCompatActivity() { + // nameField 의 데이터를 저장할 Key + val nameFieldKey = "nameField" + // pushCheckBox 의 데이터를 저장할 Key + val pushCheckBoxKey = "pushCheckBox" + // shared preference 객체, Activity 초기화 이후에 사용해야 하기 때문에 lazy 위임을 사용 + val preference by lazy { getSharedPreferences("PrefExActivity", Context.MODE_PRIVATE) } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_pref_ex) + // 저장 버튼이 클릭된 경우 + saveButton.setOnClickListener { + // SharedPreference 에서 nameFieldKey 키값으로 nameField 의 현재 텍스트를 저장한다. + preference.edit().putString(nameFieldKey, nameField.text.toString()).apply() + // SharedPreference 에서 pushCheckBoxKey 키값으로 체크 박스의 현재 체크 상태를 저장한다. + preference.edit().putBoolean(pushCheckBoxKey, pushCheckBox.isChecked).apply() + } + // 불러오기 버튼이 클릭된 경우 + loadButton.setOnClickListener { + // SharedPreference 에서 nameFieldKey 로 저장된 문자열을 불러와 nameField 의 텍스트로 설정 + nameField.setText(preference.getString(nameFieldKey, "")) + // SharedPreference 에서 pushCheckBoxKey 키값으로 불린값을 불러와 pushCheckBox 의 체크상태를 설정 + pushCheckBox.isChecked = preference.getBoolean(pushCheckBoxKey, false) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/capstone3/PrefFragmentActivity.kt b/app/src/main/java/com/example/capstone3/PrefFragmentActivity.kt new file mode 100644 index 0000000..e6b419a --- /dev/null +++ b/app/src/main/java/com/example/capstone3/PrefFragmentActivity.kt @@ -0,0 +1,23 @@ +package com.example.capstone3 + +import android.os.Bundle +import android.preference.PreferenceFragment +import android.support.v7.app.AppCompatActivity + +class PrefFragmentActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_pref_fragment) + // 액티비티의 컨텐트 뷰를 MyPrefFragment 로 교체한다 + fragmentManager.beginTransaction().replace(android.R.id.content, MyPrefFragment()).commit() + } + + // PreferenceFragment: XML 로 작성한 Preference 를 UI 로 보여주는 클래스 + class MyPrefFragment : PreferenceFragment() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + // Preference 정보가 있는 XML 파일 지정 + addPreferencesFromResource(R.xml.ex_pref) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/capstone3/QuizLockerActivity.kt b/app/src/main/java/com/example/capstone3/QuizLockerActivity.kt new file mode 100644 index 0000000..40e7f9e --- /dev/null +++ b/app/src/main/java/com/example/capstone3/QuizLockerActivity.kt @@ -0,0 +1,146 @@ +package com.example.capstone3 + +import android.app.KeyguardManager +import android.content.Context +import android.content.Intent +import android.os.Build +import android.os.Bundle +import android.os.VibrationEffect +import android.os.Vibrator +import android.support.v7.app.AppCompatActivity +import android.view.KeyEvent +import android.view.WindowManager +import android.widget.SeekBar +import com.example.capstone2.activity.EnterPinActivity +import kotlinx.android.synthetic.main.activity_quiz_locker.* +import org.json.JSONArray +import org.json.JSONObject +import java.util.* + +class QuizLockerActivity : AppCompatActivity() { + var quiz: JSONObject? = null + // 정답횟수 저장 SharedPreference + val wrongAnswerPref by lazy { getSharedPreferences("wrongAnswer", Context.MODE_PRIVATE) } + // 오답횟수 저장 SharedPreference + val correctAnswerPref by lazy { getSharedPreferences("correctAnswer", Context.MODE_PRIVATE) } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + // 잠금화면보다 상단에 위치하기 위한 설정 조정. 버전별로 사용법이 다르기 때문에 버전에 따라 적용 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { + // 잠금화면에서 보여지도록 설정 + setShowWhenLocked(true) + // 잠금 해제 + val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager + keyguardManager.requestDismissKeyguard(this, null) + } else { + // 잠금화면에서 보여지도록 설정 + window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); + // 기본 잠금화면을 해제 + window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); + } + // 화면을 켜진 상태로 유지 + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); +// setContentView(R.layout.activity_enterpin ) + var intent = Intent(this,EnterPinActivity::class.java) + startActivity(intent) + // 퀴즈 데이터를 가져온다. +// val json = assets.open("capital.json").reader().readText() +// val quizArray = JSONArray(json) +// // 퀴즈를 선택한다. +// quiz = quizArray.getJSONObject(Random().nextInt(quizArray.length())) +// // 퀴즈를 보여준다. +// quizLabel.text = quiz?.getString("question") +// choice1.text = quiz?.getString("choice1") +// choice2.text = quiz?.getString("choice2") +// // 정답횟수 오답횟수를 보여준다. +// val id = quiz?.getInt("id").toString() ?: "" +// correctCountLabel.text = "정답횟수:${correctAnswerPref.getInt(id, 0)}" +// wrongCountLabel.text = "오답횟수: ${wrongAnswerPref.getInt(id, 0)}" +// // SeekBar 의 값이 변경될때 불리는 리스너 +// seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { +// // progress 값이 변경될때 불리는 함수 +// override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { +// when { +// // SeekBar 의 우측 끝으로 가면 choice2 를 선택한 것으로 간주한다 +// progress > 95 -> { +// leftImageView.setImageResource(R.drawable.padlock) +// // 우측 이미지뷰의 자물쇠 아이콘을 열림 아이콘으로 변경 +// rightImageView.setImageResource(R.drawable.unlock) +// } +// // SeekBar 의 좌측 끝으로 가면 choice1 을 선택한 것으로 간주한다 +// progress < 5 -> { +// // 좌측 이미지뷰의 자물쇠 아이콘을 열림 아이콘으로 변경 +// leftImageView.setImageResource(R.drawable.unlock) +// rightImageView.setImageResource(R.drawable.padlock) +// } +// // 양쪽 끝이 아닌 경우 +// else -> { +// // 양쪽 이미지를 모두 잠금 아이콘으로 변경 +// leftImageView.setImageResource(R.drawable.padlock) +// rightImageView.setImageResource(R.drawable.padlock) +// } +// } +// } +// +// override fun onStartTrackingTouch(seekBar: SeekBar?) { +// } +// +// // 터치 조작을 끝낸 경우 불리는 함수 +// override fun onStopTrackingTouch(seekBar: SeekBar?) { +// val progress = seekBar?.progress ?: 50 +// when { +// // 우측 끝의 답을 선택한 경우 +// progress > 95 -> checkChoice(quiz?.getString("choice2") ?: "") +// // 좌측 끝의 답을 선택한 경우 +// progress < 5 -> checkChoice(quiz?.getString("choice1") ?: "") +// // 양끝이 아닌 경우 seekBar 의 progress 를 중앙값으로 초기화 +// else -> seekBar?.progress = 50 +// } +// } +// }) +// } +// +// // 정답 체크 함수 +// fun checkChoice(choice: String) { +// quiz?.let { +// when { +// // choice 의 텍스트가 정답 텍스트와 같으면 Activity 종료 +// choice == it.getString("answer") -> { +// // 정답인 경우 정답횟수 증가 +// val id = it.getInt("id").toString() +// var count = correctAnswerPref.getInt(id, 0) +// count++ +// correctAnswerPref.edit().putInt(id, count).apply() +// correctCountLabel.text = "정답횟수: ${count}" +// // Activity 종료 +// finish() +// } +// else -> { +// // 오답 횟수 증가 +// val id = it.getInt("id").toString() +// var count = wrongAnswerPref.getInt(id, 0) +// count++ +// wrongAnswerPref.edit().putInt(id, count).apply() +// wrongCountLabel.text = "오답횟수: ${count}" +// // 정답이 아닌경우 UI 초기화 +// leftImageView.setImageResource(R.drawable.padlock) +// rightImageView.setImageResource(R.drawable.padlock) +// seekBar?.progress = 50 +// // 정답이 아닌 경우 진동알림 추가 +// val vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator +// // SDK 버전에 따라 호출 +// if (Build.VERSION.SDK_INT >= 26) { +// // 1초동안 100의 세기(최고 255) 로 1회 진동 +// vibrator.vibrate(VibrationEffect.createOneShot(1000, 100)) +// } else { +// // 1초동안 진동 +// vibrator.vibrate(1000) +// KeyEvent.KEYCODE_HOME +// } +// } +// } +// } +// } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/capstone3/ScreenOffExActivity.kt b/app/src/main/java/com/example/capstone3/ScreenOffExActivity.kt new file mode 100644 index 0000000..ada6016 --- /dev/null +++ b/app/src/main/java/com/example/capstone3/ScreenOffExActivity.kt @@ -0,0 +1,21 @@ +package com.example.capstone3 +import android.content.Intent +import android.content.IntentFilter +import android.os.Bundle +import android.support.v7.app.AppCompatActivity + +class ScreenOffExActivity : AppCompatActivity() { + // ScreenOffReceiver 객체 + var screenOffReceiver: ScreenOffReceiver? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_screen_off_ex) + // screenOffReceiver 가 널인 경우에만 screenOffReceiver 를 생성하고 등록한다. + if (screenOffReceiver == null) { + screenOffReceiver = ScreenOffReceiver() + val intentFilter = IntentFilter(Intent.ACTION_SCREEN_OFF) + registerReceiver(screenOffReceiver, intentFilter) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/capstone3/ScreenOffReceiver.kt b/app/src/main/java/com/example/capstone3/ScreenOffReceiver.kt new file mode 100644 index 0000000..fa36afc --- /dev/null +++ b/app/src/main/java/com/example/capstone3/ScreenOffReceiver.kt @@ -0,0 +1,21 @@ +package com.example.capstone3 + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent + +class ScreenOffReceiver : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + when { + intent?.action == Intent.ACTION_SCREEN_OFF -> { + // 화면이 꺼지면 QuizLockerActivity 를 실행한다. + val intent = Intent(context, QuizLockerActivity::class.java) + // 새로운 Activity 로 실행 + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + // 기존의 Activity 스택을 제거 + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + context?.startActivity(intent) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/capstone3/SignUpActivity.java b/app/src/main/java/com/example/capstone3/SignUpActivity.java new file mode 100644 index 0000000..d1955ec --- /dev/null +++ b/app/src/main/java/com/example/capstone3/SignUpActivity.java @@ -0,0 +1,13 @@ +package com.example.capstone3; + +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; + +public class SignUpActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_sign_up); + } +} diff --git a/app/src/main/java/com/example/capstone3/locker.java b/app/src/main/java/com/example/capstone3/locker.java new file mode 100644 index 0000000..d4ea1f2 --- /dev/null +++ b/app/src/main/java/com/example/capstone3/locker.java @@ -0,0 +1,88 @@ +package com.example.capstone3; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.Button; +import android.widget.Toast; + +import com.example.capstone2.activity.EnterPinActivity; +import com.example.capstone3.R; + +public class locker extends AppCompatActivity { + + private static final String FONT_TEXT = "font/ALEAWB.TTF"; + private static final String FONT_NUMBER = "font/BLKCHCRY.TTF"; + + private static final int REQUEST_CODE = 123; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_locker); + + Button normal = (Button) findViewById(R.id.normal); + Button setPin = (Button) findViewById(R.id.setPin); + Button setFont = (Button) findViewById(R.id.setFont); + Button setPinAndFont = (Button) findViewById(R.id.setPinAndFont); + + normal.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + // start the activity, It handles the setting and checking + Intent intent = new Intent(locker.this, EnterPinActivity.class); +// startActivity(intent); + + // for handling back press + startActivityForResult(intent, REQUEST_CODE); + } + }); + + setPin.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + // set pin instead of checking it + Intent intent = EnterPinActivity.getIntent(locker.this, true); + startActivity(intent); + + } + }); + + setFont.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + // setting font for library + Intent intent = EnterPinActivity.getIntent(locker.this, FONT_TEXT, FONT_NUMBER); + startActivity(intent); + } + }); + + setPinAndFont.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + // setting font for library and set pin instead of checking it + Intent intent = EnterPinActivity.getIntent(locker.this, true, FONT_TEXT, FONT_NUMBER); + startActivity(intent); + } + }); + + + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + switch (requestCode) { + case REQUEST_CODE: + if (resultCode == EnterPinActivity.RESULT_BACK_PRESSED) { + Toast.makeText(locker.this, "back pressed", Toast.LENGTH_LONG).show(); + } + break; + } + } +} diff --git a/app/src/main/java/com/example/capstone3/subActivity.java b/app/src/main/java/com/example/capstone3/subActivity.java new file mode 100644 index 0000000..439f7f5 --- /dev/null +++ b/app/src/main/java/com/example/capstone3/subActivity.java @@ -0,0 +1,14 @@ +package com.example.capstone3; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; + + +public class subActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_sub); + } +} diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml index 1f6bb29..c7bd21d 100644 --- a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -2,13 +2,13 @@ xmlns:aapt="http://schemas.android.com/aapt" android:width="108dp" android:height="108dp" - android:viewportWidth="108" - android:viewportHeight="108"> + android:viewportHeight="108" + android:viewportWidth="108"> + android:strokeColor="#00000000" + android:strokeWidth="1"> + android:strokeColor="#00000000" + android:strokeWidth="1" /> diff --git a/app/src/main/res/drawable/actions.xml b/app/src/main/res/drawable/actions.xml new file mode 100644 index 0000000..f4d661e --- /dev/null +++ b/app/src/main/res/drawable/actions.xml @@ -0,0 +1,23 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml index 0d025f9..d5fccc5 100644 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -2,169 +2,169 @@ + android:viewportHeight="108" + android:viewportWidth="108"> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> diff --git a/app/src/main/res/drawable/padlock.jpg b/app/src/main/res/drawable/padlock.jpg new file mode 100644 index 0000000..89ec375 Binary files /dev/null and b/app/src/main/res/drawable/padlock.jpg differ diff --git a/app/src/main/res/drawable/progress.xml b/app/src/main/res/drawable/progress.xml new file mode 100644 index 0000000..a6a1dd8 --- /dev/null +++ b/app/src/main/res/drawable/progress.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/sbubble.9.png b/app/src/main/res/drawable/sbubble.9.png new file mode 100644 index 0000000..24c0c27 Binary files /dev/null and b/app/src/main/res/drawable/sbubble.9.png differ diff --git a/app/src/main/res/drawable/sbubble2.png b/app/src/main/res/drawable/sbubble2.png new file mode 100644 index 0000000..7932432 Binary files /dev/null and b/app/src/main/res/drawable/sbubble2.png differ diff --git a/app/src/main/res/drawable/slide.png b/app/src/main/res/drawable/slide.png new file mode 100644 index 0000000..4a1dc27 Binary files /dev/null and b/app/src/main/res/drawable/slide.png differ diff --git a/app/src/main/res/drawable/unlock.jpg b/app/src/main/res/drawable/unlock.jpg new file mode 100644 index 0000000..d02255e Binary files /dev/null and b/app/src/main/res/drawable/unlock.jpg differ diff --git a/app/src/main/res/layout/activity_file_ex.xml b/app/src/main/res/layout/activity_file_ex.xml new file mode 100644 index 0000000..5c70060 --- /dev/null +++ b/app/src/main/res/layout/activity_file_ex.xml @@ -0,0 +1,54 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_locker.xml b/app/src/main/res/layout/activity_locker.xml new file mode 100644 index 0000000..b122b99 --- /dev/null +++ b/app/src/main/res/layout/activity_locker.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index 760a73e..dba6004 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -18,7 +18,7 @@ android:layout_gravity="center_horizontal" /> - + + + android:text="비번설정" /> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 8bcb2ec..70c8328 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -16,8 +16,8 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_menu.xml b/app/src/main/res/layout/activity_menu.xml index bfa29be..410900c 100644 --- a/app/src/main/res/layout/activity_menu.xml +++ b/app/src/main/res/layout/activity_menu.xml @@ -71,5 +71,4 @@ /> - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_parent.xml b/app/src/main/res/layout/activity_parent.xml new file mode 100644 index 0000000..d5a743a --- /dev/null +++ b/app/src/main/res/layout/activity_parent.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_pref_ex.xml b/app/src/main/res/layout/activity_pref_ex.xml new file mode 100644 index 0000000..38612e5 --- /dev/null +++ b/app/src/main/res/layout/activity_pref_ex.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_pref_fragment.xml b/app/src/main/res/layout/activity_pref_fragment.xml new file mode 100644 index 0000000..8de0e53 --- /dev/null +++ b/app/src/main/res/layout/activity_pref_fragment.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_quiz_locker.xml b/app/src/main/res/layout/activity_quiz_locker.xml new file mode 100644 index 0000000..ecb47f5 --- /dev/null +++ b/app/src/main/res/layout/activity_quiz_locker.xml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_screen_off_ex.xml b/app/src/main/res/layout/activity_screen_off_ex.xml new file mode 100644 index 0000000..7898ca3 --- /dev/null +++ b/app/src/main/res/layout/activity_screen_off_ex.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_sign_up.xml b/app/src/main/res/layout/activity_sign_up.xml index dda687f..d4666be 100644 --- a/app/src/main/res/layout/activity_sign_up.xml +++ b/app/src/main/res/layout/activity_sign_up.xml @@ -22,7 +22,7 @@ android:text="핸드폰번호" /> + android:text="가입유형" /> - + android:layout_weight="1" + android:text="학생" /> - - - - + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 499fdbe..70c0d43 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,10 @@ Capstone1 + + + + 일반상식 + 역사 + 수도 + diff --git a/app/src/main/res/xml/ex_pref.xml b/app/src/main/res/xml/ex_pref.xml new file mode 100644 index 0000000..1df3d4a --- /dev/null +++ b/app/src/main/res/xml/ex_pref.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/pref.xml b/app/src/main/res/xml/pref.xml new file mode 100644 index 0000000..0c3c2ab --- /dev/null +++ b/app/src/main/res/xml/pref.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/app/src/test/java/com/example/capstone1/ExampleUnitTest.java b/app/src/test/java/com/example/capstone3/ExampleUnitTest.java similarity index 91% rename from app/src/test/java/com/example/capstone1/ExampleUnitTest.java rename to app/src/test/java/com/example/capstone3/ExampleUnitTest.java index d951a4e..817fd63 100644 --- a/app/src/test/java/com/example/capstone1/ExampleUnitTest.java +++ b/app/src/test/java/com/example/capstone3/ExampleUnitTest.java @@ -1,4 +1,4 @@ -package com.example.capstone1; +package com.example.capstone2; import org.junit.Test; diff --git a/build.gradle b/build.gradle index b46dcc6..64f6c73 100644 --- a/build.gradle +++ b/build.gradle @@ -1,14 +1,17 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { + ext.kotlin_version = '1.3.31' repositories { - google() jcenter() - + mavenCentral() + google() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.2' - classpath 'com.google.gms:google-services:4.2.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath 'com.android.tools.build:gradle:3.4.1' + classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4' + classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -17,9 +20,8 @@ buildscript { allprojects { repositories { - google() jcenter() - + google() } } diff --git a/demo/check.gif b/demo/check.gif new file mode 100644 index 0000000..2782db7 Binary files /dev/null and b/demo/check.gif differ diff --git a/demo/set.gif b/demo/set.gif new file mode 100644 index 0000000..6ea59ae Binary files /dev/null and b/demo/set.gif differ diff --git a/gradle.properties b/gradle.properties deleted file mode 100644 index 82618ce..0000000 --- a/gradle.properties +++ /dev/null @@ -1,15 +0,0 @@ -# Project-wide Gradle settings. -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx1536m -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true - - diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 130c63c..299e33b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,4 @@ -#Thu May 16 15:07:15 KST 2019 +#Fri May 24 22:38:50 GMT+09:00 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/lockscreen/.gitignore b/lockscreen/.gitignore new file mode 100644 index 0000000..0dd88b1 --- /dev/null +++ b/lockscreen/.gitignore @@ -0,0 +1,11 @@ +*.iml +.gradle +gradle.properties +/gradle +/local.properties +/.idea +/.idea/libraries +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/lockscreen/build.gradle b/lockscreen/build.gradle new file mode 100644 index 0000000..bd3b54c --- /dev/null +++ b/lockscreen/build.gradle @@ -0,0 +1,143 @@ +apply plugin: 'com.android.library' +apply plugin: 'com.github.dcendents.android-maven' +apply plugin: 'com.jfrog.bintray' + +android { + compileSdkVersion 27 + defaultConfig { + minSdkVersion 17 + targetSdkVersion 27 + versionCode 3 + versionName "2.0.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + exclude group: 'com.android.support', module: 'support-annotations' + }) + compile 'com.android.support:appcompat-v7:27.1.1' + compile 'com.android.support.constraint:constraint-layout:1.0.2' + testCompile 'junit:junit:4.12' + + compile 'com.android.support:appcompat-v7:27.1.1' + compile 'com.android.support:recyclerview-v7:27.1.1' +} + +ext { + bintrayRepo = 'maven' + bintrayName = 'lockscreen' + + publishedGroupId = 'com.amirarcane.lock-screen' + libraryName = 'lockscreen' + artifact = 'lockscreen' + + libraryDescription = 'Simple and beautiful Lock Screen library to set an check pin code. Integrated with fingerprint authentication.' + + siteUrl = 'https://github.com/amirarcane/lock-screen' + gitUrl = 'https://github.com/amirarcane/rlock-screen.git' + + libraryVersion = '2.0.0' + + developerId = 'amirarcane' + developerName = 'Amirhossein Naghshzan' + developerEmail = 'n.naghshzan@gmail.com' + + licenseName = 'MIT' + licenseUrl = 'https://github.com/amirarcane/lock_screen/blob/master/LICENSE.txt' + allLicenses = ["MIT"] +} + + +group = publishedGroupId // Maven Group ID for the artifact + +install { + repositories.mavenInstaller { + // This generates POM.xml with proper parameters + pom { + project { + packaging 'aar' + groupId publishedGroupId + artifactId artifact + + // Add your description here + name libraryName + description libraryDescription + url siteUrl + + // Set your license + licenses { + license { + name licenseName + url licenseUrl + } + } + developers { + developer { + id developerId + name developerName + email developerEmail + } + } + scm { + connection gitUrl + developerConnection gitUrl + url siteUrl + + } + } + } + } +} + +version = libraryVersion + +task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier = 'sources' +} + +task javadoc(type: Javadoc) { + //source = android.sourceSets.main.java.srcDirs + //classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) +} + +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} +artifacts { + archives javadocJar + archives sourcesJar +} + +// Bintray +Properties properties = new Properties() +properties.load(project.rootProject.file('local.properties').newDataInputStream()) + +bintray { + user = properties.getProperty("bintray.user") + key = properties.getProperty("bintray.apikey") + + configurations = ['archives'] + pkg { + repo = bintrayRepo + name = bintrayName + desc = libraryDescription + websiteUrl = siteUrl + vcsUrl = gitUrl + licenses = allLicenses + publish = true + publicDownloadNumbers = true + version { + desc = libraryDescription + } + } +} diff --git a/lockscreen/gradlew b/lockscreen/gradlew new file mode 100644 index 0000000..cccdd3d --- /dev/null +++ b/lockscreen/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/lockscreen/gradlew.bat b/lockscreen/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/lockscreen/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/lockscreen/proguard-rules.pro b/lockscreen/proguard-rules.pro new file mode 100644 index 0000000..5e4b346 --- /dev/null +++ b/lockscreen/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in C:\Users\pc\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/lockscreen/src/androidTest/java/com/example/capstone2/ExampleInstrumentedTest.java b/lockscreen/src/androidTest/java/com/example/capstone2/ExampleInstrumentedTest.java new file mode 100644 index 0000000..1e48620 --- /dev/null +++ b/lockscreen/src/androidTest/java/com/example/capstone2/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.example.capstone2; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumentation test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.amirarcane.lockscreen", appContext.getPackageName()); + } +} diff --git a/lockscreen/src/main/AndroidManifest.xml b/lockscreen/src/main/AndroidManifest.xml new file mode 100644 index 0000000..fc88378 --- /dev/null +++ b/lockscreen/src/main/AndroidManifest.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/lockscreen/src/main/java/com/example/capstone2/activity/EnterPinActivity.java b/lockscreen/src/main/java/com/example/capstone2/activity/EnterPinActivity.java new file mode 100644 index 0000000..3f99c56 --- /dev/null +++ b/lockscreen/src/main/java/com/example/capstone2/activity/EnterPinActivity.java @@ -0,0 +1,470 @@ +package com.example.capstone2.activity; + +import android.Manifest; +import android.animation.ObjectAnimator; +import android.app.KeyguardManager; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.graphics.Typeface; +import android.graphics.drawable.AnimatedVectorDrawable; +import android.hardware.fingerprint.FingerprintManager; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.security.keystore.KeyGenParameterSpec; +import android.security.keystore.KeyProperties; +import android.support.v4.app.ActivityCompat; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.AppCompatImageView; +import android.util.Log; +import android.view.KeyEvent; +import android.view.View; +import android.view.Window; +import android.widget.TextView; +import android.widget.Toast; +import android.app.KeyguardManager; + +import com.example.capstone2.R; +import com.example.capstone2.andrognito.pinlockview.IndicatorDots; +import com.example.capstone2.andrognito.pinlockview.PinLockListener; +import com.example.capstone2.andrognito.pinlockview.PinLockView; +import com.example.capstone2.fingerprint.FingerPrintListener; +import com.example.capstone2.fingerprint.FingerprintHandler; +import com.example.capstone2.util.Animate; +import com.example.capstone2.util.Utils; + +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.cert.CertificateException; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; + +public class EnterPinActivity extends AppCompatActivity { + + public static final String TAG = "EnterPinActivity"; + + public static final int RESULT_BACK_PRESSED = RESULT_FIRST_USER; + // public static final int RESULT_TOO_MANY_TRIES = RESULT_FIRST_USER + 1; + public static final String EXTRA_SET_PIN = "set_pin"; + public static final String EXTRA_FONT_TEXT = "textFont"; + public static final String EXTRA_FONT_NUM = "numFont"; + + private static final int PIN_LENGTH = 4; + private static final String FINGER_PRINT_KEY = "FingerPrintKey"; + + private static final String PREFERENCES = "com.example.capstone2"; + private static final String KEY_PIN = "pin"; + + private PinLockView mPinLockView; + private IndicatorDots mIndicatorDots; + private TextView mTextTitle; + private TextView mTextAttempts; + private TextView mTextFingerText; + private AppCompatImageView mImageViewFingerView; + + private Cipher mCipher; + private KeyStore mKeyStore; + private KeyGenerator mKeyGenerator; + private FingerprintManager.CryptoObject mCryptoObject; + private FingerprintManager mFingerprintManager; + private KeyguardManager mKeyguardManager; + private boolean mSetPin = false; + private String mFirstPin = ""; + // private int mTryCount = 0; + + private AnimatedVectorDrawable showFingerprint; + private AnimatedVectorDrawable fingerprintToTick; + private AnimatedVectorDrawable fingerprintToCross; + + public static Intent getIntent(Context context, boolean setPin) { + Intent intent = new Intent(context, EnterPinActivity.class); + + intent.putExtra(EXTRA_SET_PIN, setPin); + + return intent; + } + + public static Intent getIntent(Context context, String fontText, String fontNum) { + Intent intent = new Intent(context, EnterPinActivity.class); + + intent.putExtra(EXTRA_FONT_TEXT, fontText); + intent.putExtra(EXTRA_FONT_NUM, fontNum); + + return intent; + } + + public static Intent getIntent(Context context, boolean setPin, String fontText, String fontNum) { + Intent intent = getIntent(context, fontText, fontNum); + + intent.putExtra(EXTRA_SET_PIN, setPin); + + return intent; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + setContentView(R.layout.activity_enterpin); + + mTextAttempts = (TextView) findViewById(R.id.attempts); + mTextTitle = (TextView) findViewById(R.id.title); + mIndicatorDots = (IndicatorDots) findViewById(R.id.indicator_dots); + mImageViewFingerView = (AppCompatImageView) findViewById(R.id.fingerView); + mTextFingerText = (TextView) findViewById(R.id.fingerText); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + showFingerprint = (AnimatedVectorDrawable) getDrawable(R.drawable.show_fingerprint); + fingerprintToTick = (AnimatedVectorDrawable) getDrawable(R.drawable.fingerprint_to_tick); + fingerprintToCross = (AnimatedVectorDrawable) getDrawable(R.drawable.fingerprint_to_cross); + } + + mSetPin = getIntent().getBooleanExtra(EXTRA_SET_PIN, false); + + if (mSetPin) { + changeLayoutForSetPin(); + } else { + String pin = getPinFromSharedPreferences(); + if (pin.equals("")) { + changeLayoutForSetPin(); + mSetPin = true; + } else { + checkForFingerPrint(); + } + } + + final PinLockListener pinLockListener = new PinLockListener() { + + @Override + public void onComplete(String pin) { + if (mSetPin) { + setPin(pin); + } else { + checkPin(pin); + } + } + + @Override + public void onEmpty() { + Log.d(TAG, "Pin empty"); + } + + @Override + public void onPinChange(int pinLength, String intermediatePin) { + Log.d(TAG, "Pin changed, new length " + pinLength + " with intermediate pin " + intermediatePin); + } + + }; + + mPinLockView = (PinLockView) findViewById(R.id.pinlockView); + mIndicatorDots = (IndicatorDots) findViewById(R.id.indicator_dots); + + mPinLockView.attachIndicatorDots(mIndicatorDots); + mPinLockView.setPinLockListener(pinLockListener); + + mPinLockView.setPinLength(PIN_LENGTH); + + mIndicatorDots.setIndicatorType(IndicatorDots.IndicatorType.FILL_WITH_ANIMATION); + + checkForFont(); + } + + private void checkForFont() { + Intent intent = getIntent(); + + if (intent.hasExtra(EXTRA_FONT_TEXT)) { + + String font = intent.getStringExtra(EXTRA_FONT_TEXT); + setTextFont(font); + } + if (intent.hasExtra(EXTRA_FONT_NUM)) { + String font = intent.getStringExtra(EXTRA_FONT_NUM); + setNumFont(font); + } + } + + private void setTextFont(String font) { + try { + Typeface typeface = Typeface.createFromAsset(getAssets(), font); + + mTextTitle.setTypeface(typeface); + mTextAttempts.setTypeface(typeface); + mTextFingerText.setTypeface(typeface); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void setNumFont(String font) { + try { + Typeface typeface = Typeface.createFromAsset(getAssets(), font); + + mPinLockView.setTypeFace(typeface); + } catch (Exception e) { + e.printStackTrace(); + } + } + + //Create the generateKey method that we’ll use to gain access to the Android keystore and generate the encryption key// + private void generateKey() throws FingerprintException { + try { + // Obtain a reference to the Keystore using the standard Android keystore container identifier (“AndroidKeystore”)// + mKeyStore = KeyStore.getInstance("AndroidKeyStore"); + + //Generate the key// + mKeyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"); + + //Initialize an empty KeyStore// + mKeyStore.load(null); + + //Initialize the KeyGenerator// + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + mKeyGenerator.init(new + + //Specify the operation(s) this key can be used for// + KeyGenParameterSpec.Builder(FINGER_PRINT_KEY, + KeyProperties.PURPOSE_ENCRYPT | + KeyProperties.PURPOSE_DECRYPT) + .setBlockModes(KeyProperties.BLOCK_MODE_CBC) + + //Configure this key so that the user has to confirm their identity with a fingerprint each time they want to use it// + .setUserAuthenticationRequired(true) + .setEncryptionPaddings( + KeyProperties.ENCRYPTION_PADDING_PKCS7) + .build()); + } + + //Generate the key// + mKeyGenerator.generateKey(); + + } catch (KeyStoreException + | NoSuchAlgorithmException + | NoSuchProviderException + | InvalidAlgorithmParameterException + | CertificateException + | IOException exc) { + throw new FingerprintException(exc); + } + } + + //Create a new method that we’ll use to initialize our mCipher// + public boolean initCipher() { + try { + //Obtain a mCipher instance and configure it with the properties required for fingerprint authentication// + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + mCipher = Cipher.getInstance( + KeyProperties.KEY_ALGORITHM_AES + "/" + + KeyProperties.BLOCK_MODE_CBC + "/" + + KeyProperties.ENCRYPTION_PADDING_PKCS7); + } + } catch (NoSuchAlgorithmException | + NoSuchPaddingException e) { + Log.e(TAG, "Failed to get Cipher"); + return false; + } + + try { + mKeyStore.load(null); + SecretKey key = (SecretKey) mKeyStore.getKey(FINGER_PRINT_KEY, + null); + mCipher.init(Cipher.ENCRYPT_MODE, key); + //Return true if the mCipher has been initialized successfully// + return true; + } catch (Exception e) { + Log.e(TAG, "Failed to init Cipher"); + return false; + } + } + + private void writePinToSharedPreferences(String pin) { + SharedPreferences prefs = this.getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE); + prefs.edit().putString(KEY_PIN, Utils.sha256(pin)).apply(); + } + + private String getPinFromSharedPreferences() { + SharedPreferences prefs = this.getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE); + return prefs.getString(KEY_PIN, ""); + } + + private void setPin(String pin) { + if (mFirstPin.equals("")) { + mFirstPin = pin; + mTextTitle.setText(getString(R.string.pinlock_secondPin)); + mPinLockView.resetPinLockView(); + } else { + if (pin.equals(mFirstPin)) { + writePinToSharedPreferences(pin); + setResult(RESULT_OK); + finish(); + } else { + shake(); + mTextTitle.setText(getString(R.string.pinlock_tryagain)); + mPinLockView.resetPinLockView(); + mFirstPin = ""; + } + } + } + + private void checkPin(String pin) { + if (Utils.sha256(pin).equals(getPinFromSharedPreferences())) { + setResult(RESULT_OK); + finish(); + } else { + shake(); + +// mTryCount++; + + mTextAttempts.setText(getString(R.string.pinlock_wrongpin)); + mPinLockView.resetPinLockView(); + +// if (mTryCount == 1) { +// mTextAttempts.setText(getString(R.string.pinlock_firsttry)); +// mPinLockView.resetPinLockView(); +// } else if (mTryCount == 2) { +// mTextAttempts.setText(getString(R.string.pinlock_secondtry)); +// mPinLockView.resetPinLockView(); +// } else if (mTryCount > 2) { +// setResult(RESULT_TOO_MANY_TRIES); +// finish(); +// } + } + } + + private void shake() { + ObjectAnimator objectAnimator = new ObjectAnimator().ofFloat(mPinLockView, "translationX", + 0, 25, -25, 25, -25, 15, -15, 6, -6, 0).setDuration(1000); + objectAnimator.start(); + } + + private void changeLayoutForSetPin() { + mImageViewFingerView.setVisibility(View.GONE); + mTextFingerText.setVisibility(View.GONE); + mTextAttempts.setVisibility(View.GONE); + mTextTitle.setText(getString(R.string.pinlock_settitle)); + } + + private void checkForFingerPrint() { + + final FingerPrintListener fingerPrintListener = new FingerPrintListener() { + + @Override + public void onSuccess() { + setResult(RESULT_OK); + Animate.animate(mImageViewFingerView, fingerprintToTick); + final Handler handler = new Handler(); + handler.postDelayed(new Runnable() { + @Override + public void run() { + finish(); + } + }, 750); + } + + @Override + public void onFailed() { + Animate.animate(mImageViewFingerView, fingerprintToCross); + final Handler handler = new Handler(); + handler.postDelayed(new Runnable() { + @Override + public void run() { + Animate.animate(mImageViewFingerView, showFingerprint); + } + }, 750); + } + + @Override + public void onError(CharSequence errorString) { + Toast.makeText(EnterPinActivity.this, errorString, Toast.LENGTH_SHORT).show(); + } + + @Override + public void onHelp(CharSequence helpString) { + Toast.makeText(EnterPinActivity.this, helpString, Toast.LENGTH_SHORT).show(); + } + + }; + + // If you’ve set your app’s minSdkVersion to anything lower than 23, then you’ll need to verify that the device is running Marshmallow + // or higher before executing any fingerprint-related code + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + FingerprintManager fingerprintManager = (FingerprintManager) getSystemService(Context.FINGERPRINT_SERVICE); + if (fingerprintManager.isHardwareDetected()) { + //Get an instance of KeyguardManager and FingerprintManager// + mKeyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE); + mFingerprintManager = (FingerprintManager) getSystemService(FINGERPRINT_SERVICE); + + //Check whether the user has granted your app the USE_FINGERPRINT permission// + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.USE_FINGERPRINT) + != PackageManager.PERMISSION_GRANTED) { + // If your app doesn't have this permission, then display the following text// +// Toast.makeText(EnterPinActivity.this, "Please enable the fingerprint permission", Toast.LENGTH_LONG).show(); + mImageViewFingerView.setVisibility(View.GONE); +// mTextFingerText.setVisibility(View.GONE); + return; + } + + //Check that the user has registered at least one fingerprint// + if (!mFingerprintManager.hasEnrolledFingerprints()) { + // If the user hasn’t configured any fingerprints, then display the following message// +// Toast.makeText(EnterPinActivity.this, +// "No fingerprint configured. Please register at least one fingerprint in your device's Settings", +// Toast.LENGTH_LONG).show(); + mImageViewFingerView.setVisibility(View.GONE); +// mTextFingerText.setVisibility(View.GONE); + return; + } + + //Check that the lockscreen is secured// + if (!mKeyguardManager.isKeyguardSecure()) { + // If the user hasn’t secured their lockscreen with a PIN password or pattern, then display the following text// +// Toast.makeText(EnterPinActivity.this, "Please enable lockscreen security in your device's Settings", Toast.LENGTH_LONG).show(); + mImageViewFingerView.setVisibility(View.GONE); +// mTextFingerText.setVisibility(View.GONE); + return; + } else { + try { + generateKey(); + if (initCipher()) { + //If the mCipher is initialized successfully, then create a CryptoObject instance// + mCryptoObject = new FingerprintManager.CryptoObject(mCipher); + + // Here, I’m referencing the FingerprintHandler class that we’ll create in the next section. This class will be responsible + // for starting the authentication process (via the startAuth method) and processing the authentication process events// + FingerprintHandler helper = new FingerprintHandler(this); + helper.startAuth(mFingerprintManager, mCryptoObject); + helper.setFingerPrintListener(fingerPrintListener); + } + } catch (FingerprintException e) { + Log.wtf(TAG, "Failed to generate key for fingerprint.", e); + } + } + } else { + mImageViewFingerView.setVisibility(View.GONE); +// mTextFingerText.setVisibility(View.GONE); + } + } else { + mImageViewFingerView.setVisibility(View.GONE); +// mTextFingerText.setVisibility(View.GONE); + } + } + + @Override + public void onBackPressed() { + } + + private class FingerprintException extends Exception { + public FingerprintException(Exception e) { + super(e); + } + } + +} diff --git a/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/CustomizationOptionsBundle.java b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/CustomizationOptionsBundle.java new file mode 100644 index 0000000..4ef109b --- /dev/null +++ b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/CustomizationOptionsBundle.java @@ -0,0 +1,96 @@ +package com.example.capstone2.andrognito.pinlockview; + +import android.graphics.drawable.Drawable; + +/** + * The customization options for the buttons in {@link PinLockView} + * passed to the {@link PinLockAdapter} to decorate the individual views + * + * Created by aritraroy on 01/06/16. + */ +public class CustomizationOptionsBundle { + + private int textColor; + private int textSize; + private int buttonSize; + private Drawable buttonBackgroundDrawable; + private Drawable deleteButtonDrawable; + private int deleteButtonWidthSize, deleteButtonHeightSize; + private boolean showDeleteButton; + private int deleteButtonPressesColor; + + public CustomizationOptionsBundle() { + } + + public int getTextColor() { + return textColor; + } + + public void setTextColor(int textColor) { + this.textColor = textColor; + } + + public int getTextSize() { + return textSize; + } + + public void setTextSize(int textSize) { + this.textSize = textSize; + } + + public int getButtonSize() { + return buttonSize; + } + + public void setButtonSize(int buttonSize) { + this.buttonSize = buttonSize; + } + + public Drawable getButtonBackgroundDrawable() { + return buttonBackgroundDrawable; + } + + public void setButtonBackgroundDrawable(Drawable buttonBackgroundDrawable) { + this.buttonBackgroundDrawable = buttonBackgroundDrawable; + } + + public Drawable getDeleteButtonDrawable() { + return deleteButtonDrawable; + } + + public void setDeleteButtonDrawable(Drawable deleteButtonDrawable) { + this.deleteButtonDrawable = deleteButtonDrawable; + } + + public int getDeleteButtonWidthSize() { + return deleteButtonWidthSize; + } + + public int getDeleteButtonHeightSize() { + return deleteButtonHeightSize; + } + + public void setDeleteButtonWidthSize(int deleteButtonWidthSize) { + this.deleteButtonWidthSize = deleteButtonWidthSize; + } + + public void setDeleteButtonHeightSize(int deleteButtonHeightSize) { + this.deleteButtonHeightSize = deleteButtonHeightSize; + } + + public boolean isShowDeleteButton() { + return showDeleteButton; + } + + public void setShowDeleteButton(boolean showDeleteButton) { + this.showDeleteButton = showDeleteButton; + } + + public int getDeleteButtonPressesColor() { + return deleteButtonPressesColor; + } + + public void setDeleteButtonPressesColor(int deleteButtonPressesColor) { + this.deleteButtonPressesColor = deleteButtonPressesColor; + } +} diff --git a/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/IndicatorDots.java b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/IndicatorDots.java new file mode 100644 index 0000000..45dbe9f --- /dev/null +++ b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/IndicatorDots.java @@ -0,0 +1,176 @@ +package com.example.capstone2.andrognito.pinlockview; + +import android.animation.LayoutTransition; +import android.content.Context; +import android.content.res.TypedArray; +import android.support.annotation.IntDef; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; + +import com.example.capstone2.R; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * It represents a set of indicator dots which when attached with {@link PinLockView} + * can be used to indicate the current length of the input + * + * Created by aritraroy on 01/06/16. + */ +public class IndicatorDots extends LinearLayout { + + @IntDef({IndicatorType.FIXED, IndicatorType.FILL, IndicatorType.FILL_WITH_ANIMATION}) + @Retention(RetentionPolicy.SOURCE) + public @interface IndicatorType { + int FIXED = 0; + int FILL = 1; + int FILL_WITH_ANIMATION = 2; + } + + private static final int DEFAULT_PIN_LENGTH = 4; + private static final int DEFAULT_ANIMATION_DURATION = 200; + + private int mDotDiameter; + private int mDotSpacing; + private int mFillDrawable; + private int mEmptyDrawable; + private int mPinLength; + private int mIndicatorType; + + private int mPreviousLength; + + public IndicatorDots(Context context) { + this(context, null); + } + + public IndicatorDots(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public IndicatorDots(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PinLockView); + + try { + mDotDiameter = (int) typedArray.getDimension(R.styleable.PinLockView_dotDiameter, ResourceUtils.getDimensionInPx(getContext(), R.dimen.dot_diameter)); + mDotSpacing = (int) typedArray.getDimension(R.styleable.PinLockView_dotSpacing, ResourceUtils.getDimensionInPx(getContext(), R.dimen.dot_spacing)); + mFillDrawable = typedArray.getResourceId(R.styleable.PinLockView_dotFilledBackground, + R.drawable.dot_filled); + mEmptyDrawable = typedArray.getResourceId(R.styleable.PinLockView_dotEmptyBackground, + R.drawable.dot_empty); + mPinLength = typedArray.getInt(R.styleable.PinLockView_pinLength, DEFAULT_PIN_LENGTH); + mIndicatorType = typedArray.getInt(R.styleable.PinLockView_indicatorType, + IndicatorType.FIXED); + } finally { + typedArray.recycle(); + } + + initView(context); + } + + private void initView(Context context) { + if (mIndicatorType == 0) { + for (int i = 0; i < mPinLength; i++) { + View dot = new View(context); + emptyDot(dot); + + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(mDotDiameter, + mDotDiameter); + params.setMargins(mDotSpacing, 0, mDotSpacing, 0); + dot.setLayoutParams(params); + + addView(dot); + } + } else if (mIndicatorType == 2) { + LayoutTransition layoutTransition = new LayoutTransition(); + layoutTransition.setDuration(DEFAULT_ANIMATION_DURATION); + layoutTransition.setStartDelay(layoutTransition.APPEARING, 0); + setLayoutTransition(layoutTransition); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + // If the indicator type is not fixed + if (mIndicatorType != 0) { + ViewGroup.LayoutParams params = this.getLayoutParams(); + params.height = mDotDiameter; + requestLayout(); + } + } + + void updateDot(int length) { + if (mIndicatorType == 0) { + if (length > 0) { + if (length > mPreviousLength) { + fillDot(getChildAt(length - 1)); + } else { + emptyDot(getChildAt(length)); + } + mPreviousLength = length; + } else { + // When {@code mPinLength} is 0, we need to reset all the views back to empty + for (int i = 0; i < getChildCount(); i++) { + View v = getChildAt(i); + emptyDot(v); + } + mPreviousLength = 0; + } + } else { + if (length > 0) { + if (length > mPreviousLength) { + View dot = new View(getContext()); + fillDot(dot); + + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(mDotDiameter, + mDotDiameter); + params.setMargins(mDotSpacing, 0, mDotSpacing, 0); + dot.setLayoutParams(params); + + addView(dot, length - 1); + } else { + removeViewAt(length); + } + mPreviousLength = length; + } else { + removeAllViews(); + mPreviousLength = 0; + } + } + } + + private void emptyDot(View dot) { + dot.setBackgroundResource(mEmptyDrawable); + } + + private void fillDot(View dot) { + dot.setBackgroundResource(mFillDrawable); + } + + public int getPinLength() { + return mPinLength; + } + + public void setPinLength(int pinLength) { + this.mPinLength = pinLength; + removeAllViews(); + initView(getContext()); + } + + public + @IndicatorType + int getIndicatorType() { + return mIndicatorType; + } + + public void setIndicatorType(@IndicatorType int type) { + this.mIndicatorType = type; + removeAllViews(); + initView(getContext()); + } +} diff --git a/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/ItemSpaceDecoration.java b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/ItemSpaceDecoration.java new file mode 100644 index 0000000..d034d95 --- /dev/null +++ b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/ItemSpaceDecoration.java @@ -0,0 +1,46 @@ +package com.example.capstone2.andrognito.pinlockview; + +import android.graphics.Rect; +import android.support.v7.widget.RecyclerView; +import android.view.View; + +/** + * Created by aritraroy on 31/05/16. + */ +public class ItemSpaceDecoration extends RecyclerView.ItemDecoration { + + private final int mHorizontalSpaceWidth; + private final int mVerticalSpaceHeight; + private final int mSpanCount; + private final boolean mIncludeEdge; + + public ItemSpaceDecoration(int horizontalSpaceWidth, int verticalSpaceHeight, int spanCount, boolean includeEdge) { + this.mHorizontalSpaceWidth = horizontalSpaceWidth; + this.mVerticalSpaceHeight = verticalSpaceHeight; + this.mSpanCount = spanCount; + this.mIncludeEdge = includeEdge; + } + + @Override + public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { + + int position = parent.getChildAdapterPosition(view); + int column = position % mSpanCount; + + if (mIncludeEdge) { + outRect.right = mHorizontalSpaceWidth - column * mHorizontalSpaceWidth / mSpanCount; + outRect.left = (column + 1) * mHorizontalSpaceWidth / mSpanCount; + + if (position < mSpanCount) { + outRect.top = mVerticalSpaceHeight; + } + outRect.bottom = mVerticalSpaceHeight; + } else { + outRect.right = column * mHorizontalSpaceWidth / mSpanCount; + outRect.left = mHorizontalSpaceWidth - (column + 1) * mHorizontalSpaceWidth / mSpanCount; + if (position >= mSpanCount) { + outRect.top = mVerticalSpaceHeight; + } + } + } +} diff --git a/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/PinLockAdapter.java b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/PinLockAdapter.java new file mode 100644 index 0000000..235814a --- /dev/null +++ b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/PinLockAdapter.java @@ -0,0 +1,283 @@ +package com.example.capstone2.andrognito.pinlockview; + +import android.graphics.PorterDuff; +import android.graphics.Typeface; +import android.os.Build; +import android.support.v7.widget.RecyclerView; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.Animation; +import android.view.animation.ScaleAnimation; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.LinearLayout; + +import com.example.capstone2.R; + +/** + * Created by aritraroy on 31/05/16. + */ +public class PinLockAdapter extends RecyclerView.Adapter { + + private static final int VIEW_TYPE_NUMBER = 0; + private static final int VIEW_TYPE_DELETE = 1; + + private CustomizationOptionsBundle mCustomizationOptionsBundle; + private OnNumberClickListener mOnNumberClickListener; + private OnDeleteClickListener mOnDeleteClickListener; + private int mPinLength; + private int BUTTON_ANIMATION_DURATION = 150; + + private int[] mKeyValues; + + private Typeface mTypeface = null; + + public PinLockAdapter() { + this.mKeyValues = getAdjustKeyValues(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}); + } + + public void setTypeFace(Typeface typeFace) { + mTypeface = typeFace; + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + RecyclerView.ViewHolder viewHolder; + LayoutInflater inflater = LayoutInflater.from(parent.getContext()); + + if (viewType == VIEW_TYPE_NUMBER) { + View view = inflater.inflate(R.layout.layout_number_item, parent, false); + viewHolder = new NumberViewHolder(view, mTypeface); + } else { + View view = inflater.inflate(R.layout.layout_delete_item, parent, false); + viewHolder = new DeleteViewHolder(view); + } + return viewHolder; + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + if (holder.getItemViewType() == VIEW_TYPE_NUMBER) { + NumberViewHolder vh1 = (NumberViewHolder) holder; + configureNumberButtonHolder(vh1, position); + } else if (holder.getItemViewType() == VIEW_TYPE_DELETE) { + DeleteViewHolder vh2 = (DeleteViewHolder) holder; + configureDeleteButtonHolder(vh2); + } + } + + private void configureNumberButtonHolder(NumberViewHolder holder, int position) { + if (holder != null) { + if (position == 9) { + holder.mNumberButton.setVisibility(View.GONE); + } else { + holder.mNumberButton.setText(String.valueOf(mKeyValues[position])); + holder.mNumberButton.setVisibility(View.VISIBLE); + holder.mNumberButton.setTag(mKeyValues[position]); + } + + if (mCustomizationOptionsBundle != null) { + holder.mNumberButton.setTextColor(mCustomizationOptionsBundle.getTextColor()); + if (mCustomizationOptionsBundle.getButtonBackgroundDrawable() != null) { + if (Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) { + holder.mNumberButton.setBackgroundDrawable( + mCustomizationOptionsBundle.getButtonBackgroundDrawable()); + } else { + holder.mNumberButton.setBackground( + mCustomizationOptionsBundle.getButtonBackgroundDrawable()); + } + } + holder.mNumberButton.setTextSize(TypedValue.COMPLEX_UNIT_PX, + mCustomizationOptionsBundle.getTextSize()); + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( + mCustomizationOptionsBundle.getButtonSize(), + mCustomizationOptionsBundle.getButtonSize()); + holder.mNumberButton.setLayoutParams(params); + } + } + } + + private void configureDeleteButtonHolder(DeleteViewHolder holder) { + if (holder != null) { + if (mCustomizationOptionsBundle.isShowDeleteButton() && mPinLength > 0) { + holder.mButtonImage.setVisibility(View.VISIBLE); + if (mCustomizationOptionsBundle.getDeleteButtonDrawable() != null) { + holder.mButtonImage.setImageDrawable(mCustomizationOptionsBundle.getDeleteButtonDrawable()); + } + holder.mButtonImage.setColorFilter(mCustomizationOptionsBundle.getTextColor(), + PorterDuff.Mode.SRC_ATOP); + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( + mCustomizationOptionsBundle.getDeleteButtonWidthSize(), + mCustomizationOptionsBundle.getDeleteButtonHeightSize()); + holder.mButtonImage.setLayoutParams(params); + } + } + } + + @Override + public int getItemCount() { + return 12; + } + + @Override + public int getItemViewType(int position) { + if (position == getItemCount() - 1) { + return VIEW_TYPE_DELETE; + } + return VIEW_TYPE_NUMBER; + } + + public int getPinLength() { + return mPinLength; + } + + public void setPinLength(int pinLength) { + this.mPinLength = pinLength; + } + + public int[] getKeyValues() { + return mKeyValues; + } + + public void setKeyValues(int[] keyValues) { + this.mKeyValues = getAdjustKeyValues(keyValues); + notifyDataSetChanged(); + } + + private int[] getAdjustKeyValues(int[] keyValues) { + int[] adjustedKeyValues = new int[keyValues.length + 1]; + for (int i = 0; i < keyValues.length; i++) { + if (i < 9) { + adjustedKeyValues[i] = keyValues[i]; + } else { + adjustedKeyValues[i] = -1; + adjustedKeyValues[i + 1] = keyValues[i]; + } + } + return adjustedKeyValues; + } + + public OnNumberClickListener getOnItemClickListener() { + return mOnNumberClickListener; + } + + public void setOnItemClickListener(OnNumberClickListener onNumberClickListener) { + this.mOnNumberClickListener = onNumberClickListener; + } + + public OnDeleteClickListener getOnDeleteClickListener() { + return mOnDeleteClickListener; + } + + public void setOnDeleteClickListener(OnDeleteClickListener onDeleteClickListener) { + this.mOnDeleteClickListener = onDeleteClickListener; + } + + public CustomizationOptionsBundle getCustomizationOptions() { + return mCustomizationOptionsBundle; + } + + public void setCustomizationOptions(CustomizationOptionsBundle customizationOptionsBundle) { + this.mCustomizationOptionsBundle = customizationOptionsBundle; + } + + public interface OnNumberClickListener { + void onNumberClicked(int keyValue); + } + + public interface OnDeleteClickListener { + void onDeleteClicked(); + + void onDeleteLongClicked(); + } + + public class NumberViewHolder extends RecyclerView.ViewHolder { + Button mNumberButton; + + public NumberViewHolder(final View itemView, Typeface font) { + super(itemView); + mNumberButton = (Button) itemView.findViewById(R.id.button); + + if (font != null) { + mNumberButton.setTypeface(font); + } + + mNumberButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mOnNumberClickListener != null) { + mOnNumberClickListener.onNumberClicked((Integer) v.getTag()); + } + } + }); + + mNumberButton.setOnTouchListener(new View.OnTouchListener() { + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + mNumberButton.startAnimation(scale()); + } + + return false; + } + }); + } + } + + public class DeleteViewHolder extends RecyclerView.ViewHolder { + LinearLayout mDeleteButton; + ImageView mButtonImage; + + public DeleteViewHolder(final View itemView) { + super(itemView); + mDeleteButton = (LinearLayout) itemView.findViewById(R.id.button); + mButtonImage = (ImageView) itemView.findViewById(R.id.buttonImage); + + if (mCustomizationOptionsBundle.isShowDeleteButton() && mPinLength > 0) { + mDeleteButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mOnDeleteClickListener != null) { + mOnDeleteClickListener.onDeleteClicked(); + } + } + }); + + mDeleteButton.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (mOnDeleteClickListener != null) { + mOnDeleteClickListener.onDeleteLongClicked(); + } + return true; + } + }); + + mDeleteButton.setOnTouchListener(new View.OnTouchListener() { + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + + mDeleteButton.startAnimation(scale()); + } + + return false; + } + }); + } + } + } + + private Animation scale(){ + ScaleAnimation scaleAnimation = new ScaleAnimation(.75F, 1f, .75F, 1f, + Animation.RELATIVE_TO_SELF, .5F, Animation.RELATIVE_TO_SELF, .5F); + scaleAnimation.setDuration(BUTTON_ANIMATION_DURATION); + scaleAnimation.setFillAfter(true); + return scaleAnimation; + } +} diff --git a/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/PinLockListener.java b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/PinLockListener.java new file mode 100644 index 0000000..126b74e --- /dev/null +++ b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/PinLockListener.java @@ -0,0 +1,32 @@ +package com.example.capstone2.andrognito.pinlockview; + +/** + * The listener that triggers callbacks for various events + * in the {@link PinLockView} + * + * Created by aritraroy on 31/05/16. + */ +public interface PinLockListener { + + /** + * Triggers when the complete pin is entered, + * depends on the pin length set by the user + * + * @param pin the complete pin + */ + void onComplete(String pin); + + + /** + * Triggers when the pin is empty after manual deletion + */ + void onEmpty(); + + /** + * Triggers on a key press on the {@link PinLockView} + * + * @param pinLength the current pin length + * @param intermediatePin the intermediate pin + */ + void onPinChange(int pinLength, String intermediatePin); +} diff --git a/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/PinLockView.java b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/PinLockView.java new file mode 100644 index 0000000..14171b5 --- /dev/null +++ b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/PinLockView.java @@ -0,0 +1,462 @@ +package com.example.capstone2.andrognito.pinlockview; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.support.annotation.Nullable; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; + +import com.example.capstone2.R; + +/** + * Represents a numeric lock view which can used to taken numbers as input. + * The length of the input can be customized using {@link PinLockView#setPinLength(int)}, the default value being 4 + * + * It can also be used as dial pad for taking number inputs. + * Optionally, {@link IndicatorDots} can be attached to this view to indicate the length of the input taken + * Created by aritraroy on 31/05/16. + */ +public class PinLockView extends RecyclerView { + + private static final int DEFAULT_PIN_LENGTH = 4; + private static final int[] DEFAULT_KEY_SET = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}; + + private String mPin = ""; + private int mPinLength; + private int mHorizontalSpacing, mVerticalSpacing; + private int mTextColor, mDeleteButtonPressedColor; + private int mTextSize, mButtonSize, mDeleteButtonWidthSize, mDeleteButtonHeightSize; + private Drawable mButtonBackgroundDrawable; + private Drawable mDeleteButtonDrawable; + private boolean mShowDeleteButton; + + private IndicatorDots mIndicatorDots; + private PinLockAdapter mAdapter; + private PinLockListener mPinLockListener; + private CustomizationOptionsBundle mCustomizationOptionsBundle; + private int[] mCustomKeySet; + + private PinLockAdapter.OnNumberClickListener mOnNumberClickListener + = new PinLockAdapter.OnNumberClickListener() { + @Override + public void onNumberClicked(int keyValue) { + if (mPin.length() < getPinLength()) { + mPin = mPin.concat(String.valueOf(keyValue)); + + if (isIndicatorDotsAttached()) { + mIndicatorDots.updateDot(mPin.length()); + } + + if (mPin.length() == 1) { + mAdapter.setPinLength(mPin.length()); + mAdapter.notifyItemChanged(mAdapter.getItemCount() - 1); + } + + if (mPinLockListener != null) { + if (mPin.length() == mPinLength) { + mPinLockListener.onComplete(mPin); + } else { + mPinLockListener.onPinChange(mPin.length(), mPin); + } + } + } else { + if (!isShowDeleteButton()) { + resetPinLockView(); + mPin = mPin.concat(String.valueOf(keyValue)); + + if (isIndicatorDotsAttached()) { + mIndicatorDots.updateDot(mPin.length()); + } + + if (mPinLockListener != null) { + mPinLockListener.onPinChange(mPin.length(), mPin); + } + + } else { + if (mPinLockListener != null) { + mPinLockListener.onComplete(mPin); + } + } + } + } + }; + + private PinLockAdapter.OnDeleteClickListener mOnDeleteClickListener + = new PinLockAdapter.OnDeleteClickListener() { + @Override + public void onDeleteClicked() { + if (mPin.length() > 0) { + mPin = mPin.substring(0, mPin.length() - 1); + + if (isIndicatorDotsAttached()) { + mIndicatorDots.updateDot(mPin.length()); + } + + if (mPin.length() == 0) { + mAdapter.setPinLength(mPin.length()); + mAdapter.notifyItemChanged(mAdapter.getItemCount() - 1); + } + + if (mPinLockListener != null) { + if (mPin.length() == 0) { + mPinLockListener.onEmpty(); + clearInternalPin(); + } else { + mPinLockListener.onPinChange(mPin.length(), mPin); + } + } + } else { + if (mPinLockListener != null) { + mPinLockListener.onEmpty(); + } + } + } + + @Override + public void onDeleteLongClicked() { + resetPinLockView(); + if (mPinLockListener != null) { + mPinLockListener.onEmpty(); + } + } + }; + + public PinLockView(Context context) { + super(context); + init(null, 0); + } + + public PinLockView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(attrs, 0); + } + + public PinLockView(Context context, @Nullable AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(attrs, defStyle); + } + + private void init(AttributeSet attributeSet, int defStyle) { + + TypedArray typedArray = getContext().obtainStyledAttributes(attributeSet, R.styleable.PinLockView); + + try { + mPinLength = typedArray.getInt(R.styleable.PinLockView_pinLength, DEFAULT_PIN_LENGTH); + mHorizontalSpacing = (int) typedArray.getDimension(R.styleable.PinLockView_keypadHorizontalSpacing, ResourceUtils.getDimensionInPx(getContext(), R.dimen.default_horizontal_spacing)); + mVerticalSpacing = (int) typedArray.getDimension(R.styleable.PinLockView_keypadVerticalSpacing, ResourceUtils.getDimensionInPx(getContext(), R.dimen.default_vertical_spacing)); + mTextColor = typedArray.getColor(R.styleable.PinLockView_keypadTextColor, ResourceUtils.getColor(getContext(), R.color.text_numberpressed)); + mTextSize = (int) typedArray.getDimension(R.styleable.PinLockView_keypadTextSize, ResourceUtils.getDimensionInPx(getContext(), R.dimen.default_text_size)); + mButtonSize = (int) typedArray.getDimension(R.styleable.PinLockView_keypadButtonSize, ResourceUtils.getDimensionInPx(getContext(), R.dimen.default_button_size)); + mDeleteButtonWidthSize = (int) typedArray.getDimension(R.styleable.PinLockView_keypadDeleteButtonSize, ResourceUtils.getDimensionInPx(getContext(), R.dimen.default_delete_button_size_width)); + mDeleteButtonHeightSize = (int) typedArray.getDimension(R.styleable.PinLockView_keypadDeleteButtonSize, ResourceUtils.getDimensionInPx(getContext(), R.dimen.default_delete_button_size_height)); + mButtonBackgroundDrawable = typedArray.getDrawable(R.styleable.PinLockView_keypadButtonBackgroundDrawable); + mDeleteButtonDrawable = typedArray.getDrawable(R.styleable.PinLockView_keypadDeleteButtonDrawable); + mShowDeleteButton = typedArray.getBoolean(R.styleable.PinLockView_keypadShowDeleteButton, true); + mDeleteButtonPressedColor = typedArray.getColor(R.styleable.PinLockView_keypadDeleteButtonPressedColor, ResourceUtils.getColor(getContext(), R.color.text_numberpressed)); + } finally { + typedArray.recycle(); + } + + mCustomizationOptionsBundle = new CustomizationOptionsBundle(); + mCustomizationOptionsBundle.setTextColor(mTextColor); + mCustomizationOptionsBundle.setTextSize(mTextSize); + mCustomizationOptionsBundle.setButtonSize(mButtonSize); + mCustomizationOptionsBundle.setButtonBackgroundDrawable(mButtonBackgroundDrawable); + mCustomizationOptionsBundle.setDeleteButtonDrawable(mDeleteButtonDrawable); + mCustomizationOptionsBundle.setDeleteButtonWidthSize(mDeleteButtonWidthSize); + mCustomizationOptionsBundle.setDeleteButtonHeightSize(mDeleteButtonHeightSize); + mCustomizationOptionsBundle.setShowDeleteButton(mShowDeleteButton); + mCustomizationOptionsBundle.setDeleteButtonPressesColor(mDeleteButtonPressedColor); + + initView(); + } + + private void initView() { + setLayoutManager(new GridLayoutManager(getContext(), 3)); + + mAdapter = new PinLockAdapter(); + mAdapter.setOnItemClickListener(mOnNumberClickListener); + mAdapter.setOnDeleteClickListener(mOnDeleteClickListener); + mAdapter.setCustomizationOptions(mCustomizationOptionsBundle); + setAdapter(mAdapter); + + addItemDecoration(new ItemSpaceDecoration(mHorizontalSpacing, mVerticalSpacing, 3, false)); + setOverScrollMode(OVER_SCROLL_NEVER); + } + + public void setTypeFace(Typeface typeFace) { + mAdapter.setTypeFace(typeFace); + } + + /** + * Sets a {@link PinLockListener} to the to listen to pin update events + * + * @param pinLockListener the listener + */ + public void setPinLockListener(PinLockListener pinLockListener) { + this.mPinLockListener = pinLockListener; + } + + /** + * Get the length of the current pin length + * + * @return the length of the pin + */ + public int getPinLength() { + return mPinLength; + } + + /** + * Sets the pin length dynamically + * + * @param pinLength the pin length + */ + public void setPinLength(int pinLength) { + this.mPinLength = pinLength; + + if (isIndicatorDotsAttached()) { + mIndicatorDots.setPinLength(pinLength); + } + } + + /** + * Get the text color in the buttons + * + * @return the text color + */ + public int getTextColor() { + return mTextColor; + } + + /** + * Set the text color of the buttons dynamically + * + * @param textColor the text color + */ + public void setTextColor(int textColor) { + this.mTextColor = textColor; + mCustomizationOptionsBundle.setTextColor(textColor); + mAdapter.notifyDataSetChanged(); + } + + /** + * Get the size of the text in the buttons + * + * @return the size of the text in pixels + */ + public int getTextSize() { + return mTextSize; + } + + /** + * Set the size of text in pixels + * + * @param textSize the text size in pixels + */ + public void setTextSize(int textSize) { + this.mTextSize = textSize; + mCustomizationOptionsBundle.setTextSize(textSize); + mAdapter.notifyDataSetChanged(); + } + + /** + * Get the size of the pin buttons + * + * @return the size of the button in pixels + */ + public int getButtonSize() { + return mButtonSize; + } + + /** + * Set the size of the pin buttons dynamically + * + * @param buttonSize the button size + */ + public void setButtonSize(int buttonSize) { + this.mButtonSize = buttonSize; + mCustomizationOptionsBundle.setButtonSize(buttonSize); + mAdapter.notifyDataSetChanged(); + } + + /** + * Get the current background drawable of the buttons, can be null + * + * @return the background drawable + */ + public Drawable getButtonBackgroundDrawable() { + return mButtonBackgroundDrawable; + } + + /** + * Set the background drawable of the buttons dynamically + * + * @param buttonBackgroundDrawable the background drawable + */ + public void setButtonBackgroundDrawable(Drawable buttonBackgroundDrawable) { + this.mButtonBackgroundDrawable = buttonBackgroundDrawable; + mCustomizationOptionsBundle.setButtonBackgroundDrawable(buttonBackgroundDrawable); + mAdapter.notifyDataSetChanged(); + } + + /** + * Get the drawable of the delete button + * + * @return the delete button drawable + */ + public Drawable getDeleteButtonDrawable() { + return mDeleteButtonDrawable; + } + + /** + * Set the drawable of the delete button dynamically + * + * @param deleteBackgroundDrawable the delete button drawable + */ + public void setDeleteButtonDrawable(Drawable deleteBackgroundDrawable) { + this.mDeleteButtonDrawable = deleteBackgroundDrawable; + mCustomizationOptionsBundle.setDeleteButtonDrawable(deleteBackgroundDrawable); + mAdapter.notifyDataSetChanged(); + } + + /** + * Get the delete button width size in pixels + * + * @return size in pixels + */ + public int getDeleteButtonWidthSize() { + return mDeleteButtonWidthSize; + } + + /** + * Get the delete button size height in pixels + * + * @return size in pixels + */ + public int getDeleteButtonHeightSize() { + return mDeleteButtonHeightSize; + } + + /** + * Set the size of the delete button width in pixels + * + * @param deleteButtonWidthSize size in pixels + */ + public void setDeleteButtonWidthSize(int deleteButtonWidthSize) { + this.mDeleteButtonWidthSize = deleteButtonWidthSize; + mCustomizationOptionsBundle.setDeleteButtonWidthSize(deleteButtonWidthSize); + mAdapter.notifyDataSetChanged(); + } + + /** + * Set the size of the delete button height in pixels + * + * @param deleteButtonHeightSize size in pixels + */ + public void setDeleteButtonHeightSize(int deleteButtonHeightSize) { + this.mDeleteButtonHeightSize = deleteButtonHeightSize; + mCustomizationOptionsBundle.setDeleteButtonWidthSize(deleteButtonHeightSize); + mAdapter.notifyDataSetChanged(); + } + + /** + * Is the delete button shown + * + * @return returns true if shown, false otherwise + */ + public boolean isShowDeleteButton() { + return mShowDeleteButton; + } + + /** + * Dynamically set if the delete button should be shown + * + * @param showDeleteButton true if the delete button should be shown, false otherwise + */ + public void setShowDeleteButton(boolean showDeleteButton) { + this.mShowDeleteButton = showDeleteButton; + mCustomizationOptionsBundle.setShowDeleteButton(showDeleteButton); + mAdapter.notifyDataSetChanged(); + } + + /** + * Get the delete button pressed/focused state color + * + * @return color of the button + */ + public int getDeleteButtonPressedColor() { + return mDeleteButtonPressedColor; + } + + /** + * Set the pressed/focused state color of the delete button + * + * @param deleteButtonPressedColor the color of the delete button + */ + public void setDeleteButtonPressedColor(int deleteButtonPressedColor) { + this.mDeleteButtonPressedColor = deleteButtonPressedColor; + mCustomizationOptionsBundle.setDeleteButtonPressesColor(deleteButtonPressedColor); + mAdapter.notifyDataSetChanged(); + } + + public int[] getCustomKeySet() { + return mCustomKeySet; + } + + public void setCustomKeySet(int[] customKeySet) { + this.mCustomKeySet = customKeySet; + + if (mAdapter != null) { + mAdapter.setKeyValues(customKeySet); + } + } + + public void enableLayoutShuffling() { + this.mCustomKeySet = ShuffleArrayUtils.shuffle(DEFAULT_KEY_SET); + + if (mAdapter != null) { + mAdapter.setKeyValues(mCustomKeySet); + } + } + + private void clearInternalPin() { + mPin = ""; + } + + /** + * Resets the {@link PinLockView}, clearing the entered pin + * and resetting the {@link IndicatorDots} if attached + */ + public void resetPinLockView() { + + clearInternalPin(); + + mAdapter.setPinLength(mPin.length()); + mAdapter.notifyItemChanged(mAdapter.getItemCount() - 1); + + if (mIndicatorDots != null) { + mIndicatorDots.updateDot(mPin.length()); + } + } + + /** + * Returns true if {@link IndicatorDots} are attached to {@link PinLockView} + * + * @return true if attached, false otherwise + */ + public boolean isIndicatorDotsAttached() { + return mIndicatorDots != null; + } + + /** + * Attaches {@link IndicatorDots} to {@link PinLockView} + * + * @param mIndicatorDots the view to attach + */ + public void attachIndicatorDots(IndicatorDots mIndicatorDots) { + this.mIndicatorDots = mIndicatorDots; + } +} diff --git a/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/ResourceUtils.java b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/ResourceUtils.java new file mode 100644 index 0000000..dc397eb --- /dev/null +++ b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/ResourceUtils.java @@ -0,0 +1,30 @@ +package com.example.capstone2.andrognito.pinlockview; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.support.annotation.ColorRes; +import android.support.annotation.DimenRes; +import android.support.annotation.DrawableRes; +import android.support.v4.content.ContextCompat; + +/** + * Created by aritraroy on 10/06/16. + */ +public class ResourceUtils { + + private ResourceUtils() { + throw new AssertionError(); + } + + public static int getColor(Context context, @ColorRes int id) { + return ContextCompat.getColor(context, id); + } + + public static float getDimensionInPx(Context context, @DimenRes int id) { + return context.getResources().getDimension(id); + } + + public static Drawable getDrawable(Context context, @DrawableRes int id) { + return ContextCompat.getDrawable(context, id); + } +} diff --git a/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/ShuffleArrayUtils.java b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/ShuffleArrayUtils.java new file mode 100644 index 0000000..f95b584 --- /dev/null +++ b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/ShuffleArrayUtils.java @@ -0,0 +1,33 @@ +package com.example.capstone2.andrognito.pinlockview; + +import java.util.Random; + +/** + * Created by aritraroy on 10/03/17. + */ + +public class ShuffleArrayUtils { + + /** + * Shuffle an array + * + * @param array + */ + static int[] shuffle(int[] array) { + int length = array.length; + Random random = new Random(); + random.nextInt(); + + for (int i = 0; i < length; i++) { + int change = i + random.nextInt(length - i); + swap(array, i, change); + } + return array; + } + + private static void swap(int[] array, int index, int change) { + int temp = array[index]; + array[index] = array[change]; + array[change] = temp; + } +} \ No newline at end of file diff --git a/lockscreen/src/main/java/com/example/capstone2/fingerprint/FingerPrintListener.java b/lockscreen/src/main/java/com/example/capstone2/fingerprint/FingerPrintListener.java new file mode 100644 index 0000000..ba45c8d --- /dev/null +++ b/lockscreen/src/main/java/com/example/capstone2/fingerprint/FingerPrintListener.java @@ -0,0 +1,18 @@ +package com.example.capstone2.fingerprint; + +/** + * Created by Arcane on 7/11/2017. + */ + +public interface FingerPrintListener { + + void onSuccess(); + + void onFailed(); + + void onError(CharSequence errorString); + + void onHelp(CharSequence helpString); + +} + diff --git a/lockscreen/src/main/java/com/example/capstone2/fingerprint/FingerprintHandler.java b/lockscreen/src/main/java/com/example/capstone2/fingerprint/FingerprintHandler.java new file mode 100644 index 0000000..298e673 --- /dev/null +++ b/lockscreen/src/main/java/com/example/capstone2/fingerprint/FingerprintHandler.java @@ -0,0 +1,71 @@ +package com.example.capstone2.fingerprint; + +/** + * Created by Arcane on 7/11/2017. + */ + +import android.Manifest; +import android.annotation.TargetApi; +import android.content.Context; +import android.content.pm.PackageManager; +import android.hardware.fingerprint.FingerprintManager; +import android.os.Build; +import android.os.CancellationSignal; +import android.support.v4.app.ActivityCompat; + +@TargetApi(Build.VERSION_CODES.M) +public class FingerprintHandler extends FingerprintManager.AuthenticationCallback { + + // You should use the CancellationSignal method whenever your app can no longer process user input, for example when your app goes + // into the background. If you don’t use this method, then other apps will be unable to access the touch sensor, including the lockscreen!// + + private CancellationSignal cancellationSignal; + private Context context; + private FingerPrintListener mfingerPrintListener; + + public FingerprintHandler(Context mContext) { + context = mContext; + } + + //Implement the startAuth method, which is responsible for starting the fingerprint authentication process// + + public void startAuth(FingerprintManager manager, FingerprintManager.CryptoObject cryptoObject) { + + cancellationSignal = new CancellationSignal(); + if (ActivityCompat.checkSelfPermission(context, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) { + return; + } + manager.authenticate(cryptoObject, cancellationSignal, 0, this, null); + } + + public void setFingerPrintListener(FingerPrintListener mfingerPrintListener) { + this.mfingerPrintListener = mfingerPrintListener; + } + + @Override + //onAuthenticationError is called when a fatal error has occurred. It provides the error code and error message as its parameters// + public void onAuthenticationError(int errMsgId, CharSequence errString) { + mfingerPrintListener.onError(errString); + } + + @Override + //onAuthenticationFailed is called when the fingerprint doesn’t match with any of the fingerprints registered on the device// + + public void onAuthenticationFailed() { + mfingerPrintListener.onFailed(); + } + + @Override + //onAuthenticationHelp is called when a non-fatal error has occurred. This method provides additional information about the error, + //so to provide the user with as much feedback as possible I’m incorporating this information into my toast// + public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { + mfingerPrintListener.onHelp(helpString); + } + + @Override + //onAuthenticationSucceeded is called when a fingerprint has been successfully matched to one of the fingerprints stored on the user’s device// + public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) { + mfingerPrintListener.onSuccess(); + } + +} diff --git a/lockscreen/src/main/java/com/example/capstone2/util/Animate.java b/lockscreen/src/main/java/com/example/capstone2/util/Animate.java new file mode 100644 index 0000000..b42e5e4 --- /dev/null +++ b/lockscreen/src/main/java/com/example/capstone2/util/Animate.java @@ -0,0 +1,19 @@ +package com.example.capstone2.util; + +import android.annotation.TargetApi; +import android.graphics.drawable.AnimatedVectorDrawable; +import android.os.Build; +import android.support.v7.widget.AppCompatImageView; + +/** + * Created by Arcane on 7/23/2017. + */ + +public class Animate { + + @TargetApi(Build.VERSION_CODES.M) + public static void animate(AppCompatImageView view, AnimatedVectorDrawable scanFingerprint) { + view.setImageDrawable(scanFingerprint); + scanFingerprint.start(); + } +} diff --git a/lockscreen/src/main/java/com/example/capstone2/util/Utils.java b/lockscreen/src/main/java/com/example/capstone2/util/Utils.java new file mode 100644 index 0000000..422187c --- /dev/null +++ b/lockscreen/src/main/java/com/example/capstone2/util/Utils.java @@ -0,0 +1,42 @@ +package com.example.capstone2.util; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * Created by pc on 07/16/2017. + */ + +public class Utils { + // utility function + private static String bytesToHexString(byte[] bytes) { + // http://stackoverflow.com/questions/332079 + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < bytes.length; i++) { + String hex = Integer.toHexString(0xFF & bytes[i]); + if (hex.length() == 1) { + sb.append('0'); + } + sb.append(hex); + } + return sb.toString(); + } + + // generate a hash + public static String sha256(String s) { + MessageDigest digest; + String hash; + + try { + digest = MessageDigest.getInstance("SHA-256"); + digest.update(s.getBytes()); + + hash = bytesToHexString(digest.digest()); + + return hash; + } catch (NoSuchAlgorithmException e1) { + return s; + } + } + +} diff --git a/lockscreen/src/main/res/animator/fingerprint_appear.xml b/lockscreen/src/main/res/animator/fingerprint_appear.xml new file mode 100644 index 0000000..6ca3e1b --- /dev/null +++ b/lockscreen/src/main/res/animator/fingerprint_appear.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + diff --git a/lockscreen/src/main/res/animator/hide_ridge.xml b/lockscreen/src/main/res/animator/hide_ridge.xml new file mode 100644 index 0000000..34bf246 --- /dev/null +++ b/lockscreen/src/main/res/animator/hide_ridge.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + diff --git a/lockscreen/src/main/res/animator/morph_ridge_2_to_cross_1.xml b/lockscreen/src/main/res/animator/morph_ridge_2_to_cross_1.xml new file mode 100644 index 0000000..c3dbd10 --- /dev/null +++ b/lockscreen/src/main/res/animator/morph_ridge_2_to_cross_1.xml @@ -0,0 +1,38 @@ + + + + + + + + + + diff --git a/lockscreen/src/main/res/animator/morph_ridge_2_to_tick.xml b/lockscreen/src/main/res/animator/morph_ridge_2_to_tick.xml new file mode 100644 index 0000000..7609385 --- /dev/null +++ b/lockscreen/src/main/res/animator/morph_ridge_2_to_tick.xml @@ -0,0 +1,38 @@ + + + + + + + + + + diff --git a/lockscreen/src/main/res/animator/morph_ridge_5_to_cross_2.xml b/lockscreen/src/main/res/animator/morph_ridge_5_to_cross_2.xml new file mode 100644 index 0000000..426b7a8 --- /dev/null +++ b/lockscreen/src/main/res/animator/morph_ridge_5_to_cross_2.xml @@ -0,0 +1,38 @@ + + + + + + + + + + diff --git a/lockscreen/src/main/res/animator/show_ridge.xml b/lockscreen/src/main/res/animator/show_ridge.xml new file mode 100644 index 0000000..fd46626 --- /dev/null +++ b/lockscreen/src/main/res/animator/show_ridge.xml @@ -0,0 +1,38 @@ + + + + + + + + + + diff --git a/lockscreen/src/main/res/drawable-hdpi/ic_backspace.png b/lockscreen/src/main/res/drawable-hdpi/ic_backspace.png new file mode 100644 index 0000000..c718c89 Binary files /dev/null and b/lockscreen/src/main/res/drawable-hdpi/ic_backspace.png differ diff --git a/lockscreen/src/main/res/drawable-hdpi/ic_fingerprint.png b/lockscreen/src/main/res/drawable-hdpi/ic_fingerprint.png new file mode 100644 index 0000000..b2f3994 Binary files /dev/null and b/lockscreen/src/main/res/drawable-hdpi/ic_fingerprint.png differ diff --git a/lockscreen/src/main/res/drawable-mdpi/ic_backspace.png b/lockscreen/src/main/res/drawable-mdpi/ic_backspace.png new file mode 100644 index 0000000..5b96855 Binary files /dev/null and b/lockscreen/src/main/res/drawable-mdpi/ic_backspace.png differ diff --git a/lockscreen/src/main/res/drawable-mdpi/ic_fingerprint.png b/lockscreen/src/main/res/drawable-mdpi/ic_fingerprint.png new file mode 100644 index 0000000..8a832ad Binary files /dev/null and b/lockscreen/src/main/res/drawable-mdpi/ic_fingerprint.png differ diff --git a/lockscreen/src/main/res/drawable-v23/fingerprint.xml b/lockscreen/src/main/res/drawable-v23/fingerprint.xml new file mode 100644 index 0000000..6794fb4 --- /dev/null +++ b/lockscreen/src/main/res/drawable-v23/fingerprint.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + diff --git a/lockscreen/src/main/res/drawable-v23/fingerprint_scan.xml b/lockscreen/src/main/res/drawable-v23/fingerprint_scan.xml new file mode 100644 index 0000000..a313e70 --- /dev/null +++ b/lockscreen/src/main/res/drawable-v23/fingerprint_scan.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lockscreen/src/main/res/drawable-v23/fingerprint_to_cross.xml b/lockscreen/src/main/res/drawable-v23/fingerprint_to_cross.xml new file mode 100644 index 0000000..30d0ddd --- /dev/null +++ b/lockscreen/src/main/res/drawable-v23/fingerprint_to_cross.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + diff --git a/lockscreen/src/main/res/drawable-v23/fingerprint_to_tick.xml b/lockscreen/src/main/res/drawable-v23/fingerprint_to_tick.xml new file mode 100644 index 0000000..9fd7f89 --- /dev/null +++ b/lockscreen/src/main/res/drawable-v23/fingerprint_to_tick.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + diff --git a/lockscreen/src/main/res/drawable-v23/show_fingerprint.xml b/lockscreen/src/main/res/drawable-v23/show_fingerprint.xml new file mode 100644 index 0000000..77618b0 --- /dev/null +++ b/lockscreen/src/main/res/drawable-v23/show_fingerprint.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + diff --git a/lockscreen/src/main/res/drawable-xhdpi/ic_backspace.png b/lockscreen/src/main/res/drawable-xhdpi/ic_backspace.png new file mode 100644 index 0000000..d2e924e Binary files /dev/null and b/lockscreen/src/main/res/drawable-xhdpi/ic_backspace.png differ diff --git a/lockscreen/src/main/res/drawable-xhdpi/ic_fingerprint.png b/lockscreen/src/main/res/drawable-xhdpi/ic_fingerprint.png new file mode 100644 index 0000000..8f54b79 Binary files /dev/null and b/lockscreen/src/main/res/drawable-xhdpi/ic_fingerprint.png differ diff --git a/lockscreen/src/main/res/drawable-xxhdpi/ic_backspace.png b/lockscreen/src/main/res/drawable-xxhdpi/ic_backspace.png new file mode 100644 index 0000000..95cd24d Binary files /dev/null and b/lockscreen/src/main/res/drawable-xxhdpi/ic_backspace.png differ diff --git a/lockscreen/src/main/res/drawable-xxhdpi/ic_fingerprint.png b/lockscreen/src/main/res/drawable-xxhdpi/ic_fingerprint.png new file mode 100644 index 0000000..b76edd7 Binary files /dev/null and b/lockscreen/src/main/res/drawable-xxhdpi/ic_fingerprint.png differ diff --git a/lockscreen/src/main/res/drawable-xxxhdpi/ic_backspace.png b/lockscreen/src/main/res/drawable-xxxhdpi/ic_backspace.png new file mode 100644 index 0000000..5f4c65b Binary files /dev/null and b/lockscreen/src/main/res/drawable-xxxhdpi/ic_backspace.png differ diff --git a/lockscreen/src/main/res/drawable-xxxhdpi/ic_fingerprint.png b/lockscreen/src/main/res/drawable-xxxhdpi/ic_fingerprint.png new file mode 100644 index 0000000..ef86914 Binary files /dev/null and b/lockscreen/src/main/res/drawable-xxxhdpi/ic_fingerprint.png differ diff --git a/lockscreen/src/main/res/drawable/circlebutton_notpressed.xml b/lockscreen/src/main/res/drawable/circlebutton_notpressed.xml new file mode 100644 index 0000000..aadda07 --- /dev/null +++ b/lockscreen/src/main/res/drawable/circlebutton_notpressed.xml @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/lockscreen/src/main/res/drawable/circlebutton_pressed.xml b/lockscreen/src/main/res/drawable/circlebutton_pressed.xml new file mode 100644 index 0000000..7b68db7 --- /dev/null +++ b/lockscreen/src/main/res/drawable/circlebutton_pressed.xml @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/lockscreen/src/main/res/drawable/circlebutton_selector.xml b/lockscreen/src/main/res/drawable/circlebutton_selector.xml new file mode 100644 index 0000000..168e805 --- /dev/null +++ b/lockscreen/src/main/res/drawable/circlebutton_selector.xml @@ -0,0 +1,5 @@ + + + + //means normal + \ No newline at end of file diff --git a/lockscreen/src/main/res/drawable/dot_empty.xml b/lockscreen/src/main/res/drawable/dot_empty.xml new file mode 100644 index 0000000..71a1bef --- /dev/null +++ b/lockscreen/src/main/res/drawable/dot_empty.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/lockscreen/src/main/res/drawable/dot_filled.xml b/lockscreen/src/main/res/drawable/dot_filled.xml new file mode 100644 index 0000000..7b68db7 --- /dev/null +++ b/lockscreen/src/main/res/drawable/dot_filled.xml @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/lockscreen/src/main/res/drawable/rectanglebutton_notpressed.xml b/lockscreen/src/main/res/drawable/rectanglebutton_notpressed.xml new file mode 100644 index 0000000..fc04ede --- /dev/null +++ b/lockscreen/src/main/res/drawable/rectanglebutton_notpressed.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/lockscreen/src/main/res/drawable/rectanglebutton_pressed.xml b/lockscreen/src/main/res/drawable/rectanglebutton_pressed.xml new file mode 100644 index 0000000..5e316b9 --- /dev/null +++ b/lockscreen/src/main/res/drawable/rectanglebutton_pressed.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/lockscreen/src/main/res/drawable/rectanglebutton_selector.xml b/lockscreen/src/main/res/drawable/rectanglebutton_selector.xml new file mode 100644 index 0000000..e7fbace --- /dev/null +++ b/lockscreen/src/main/res/drawable/rectanglebutton_selector.xml @@ -0,0 +1,5 @@ + + + + //means normal + \ No newline at end of file diff --git a/lockscreen/src/main/res/layout-v23/activity_enterpin.xml b/lockscreen/src/main/res/layout-v23/activity_enterpin.xml new file mode 100644 index 0000000..2fc895c --- /dev/null +++ b/lockscreen/src/main/res/layout-v23/activity_enterpin.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + diff --git a/lockscreen/src/main/res/layout/activity_enterpin.xml b/lockscreen/src/main/res/layout/activity_enterpin.xml new file mode 100644 index 0000000..bbdbd0a --- /dev/null +++ b/lockscreen/src/main/res/layout/activity_enterpin.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + diff --git a/lockscreen/src/main/res/layout/layout_delete_item.xml b/lockscreen/src/main/res/layout/layout_delete_item.xml new file mode 100644 index 0000000..fcdee71 --- /dev/null +++ b/lockscreen/src/main/res/layout/layout_delete_item.xml @@ -0,0 +1,29 @@ + + + + + + + + \ No newline at end of file diff --git a/lockscreen/src/main/res/layout/layout_number_item.xml b/lockscreen/src/main/res/layout/layout_number_item.xml new file mode 100644 index 0000000..48dc8ba --- /dev/null +++ b/lockscreen/src/main/res/layout/layout_number_item.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/lockscreen/src/main/res/values/attrs.xml b/lockscreen/src/main/res/values/attrs.xml new file mode 100644 index 0000000..f5ffc75 --- /dev/null +++ b/lockscreen/src/main/res/values/attrs.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lockscreen/src/main/res/values/colors.xml b/lockscreen/src/main/res/values/colors.xml new file mode 100644 index 0000000..24be60b --- /dev/null +++ b/lockscreen/src/main/res/values/colors.xml @@ -0,0 +1,14 @@ + + + @color/layout_background + @color/layout_background + #FF4081 + #fff + #dcddde + #18191A + #bfc0c1 + #FF87898C + #2136FFD9 + #87898C + #00000000 + \ No newline at end of file diff --git a/lockscreen/src/main/res/values/dimen.xml b/lockscreen/src/main/res/values/dimen.xml new file mode 100644 index 0000000..e461388 --- /dev/null +++ b/lockscreen/src/main/res/values/dimen.xml @@ -0,0 +1,47 @@ + + + // default + 39dp + 27dp + 40dp + 18dp + + // text size + 25sp + 60dp + + // dots + 12dp + 12.2dp + 25dp + + //attempts + 15dp + 13sp + + 10dp + + // layout + 500dp + 10dp + + // title + 20sp + 54dp + 10dp + 10dp + + 6dp + + // border + 0.5dp + 1dp + + // fingerprint + 22dp + 60dp + 80dp + 10dp + 14sp + + \ No newline at end of file diff --git a/lockscreen/src/main/res/values/fingerprint.xml b/lockscreen/src/main/res/values/fingerprint.xml new file mode 100644 index 0000000..f9efcd6 --- /dev/null +++ b/lockscreen/src/main/res/values/fingerprint.xml @@ -0,0 +1,49 @@ + + + + + + 160 + 160 + 80 + 5 + 7 + 80dp + 80dp + + + M56.2199243,45.3815903 C56.2199243,45.3815903 65.1913567,38.8543184 80.3604294,38.8543184 C95.5295022,38.8543184 103.720044,45.2851323 103.720044,45.2851323 + M45.5181172,67.9181841 C49.9761548,62.7019025 59.122049,49.7790452 80.2279027,49.7790452 C101.333756,49.7790452 110.740506,62.0384399 114.937688,67.9181841 + M51.7375623,107.718438 C51.7375623,107.718438 48.7129745,99.6302234 48.7129745,91.6334356 C48.7129745,81.3266864 55.9028711,60.2476586 80.4057228,60.2478671 C100.798248,60.2480407 112.457463,79.7942647 112.457463,88.386575 C112.457462,99.2963939 105.619846,103.039218 100.781849,102.18762 C95.9438519,101.336021 90.4490979,97.2187731 91.0639139,92.3178681 C91.67873,87.4169631 85.2177374,81.3265129 80.7504553,81.3266871 C73.0900115,81.3269859 69.8146331,85.3921834 69.8146329,92.2700585 C69.8146324,107.718437 83.7557525,121.02713 91.6787289,121.027128 + M70.7357889,121.024995 C70.7357889,121.024995 57.4650216,106.018711 58.8888756,91.5596242 C60.5513085,74.6777941 73.9858126,70.313752 80.3788823,70.3807995 C86.771952,70.4478469 101.550994,76.8218704 101.550997,92.2895418 + M80.1599645,91.1813411 C80.1599645,91.1813411 81.1144795,102.68333 86.7971146,107.529716 C93.2390527,113.023667 105.911091,112.342743 105.911091,112.342743 + + M52.2735573,79.0771904 C52.2735573,79.0771904 69.2403688,96.0440006 69.2403683,96.0440014 C69.2403679,96.0440021 109.820188,55.4641819 109.820188,55.4641819 + + M62,62 C62,62 80.5647617,80.5647617 80.564762,80.564762 C80.5647622,80.5647622 94.2921789,94.223714 98.5644262,98.5644262 + M98.5644262,62 C98.5644251,62 81.1979242,79.366503 81.1979229,79.3665033 C81.1979216,79.3665035 62,98.5644262 62,98.5644262 + + + M0 0 L160 0 L160 40 L0 40 Z + M0 120 L160 120 L160 160 L0 160 Z + + @color/colorAccent + #ff607d8b + + diff --git a/lockscreen/src/main/res/values/strings.xml b/lockscreen/src/main/res/values/strings.xml new file mode 100644 index 0000000..bc15b1a --- /dev/null +++ b/lockscreen/src/main/res/values/strings.xml @@ -0,0 +1,11 @@ + + LockScreen + Enter Pin Code + Set Pin Code + Wrong Pin Code: First Try + Wrong Pin Code: Second Try + Wrong Pin code + Enter Pin Code Again + Pin Codes Are Not The Same + Use Fingerprint + diff --git a/lockscreen/src/main/res/values/styles.xml b/lockscreen/src/main/res/values/styles.xml new file mode 100644 index 0000000..066d163 --- /dev/null +++ b/lockscreen/src/main/res/values/styles.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/lockscreen/src/test/java/com/example/capstone2/ExampleUnitTest.java b/lockscreen/src/test/java/com/example/capstone2/ExampleUnitTest.java new file mode 100644 index 0000000..7a67b48 --- /dev/null +++ b/lockscreen/src/test/java/com/example/capstone2/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.example.capstone2; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/sample/.gitignore b/sample/.gitignore new file mode 100644 index 0000000..0dd88b1 --- /dev/null +++ b/sample/.gitignore @@ -0,0 +1,11 @@ +*.iml +.gradle +gradle.properties +/gradle +/local.properties +/.idea +/.idea/libraries +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/sample/build.gradle b/sample/build.gradle new file mode 100644 index 0000000..2f4a4b4 --- /dev/null +++ b/sample/build.gradle @@ -0,0 +1,30 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 27 + defaultConfig { + applicationId "com.example.capstone1" + minSdkVersion 17 + targetSdkVersion 27 + versionCode 1 + versionName "1.0.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(include: ['*.jar'], dir: 'libs') + androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + exclude group: 'com.android.support', module: 'support-annotations' + }) + implementation 'com.android.support:appcompat-v7:27.1.1' + implementation 'com.android.support.constraint:constraint-layout:1.0.2' + testCompile 'junit:junit:4.12' + compile project(':lockscreen') +} diff --git a/sample/proguard-rules.pro b/sample/proguard-rules.pro new file mode 100644 index 0000000..5e4b346 --- /dev/null +++ b/sample/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in C:\Users\pc\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/sample/src/androidTest/java/com/example/sample/ExampleInstrumentedTest.java b/sample/src/androidTest/java/com/example/sample/ExampleInstrumentedTest.java new file mode 100644 index 0000000..de6b6d7 --- /dev/null +++ b/sample/src/androidTest/java/com/example/sample/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.example.sample; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumentation test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.amirarcane.sample", appContext.getPackageName()); + } +} diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml new file mode 100644 index 0000000..27abad3 --- /dev/null +++ b/sample/src/main/AndroidManifest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sample/src/main/assets/font/ALEAWB.TTF b/sample/src/main/assets/font/ALEAWB.TTF new file mode 100644 index 0000000..11f5d03 Binary files /dev/null and b/sample/src/main/assets/font/ALEAWB.TTF differ diff --git a/sample/src/main/assets/font/BLKCHCRY.TTF b/sample/src/main/assets/font/BLKCHCRY.TTF new file mode 100644 index 0000000..cca5091 Binary files /dev/null and b/sample/src/main/assets/font/BLKCHCRY.TTF differ diff --git a/sample/src/main/java/com/example/sample/Main3Activity.java b/sample/src/main/java/com/example/sample/Main3Activity.java new file mode 100644 index 0000000..aa0269e --- /dev/null +++ b/sample/src/main/java/com/example/sample/Main3Activity.java @@ -0,0 +1,87 @@ +package com.example.sample; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.Button; +import android.widget.Toast; + +import com.example.capstone2.activity.EnterPinActivity; + +public class Main3Activity extends AppCompatActivity { + + private static final String FONT_TEXT = "font/ALEAWB.TTF"; + private static final String FONT_NUMBER = "font/BLKCHCRY.TTF"; + + private static final int REQUEST_CODE = 123; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + Button normal = (Button) findViewById(R.id.normal); + Button setPin = (Button) findViewById(R.id.setPin); + Button setFont = (Button) findViewById(R.id.setFont); + Button setPinAndFont = (Button) findViewById(R.id.setPinAndFont); + + normal.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + // start the activity, It handles the setting and checking + Intent intent = new Intent(Main3Activity.this, EnterPinActivity.class); +// startActivity(intent); + + // for handling back press + startActivityForResult(intent, REQUEST_CODE); + } + }); + + setPin.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + // set pin instead of checking it + Intent intent = EnterPinActivity.getIntent(Main3Activity.this, true); + startActivity(intent); + + } + }); + + setFont.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + // setting font for library + Intent intent = EnterPinActivity.getIntent(Main3Activity.this, FONT_TEXT, FONT_NUMBER); + startActivity(intent); + } + }); + + setPinAndFont.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + // setting font for library and set pin instead of checking it + Intent intent = EnterPinActivity.getIntent(Main3Activity.this, true, FONT_TEXT, FONT_NUMBER); + startActivity(intent); + } + }); + + + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + switch (requestCode) { + case REQUEST_CODE: + if (resultCode == EnterPinActivity.RESULT_BACK_PRESSED) { + Toast.makeText(Main3Activity.this, "back pressed", Toast.LENGTH_LONG).show(); + } + break; + } + } +} diff --git a/sample/src/main/java/com/example/sample/MainActivity.java b/sample/src/main/java/com/example/sample/MainActivity.java new file mode 100644 index 0000000..67b6371 --- /dev/null +++ b/sample/src/main/java/com/example/sample/MainActivity.java @@ -0,0 +1,87 @@ +package com.example.sample; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.Button; +import android.widget.Toast; + +import com.example.capstone2.activity.EnterPinActivity; + +public class MainActivity extends AppCompatActivity { + + private static final String FONT_TEXT = "font/ALEAWB.TTF"; + private static final String FONT_NUMBER = "font/BLKCHCRY.TTF"; + + private static final int REQUEST_CODE = 123; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + Button normal = (Button) findViewById(R.id.normal); + Button setPin = (Button) findViewById(R.id.setPin); + Button setFont = (Button) findViewById(R.id.setFont); + Button setPinAndFont = (Button) findViewById(R.id.setPinAndFont); + + normal.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + // start the activity, It handles the setting and checking + Intent intent = new Intent(MainActivity.this, EnterPinActivity.class); +// startActivity(intent); + + // for handling back press + startActivityForResult(intent, REQUEST_CODE); + } + }); + + setPin.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + // set pin instead of checking it + Intent intent = EnterPinActivity.getIntent(MainActivity.this, true); + startActivity(intent); + + } + }); + + setFont.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + // setting font for library + Intent intent = EnterPinActivity.getIntent(MainActivity.this, FONT_TEXT, FONT_NUMBER); + startActivity(intent); + } + }); + + setPinAndFont.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + // setting font for library and set pin instead of checking it + Intent intent = EnterPinActivity.getIntent(MainActivity.this, true, FONT_TEXT, FONT_NUMBER); + startActivity(intent); + } + }); + + + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + switch (requestCode) { + case REQUEST_CODE: + if (resultCode == EnterPinActivity.RESULT_BACK_PRESSED) { + Toast.makeText(MainActivity.this, "back pressed", Toast.LENGTH_LONG).show(); + } + break; + } + } +} diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..b43922f --- /dev/null +++ b/sample/src/main/res/layout/activity_main.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + diff --git a/sample/src/main/res/layout/activity_main3.xml b/sample/src/main/res/layout/activity_main3.xml new file mode 100644 index 0000000..b43922f --- /dev/null +++ b/sample/src/main/res/layout/activity_main3.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + diff --git a/sample/src/main/res/mipmap-hdpi/ic_launcher.png b/sample/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..cde69bc Binary files /dev/null and b/sample/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png b/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..9a078e3 Binary files /dev/null and b/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/sample/src/main/res/mipmap-mdpi/ic_launcher.png b/sample/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..c133a0c Binary files /dev/null and b/sample/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png b/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..efc028a Binary files /dev/null and b/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/sample/src/main/res/mipmap-xhdpi/ic_launcher.png b/sample/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..bfa42f0 Binary files /dev/null and b/sample/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..3af2608 Binary files /dev/null and b/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png b/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..324e72c Binary files /dev/null and b/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..9bec2e6 Binary files /dev/null and b/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..aee44e1 Binary files /dev/null and b/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..34947cd Binary files /dev/null and b/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/sample/src/main/res/values/colors.xml b/sample/src/main/res/values/colors.xml new file mode 100644 index 0000000..3ab3e9c --- /dev/null +++ b/sample/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #3F51B5 + #303F9F + #FF4081 + diff --git a/sample/src/main/res/values/strings.xml b/sample/src/main/res/values/strings.xml new file mode 100644 index 0000000..1ad8c29 --- /dev/null +++ b/sample/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Sample + diff --git a/sample/src/main/res/values/styles.xml b/sample/src/main/res/values/styles.xml new file mode 100644 index 0000000..5885930 --- /dev/null +++ b/sample/src/main/res/values/styles.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/sample/src/test/java/com/example/sample/ExampleUnitTest.java b/sample/src/test/java/com/example/sample/ExampleUnitTest.java new file mode 100644 index 0000000..0702fe9 --- /dev/null +++ b/sample/src/test/java/com/example/sample/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.example.sample; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index e7b4def..c1099ad 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':app' +include ':sample', ':lockscreen',':app'
+ * Created by aritraroy on 01/06/16. + */ +public class IndicatorDots extends LinearLayout { + + @IntDef({IndicatorType.FIXED, IndicatorType.FILL, IndicatorType.FILL_WITH_ANIMATION}) + @Retention(RetentionPolicy.SOURCE) + public @interface IndicatorType { + int FIXED = 0; + int FILL = 1; + int FILL_WITH_ANIMATION = 2; + } + + private static final int DEFAULT_PIN_LENGTH = 4; + private static final int DEFAULT_ANIMATION_DURATION = 200; + + private int mDotDiameter; + private int mDotSpacing; + private int mFillDrawable; + private int mEmptyDrawable; + private int mPinLength; + private int mIndicatorType; + + private int mPreviousLength; + + public IndicatorDots(Context context) { + this(context, null); + } + + public IndicatorDots(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public IndicatorDots(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PinLockView); + + try { + mDotDiameter = (int) typedArray.getDimension(R.styleable.PinLockView_dotDiameter, ResourceUtils.getDimensionInPx(getContext(), R.dimen.dot_diameter)); + mDotSpacing = (int) typedArray.getDimension(R.styleable.PinLockView_dotSpacing, ResourceUtils.getDimensionInPx(getContext(), R.dimen.dot_spacing)); + mFillDrawable = typedArray.getResourceId(R.styleable.PinLockView_dotFilledBackground, + R.drawable.dot_filled); + mEmptyDrawable = typedArray.getResourceId(R.styleable.PinLockView_dotEmptyBackground, + R.drawable.dot_empty); + mPinLength = typedArray.getInt(R.styleable.PinLockView_pinLength, DEFAULT_PIN_LENGTH); + mIndicatorType = typedArray.getInt(R.styleable.PinLockView_indicatorType, + IndicatorType.FIXED); + } finally { + typedArray.recycle(); + } + + initView(context); + } + + private void initView(Context context) { + if (mIndicatorType == 0) { + for (int i = 0; i < mPinLength; i++) { + View dot = new View(context); + emptyDot(dot); + + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(mDotDiameter, + mDotDiameter); + params.setMargins(mDotSpacing, 0, mDotSpacing, 0); + dot.setLayoutParams(params); + + addView(dot); + } + } else if (mIndicatorType == 2) { + LayoutTransition layoutTransition = new LayoutTransition(); + layoutTransition.setDuration(DEFAULT_ANIMATION_DURATION); + layoutTransition.setStartDelay(layoutTransition.APPEARING, 0); + setLayoutTransition(layoutTransition); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + // If the indicator type is not fixed + if (mIndicatorType != 0) { + ViewGroup.LayoutParams params = this.getLayoutParams(); + params.height = mDotDiameter; + requestLayout(); + } + } + + void updateDot(int length) { + if (mIndicatorType == 0) { + if (length > 0) { + if (length > mPreviousLength) { + fillDot(getChildAt(length - 1)); + } else { + emptyDot(getChildAt(length)); + } + mPreviousLength = length; + } else { + // When {@code mPinLength} is 0, we need to reset all the views back to empty + for (int i = 0; i < getChildCount(); i++) { + View v = getChildAt(i); + emptyDot(v); + } + mPreviousLength = 0; + } + } else { + if (length > 0) { + if (length > mPreviousLength) { + View dot = new View(getContext()); + fillDot(dot); + + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(mDotDiameter, + mDotDiameter); + params.setMargins(mDotSpacing, 0, mDotSpacing, 0); + dot.setLayoutParams(params); + + addView(dot, length - 1); + } else { + removeViewAt(length); + } + mPreviousLength = length; + } else { + removeAllViews(); + mPreviousLength = 0; + } + } + } + + private void emptyDot(View dot) { + dot.setBackgroundResource(mEmptyDrawable); + } + + private void fillDot(View dot) { + dot.setBackgroundResource(mFillDrawable); + } + + public int getPinLength() { + return mPinLength; + } + + public void setPinLength(int pinLength) { + this.mPinLength = pinLength; + removeAllViews(); + initView(getContext()); + } + + public + @IndicatorType + int getIndicatorType() { + return mIndicatorType; + } + + public void setIndicatorType(@IndicatorType int type) { + this.mIndicatorType = type; + removeAllViews(); + initView(getContext()); + } +} diff --git a/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/ItemSpaceDecoration.java b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/ItemSpaceDecoration.java new file mode 100644 index 0000000..d034d95 --- /dev/null +++ b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/ItemSpaceDecoration.java @@ -0,0 +1,46 @@ +package com.example.capstone2.andrognito.pinlockview; + +import android.graphics.Rect; +import android.support.v7.widget.RecyclerView; +import android.view.View; + +/** + * Created by aritraroy on 31/05/16. + */ +public class ItemSpaceDecoration extends RecyclerView.ItemDecoration { + + private final int mHorizontalSpaceWidth; + private final int mVerticalSpaceHeight; + private final int mSpanCount; + private final boolean mIncludeEdge; + + public ItemSpaceDecoration(int horizontalSpaceWidth, int verticalSpaceHeight, int spanCount, boolean includeEdge) { + this.mHorizontalSpaceWidth = horizontalSpaceWidth; + this.mVerticalSpaceHeight = verticalSpaceHeight; + this.mSpanCount = spanCount; + this.mIncludeEdge = includeEdge; + } + + @Override + public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { + + int position = parent.getChildAdapterPosition(view); + int column = position % mSpanCount; + + if (mIncludeEdge) { + outRect.right = mHorizontalSpaceWidth - column * mHorizontalSpaceWidth / mSpanCount; + outRect.left = (column + 1) * mHorizontalSpaceWidth / mSpanCount; + + if (position < mSpanCount) { + outRect.top = mVerticalSpaceHeight; + } + outRect.bottom = mVerticalSpaceHeight; + } else { + outRect.right = column * mHorizontalSpaceWidth / mSpanCount; + outRect.left = mHorizontalSpaceWidth - (column + 1) * mHorizontalSpaceWidth / mSpanCount; + if (position >= mSpanCount) { + outRect.top = mVerticalSpaceHeight; + } + } + } +} diff --git a/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/PinLockAdapter.java b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/PinLockAdapter.java new file mode 100644 index 0000000..235814a --- /dev/null +++ b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/PinLockAdapter.java @@ -0,0 +1,283 @@ +package com.example.capstone2.andrognito.pinlockview; + +import android.graphics.PorterDuff; +import android.graphics.Typeface; +import android.os.Build; +import android.support.v7.widget.RecyclerView; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.Animation; +import android.view.animation.ScaleAnimation; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.LinearLayout; + +import com.example.capstone2.R; + +/** + * Created by aritraroy on 31/05/16. + */ +public class PinLockAdapter extends RecyclerView.Adapter { + + private static final int VIEW_TYPE_NUMBER = 0; + private static final int VIEW_TYPE_DELETE = 1; + + private CustomizationOptionsBundle mCustomizationOptionsBundle; + private OnNumberClickListener mOnNumberClickListener; + private OnDeleteClickListener mOnDeleteClickListener; + private int mPinLength; + private int BUTTON_ANIMATION_DURATION = 150; + + private int[] mKeyValues; + + private Typeface mTypeface = null; + + public PinLockAdapter() { + this.mKeyValues = getAdjustKeyValues(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}); + } + + public void setTypeFace(Typeface typeFace) { + mTypeface = typeFace; + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + RecyclerView.ViewHolder viewHolder; + LayoutInflater inflater = LayoutInflater.from(parent.getContext()); + + if (viewType == VIEW_TYPE_NUMBER) { + View view = inflater.inflate(R.layout.layout_number_item, parent, false); + viewHolder = new NumberViewHolder(view, mTypeface); + } else { + View view = inflater.inflate(R.layout.layout_delete_item, parent, false); + viewHolder = new DeleteViewHolder(view); + } + return viewHolder; + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + if (holder.getItemViewType() == VIEW_TYPE_NUMBER) { + NumberViewHolder vh1 = (NumberViewHolder) holder; + configureNumberButtonHolder(vh1, position); + } else if (holder.getItemViewType() == VIEW_TYPE_DELETE) { + DeleteViewHolder vh2 = (DeleteViewHolder) holder; + configureDeleteButtonHolder(vh2); + } + } + + private void configureNumberButtonHolder(NumberViewHolder holder, int position) { + if (holder != null) { + if (position == 9) { + holder.mNumberButton.setVisibility(View.GONE); + } else { + holder.mNumberButton.setText(String.valueOf(mKeyValues[position])); + holder.mNumberButton.setVisibility(View.VISIBLE); + holder.mNumberButton.setTag(mKeyValues[position]); + } + + if (mCustomizationOptionsBundle != null) { + holder.mNumberButton.setTextColor(mCustomizationOptionsBundle.getTextColor()); + if (mCustomizationOptionsBundle.getButtonBackgroundDrawable() != null) { + if (Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) { + holder.mNumberButton.setBackgroundDrawable( + mCustomizationOptionsBundle.getButtonBackgroundDrawable()); + } else { + holder.mNumberButton.setBackground( + mCustomizationOptionsBundle.getButtonBackgroundDrawable()); + } + } + holder.mNumberButton.setTextSize(TypedValue.COMPLEX_UNIT_PX, + mCustomizationOptionsBundle.getTextSize()); + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( + mCustomizationOptionsBundle.getButtonSize(), + mCustomizationOptionsBundle.getButtonSize()); + holder.mNumberButton.setLayoutParams(params); + } + } + } + + private void configureDeleteButtonHolder(DeleteViewHolder holder) { + if (holder != null) { + if (mCustomizationOptionsBundle.isShowDeleteButton() && mPinLength > 0) { + holder.mButtonImage.setVisibility(View.VISIBLE); + if (mCustomizationOptionsBundle.getDeleteButtonDrawable() != null) { + holder.mButtonImage.setImageDrawable(mCustomizationOptionsBundle.getDeleteButtonDrawable()); + } + holder.mButtonImage.setColorFilter(mCustomizationOptionsBundle.getTextColor(), + PorterDuff.Mode.SRC_ATOP); + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( + mCustomizationOptionsBundle.getDeleteButtonWidthSize(), + mCustomizationOptionsBundle.getDeleteButtonHeightSize()); + holder.mButtonImage.setLayoutParams(params); + } + } + } + + @Override + public int getItemCount() { + return 12; + } + + @Override + public int getItemViewType(int position) { + if (position == getItemCount() - 1) { + return VIEW_TYPE_DELETE; + } + return VIEW_TYPE_NUMBER; + } + + public int getPinLength() { + return mPinLength; + } + + public void setPinLength(int pinLength) { + this.mPinLength = pinLength; + } + + public int[] getKeyValues() { + return mKeyValues; + } + + public void setKeyValues(int[] keyValues) { + this.mKeyValues = getAdjustKeyValues(keyValues); + notifyDataSetChanged(); + } + + private int[] getAdjustKeyValues(int[] keyValues) { + int[] adjustedKeyValues = new int[keyValues.length + 1]; + for (int i = 0; i < keyValues.length; i++) { + if (i < 9) { + adjustedKeyValues[i] = keyValues[i]; + } else { + adjustedKeyValues[i] = -1; + adjustedKeyValues[i + 1] = keyValues[i]; + } + } + return adjustedKeyValues; + } + + public OnNumberClickListener getOnItemClickListener() { + return mOnNumberClickListener; + } + + public void setOnItemClickListener(OnNumberClickListener onNumberClickListener) { + this.mOnNumberClickListener = onNumberClickListener; + } + + public OnDeleteClickListener getOnDeleteClickListener() { + return mOnDeleteClickListener; + } + + public void setOnDeleteClickListener(OnDeleteClickListener onDeleteClickListener) { + this.mOnDeleteClickListener = onDeleteClickListener; + } + + public CustomizationOptionsBundle getCustomizationOptions() { + return mCustomizationOptionsBundle; + } + + public void setCustomizationOptions(CustomizationOptionsBundle customizationOptionsBundle) { + this.mCustomizationOptionsBundle = customizationOptionsBundle; + } + + public interface OnNumberClickListener { + void onNumberClicked(int keyValue); + } + + public interface OnDeleteClickListener { + void onDeleteClicked(); + + void onDeleteLongClicked(); + } + + public class NumberViewHolder extends RecyclerView.ViewHolder { + Button mNumberButton; + + public NumberViewHolder(final View itemView, Typeface font) { + super(itemView); + mNumberButton = (Button) itemView.findViewById(R.id.button); + + if (font != null) { + mNumberButton.setTypeface(font); + } + + mNumberButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mOnNumberClickListener != null) { + mOnNumberClickListener.onNumberClicked((Integer) v.getTag()); + } + } + }); + + mNumberButton.setOnTouchListener(new View.OnTouchListener() { + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + mNumberButton.startAnimation(scale()); + } + + return false; + } + }); + } + } + + public class DeleteViewHolder extends RecyclerView.ViewHolder { + LinearLayout mDeleteButton; + ImageView mButtonImage; + + public DeleteViewHolder(final View itemView) { + super(itemView); + mDeleteButton = (LinearLayout) itemView.findViewById(R.id.button); + mButtonImage = (ImageView) itemView.findViewById(R.id.buttonImage); + + if (mCustomizationOptionsBundle.isShowDeleteButton() && mPinLength > 0) { + mDeleteButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mOnDeleteClickListener != null) { + mOnDeleteClickListener.onDeleteClicked(); + } + } + }); + + mDeleteButton.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (mOnDeleteClickListener != null) { + mOnDeleteClickListener.onDeleteLongClicked(); + } + return true; + } + }); + + mDeleteButton.setOnTouchListener(new View.OnTouchListener() { + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + + mDeleteButton.startAnimation(scale()); + } + + return false; + } + }); + } + } + } + + private Animation scale(){ + ScaleAnimation scaleAnimation = new ScaleAnimation(.75F, 1f, .75F, 1f, + Animation.RELATIVE_TO_SELF, .5F, Animation.RELATIVE_TO_SELF, .5F); + scaleAnimation.setDuration(BUTTON_ANIMATION_DURATION); + scaleAnimation.setFillAfter(true); + return scaleAnimation; + } +} diff --git a/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/PinLockListener.java b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/PinLockListener.java new file mode 100644 index 0000000..126b74e --- /dev/null +++ b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/PinLockListener.java @@ -0,0 +1,32 @@ +package com.example.capstone2.andrognito.pinlockview; + +/** + * The listener that triggers callbacks for various events + * in the {@link PinLockView} + * + * Created by aritraroy on 31/05/16. + */ +public interface PinLockListener { + + /** + * Triggers when the complete pin is entered, + * depends on the pin length set by the user + * + * @param pin the complete pin + */ + void onComplete(String pin); + + + /** + * Triggers when the pin is empty after manual deletion + */ + void onEmpty(); + + /** + * Triggers on a key press on the {@link PinLockView} + * + * @param pinLength the current pin length + * @param intermediatePin the intermediate pin + */ + void onPinChange(int pinLength, String intermediatePin); +} diff --git a/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/PinLockView.java b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/PinLockView.java new file mode 100644 index 0000000..14171b5 --- /dev/null +++ b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/PinLockView.java @@ -0,0 +1,462 @@ +package com.example.capstone2.andrognito.pinlockview; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.support.annotation.Nullable; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; + +import com.example.capstone2.R; + +/** + * Represents a numeric lock view which can used to taken numbers as input. + * The length of the input can be customized using {@link PinLockView#setPinLength(int)}, the default value being 4 + * + * It can also be used as dial pad for taking number inputs. + * Optionally, {@link IndicatorDots} can be attached to this view to indicate the length of the input taken + * Created by aritraroy on 31/05/16. + */ +public class PinLockView extends RecyclerView { + + private static final int DEFAULT_PIN_LENGTH = 4; + private static final int[] DEFAULT_KEY_SET = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}; + + private String mPin = ""; + private int mPinLength; + private int mHorizontalSpacing, mVerticalSpacing; + private int mTextColor, mDeleteButtonPressedColor; + private int mTextSize, mButtonSize, mDeleteButtonWidthSize, mDeleteButtonHeightSize; + private Drawable mButtonBackgroundDrawable; + private Drawable mDeleteButtonDrawable; + private boolean mShowDeleteButton; + + private IndicatorDots mIndicatorDots; + private PinLockAdapter mAdapter; + private PinLockListener mPinLockListener; + private CustomizationOptionsBundle mCustomizationOptionsBundle; + private int[] mCustomKeySet; + + private PinLockAdapter.OnNumberClickListener mOnNumberClickListener + = new PinLockAdapter.OnNumberClickListener() { + @Override + public void onNumberClicked(int keyValue) { + if (mPin.length() < getPinLength()) { + mPin = mPin.concat(String.valueOf(keyValue)); + + if (isIndicatorDotsAttached()) { + mIndicatorDots.updateDot(mPin.length()); + } + + if (mPin.length() == 1) { + mAdapter.setPinLength(mPin.length()); + mAdapter.notifyItemChanged(mAdapter.getItemCount() - 1); + } + + if (mPinLockListener != null) { + if (mPin.length() == mPinLength) { + mPinLockListener.onComplete(mPin); + } else { + mPinLockListener.onPinChange(mPin.length(), mPin); + } + } + } else { + if (!isShowDeleteButton()) { + resetPinLockView(); + mPin = mPin.concat(String.valueOf(keyValue)); + + if (isIndicatorDotsAttached()) { + mIndicatorDots.updateDot(mPin.length()); + } + + if (mPinLockListener != null) { + mPinLockListener.onPinChange(mPin.length(), mPin); + } + + } else { + if (mPinLockListener != null) { + mPinLockListener.onComplete(mPin); + } + } + } + } + }; + + private PinLockAdapter.OnDeleteClickListener mOnDeleteClickListener + = new PinLockAdapter.OnDeleteClickListener() { + @Override + public void onDeleteClicked() { + if (mPin.length() > 0) { + mPin = mPin.substring(0, mPin.length() - 1); + + if (isIndicatorDotsAttached()) { + mIndicatorDots.updateDot(mPin.length()); + } + + if (mPin.length() == 0) { + mAdapter.setPinLength(mPin.length()); + mAdapter.notifyItemChanged(mAdapter.getItemCount() - 1); + } + + if (mPinLockListener != null) { + if (mPin.length() == 0) { + mPinLockListener.onEmpty(); + clearInternalPin(); + } else { + mPinLockListener.onPinChange(mPin.length(), mPin); + } + } + } else { + if (mPinLockListener != null) { + mPinLockListener.onEmpty(); + } + } + } + + @Override + public void onDeleteLongClicked() { + resetPinLockView(); + if (mPinLockListener != null) { + mPinLockListener.onEmpty(); + } + } + }; + + public PinLockView(Context context) { + super(context); + init(null, 0); + } + + public PinLockView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(attrs, 0); + } + + public PinLockView(Context context, @Nullable AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(attrs, defStyle); + } + + private void init(AttributeSet attributeSet, int defStyle) { + + TypedArray typedArray = getContext().obtainStyledAttributes(attributeSet, R.styleable.PinLockView); + + try { + mPinLength = typedArray.getInt(R.styleable.PinLockView_pinLength, DEFAULT_PIN_LENGTH); + mHorizontalSpacing = (int) typedArray.getDimension(R.styleable.PinLockView_keypadHorizontalSpacing, ResourceUtils.getDimensionInPx(getContext(), R.dimen.default_horizontal_spacing)); + mVerticalSpacing = (int) typedArray.getDimension(R.styleable.PinLockView_keypadVerticalSpacing, ResourceUtils.getDimensionInPx(getContext(), R.dimen.default_vertical_spacing)); + mTextColor = typedArray.getColor(R.styleable.PinLockView_keypadTextColor, ResourceUtils.getColor(getContext(), R.color.text_numberpressed)); + mTextSize = (int) typedArray.getDimension(R.styleable.PinLockView_keypadTextSize, ResourceUtils.getDimensionInPx(getContext(), R.dimen.default_text_size)); + mButtonSize = (int) typedArray.getDimension(R.styleable.PinLockView_keypadButtonSize, ResourceUtils.getDimensionInPx(getContext(), R.dimen.default_button_size)); + mDeleteButtonWidthSize = (int) typedArray.getDimension(R.styleable.PinLockView_keypadDeleteButtonSize, ResourceUtils.getDimensionInPx(getContext(), R.dimen.default_delete_button_size_width)); + mDeleteButtonHeightSize = (int) typedArray.getDimension(R.styleable.PinLockView_keypadDeleteButtonSize, ResourceUtils.getDimensionInPx(getContext(), R.dimen.default_delete_button_size_height)); + mButtonBackgroundDrawable = typedArray.getDrawable(R.styleable.PinLockView_keypadButtonBackgroundDrawable); + mDeleteButtonDrawable = typedArray.getDrawable(R.styleable.PinLockView_keypadDeleteButtonDrawable); + mShowDeleteButton = typedArray.getBoolean(R.styleable.PinLockView_keypadShowDeleteButton, true); + mDeleteButtonPressedColor = typedArray.getColor(R.styleable.PinLockView_keypadDeleteButtonPressedColor, ResourceUtils.getColor(getContext(), R.color.text_numberpressed)); + } finally { + typedArray.recycle(); + } + + mCustomizationOptionsBundle = new CustomizationOptionsBundle(); + mCustomizationOptionsBundle.setTextColor(mTextColor); + mCustomizationOptionsBundle.setTextSize(mTextSize); + mCustomizationOptionsBundle.setButtonSize(mButtonSize); + mCustomizationOptionsBundle.setButtonBackgroundDrawable(mButtonBackgroundDrawable); + mCustomizationOptionsBundle.setDeleteButtonDrawable(mDeleteButtonDrawable); + mCustomizationOptionsBundle.setDeleteButtonWidthSize(mDeleteButtonWidthSize); + mCustomizationOptionsBundle.setDeleteButtonHeightSize(mDeleteButtonHeightSize); + mCustomizationOptionsBundle.setShowDeleteButton(mShowDeleteButton); + mCustomizationOptionsBundle.setDeleteButtonPressesColor(mDeleteButtonPressedColor); + + initView(); + } + + private void initView() { + setLayoutManager(new GridLayoutManager(getContext(), 3)); + + mAdapter = new PinLockAdapter(); + mAdapter.setOnItemClickListener(mOnNumberClickListener); + mAdapter.setOnDeleteClickListener(mOnDeleteClickListener); + mAdapter.setCustomizationOptions(mCustomizationOptionsBundle); + setAdapter(mAdapter); + + addItemDecoration(new ItemSpaceDecoration(mHorizontalSpacing, mVerticalSpacing, 3, false)); + setOverScrollMode(OVER_SCROLL_NEVER); + } + + public void setTypeFace(Typeface typeFace) { + mAdapter.setTypeFace(typeFace); + } + + /** + * Sets a {@link PinLockListener} to the to listen to pin update events + * + * @param pinLockListener the listener + */ + public void setPinLockListener(PinLockListener pinLockListener) { + this.mPinLockListener = pinLockListener; + } + + /** + * Get the length of the current pin length + * + * @return the length of the pin + */ + public int getPinLength() { + return mPinLength; + } + + /** + * Sets the pin length dynamically + * + * @param pinLength the pin length + */ + public void setPinLength(int pinLength) { + this.mPinLength = pinLength; + + if (isIndicatorDotsAttached()) { + mIndicatorDots.setPinLength(pinLength); + } + } + + /** + * Get the text color in the buttons + * + * @return the text color + */ + public int getTextColor() { + return mTextColor; + } + + /** + * Set the text color of the buttons dynamically + * + * @param textColor the text color + */ + public void setTextColor(int textColor) { + this.mTextColor = textColor; + mCustomizationOptionsBundle.setTextColor(textColor); + mAdapter.notifyDataSetChanged(); + } + + /** + * Get the size of the text in the buttons + * + * @return the size of the text in pixels + */ + public int getTextSize() { + return mTextSize; + } + + /** + * Set the size of text in pixels + * + * @param textSize the text size in pixels + */ + public void setTextSize(int textSize) { + this.mTextSize = textSize; + mCustomizationOptionsBundle.setTextSize(textSize); + mAdapter.notifyDataSetChanged(); + } + + /** + * Get the size of the pin buttons + * + * @return the size of the button in pixels + */ + public int getButtonSize() { + return mButtonSize; + } + + /** + * Set the size of the pin buttons dynamically + * + * @param buttonSize the button size + */ + public void setButtonSize(int buttonSize) { + this.mButtonSize = buttonSize; + mCustomizationOptionsBundle.setButtonSize(buttonSize); + mAdapter.notifyDataSetChanged(); + } + + /** + * Get the current background drawable of the buttons, can be null + * + * @return the background drawable + */ + public Drawable getButtonBackgroundDrawable() { + return mButtonBackgroundDrawable; + } + + /** + * Set the background drawable of the buttons dynamically + * + * @param buttonBackgroundDrawable the background drawable + */ + public void setButtonBackgroundDrawable(Drawable buttonBackgroundDrawable) { + this.mButtonBackgroundDrawable = buttonBackgroundDrawable; + mCustomizationOptionsBundle.setButtonBackgroundDrawable(buttonBackgroundDrawable); + mAdapter.notifyDataSetChanged(); + } + + /** + * Get the drawable of the delete button + * + * @return the delete button drawable + */ + public Drawable getDeleteButtonDrawable() { + return mDeleteButtonDrawable; + } + + /** + * Set the drawable of the delete button dynamically + * + * @param deleteBackgroundDrawable the delete button drawable + */ + public void setDeleteButtonDrawable(Drawable deleteBackgroundDrawable) { + this.mDeleteButtonDrawable = deleteBackgroundDrawable; + mCustomizationOptionsBundle.setDeleteButtonDrawable(deleteBackgroundDrawable); + mAdapter.notifyDataSetChanged(); + } + + /** + * Get the delete button width size in pixels + * + * @return size in pixels + */ + public int getDeleteButtonWidthSize() { + return mDeleteButtonWidthSize; + } + + /** + * Get the delete button size height in pixels + * + * @return size in pixels + */ + public int getDeleteButtonHeightSize() { + return mDeleteButtonHeightSize; + } + + /** + * Set the size of the delete button width in pixels + * + * @param deleteButtonWidthSize size in pixels + */ + public void setDeleteButtonWidthSize(int deleteButtonWidthSize) { + this.mDeleteButtonWidthSize = deleteButtonWidthSize; + mCustomizationOptionsBundle.setDeleteButtonWidthSize(deleteButtonWidthSize); + mAdapter.notifyDataSetChanged(); + } + + /** + * Set the size of the delete button height in pixels + * + * @param deleteButtonHeightSize size in pixels + */ + public void setDeleteButtonHeightSize(int deleteButtonHeightSize) { + this.mDeleteButtonHeightSize = deleteButtonHeightSize; + mCustomizationOptionsBundle.setDeleteButtonWidthSize(deleteButtonHeightSize); + mAdapter.notifyDataSetChanged(); + } + + /** + * Is the delete button shown + * + * @return returns true if shown, false otherwise + */ + public boolean isShowDeleteButton() { + return mShowDeleteButton; + } + + /** + * Dynamically set if the delete button should be shown + * + * @param showDeleteButton true if the delete button should be shown, false otherwise + */ + public void setShowDeleteButton(boolean showDeleteButton) { + this.mShowDeleteButton = showDeleteButton; + mCustomizationOptionsBundle.setShowDeleteButton(showDeleteButton); + mAdapter.notifyDataSetChanged(); + } + + /** + * Get the delete button pressed/focused state color + * + * @return color of the button + */ + public int getDeleteButtonPressedColor() { + return mDeleteButtonPressedColor; + } + + /** + * Set the pressed/focused state color of the delete button + * + * @param deleteButtonPressedColor the color of the delete button + */ + public void setDeleteButtonPressedColor(int deleteButtonPressedColor) { + this.mDeleteButtonPressedColor = deleteButtonPressedColor; + mCustomizationOptionsBundle.setDeleteButtonPressesColor(deleteButtonPressedColor); + mAdapter.notifyDataSetChanged(); + } + + public int[] getCustomKeySet() { + return mCustomKeySet; + } + + public void setCustomKeySet(int[] customKeySet) { + this.mCustomKeySet = customKeySet; + + if (mAdapter != null) { + mAdapter.setKeyValues(customKeySet); + } + } + + public void enableLayoutShuffling() { + this.mCustomKeySet = ShuffleArrayUtils.shuffle(DEFAULT_KEY_SET); + + if (mAdapter != null) { + mAdapter.setKeyValues(mCustomKeySet); + } + } + + private void clearInternalPin() { + mPin = ""; + } + + /** + * Resets the {@link PinLockView}, clearing the entered pin + * and resetting the {@link IndicatorDots} if attached + */ + public void resetPinLockView() { + + clearInternalPin(); + + mAdapter.setPinLength(mPin.length()); + mAdapter.notifyItemChanged(mAdapter.getItemCount() - 1); + + if (mIndicatorDots != null) { + mIndicatorDots.updateDot(mPin.length()); + } + } + + /** + * Returns true if {@link IndicatorDots} are attached to {@link PinLockView} + * + * @return true if attached, false otherwise + */ + public boolean isIndicatorDotsAttached() { + return mIndicatorDots != null; + } + + /** + * Attaches {@link IndicatorDots} to {@link PinLockView} + * + * @param mIndicatorDots the view to attach + */ + public void attachIndicatorDots(IndicatorDots mIndicatorDots) { + this.mIndicatorDots = mIndicatorDots; + } +} diff --git a/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/ResourceUtils.java b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/ResourceUtils.java new file mode 100644 index 0000000..dc397eb --- /dev/null +++ b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/ResourceUtils.java @@ -0,0 +1,30 @@ +package com.example.capstone2.andrognito.pinlockview; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.support.annotation.ColorRes; +import android.support.annotation.DimenRes; +import android.support.annotation.DrawableRes; +import android.support.v4.content.ContextCompat; + +/** + * Created by aritraroy on 10/06/16. + */ +public class ResourceUtils { + + private ResourceUtils() { + throw new AssertionError(); + } + + public static int getColor(Context context, @ColorRes int id) { + return ContextCompat.getColor(context, id); + } + + public static float getDimensionInPx(Context context, @DimenRes int id) { + return context.getResources().getDimension(id); + } + + public static Drawable getDrawable(Context context, @DrawableRes int id) { + return ContextCompat.getDrawable(context, id); + } +} diff --git a/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/ShuffleArrayUtils.java b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/ShuffleArrayUtils.java new file mode 100644 index 0000000..f95b584 --- /dev/null +++ b/lockscreen/src/main/java/com/example/capstone2/andrognito/pinlockview/ShuffleArrayUtils.java @@ -0,0 +1,33 @@ +package com.example.capstone2.andrognito.pinlockview; + +import java.util.Random; + +/** + * Created by aritraroy on 10/03/17. + */ + +public class ShuffleArrayUtils { + + /** + * Shuffle an array + * + * @param array + */ + static int[] shuffle(int[] array) { + int length = array.length; + Random random = new Random(); + random.nextInt(); + + for (int i = 0; i < length; i++) { + int change = i + random.nextInt(length - i); + swap(array, i, change); + } + return array; + } + + private static void swap(int[] array, int index, int change) { + int temp = array[index]; + array[index] = array[change]; + array[change] = temp; + } +} \ No newline at end of file diff --git a/lockscreen/src/main/java/com/example/capstone2/fingerprint/FingerPrintListener.java b/lockscreen/src/main/java/com/example/capstone2/fingerprint/FingerPrintListener.java new file mode 100644 index 0000000..ba45c8d --- /dev/null +++ b/lockscreen/src/main/java/com/example/capstone2/fingerprint/FingerPrintListener.java @@ -0,0 +1,18 @@ +package com.example.capstone2.fingerprint; + +/** + * Created by Arcane on 7/11/2017. + */ + +public interface FingerPrintListener { + + void onSuccess(); + + void onFailed(); + + void onError(CharSequence errorString); + + void onHelp(CharSequence helpString); + +} + diff --git a/lockscreen/src/main/java/com/example/capstone2/fingerprint/FingerprintHandler.java b/lockscreen/src/main/java/com/example/capstone2/fingerprint/FingerprintHandler.java new file mode 100644 index 0000000..298e673 --- /dev/null +++ b/lockscreen/src/main/java/com/example/capstone2/fingerprint/FingerprintHandler.java @@ -0,0 +1,71 @@ +package com.example.capstone2.fingerprint; + +/** + * Created by Arcane on 7/11/2017. + */ + +import android.Manifest; +import android.annotation.TargetApi; +import android.content.Context; +import android.content.pm.PackageManager; +import android.hardware.fingerprint.FingerprintManager; +import android.os.Build; +import android.os.CancellationSignal; +import android.support.v4.app.ActivityCompat; + +@TargetApi(Build.VERSION_CODES.M) +public class FingerprintHandler extends FingerprintManager.AuthenticationCallback { + + // You should use the CancellationSignal method whenever your app can no longer process user input, for example when your app goes + // into the background. If you don’t use this method, then other apps will be unable to access the touch sensor, including the lockscreen!// + + private CancellationSignal cancellationSignal; + private Context context; + private FingerPrintListener mfingerPrintListener; + + public FingerprintHandler(Context mContext) { + context = mContext; + } + + //Implement the startAuth method, which is responsible for starting the fingerprint authentication process// + + public void startAuth(FingerprintManager manager, FingerprintManager.CryptoObject cryptoObject) { + + cancellationSignal = new CancellationSignal(); + if (ActivityCompat.checkSelfPermission(context, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) { + return; + } + manager.authenticate(cryptoObject, cancellationSignal, 0, this, null); + } + + public void setFingerPrintListener(FingerPrintListener mfingerPrintListener) { + this.mfingerPrintListener = mfingerPrintListener; + } + + @Override + //onAuthenticationError is called when a fatal error has occurred. It provides the error code and error message as its parameters// + public void onAuthenticationError(int errMsgId, CharSequence errString) { + mfingerPrintListener.onError(errString); + } + + @Override + //onAuthenticationFailed is called when the fingerprint doesn’t match with any of the fingerprints registered on the device// + + public void onAuthenticationFailed() { + mfingerPrintListener.onFailed(); + } + + @Override + //onAuthenticationHelp is called when a non-fatal error has occurred. This method provides additional information about the error, + //so to provide the user with as much feedback as possible I’m incorporating this information into my toast// + public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { + mfingerPrintListener.onHelp(helpString); + } + + @Override + //onAuthenticationSucceeded is called when a fingerprint has been successfully matched to one of the fingerprints stored on the user’s device// + public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) { + mfingerPrintListener.onSuccess(); + } + +} diff --git a/lockscreen/src/main/java/com/example/capstone2/util/Animate.java b/lockscreen/src/main/java/com/example/capstone2/util/Animate.java new file mode 100644 index 0000000..b42e5e4 --- /dev/null +++ b/lockscreen/src/main/java/com/example/capstone2/util/Animate.java @@ -0,0 +1,19 @@ +package com.example.capstone2.util; + +import android.annotation.TargetApi; +import android.graphics.drawable.AnimatedVectorDrawable; +import android.os.Build; +import android.support.v7.widget.AppCompatImageView; + +/** + * Created by Arcane on 7/23/2017. + */ + +public class Animate { + + @TargetApi(Build.VERSION_CODES.M) + public static void animate(AppCompatImageView view, AnimatedVectorDrawable scanFingerprint) { + view.setImageDrawable(scanFingerprint); + scanFingerprint.start(); + } +} diff --git a/lockscreen/src/main/java/com/example/capstone2/util/Utils.java b/lockscreen/src/main/java/com/example/capstone2/util/Utils.java new file mode 100644 index 0000000..422187c --- /dev/null +++ b/lockscreen/src/main/java/com/example/capstone2/util/Utils.java @@ -0,0 +1,42 @@ +package com.example.capstone2.util; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * Created by pc on 07/16/2017. + */ + +public class Utils { + // utility function + private static String bytesToHexString(byte[] bytes) { + // http://stackoverflow.com/questions/332079 + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < bytes.length; i++) { + String hex = Integer.toHexString(0xFF & bytes[i]); + if (hex.length() == 1) { + sb.append('0'); + } + sb.append(hex); + } + return sb.toString(); + } + + // generate a hash + public static String sha256(String s) { + MessageDigest digest; + String hash; + + try { + digest = MessageDigest.getInstance("SHA-256"); + digest.update(s.getBytes()); + + hash = bytesToHexString(digest.digest()); + + return hash; + } catch (NoSuchAlgorithmException e1) { + return s; + } + } + +} diff --git a/lockscreen/src/main/res/animator/fingerprint_appear.xml b/lockscreen/src/main/res/animator/fingerprint_appear.xml new file mode 100644 index 0000000..6ca3e1b --- /dev/null +++ b/lockscreen/src/main/res/animator/fingerprint_appear.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + diff --git a/lockscreen/src/main/res/animator/hide_ridge.xml b/lockscreen/src/main/res/animator/hide_ridge.xml new file mode 100644 index 0000000..34bf246 --- /dev/null +++ b/lockscreen/src/main/res/animator/hide_ridge.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + diff --git a/lockscreen/src/main/res/animator/morph_ridge_2_to_cross_1.xml b/lockscreen/src/main/res/animator/morph_ridge_2_to_cross_1.xml new file mode 100644 index 0000000..c3dbd10 --- /dev/null +++ b/lockscreen/src/main/res/animator/morph_ridge_2_to_cross_1.xml @@ -0,0 +1,38 @@ + + + + + + + + + + diff --git a/lockscreen/src/main/res/animator/morph_ridge_2_to_tick.xml b/lockscreen/src/main/res/animator/morph_ridge_2_to_tick.xml new file mode 100644 index 0000000..7609385 --- /dev/null +++ b/lockscreen/src/main/res/animator/morph_ridge_2_to_tick.xml @@ -0,0 +1,38 @@ + + + + + + + + + + diff --git a/lockscreen/src/main/res/animator/morph_ridge_5_to_cross_2.xml b/lockscreen/src/main/res/animator/morph_ridge_5_to_cross_2.xml new file mode 100644 index 0000000..426b7a8 --- /dev/null +++ b/lockscreen/src/main/res/animator/morph_ridge_5_to_cross_2.xml @@ -0,0 +1,38 @@ + + + + + + + + + + diff --git a/lockscreen/src/main/res/animator/show_ridge.xml b/lockscreen/src/main/res/animator/show_ridge.xml new file mode 100644 index 0000000..fd46626 --- /dev/null +++ b/lockscreen/src/main/res/animator/show_ridge.xml @@ -0,0 +1,38 @@ + + + + + + + + + + diff --git a/lockscreen/src/main/res/drawable-hdpi/ic_backspace.png b/lockscreen/src/main/res/drawable-hdpi/ic_backspace.png new file mode 100644 index 0000000..c718c89 Binary files /dev/null and b/lockscreen/src/main/res/drawable-hdpi/ic_backspace.png differ diff --git a/lockscreen/src/main/res/drawable-hdpi/ic_fingerprint.png b/lockscreen/src/main/res/drawable-hdpi/ic_fingerprint.png new file mode 100644 index 0000000..b2f3994 Binary files /dev/null and b/lockscreen/src/main/res/drawable-hdpi/ic_fingerprint.png differ diff --git a/lockscreen/src/main/res/drawable-mdpi/ic_backspace.png b/lockscreen/src/main/res/drawable-mdpi/ic_backspace.png new file mode 100644 index 0000000..5b96855 Binary files /dev/null and b/lockscreen/src/main/res/drawable-mdpi/ic_backspace.png differ diff --git a/lockscreen/src/main/res/drawable-mdpi/ic_fingerprint.png b/lockscreen/src/main/res/drawable-mdpi/ic_fingerprint.png new file mode 100644 index 0000000..8a832ad Binary files /dev/null and b/lockscreen/src/main/res/drawable-mdpi/ic_fingerprint.png differ diff --git a/lockscreen/src/main/res/drawable-v23/fingerprint.xml b/lockscreen/src/main/res/drawable-v23/fingerprint.xml new file mode 100644 index 0000000..6794fb4 --- /dev/null +++ b/lockscreen/src/main/res/drawable-v23/fingerprint.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + diff --git a/lockscreen/src/main/res/drawable-v23/fingerprint_scan.xml b/lockscreen/src/main/res/drawable-v23/fingerprint_scan.xml new file mode 100644 index 0000000..a313e70 --- /dev/null +++ b/lockscreen/src/main/res/drawable-v23/fingerprint_scan.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lockscreen/src/main/res/drawable-v23/fingerprint_to_cross.xml b/lockscreen/src/main/res/drawable-v23/fingerprint_to_cross.xml new file mode 100644 index 0000000..30d0ddd --- /dev/null +++ b/lockscreen/src/main/res/drawable-v23/fingerprint_to_cross.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + diff --git a/lockscreen/src/main/res/drawable-v23/fingerprint_to_tick.xml b/lockscreen/src/main/res/drawable-v23/fingerprint_to_tick.xml new file mode 100644 index 0000000..9fd7f89 --- /dev/null +++ b/lockscreen/src/main/res/drawable-v23/fingerprint_to_tick.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + diff --git a/lockscreen/src/main/res/drawable-v23/show_fingerprint.xml b/lockscreen/src/main/res/drawable-v23/show_fingerprint.xml new file mode 100644 index 0000000..77618b0 --- /dev/null +++ b/lockscreen/src/main/res/drawable-v23/show_fingerprint.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + diff --git a/lockscreen/src/main/res/drawable-xhdpi/ic_backspace.png b/lockscreen/src/main/res/drawable-xhdpi/ic_backspace.png new file mode 100644 index 0000000..d2e924e Binary files /dev/null and b/lockscreen/src/main/res/drawable-xhdpi/ic_backspace.png differ diff --git a/lockscreen/src/main/res/drawable-xhdpi/ic_fingerprint.png b/lockscreen/src/main/res/drawable-xhdpi/ic_fingerprint.png new file mode 100644 index 0000000..8f54b79 Binary files /dev/null and b/lockscreen/src/main/res/drawable-xhdpi/ic_fingerprint.png differ diff --git a/lockscreen/src/main/res/drawable-xxhdpi/ic_backspace.png b/lockscreen/src/main/res/drawable-xxhdpi/ic_backspace.png new file mode 100644 index 0000000..95cd24d Binary files /dev/null and b/lockscreen/src/main/res/drawable-xxhdpi/ic_backspace.png differ diff --git a/lockscreen/src/main/res/drawable-xxhdpi/ic_fingerprint.png b/lockscreen/src/main/res/drawable-xxhdpi/ic_fingerprint.png new file mode 100644 index 0000000..b76edd7 Binary files /dev/null and b/lockscreen/src/main/res/drawable-xxhdpi/ic_fingerprint.png differ diff --git a/lockscreen/src/main/res/drawable-xxxhdpi/ic_backspace.png b/lockscreen/src/main/res/drawable-xxxhdpi/ic_backspace.png new file mode 100644 index 0000000..5f4c65b Binary files /dev/null and b/lockscreen/src/main/res/drawable-xxxhdpi/ic_backspace.png differ diff --git a/lockscreen/src/main/res/drawable-xxxhdpi/ic_fingerprint.png b/lockscreen/src/main/res/drawable-xxxhdpi/ic_fingerprint.png new file mode 100644 index 0000000..ef86914 Binary files /dev/null and b/lockscreen/src/main/res/drawable-xxxhdpi/ic_fingerprint.png differ diff --git a/lockscreen/src/main/res/drawable/circlebutton_notpressed.xml b/lockscreen/src/main/res/drawable/circlebutton_notpressed.xml new file mode 100644 index 0000000..aadda07 --- /dev/null +++ b/lockscreen/src/main/res/drawable/circlebutton_notpressed.xml @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/lockscreen/src/main/res/drawable/circlebutton_pressed.xml b/lockscreen/src/main/res/drawable/circlebutton_pressed.xml new file mode 100644 index 0000000..7b68db7 --- /dev/null +++ b/lockscreen/src/main/res/drawable/circlebutton_pressed.xml @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/lockscreen/src/main/res/drawable/circlebutton_selector.xml b/lockscreen/src/main/res/drawable/circlebutton_selector.xml new file mode 100644 index 0000000..168e805 --- /dev/null +++ b/lockscreen/src/main/res/drawable/circlebutton_selector.xml @@ -0,0 +1,5 @@ + + + + //means normal + \ No newline at end of file diff --git a/lockscreen/src/main/res/drawable/dot_empty.xml b/lockscreen/src/main/res/drawable/dot_empty.xml new file mode 100644 index 0000000..71a1bef --- /dev/null +++ b/lockscreen/src/main/res/drawable/dot_empty.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/lockscreen/src/main/res/drawable/dot_filled.xml b/lockscreen/src/main/res/drawable/dot_filled.xml new file mode 100644 index 0000000..7b68db7 --- /dev/null +++ b/lockscreen/src/main/res/drawable/dot_filled.xml @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/lockscreen/src/main/res/drawable/rectanglebutton_notpressed.xml b/lockscreen/src/main/res/drawable/rectanglebutton_notpressed.xml new file mode 100644 index 0000000..fc04ede --- /dev/null +++ b/lockscreen/src/main/res/drawable/rectanglebutton_notpressed.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/lockscreen/src/main/res/drawable/rectanglebutton_pressed.xml b/lockscreen/src/main/res/drawable/rectanglebutton_pressed.xml new file mode 100644 index 0000000..5e316b9 --- /dev/null +++ b/lockscreen/src/main/res/drawable/rectanglebutton_pressed.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/lockscreen/src/main/res/drawable/rectanglebutton_selector.xml b/lockscreen/src/main/res/drawable/rectanglebutton_selector.xml new file mode 100644 index 0000000..e7fbace --- /dev/null +++ b/lockscreen/src/main/res/drawable/rectanglebutton_selector.xml @@ -0,0 +1,5 @@ + + + + //means normal + \ No newline at end of file diff --git a/lockscreen/src/main/res/layout-v23/activity_enterpin.xml b/lockscreen/src/main/res/layout-v23/activity_enterpin.xml new file mode 100644 index 0000000..2fc895c --- /dev/null +++ b/lockscreen/src/main/res/layout-v23/activity_enterpin.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + diff --git a/lockscreen/src/main/res/layout/activity_enterpin.xml b/lockscreen/src/main/res/layout/activity_enterpin.xml new file mode 100644 index 0000000..bbdbd0a --- /dev/null +++ b/lockscreen/src/main/res/layout/activity_enterpin.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + diff --git a/lockscreen/src/main/res/layout/layout_delete_item.xml b/lockscreen/src/main/res/layout/layout_delete_item.xml new file mode 100644 index 0000000..fcdee71 --- /dev/null +++ b/lockscreen/src/main/res/layout/layout_delete_item.xml @@ -0,0 +1,29 @@ + + + + + + + + \ No newline at end of file diff --git a/lockscreen/src/main/res/layout/layout_number_item.xml b/lockscreen/src/main/res/layout/layout_number_item.xml new file mode 100644 index 0000000..48dc8ba --- /dev/null +++ b/lockscreen/src/main/res/layout/layout_number_item.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/lockscreen/src/main/res/values/attrs.xml b/lockscreen/src/main/res/values/attrs.xml new file mode 100644 index 0000000..f5ffc75 --- /dev/null +++ b/lockscreen/src/main/res/values/attrs.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lockscreen/src/main/res/values/colors.xml b/lockscreen/src/main/res/values/colors.xml new file mode 100644 index 0000000..24be60b --- /dev/null +++ b/lockscreen/src/main/res/values/colors.xml @@ -0,0 +1,14 @@ + + + @color/layout_background + @color/layout_background + #FF4081 + #fff + #dcddde + #18191A + #bfc0c1 + #FF87898C + #2136FFD9 + #87898C + #00000000 + \ No newline at end of file diff --git a/lockscreen/src/main/res/values/dimen.xml b/lockscreen/src/main/res/values/dimen.xml new file mode 100644 index 0000000..e461388 --- /dev/null +++ b/lockscreen/src/main/res/values/dimen.xml @@ -0,0 +1,47 @@ + + + // default + 39dp + 27dp + 40dp + 18dp + + // text size + 25sp + 60dp + + // dots + 12dp + 12.2dp + 25dp + + //attempts + 15dp + 13sp + + 10dp + + // layout + 500dp + 10dp + + // title + 20sp + 54dp + 10dp + 10dp + + 6dp + + // border + 0.5dp + 1dp + + // fingerprint + 22dp + 60dp + 80dp + 10dp + 14sp + + \ No newline at end of file diff --git a/lockscreen/src/main/res/values/fingerprint.xml b/lockscreen/src/main/res/values/fingerprint.xml new file mode 100644 index 0000000..f9efcd6 --- /dev/null +++ b/lockscreen/src/main/res/values/fingerprint.xml @@ -0,0 +1,49 @@ + + + + + + 160 + 160 + 80 + 5 + 7 + 80dp + 80dp + + + M56.2199243,45.3815903 C56.2199243,45.3815903 65.1913567,38.8543184 80.3604294,38.8543184 C95.5295022,38.8543184 103.720044,45.2851323 103.720044,45.2851323 + M45.5181172,67.9181841 C49.9761548,62.7019025 59.122049,49.7790452 80.2279027,49.7790452 C101.333756,49.7790452 110.740506,62.0384399 114.937688,67.9181841 + M51.7375623,107.718438 C51.7375623,107.718438 48.7129745,99.6302234 48.7129745,91.6334356 C48.7129745,81.3266864 55.9028711,60.2476586 80.4057228,60.2478671 C100.798248,60.2480407 112.457463,79.7942647 112.457463,88.386575 C112.457462,99.2963939 105.619846,103.039218 100.781849,102.18762 C95.9438519,101.336021 90.4490979,97.2187731 91.0639139,92.3178681 C91.67873,87.4169631 85.2177374,81.3265129 80.7504553,81.3266871 C73.0900115,81.3269859 69.8146331,85.3921834 69.8146329,92.2700585 C69.8146324,107.718437 83.7557525,121.02713 91.6787289,121.027128 + M70.7357889,121.024995 C70.7357889,121.024995 57.4650216,106.018711 58.8888756,91.5596242 C60.5513085,74.6777941 73.9858126,70.313752 80.3788823,70.3807995 C86.771952,70.4478469 101.550994,76.8218704 101.550997,92.2895418 + M80.1599645,91.1813411 C80.1599645,91.1813411 81.1144795,102.68333 86.7971146,107.529716 C93.2390527,113.023667 105.911091,112.342743 105.911091,112.342743 + + M52.2735573,79.0771904 C52.2735573,79.0771904 69.2403688,96.0440006 69.2403683,96.0440014 C69.2403679,96.0440021 109.820188,55.4641819 109.820188,55.4641819 + + M62,62 C62,62 80.5647617,80.5647617 80.564762,80.564762 C80.5647622,80.5647622 94.2921789,94.223714 98.5644262,98.5644262 + M98.5644262,62 C98.5644251,62 81.1979242,79.366503 81.1979229,79.3665033 C81.1979216,79.3665035 62,98.5644262 62,98.5644262 + + + M0 0 L160 0 L160 40 L0 40 Z + M0 120 L160 120 L160 160 L0 160 Z + + @color/colorAccent + #ff607d8b + + diff --git a/lockscreen/src/main/res/values/strings.xml b/lockscreen/src/main/res/values/strings.xml new file mode 100644 index 0000000..bc15b1a --- /dev/null +++ b/lockscreen/src/main/res/values/strings.xml @@ -0,0 +1,11 @@ + + LockScreen + Enter Pin Code + Set Pin Code + Wrong Pin Code: First Try + Wrong Pin Code: Second Try + Wrong Pin code + Enter Pin Code Again + Pin Codes Are Not The Same + Use Fingerprint + diff --git a/lockscreen/src/main/res/values/styles.xml b/lockscreen/src/main/res/values/styles.xml new file mode 100644 index 0000000..066d163 --- /dev/null +++ b/lockscreen/src/main/res/values/styles.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/lockscreen/src/test/java/com/example/capstone2/ExampleUnitTest.java b/lockscreen/src/test/java/com/example/capstone2/ExampleUnitTest.java new file mode 100644 index 0000000..7a67b48 --- /dev/null +++ b/lockscreen/src/test/java/com/example/capstone2/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.example.capstone2; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/sample/.gitignore b/sample/.gitignore new file mode 100644 index 0000000..0dd88b1 --- /dev/null +++ b/sample/.gitignore @@ -0,0 +1,11 @@ +*.iml +.gradle +gradle.properties +/gradle +/local.properties +/.idea +/.idea/libraries +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/sample/build.gradle b/sample/build.gradle new file mode 100644 index 0000000..2f4a4b4 --- /dev/null +++ b/sample/build.gradle @@ -0,0 +1,30 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 27 + defaultConfig { + applicationId "com.example.capstone1" + minSdkVersion 17 + targetSdkVersion 27 + versionCode 1 + versionName "1.0.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(include: ['*.jar'], dir: 'libs') + androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + exclude group: 'com.android.support', module: 'support-annotations' + }) + implementation 'com.android.support:appcompat-v7:27.1.1' + implementation 'com.android.support.constraint:constraint-layout:1.0.2' + testCompile 'junit:junit:4.12' + compile project(':lockscreen') +} diff --git a/sample/proguard-rules.pro b/sample/proguard-rules.pro new file mode 100644 index 0000000..5e4b346 --- /dev/null +++ b/sample/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in C:\Users\pc\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/sample/src/androidTest/java/com/example/sample/ExampleInstrumentedTest.java b/sample/src/androidTest/java/com/example/sample/ExampleInstrumentedTest.java new file mode 100644 index 0000000..de6b6d7 --- /dev/null +++ b/sample/src/androidTest/java/com/example/sample/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.example.sample; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumentation test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.amirarcane.sample", appContext.getPackageName()); + } +} diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml new file mode 100644 index 0000000..27abad3 --- /dev/null +++ b/sample/src/main/AndroidManifest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sample/src/main/assets/font/ALEAWB.TTF b/sample/src/main/assets/font/ALEAWB.TTF new file mode 100644 index 0000000..11f5d03 Binary files /dev/null and b/sample/src/main/assets/font/ALEAWB.TTF differ diff --git a/sample/src/main/assets/font/BLKCHCRY.TTF b/sample/src/main/assets/font/BLKCHCRY.TTF new file mode 100644 index 0000000..cca5091 Binary files /dev/null and b/sample/src/main/assets/font/BLKCHCRY.TTF differ diff --git a/sample/src/main/java/com/example/sample/Main3Activity.java b/sample/src/main/java/com/example/sample/Main3Activity.java new file mode 100644 index 0000000..aa0269e --- /dev/null +++ b/sample/src/main/java/com/example/sample/Main3Activity.java @@ -0,0 +1,87 @@ +package com.example.sample; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.Button; +import android.widget.Toast; + +import com.example.capstone2.activity.EnterPinActivity; + +public class Main3Activity extends AppCompatActivity { + + private static final String FONT_TEXT = "font/ALEAWB.TTF"; + private static final String FONT_NUMBER = "font/BLKCHCRY.TTF"; + + private static final int REQUEST_CODE = 123; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + Button normal = (Button) findViewById(R.id.normal); + Button setPin = (Button) findViewById(R.id.setPin); + Button setFont = (Button) findViewById(R.id.setFont); + Button setPinAndFont = (Button) findViewById(R.id.setPinAndFont); + + normal.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + // start the activity, It handles the setting and checking + Intent intent = new Intent(Main3Activity.this, EnterPinActivity.class); +// startActivity(intent); + + // for handling back press + startActivityForResult(intent, REQUEST_CODE); + } + }); + + setPin.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + // set pin instead of checking it + Intent intent = EnterPinActivity.getIntent(Main3Activity.this, true); + startActivity(intent); + + } + }); + + setFont.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + // setting font for library + Intent intent = EnterPinActivity.getIntent(Main3Activity.this, FONT_TEXT, FONT_NUMBER); + startActivity(intent); + } + }); + + setPinAndFont.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + // setting font for library and set pin instead of checking it + Intent intent = EnterPinActivity.getIntent(Main3Activity.this, true, FONT_TEXT, FONT_NUMBER); + startActivity(intent); + } + }); + + + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + switch (requestCode) { + case REQUEST_CODE: + if (resultCode == EnterPinActivity.RESULT_BACK_PRESSED) { + Toast.makeText(Main3Activity.this, "back pressed", Toast.LENGTH_LONG).show(); + } + break; + } + } +} diff --git a/sample/src/main/java/com/example/sample/MainActivity.java b/sample/src/main/java/com/example/sample/MainActivity.java new file mode 100644 index 0000000..67b6371 --- /dev/null +++ b/sample/src/main/java/com/example/sample/MainActivity.java @@ -0,0 +1,87 @@ +package com.example.sample; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.Button; +import android.widget.Toast; + +import com.example.capstone2.activity.EnterPinActivity; + +public class MainActivity extends AppCompatActivity { + + private static final String FONT_TEXT = "font/ALEAWB.TTF"; + private static final String FONT_NUMBER = "font/BLKCHCRY.TTF"; + + private static final int REQUEST_CODE = 123; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + Button normal = (Button) findViewById(R.id.normal); + Button setPin = (Button) findViewById(R.id.setPin); + Button setFont = (Button) findViewById(R.id.setFont); + Button setPinAndFont = (Button) findViewById(R.id.setPinAndFont); + + normal.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + // start the activity, It handles the setting and checking + Intent intent = new Intent(MainActivity.this, EnterPinActivity.class); +// startActivity(intent); + + // for handling back press + startActivityForResult(intent, REQUEST_CODE); + } + }); + + setPin.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + // set pin instead of checking it + Intent intent = EnterPinActivity.getIntent(MainActivity.this, true); + startActivity(intent); + + } + }); + + setFont.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + // setting font for library + Intent intent = EnterPinActivity.getIntent(MainActivity.this, FONT_TEXT, FONT_NUMBER); + startActivity(intent); + } + }); + + setPinAndFont.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + // setting font for library and set pin instead of checking it + Intent intent = EnterPinActivity.getIntent(MainActivity.this, true, FONT_TEXT, FONT_NUMBER); + startActivity(intent); + } + }); + + + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + switch (requestCode) { + case REQUEST_CODE: + if (resultCode == EnterPinActivity.RESULT_BACK_PRESSED) { + Toast.makeText(MainActivity.this, "back pressed", Toast.LENGTH_LONG).show(); + } + break; + } + } +} diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..b43922f --- /dev/null +++ b/sample/src/main/res/layout/activity_main.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + diff --git a/sample/src/main/res/layout/activity_main3.xml b/sample/src/main/res/layout/activity_main3.xml new file mode 100644 index 0000000..b43922f --- /dev/null +++ b/sample/src/main/res/layout/activity_main3.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + diff --git a/sample/src/main/res/mipmap-hdpi/ic_launcher.png b/sample/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..cde69bc Binary files /dev/null and b/sample/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png b/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..9a078e3 Binary files /dev/null and b/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/sample/src/main/res/mipmap-mdpi/ic_launcher.png b/sample/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..c133a0c Binary files /dev/null and b/sample/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png b/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..efc028a Binary files /dev/null and b/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/sample/src/main/res/mipmap-xhdpi/ic_launcher.png b/sample/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..bfa42f0 Binary files /dev/null and b/sample/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..3af2608 Binary files /dev/null and b/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png b/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..324e72c Binary files /dev/null and b/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..9bec2e6 Binary files /dev/null and b/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..aee44e1 Binary files /dev/null and b/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..34947cd Binary files /dev/null and b/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/sample/src/main/res/values/colors.xml b/sample/src/main/res/values/colors.xml new file mode 100644 index 0000000..3ab3e9c --- /dev/null +++ b/sample/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #3F51B5 + #303F9F + #FF4081 + diff --git a/sample/src/main/res/values/strings.xml b/sample/src/main/res/values/strings.xml new file mode 100644 index 0000000..1ad8c29 --- /dev/null +++ b/sample/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Sample + diff --git a/sample/src/main/res/values/styles.xml b/sample/src/main/res/values/styles.xml new file mode 100644 index 0000000..5885930 --- /dev/null +++ b/sample/src/main/res/values/styles.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/sample/src/test/java/com/example/sample/ExampleUnitTest.java b/sample/src/test/java/com/example/sample/ExampleUnitTest.java new file mode 100644 index 0000000..0702fe9 --- /dev/null +++ b/sample/src/test/java/com/example/sample/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.example.sample; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index e7b4def..c1099ad 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':app' +include ':sample', ':lockscreen',':app'