From e01380090ddadfcd3620f8793d6db611048e345b Mon Sep 17 00:00:00 2001 From: tom5079 Date: Sun, 23 Jun 2019 10:27:07 +0900 Subject: [PATCH] Added lock --- .../java/xyz/quaver/pupil/ui/LockActivity.kt | 66 +++++++++- .../java/xyz/quaver/pupil/ui/MainActivity.kt | 15 ++- .../quaver/pupil/ui/PatternLockFragment.kt | 3 +- .../xyz/quaver/pupil/ui/SettingsActivity.kt | 123 +++++++++++++++++- .../main/java/xyz/quaver/pupil/util/lock.kt | 70 +++++++++- app/src/main/res/layout/activity_lock.xml | 10 ++ app/src/main/res/values-ja/strings.xml | 9 ++ app/src/main/res/values-ko/strings.xml | 9 ++ app/src/main/res/values/strings.xml | 11 ++ app/src/main/res/xml/lock_preferences.xml | 26 ++++ 10 files changed, 325 insertions(+), 17 deletions(-) create mode 100644 app/src/main/res/xml/lock_preferences.xml diff --git a/app/src/main/java/xyz/quaver/pupil/ui/LockActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/LockActivity.kt index 30f1ce1b..d8eee511 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/LockActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/LockActivity.kt @@ -1,10 +1,17 @@ package xyz.quaver.pupil.ui +import android.app.Activity import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.Toast +import com.andrognito.patternlockview.PatternLockView +import com.google.android.material.snackbar.Snackbar import kotlinx.android.synthetic.main.activity_lock.* +import kotlinx.android.synthetic.main.fragment_pattern_lock.* +import kotlinx.android.synthetic.main.settings_activity.* import xyz.quaver.pupil.R +import xyz.quaver.pupil.util.Lock +import xyz.quaver.pupil.util.LockManager class LockActivity : AppCompatActivity() { @@ -12,18 +19,67 @@ class LockActivity : AppCompatActivity() { super.onCreate(savedInstanceState) setContentView(R.layout.activity_lock) + val lockManager = LockManager(this) + + val mode = intent.getStringExtra("mode") + + lock_pattern.isEnabled = false + lock_pin.isEnabled = false + lock_fingerprint.isEnabled = false + lock_password.isEnabled = false + + when(mode) { + null -> { + if (lockManager.empty()) { + setResult(RESULT_OK) + finish() + } + } + "add_lock" -> { + when(intent.getStringExtra("type")!!) { + "pattern" -> { + + } + } + } + } + supportFragmentManager.beginTransaction().add( R.id.lock_content, PatternLockFragment().apply { + var lastPass = "" onPatternDrawn = { - Toast.makeText(context, it, Toast.LENGTH_SHORT).show() + when(mode) { + null -> { + val result = lockManager.check(it) + + if (result == true) { + setResult(Activity.RESULT_OK) + finish() + } else + lock_pattern_view.setViewMode(PatternLockView.PatternViewMode.WRONG) + } + "add_lock" -> { + if (lastPass.isEmpty()) { + lastPass = it + + Snackbar.make(view!!, R.string.settings_lock_confirm, Snackbar.LENGTH_LONG).show() + } else { + if (lastPass == it) { + LockManager(context!!).add(Lock.generate(Lock.Type.PATTERN, it)) + finish() + } else { + lock_pattern_view.setViewMode(PatternLockView.PatternViewMode.WRONG) + lastPass = "" + + Snackbar.make(view!!, R.string.settings_lock_wrong_confirm, Snackbar.LENGTH_LONG).show() + } + } + } + } } } ).commit() - - lock_pattern.isEnabled = false - lock_fingerprint.isEnabled = false - lock_password.isEnabled = false } } diff --git a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt index 87cd9027..8b59da1c 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt @@ -1,6 +1,7 @@ package xyz.quaver.pupil.ui import android.Manifest +import android.app.Activity import android.content.Intent import android.content.pm.PackageManager import android.graphics.drawable.Animatable @@ -76,7 +77,8 @@ class MainActivity : AppCompatActivity() { private var mode = Mode.SEARCH - private val SETTINGS = 45162 + private val REQUEST_SETTINGS = 45162 + private val REQUEST_LOCK = 561 private var galleryIDs: Deferred>? = null private var totalItems = 0 @@ -90,6 +92,8 @@ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + startActivityForResult(Intent(this, LockActivity::class.java), REQUEST_LOCK) + checkPermissions() val preference = PreferenceManager.getDefaultSharedPreferences(this) @@ -143,6 +147,7 @@ class MainActivity : AppCompatActivity() { WindowManager.LayoutParams.FLAG_SECURE) else window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE) + super.onResume() } @@ -187,7 +192,7 @@ class MainActivity : AppCompatActivity() { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when(requestCode) { - SETTINGS -> { + REQUEST_SETTINGS -> { runOnUiThread { cancelFetch() clearGalleries() @@ -195,6 +200,10 @@ class MainActivity : AppCompatActivity() { loadBlocks() } } + REQUEST_LOCK -> { + if (resultCode != Activity.RESULT_OK) + finish() + } } } @@ -679,7 +688,7 @@ class MainActivity : AppCompatActivity() { setOnMenuItemClickListener { when(it.itemId) { - R.id.main_menu_settings -> startActivityForResult(Intent(this@MainActivity, SettingsActivity::class.java), SETTINGS) + R.id.main_menu_settings -> startActivityForResult(Intent(this@MainActivity, SettingsActivity::class.java), REQUEST_SETTINGS) R.id.main_menu_jump -> { val preference = PreferenceManager.getDefaultSharedPreferences(context) val perPage = preference.getString("per_page", "25")!!.toInt() diff --git a/app/src/main/java/xyz/quaver/pupil/ui/PatternLockFragment.kt b/app/src/main/java/xyz/quaver/pupil/ui/PatternLockFragment.kt index 73a7197f..11d8a53b 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/PatternLockFragment.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/PatternLockFragment.kt @@ -12,6 +12,7 @@ import kotlinx.android.synthetic.main.fragment_pattern_lock.* import kotlinx.android.synthetic.main.fragment_pattern_lock.view.* import xyz.quaver.pupil.R import xyz.quaver.pupil.util.hash +import xyz.quaver.pupil.util.hashWithSalt class PatternLockFragment : Fragment(), PatternLockViewListener { @@ -32,7 +33,7 @@ class PatternLockFragment : Fragment(), PatternLockViewListener { override fun onComplete(pattern: MutableList?) { val password = PatternLockUtils.patternToMD5(lock_pattern_view, pattern) - onPatternDrawn?.invoke(hash(password)) + onPatternDrawn?.invoke(password) } override fun onProgress(progressPattern: MutableList?) { diff --git a/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt index 850ed55d..1f995222 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt @@ -1,5 +1,7 @@ package xyz.quaver.pupil.ui +import android.app.Activity +import android.content.Intent import android.os.Bundle import android.text.Editable import android.text.TextWatcher @@ -14,14 +16,17 @@ import androidx.appcompat.app.AppCompatActivity import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceManager -import io.fabric.sdk.android.BuildConfig import kotlinx.android.synthetic.main.dialog_default_query.view.* import xyz.quaver.pupil.R import xyz.quaver.pupil.types.Tags +import xyz.quaver.pupil.util.Lock +import xyz.quaver.pupil.util.LockManager import java.io.File class SettingsActivity : AppCompatActivity() { + val REQUEST_LOCK = 38238 + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -59,6 +64,24 @@ class SettingsActivity : AppCompatActivity() { "TB" //really? ) + override fun onResume() { + super.onResume() + + val lockManager = LockManager(context!!) + + findPreference("app_lock")?.summary = if (lockManager.locks.isNullOrEmpty()) { + getString(R.string.settings_lock_none) + } else { + lockManager.locks?.joinToString(", ") { + when(it.type) { + Lock.Type.PATTERN -> getString(R.string.settings_lock_pattern) + Lock.Type.PIN -> getString(R.string.settings_lock_pin) + Lock.Type.PASSWORD -> getString(R.string.settings_lock_password) + } + } + } + } + private fun getDirSize(dir: File) : String { var size = dir.walk().map { it.length() }.sum() var suffixIndex = 0 @@ -75,7 +98,7 @@ class SettingsActivity : AppCompatActivity() { setPreferencesFromResource(R.xml.root_preferences, rootKey) with(findPreference("app_version")) { - this ?: return@with + this!! val manager = context.packageManager val info = manager.getPackageInfo(context.packageName, 0) @@ -84,7 +107,7 @@ class SettingsActivity : AppCompatActivity() { } with(findPreference("delete_cache")) { - this ?: return@with + this!! val dir = File(context.cacheDir, "imageCache") @@ -108,7 +131,7 @@ class SettingsActivity : AppCompatActivity() { } with(findPreference("delete_downloads")) { - this ?: return@with + this!! val dir = context.getExternalFilesDir("Pupil") ?: return@with @@ -135,7 +158,7 @@ class SettingsActivity : AppCompatActivity() { } } with(findPreference("clear_history")) { - this ?: return@with + this!! val histories = (activity!!.application as Pupil).histories @@ -157,7 +180,7 @@ class SettingsActivity : AppCompatActivity() { } with(findPreference("default_query")) { - this ?: return@with + this!! val preferences = PreferenceManager.getDefaultSharedPreferences(context) @@ -272,7 +295,80 @@ class SettingsActivity : AppCompatActivity() { } } with(findPreference("app_lock")) { + this!! + val lockManager = LockManager(context) + + summary = if (lockManager.locks.isNullOrEmpty()) { + getString(R.string.settings_lock_none) + } else { + lockManager.locks?.joinToString(", ") { + when(it.type) { + Lock.Type.PATTERN -> getString(R.string.settings_lock_pattern) + Lock.Type.PIN -> getString(R.string.settings_lock_pin) + Lock.Type.PASSWORD -> getString(R.string.settings_lock_password) + } + } + } + + onPreferenceClickListener = Preference.OnPreferenceClickListener { + val intent = Intent(context, LockActivity::class.java) + activity?.startActivityForResult(intent, (activity as SettingsActivity).REQUEST_LOCK) + + true + } + } + } + } + + class LockFragment : PreferenceFragmentCompat() { + + override fun onResume() { + super.onResume() + + val lockManager = LockManager(context!!) + + findPreference("lock_pattern")?.summary = + if (lockManager.contains(Lock.Type.PATTERN)) + getString(R.string.settings_lock_enabled) + else + "" + } + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + setPreferencesFromResource(R.xml.lock_preferences, rootKey) + + with(findPreference("lock_pattern")) { + this!! + + if (LockManager(context!!).contains(Lock.Type.PATTERN)) + summary = getString(R.string.settings_lock_enabled) + + onPreferenceClickListener = Preference.OnPreferenceClickListener { + val lockManager = LockManager(context!!) + + if (lockManager.contains(Lock.Type.PATTERN)) { + AlertDialog.Builder(context).apply { + setTitle(R.string.warning) + setMessage(R.string.settings_lock_remove_message) + + setPositiveButton(android.R.string.yes) { _, _ -> + lockManager.remove(Lock.Type.PATTERN) + onResume() + } + setNegativeButton(android.R.string.no) { _, _ -> } + }.show() + } else { + val intent = Intent(context, LockActivity::class.java).apply { + putExtra("mode", "add_lock") + putExtra("type", "pattern") + } + + startActivity(intent) + } + + true + } } } } @@ -284,4 +380,19 @@ class SettingsActivity : AppCompatActivity() { return true } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + when(requestCode) { + REQUEST_LOCK -> { + if (resultCode == Activity.RESULT_OK) { + supportFragmentManager + .beginTransaction() + .replace(R.id.settings, LockFragment()) + .addToBackStack("Lock") + .commitAllowingStateLoss() + } + } + else -> super.onActivityResult(requestCode, resultCode, data) + } + } } \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/util/lock.kt b/app/src/main/java/xyz/quaver/pupil/util/lock.kt index fefcc698..13f87003 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/lock.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/lock.kt @@ -2,6 +2,11 @@ package xyz.quaver.pupil.util import android.content.Context import android.content.ContextWrapper +import androidx.core.content.ContextCompat +import kotlinx.serialization.* +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonConfiguration +import java.io.File import java.security.MessageDigest fun hash(password: String): String { @@ -21,10 +26,20 @@ fun hashWithSalt(password: String): Pair { val source = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" -data class Lock(private val type: Type, private val hash: String, private val salt: String) { +@Serializable +data class Lock(val type: Type, val hash: String, val salt: String) { enum class Type { - PATTERN + PATTERN, + PIN, + PASSWORD + } + + companion object { + fun generate(type: Type, password: String): Lock { + val (hash, salt) = hashWithSalt(password) + return Lock(type, hash, salt) + } } fun match(password: String): Boolean { @@ -34,6 +49,57 @@ data class Lock(private val type: Type, private val hash: String, private val sa class LockManager(base: Context): ContextWrapper(base) { + var locks: ArrayList? = null + init { + load() + } + + @UseExperimental(ImplicitReflectionSerializer::class) + private fun load() { + val lock = File(ContextCompat.getDataDir(this), "lock.json") + + if (!lock.exists()) { + lock.createNewFile() + lock.writeText("[]") + } + + locks = ArrayList(Json(JsonConfiguration.Stable).parseList(lock.readText())) + } + + @UseExperimental(ImplicitReflectionSerializer::class) + private fun save() { + val lock = File(ContextCompat.getDataDir(this), "lock.json") + + if (!lock.exists()) + lock.createNewFile() + + lock.writeText(Json(JsonConfiguration.Stable).stringify(locks?.toList() ?: listOf())) + } + + fun add(lock: Lock) { + remove(lock.type) + locks?.add(lock) + save() + } + + fun remove(type: Lock.Type) { + locks?.removeAll { it.type == type } + save() + } + + fun check(password: String): Boolean? { + return locks?.any { + it.match(password) + } + } + + fun empty(): Boolean { + return locks.isNullOrEmpty() + } + + fun contains(type: Lock.Type): Boolean { + return locks?.any { it.type == type } ?: false + } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_lock.xml b/app/src/main/res/layout/activity_lock.xml index 6cb1e1c8..e4632044 100644 --- a/app/src/main/res/layout/activity_lock.xml +++ b/app/src/main/res/layout/activity_lock.xml @@ -31,6 +31,16 @@ app:backgroundTint="@color/colorPrimary" app:fabSize="mini"/> + + アプリロック アップロックの種類 バージョン + 生体認識 + ロック確認のためもう一回入力してください。 + 有効 + 指紋 + パスワード + パターン + ロックが一致しません。やり直してください。 + なし + ロックを無効にしますか? \ No newline at end of file diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 2ffdb6ec..d7c7956d 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -70,4 +70,13 @@ 앱 잠금 앱 잠금 종류 앱 버전 + 생체 인식 + 잠금 확인을 위해 한번 더 입력해주세요 + 사용 중 + 지문 + 비밀번호 + 패턴 + 잠금이 일치하지 않습니다. 다시 시도하세요. + 없음 + 잠금을 해제할까요? \ 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 b8fd3621..c8ef231d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -101,6 +101,17 @@ Enable security mode Enable security mode to make the screen invisible on recent app window + None + Pattern + PIN + Password + Biomatrics + Fingerprint + Enabled + Input same lock once more to confirm Lock + Do you want to remove lock? + Lock is different from last one. Please try again. + Set default query Language: Filter BL diff --git a/app/src/main/res/xml/lock_preferences.xml b/app/src/main/res/xml/lock_preferences.xml new file mode 100644 index 00000000..9fed9fba --- /dev/null +++ b/app/src/main/res/xml/lock_preferences.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + \ No newline at end of file