From 012416c8814c862d49eb3f19533991cb557a8453 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Sat, 10 Apr 2021 16:34:44 +0300 Subject: [PATCH] Improve password protection --- .idea/misc.xml | 6 +- app/src/main/AndroidManifest.xml | 10 +- .../java/org/koitharu/kotatsu/KotatsuApp.kt | 2 + .../koitharu/kotatsu/base/ui/BaseActivity.kt | 5 +- .../org/koitharu/kotatsu/main/MainModule.kt | 2 +- .../koitharu/kotatsu/main/ui/MainActivity.kt | 8 -- .../main/ui/protect/AppProtectHelper.kt | 61 ++++++++---- .../main/ui/protect/ProtectActivity.kt | 66 ++++++------- .../main/ui/protect/ProtectViewModel.kt | 12 ++- .../kotatsu/settings/MainSettingsFragment.kt | 12 ++- .../kotatsu/settings/SettingsModule.kt | 2 + .../settings/protect/ProtectSetupActivity.kt | 97 +++++++++++++++++++ .../settings/protect/ProtectSetupViewModel.kt | 38 ++++++++ app/src/main/res/drawable/ic_lock.xml | 11 +++ app/src/main/res/layout/activity_protect.xml | 85 +++++++++++----- .../res/layout/activity_setup_protect.xml | 82 ++++++++++++++++ app/src/main/res/menu/opt_protect.xml | 12 --- app/src/main/res/values-ru/strings.xml | 4 + app/src/main/res/values/dimens.xml | 1 + app/src/main/res/values/strings.xml | 4 + 20 files changed, 412 insertions(+), 108 deletions(-) create mode 100644 app/src/main/java/org/koitharu/kotatsu/settings/protect/ProtectSetupActivity.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/settings/protect/ProtectSetupViewModel.kt create mode 100644 app/src/main/res/drawable/ic_lock.xml create mode 100644 app/src/main/res/layout/activity_setup_protect.xml delete mode 100644 app/src/main/res/menu/opt_protect.xml diff --git a/.idea/misc.xml b/.idea/misc.xml index caf674bd0..8309c45df 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -3,13 +3,15 @@ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5c6214466..78f461918 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -78,6 +78,10 @@ android:label="@string/search" /> + - - diff --git a/app/src/main/java/org/koitharu/kotatsu/KotatsuApp.kt b/app/src/main/java/org/koitharu/kotatsu/KotatsuApp.kt index 63a8692b7..d0d21eb6f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/KotatsuApp.kt +++ b/app/src/main/java/org/koitharu/kotatsu/KotatsuApp.kt @@ -23,6 +23,7 @@ import org.koitharu.kotatsu.local.data.PagesCache import org.koitharu.kotatsu.local.domain.LocalMangaRepository import org.koitharu.kotatsu.local.localModule import org.koitharu.kotatsu.main.mainModule +import org.koitharu.kotatsu.main.ui.protect.AppProtectHelper import org.koitharu.kotatsu.reader.readerModule import org.koitharu.kotatsu.remotelist.remoteListModule import org.koitharu.kotatsu.search.searchModule @@ -55,6 +56,7 @@ class KotatsuApp : Application() { initKoin() Thread.setDefaultUncaughtExceptionHandler(AppCrashHandler(applicationContext)) AppCompatDelegate.setDefaultNightMode(get().theme) + registerActivityLifecycleCallbacks(get()) val widgetUpdater = WidgetUpdater(applicationContext) FavouritesRepository.subscribe(widgetUpdater) HistoryRepository.subscribe(widgetUpdater) diff --git a/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseActivity.kt b/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseActivity.kt index dae9da771..b4c3ae13d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseActivity.kt @@ -27,7 +27,6 @@ abstract class BaseActivity : AppCompatActivity(), OnApplyWindo protected lateinit var binding: B private set - protected val exceptionResolver by lazy(LazyThreadSafetyMode.NONE) { ExceptionResolver(this, supportFragmentManager) } @@ -60,7 +59,9 @@ abstract class BaseActivity : AppCompatActivity(), OnApplyWindo } override fun onApplyWindowInsets(v: View, insets: WindowInsetsCompat): WindowInsetsCompat { - onWindowInsetsChanged(insets.getInsets(WindowInsetsCompat.Type.systemBars())) + val baseInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars()) + val imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime()) + onWindowInsetsChanged(Insets.max(baseInsets, imeInsets)) return insets } diff --git a/app/src/main/java/org/koitharu/kotatsu/main/MainModule.kt b/app/src/main/java/org/koitharu/kotatsu/main/MainModule.kt index f8a46d4d6..e8c69d824 100644 --- a/app/src/main/java/org/koitharu/kotatsu/main/MainModule.kt +++ b/app/src/main/java/org/koitharu/kotatsu/main/MainModule.kt @@ -13,5 +13,5 @@ val mainModule single { AppProtectHelper(get()) } single { ShortcutsRepository(androidContext(), get(), get(), get()) } viewModel { MainViewModel(get(), get()) } - viewModel { ProtectViewModel(get()) } + viewModel { ProtectViewModel(get(), get()) } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/main/ui/MainActivity.kt b/app/src/main/java/org/koitharu/kotatsu/main/ui/MainActivity.kt index e86308b7d..4bb59dd74 100644 --- a/app/src/main/java/org/koitharu/kotatsu/main/ui/MainActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/main/ui/MainActivity.kt @@ -17,7 +17,6 @@ import androidx.fragment.app.Fragment import androidx.swiperefreshlayout.widget.CircularProgressDrawable import com.google.android.material.navigation.NavigationView import com.google.android.material.snackbar.Snackbar -import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.viewModel import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseActivity @@ -28,7 +27,6 @@ import org.koitharu.kotatsu.databinding.ActivityMainBinding import org.koitharu.kotatsu.favourites.ui.FavouritesContainerFragment import org.koitharu.kotatsu.history.ui.HistoryListFragment import org.koitharu.kotatsu.local.ui.LocalListFragment -import org.koitharu.kotatsu.main.ui.protect.AppProtectHelper import org.koitharu.kotatsu.reader.ui.ReaderActivity import org.koitharu.kotatsu.remotelist.ui.RemoteListFragment import org.koitharu.kotatsu.search.ui.SearchHelper @@ -45,17 +43,12 @@ class MainActivity : BaseActivity(), View.OnClickListener { private val viewModel by viewModel() - private val protectHelper by inject() private lateinit var drawerToggle: ActionBarDrawerToggle private var closeable: Closeable? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - if (protectHelper.check(this)) { - finish() - return - } setContentView(ActivityMainBinding.inflate(layoutInflater)) drawerToggle = ActionBarDrawerToggle( @@ -93,7 +86,6 @@ class MainActivity : BaseActivity(), override fun onDestroy() { closeable?.close() - protectHelper.lock() super.onDestroy() } diff --git a/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/AppProtectHelper.kt b/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/AppProtectHelper.kt index 0b71f21bf..ac0172d03 100644 --- a/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/AppProtectHelper.kt +++ b/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/AppProtectHelper.kt @@ -1,33 +1,58 @@ package org.koitharu.kotatsu.main.ui.protect import android.app.Activity +import android.app.Application import android.content.Intent +import android.os.Bundle import org.koitharu.kotatsu.core.prefs.AppSettings -import org.koitharu.kotatsu.main.ui.MainActivity -class AppProtectHelper(private val settings: AppSettings) { +class AppProtectHelper(private val settings: AppSettings) : Application.ActivityLifecycleCallbacks { private var isUnlocked = settings.appPassword.isNullOrEmpty() + private var activityCounter = 0 - fun unlock(activity: Activity) { - isUnlocked = true - with(activity) { - startActivity(Intent(this, MainActivity::class.java) - .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)) + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { + if (activity is ProtectActivity) { + return + } + activityCounter++ + if (!isUnlocked) { + val sourceIntent = Intent(activity, activity.javaClass) + activity.intent?.let { + sourceIntent.putExtras(it) + sourceIntent.action = it.action + sourceIntent.setDataAndType(it.data, it.type) + } + activity.startActivity(ProtectActivity.newIntent(activity, sourceIntent)) + activity.finishAfterTransition() } } - fun lock() { + override fun onActivityStarted(activity: Activity) = Unit + + override fun onActivityResumed(activity: Activity) = Unit + + override fun onActivityPaused(activity: Activity) = Unit + + override fun onActivityStopped(activity: Activity) = Unit + + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) = Unit + + override fun onActivityDestroyed(activity: Activity) { + if (activity is ProtectActivity) { + return + } + activityCounter-- + if (activityCounter == 0) { + restoreLock() + } + } + + fun unlock() { + isUnlocked = true + } + + private fun restoreLock() { isUnlocked = settings.appPassword.isNullOrEmpty() } - - fun check(activity: Activity): Boolean { - return if (!isUnlocked) { - activity.startActivity(ProtectActivity.newIntent(activity) - .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)) - true - } else { - false - } - } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/ProtectActivity.kt b/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/ProtectActivity.kt index 595defa76..97ca1df3a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/ProtectActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/ProtectActivity.kt @@ -6,13 +6,10 @@ import android.os.Bundle import android.text.Editable import android.text.TextWatcher import android.view.KeyEvent -import android.view.Menu -import android.view.MenuItem +import android.view.View import android.view.inputmethod.EditorInfo import android.widget.TextView import androidx.core.graphics.Insets -import androidx.core.view.updatePadding -import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.viewModel import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseActivity @@ -20,50 +17,49 @@ import org.koitharu.kotatsu.databinding.ActivityProtectBinding import org.koitharu.kotatsu.utils.ext.getDisplayMessage class ProtectActivity : BaseActivity(), TextView.OnEditorActionListener, - TextWatcher { + TextWatcher, View.OnClickListener { private val viewModel by viewModel() - private val appProtectHelper by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(ActivityProtectBinding.inflate(layoutInflater)) binding.editPassword.setOnEditorActionListener(this) binding.editPassword.addTextChangedListener(this) - supportActionBar?.run { - setDisplayHomeAsUpEnabled(true) - setHomeAsUpIndicator(R.drawable.ic_cross) - } + binding.buttonNext.setOnClickListener(this) + binding.buttonCancel.setOnClickListener(this) viewModel.onError.observe(this, this::onError) viewModel.isLoading.observe(this, this::onLoadingStateChanged) - viewModel.onUnlockSuccess.observe(this, this::onUnlockSuccess) - } - - override fun onCreateOptionsMenu(menu: Menu?): Boolean { - menuInflater.inflate(R.menu.opt_protect, menu) - return super.onCreateOptionsMenu(menu) - } - - override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { - R.id.action_done -> { - viewModel.tryUnlock(binding.editPassword.text.toString().orEmpty()) - true + viewModel.onUnlockSuccess.observe(this) { + val intent = intent.getParcelableExtra(EXTRA_INTENT) + startActivity(intent) + finishAfterTransition() } - else -> super.onOptionsItemSelected(item) + + binding.editPassword.requestFocus() } override fun onWindowInsetsChanged(insets: Insets) { - binding.toolbar.updatePadding( - left = insets.left, - right = insets.right, - top = insets.top + val basePadding = resources.getDimensionPixelOffset(R.dimen.screen_padding) + binding.root.setPadding( + basePadding + insets.left, + basePadding + insets.top, + basePadding + insets.right, + basePadding + insets.bottom ) } + override fun onClick(v: View) { + when (v.id) { + R.id.button_next -> viewModel.tryUnlock(binding.editPassword.text?.toString().orEmpty()) + R.id.button_cancel -> finish() + } + } + override fun onEditorAction(v: TextView?, actionId: Int, event: KeyEvent?): Boolean { - return if (actionId == EditorInfo.IME_ACTION_DONE) { - viewModel.tryUnlock(binding.editPassword.text.toString().orEmpty()) + return if (actionId == EditorInfo.IME_ACTION_DONE && binding.buttonNext.isEnabled) { + binding.buttonNext.performClick() true } else { false @@ -76,10 +72,7 @@ class ProtectActivity : BaseActivity(), TextView.OnEdito override fun afterTextChanged(s: Editable?) { binding.layoutPassword.error = null - } - - private fun onUnlockSuccess(unit: Unit) { - appProtectHelper.unlock(this) + binding.buttonNext.isEnabled = !s.isNullOrEmpty() } private fun onError(e: Throwable) { @@ -92,6 +85,11 @@ class ProtectActivity : BaseActivity(), TextView.OnEdito companion object { - fun newIntent(context: Context) = Intent(context, ProtectActivity::class.java) + private const val EXTRA_INTENT = "src_intent" + + fun newIntent(context: Context, sourceIntent: Intent): Intent { + return Intent(context, ProtectActivity::class.java) + .putExtra(EXTRA_INTENT, sourceIntent) + } } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/ProtectViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/ProtectViewModel.kt index 3957fa797..ca1c9cbd1 100644 --- a/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/ProtectViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/main/ui/protect/ProtectViewModel.kt @@ -1,5 +1,6 @@ package org.koitharu.kotatsu.main.ui.protect +import kotlinx.coroutines.Job import kotlinx.coroutines.delay import org.koitharu.kotatsu.base.ui.BaseViewModel import org.koitharu.kotatsu.core.exceptions.WrongPasswordException @@ -8,16 +9,23 @@ import org.koitharu.kotatsu.utils.SingleLiveEvent import org.koitharu.kotatsu.utils.ext.md5 class ProtectViewModel( - private val settings: AppSettings + private val settings: AppSettings, + private val protectHelper: AppProtectHelper, ) : BaseViewModel() { + private var job: Job? = null + val onUnlockSuccess = SingleLiveEvent() fun tryUnlock(password: String) { - launchLoadingJob { + if (job?.isActive == true) { + return + } + job = launchLoadingJob { val passwordHash = password.md5() val appPasswordHash = settings.appPassword if (passwordHash == appPasswordHash) { + protectHelper.unlock() onUnlockSuccess.call(Unit) } else { delay(PASSWORD_COMPARE_DELAY) diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/MainSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/settings/MainSettingsFragment.kt index 0d6223e85..c1a4a104d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/MainSettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/MainSettingsFragment.kt @@ -1,6 +1,7 @@ package org.koitharu.kotatsu.settings import android.content.DialogInterface +import android.content.Intent import android.content.SharedPreferences import android.os.Bundle import android.text.InputType @@ -17,6 +18,7 @@ import org.koitharu.kotatsu.base.ui.dialog.TextInputDialog import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.ListMode +import org.koitharu.kotatsu.settings.protect.ProtectSetupActivity import org.koitharu.kotatsu.utils.ext.* import java.io.File @@ -77,6 +79,10 @@ class MainSettingsFragment : BasePreferenceFragment(R.string.settings), ?: getString(R.string.not_available) } } + AppSettings.KEY_APP_PASSWORD -> { + findPreference(AppSettings.KEY_PROTECT_APP) + ?.isChecked = !settings.appPassword.isNullOrEmpty() + } } } @@ -102,8 +108,10 @@ class MainSettingsFragment : BasePreferenceFragment(R.string.settings), true } AppSettings.KEY_PROTECT_APP -> { - if ((preference as? SwitchPreference ?: return false).isChecked) { - enableAppProtection(preference) + val pref = (preference as? SwitchPreference ?: return false) + if (pref.isChecked) { + pref.isChecked = false + startActivity(Intent(preference.context, ProtectSetupActivity::class.java)) } else { settings.appPassword = null } diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/SettingsModule.kt b/app/src/main/java/org/koitharu/kotatsu/settings/SettingsModule.kt index 3e45efd24..222f20258 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/SettingsModule.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/SettingsModule.kt @@ -9,6 +9,7 @@ import org.koitharu.kotatsu.core.backup.RestoreRepository import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.settings.backup.BackupViewModel import org.koitharu.kotatsu.settings.backup.RestoreViewModel +import org.koitharu.kotatsu.settings.protect.ProtectSetupViewModel val settingsModule get() = module { @@ -19,4 +20,5 @@ val settingsModule viewModel { BackupViewModel(get(), androidContext()) } viewModel { (uri: Uri?) -> RestoreViewModel(uri, get(), androidContext()) } + viewModel { ProtectSetupViewModel(get()) } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/protect/ProtectSetupActivity.kt b/app/src/main/java/org/koitharu/kotatsu/settings/protect/ProtectSetupActivity.kt new file mode 100644 index 000000000..5ab950d69 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/settings/protect/ProtectSetupActivity.kt @@ -0,0 +1,97 @@ +package org.koitharu.kotatsu.settings.protect + +import android.os.Bundle +import android.text.Editable +import android.text.TextWatcher +import android.view.KeyEvent +import android.view.View +import android.view.inputmethod.EditorInfo +import android.widget.TextView +import androidx.core.graphics.Insets +import androidx.core.view.isGone +import org.koin.androidx.viewmodel.ext.android.viewModel +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.base.ui.BaseActivity +import org.koitharu.kotatsu.databinding.ActivitySetupProtectBinding + +class ProtectSetupActivity : BaseActivity(), TextWatcher, + View.OnClickListener, TextView.OnEditorActionListener { + + private val viewModel by viewModel() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(ActivitySetupProtectBinding.inflate(layoutInflater)) + binding.editPassword.addTextChangedListener(this) + binding.editPassword.setOnEditorActionListener(this) + binding.buttonNext.setOnClickListener(this) + binding.buttonCancel.setOnClickListener(this) + + viewModel.isSecondStep.observe(this, this::onStepChanged) + viewModel.onPasswordSet.observe(this) { + finishAfterTransition() + } + viewModel.onPasswordMismatch.observe(this) { + binding.editPassword.error = getString(R.string.passwords_mismatch) + } + viewModel.onClearText.observe(this) { + binding.editPassword.text?.clear() + } + } + + override fun onWindowInsetsChanged(insets: Insets) { + val basePadding = resources.getDimensionPixelOffset(R.dimen.screen_padding) + binding.root.setPadding( + basePadding + insets.left, + basePadding + insets.top, + basePadding + insets.right, + basePadding + insets.bottom + ) + } + + override fun onClick(v: View) { + when (v.id) { + R.id.button_cancel -> finish() + R.id.button_next -> viewModel.onNextClick( + password = binding.editPassword.text?.toString() ?: return + ) + } + } + + override fun onEditorAction(v: TextView?, actionId: Int, event: KeyEvent?): Boolean { + return if (actionId == EditorInfo.IME_ACTION_DONE && binding.buttonNext.isEnabled) { + binding.buttonNext.performClick() + true + } else { + false + } + } + + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit + + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit + + override fun afterTextChanged(s: Editable?) { + binding.editPassword.error = null + val isEnoughLength = (s?.length ?: 0) >= MIN_PASSWORD_LENGTH + binding.buttonNext.isEnabled = isEnoughLength + binding.layoutPassword.isHelperTextEnabled = + !isEnoughLength || viewModel.isSecondStep.value == true + } + + private fun onStepChanged(isSecondStep: Boolean) { + binding.buttonCancel.isGone = isSecondStep + if (isSecondStep) { + binding.layoutPassword.helperText = getString(R.string.repeat_password) + binding.buttonNext.setText(R.string.confirm) + } else { + binding.layoutPassword.helperText = getString(R.string.password_length_hint) + binding.buttonNext.setText(R.string.next) + } + } + + private companion object { + + const val MIN_PASSWORD_LENGTH = 4 + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/protect/ProtectSetupViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/settings/protect/ProtectSetupViewModel.kt new file mode 100644 index 000000000..0e3950a24 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/settings/protect/ProtectSetupViewModel.kt @@ -0,0 +1,38 @@ +package org.koitharu.kotatsu.settings.protect + +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.map +import org.koitharu.kotatsu.base.ui.BaseViewModel +import org.koitharu.kotatsu.core.prefs.AppSettings +import org.koitharu.kotatsu.utils.SingleLiveEvent +import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct +import org.koitharu.kotatsu.utils.ext.md5 + +class ProtectSetupViewModel( + private val settings: AppSettings +) : BaseViewModel() { + + private val firstPassword = MutableStateFlow(null) + + val isSecondStep = firstPassword.map { + it != null + }.asLiveDataDistinct(viewModelScope.coroutineContext) + val onPasswordSet = SingleLiveEvent() + val onPasswordMismatch = SingleLiveEvent() + val onClearText = SingleLiveEvent() + + fun onNextClick(password: String) { + if (firstPassword.value == null) { + firstPassword.value = password + onClearText.call(Unit) + } else { + if (firstPassword.value == password) { + settings.appPassword = password.md5() + onPasswordSet.call(Unit) + } else { + onPasswordMismatch.call(Unit) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_lock.xml b/app/src/main/res/drawable/ic_lock.xml new file mode 100644 index 000000000..220e1b503 --- /dev/null +++ b/app/src/main/res/drawable/ic_lock.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/layout/activity_protect.xml b/app/src/main/res/layout/activity_protect.xml index a218369c1..b17229948 100644 --- a/app/src/main/res/layout/activity_protect.xml +++ b/app/src/main/res/layout/activity_protect.xml @@ -1,47 +1,80 @@ - + android:padding="@dimen/screen_padding"> - + android:layout_marginTop="8dp" + android:drawablePadding="16dp" + android:gravity="center_horizontal" + android:text="@string/app_name" + android:textAppearance="?textAppearanceHeadline5" + app:drawableTint="?colorPrimary" + app:drawableTopCompat="@drawable/ic_lock" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> - - - + + android:layout_marginTop="30dp" + app:errorIconDrawable="@null" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/textView_subtitle"> + android:inputType="textPassword" + android:maxLength="24" + android:singleLine="true" + android:textAlignment="center" + android:textSize="16sp" + tools:text="1234" /> - \ No newline at end of file +