diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7672575f..2bd290d2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,7 @@ All notable changes to this project will be documented in this file.
- 'Show hidden folders' is now 'Show hidden files and folders'
- Generated SSH keys are now stored in the Android Keystore if available, and encrypted at rest otherwise
- Allow using device's screen lock credentials to secure generated SSH key
+- Update onboarding UI
### Fixed
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 034124f9..3b0781f4 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -33,7 +33,7 @@
android:label="@string/app_name" />
+ if (result.resultCode == AppCompatActivity.RESULT_OK) {
+ settings.edit { putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, true) }
+ finish()
+ }
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ binding.cloneRemote.setOnClickListener {
+ cloneToHiddenDir()
+ }
+ binding.createLocal.setOnClickListener {
+ parentFragmentManager.performTransactionWithBackStack(RepoLocationFragment.newInstance())
+ }
+ }
+
+ /**
+ * Clones a remote Git repository to the app's private directory
+ */
+ private fun cloneToHiddenDir() {
+ settings.edit { putBoolean(PreferenceKeys.GIT_EXTERNAL, false) }
+ cloneAction.launch(GitServerConfigActivity.createCloneIntent(requireContext()))
+ }
+
+ companion object {
+
+ fun newInstance(): CloneFragment = CloneFragment()
+ }
+}
diff --git a/app/src/main/java/com/zeapo/pwdstore/OnboardingActivity.kt b/app/src/main/java/com/zeapo/pwdstore/ui/onboarding/fragments/RepoLocationFragment.kt
similarity index 62%
rename from app/src/main/java/com/zeapo/pwdstore/OnboardingActivity.kt
rename to app/src/main/java/com/zeapo/pwdstore/ui/onboarding/fragments/RepoLocationFragment.kt
index 45bf1d1c..7ff85237 100644
--- a/app/src/main/java/com/zeapo/pwdstore/OnboardingActivity.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/ui/onboarding/fragments/RepoLocationFragment.kt
@@ -1,55 +1,51 @@
/*
* Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
* SPDX-License-Identifier: GPL-3.0-only
+ *
*/
-package com.zeapo.pwdstore
+package com.zeapo.pwdstore.ui.onboarding.fragments
import android.Manifest
-import android.content.Intent
import android.os.Bundle
-import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
-import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
+import android.view.View
+import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.edit
+import androidx.fragment.app.Fragment
import com.github.ajalt.timberkt.d
import com.github.michaelbull.result.onFailure
import com.github.michaelbull.result.runCatching
import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import com.zeapo.pwdstore.databinding.ActivityOnboardingBinding
-import com.zeapo.pwdstore.git.GitServerConfigActivity
+import com.zeapo.pwdstore.R
+import com.zeapo.pwdstore.UserPreference
+import com.zeapo.pwdstore.databinding.FragmentRepoLocationBinding
import com.zeapo.pwdstore.utils.PasswordRepository
import com.zeapo.pwdstore.utils.PreferenceKeys
-import com.zeapo.pwdstore.utils.isPermissionGranted
+import com.zeapo.pwdstore.utils.finish
import com.zeapo.pwdstore.utils.getString
+import com.zeapo.pwdstore.utils.isPermissionGranted
import com.zeapo.pwdstore.utils.listFilesRecursively
import com.zeapo.pwdstore.utils.sharedPrefs
import com.zeapo.pwdstore.utils.viewBinding
import java.io.File
-class OnboardingActivity : AppCompatActivity() {
+class RepoLocationFragment : Fragment(R.layout.fragment_repo_location) {
- private val binding by viewBinding(ActivityOnboardingBinding::inflate)
- private val settings by lazy { applicationContext.sharedPrefs }
+ private val firstRunActivity by lazy { requireActivity() }
+ private val settings by lazy { firstRunActivity.applicationContext.sharedPrefs }
+ private val binding by viewBinding(FragmentRepoLocationBinding::bind)
private val sortOrder: PasswordRepository.PasswordSortOrder
get() = PasswordRepository.PasswordSortOrder.getSortOrder(settings)
- private val cloneAction = registerForActivityResult(StartActivityForResult()) { result ->
- if (result.resultCode == RESULT_OK) {
- settings.edit { putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, true) }
- finish()
- }
- }
-
- private val repositoryInitAction = registerForActivityResult(StartActivityForResult()) { result ->
- if (result.resultCode == RESULT_OK) {
+ private val repositoryInitAction = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
+ if (result.resultCode == AppCompatActivity.RESULT_OK) {
initializeRepositoryInfo()
- finish()
}
}
- private val externalDirectorySelectAction = registerForActivityResult(StartActivityForResult()) { result ->
- if (result.resultCode == RESULT_OK) {
+ private val externalDirectorySelectAction = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
+ if (result.resultCode == AppCompatActivity.RESULT_OK) {
if (checkExternalDirectory()) {
finish()
} else {
@@ -58,40 +54,27 @@ class OnboardingActivity : AppCompatActivity() {
}
}
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- supportActionBar?.hide()
- setContentView(binding.root)
- binding.settingsButton.setOnClickListener {
- startActivity(Intent(this, UserPreference::class.java))
- }
- binding.localDirectoryButton.setOnClickListener {
- MaterialAlertDialogBuilder(this)
- .setTitle(resources.getString(R.string.location_dialog_title))
- .setMessage(resources.getString(R.string.location_dialog_create_text))
- .setPositiveButton(resources.getString(R.string.location_hidden)) { _, _ ->
- createRepoInHiddenDir()
- }
- .setNegativeButton(resources.getString(R.string.location_sdcard)) { _, _ ->
- createRepoFromExternalDir()
- }
- .show()
- }
- binding.cloneFromServerButton.setOnClickListener {
- cloneToHiddenDir()
- }
+ private val externalDirPermGrantedAction = createPermGrantedAction {
+ externalDirectorySelectAction.launch(UserPreference.createDirectorySelectionIntent(requireContext()))
}
- override fun onBackPressed() {
- finishAffinity()
+ private val repositoryUsePermGrantedAction = createPermGrantedAction {
+ initializeRepositoryInfo()
}
- /**
- * Clones a remote Git repository to the app's private directory
- */
- private fun cloneToHiddenDir() {
- settings.edit { putBoolean(PreferenceKeys.GIT_EXTERNAL, false) }
- cloneAction.launch(GitServerConfigActivity.createCloneIntent(this))
+ private val repositoryChangePermGrantedAction = createPermGrantedAction {
+ repositoryInitAction.launch(UserPreference.createDirectorySelectionIntent(requireContext()))
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ binding.hidden.setOnClickListener {
+ createRepoInHiddenDir()
+ }
+
+ binding.sdcard.setOnClickListener {
+ createRepoFromExternalDir()
+ }
}
/**
@@ -103,7 +86,6 @@ class OnboardingActivity : AppCompatActivity() {
remove(PreferenceKeys.GIT_EXTERNAL_REPO)
}
initializeRepositoryInfo()
- finish()
}
/**
@@ -114,40 +96,28 @@ class OnboardingActivity : AppCompatActivity() {
val externalRepo = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO)
if (externalRepo == null) {
if (!isPermissionGranted(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
- registerForActivityResult(RequestPermission()) { granted ->
- if (granted) {
- externalDirectorySelectAction.launch(UserPreference.createDirectorySelectionIntent(this))
- }
- }.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
+ externalDirPermGrantedAction.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
} else {
// Unlikely we have storage permissions without user ever selecting a directory,
// but let's not assume.
- externalDirectorySelectAction.launch(UserPreference.createDirectorySelectionIntent(this))
+ externalDirectorySelectAction.launch(UserPreference.createDirectorySelectionIntent(requireContext()))
}
} else {
- MaterialAlertDialogBuilder(this)
+ MaterialAlertDialogBuilder(requireActivity())
.setTitle(resources.getString(R.string.directory_selected_title))
.setMessage(resources.getString(R.string.directory_selected_message, externalRepo))
.setPositiveButton(resources.getString(R.string.use)) { _, _ ->
if (!isPermissionGranted(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
- registerForActivityResult(RequestPermission()) { granted ->
- if (granted) {
- initializeRepositoryInfo()
- }
- }.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
+ repositoryUsePermGrantedAction.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
} else {
initializeRepositoryInfo()
}
}
.setNegativeButton(resources.getString(R.string.change)) { _, _ ->
if (!isPermissionGranted(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
- registerForActivityResult(RequestPermission()) { granted ->
- if (granted) {
- repositoryInitAction.launch(UserPreference.createDirectorySelectionIntent(this))
- }
- }.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
+ repositoryChangePermGrantedAction.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
} else {
- repositoryInitAction.launch(UserPreference.createDirectorySelectionIntent(this))
+ repositoryInitAction.launch(UserPreference.createDirectorySelectionIntent(requireContext()))
}
}
.show()
@@ -192,6 +162,7 @@ class OnboardingActivity : AppCompatActivity() {
d { "Failed to delete local repository: $localDir" }
}
}
+ finish()
}
private fun initializeRepositoryInfo() {
@@ -205,4 +176,16 @@ class OnboardingActivity : AppCompatActivity() {
}
createRepository()
}
+
+ private fun createPermGrantedAction(block: () -> Unit) =
+ registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
+ if (granted) {
+ block.invoke()
+ }
+ }
+
+ companion object {
+
+ fun newInstance(): RepoLocationFragment = RepoLocationFragment()
+ }
}
diff --git a/app/src/main/java/com/zeapo/pwdstore/ui/onboarding/fragments/WelcomeFragment.kt b/app/src/main/java/com/zeapo/pwdstore/ui/onboarding/fragments/WelcomeFragment.kt
new file mode 100644
index 00000000..2cf4dd49
--- /dev/null
+++ b/app/src/main/java/com/zeapo/pwdstore/ui/onboarding/fragments/WelcomeFragment.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright © 2019-2020 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package com.zeapo.pwdstore.ui.onboarding.fragments
+
+import android.os.Bundle
+import android.view.View
+import androidx.fragment.app.Fragment
+import com.zeapo.pwdstore.R
+import com.zeapo.pwdstore.databinding.FragmentWelcomeBinding
+import com.zeapo.pwdstore.utils.performTransactionWithBackStack
+import com.zeapo.pwdstore.utils.viewBinding
+
+@Suppress("unused")
+class WelcomeFragment : Fragment(R.layout.fragment_welcome) {
+
+ private val binding by viewBinding(FragmentWelcomeBinding::bind)
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ binding.letsGo.setOnClickListener { parentFragmentManager.performTransactionWithBackStack(CloneFragment.newInstance()) }
+ }
+}
diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt b/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt
index d9277bef..2a11f56a 100644
--- a/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt
@@ -56,7 +56,8 @@ fun String.base64(): String {
return Base64.encodeToString(encodeToByteArray(), Base64.NO_WRAP)
}
-val Context.clipboard get() = getSystemService()
+val Context.clipboard
+ get() = getSystemService()
fun FragmentActivity.snackbar(
view: View = findViewById(android.R.id.content),
diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/FragmentExtensions.kt b/app/src/main/java/com/zeapo/pwdstore/utils/FragmentExtensions.kt
new file mode 100644
index 00000000..01103d19
--- /dev/null
+++ b/app/src/main/java/com/zeapo/pwdstore/utils/FragmentExtensions.kt
@@ -0,0 +1,52 @@
+package com.zeapo.pwdstore.utils
+
+import android.content.pm.PackageManager
+import android.view.View
+import androidx.annotation.IdRes
+import androidx.core.content.ContextCompat
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentManager
+import androidx.fragment.app.commit
+import com.zeapo.pwdstore.R
+
+fun Fragment.isPermissionGranted(permission: String): Boolean {
+ return ContextCompat.checkSelfPermission(requireActivity(), permission) == PackageManager.PERMISSION_GRANTED
+}
+
+fun Fragment.finish() = requireActivity().finish()
+
+fun FragmentManager.performTransaction(destinationFragment: Fragment, @IdRes containerViewId: Int = android.R.id.content) {
+ this.commit {
+ beginTransaction()
+ setCustomAnimations(
+ R.animator.slide_in_left,
+ R.animator.slide_out_left,
+ R.animator.slide_in_right,
+ R.animator.slide_out_right)
+ replace(containerViewId, destinationFragment)
+ }
+}
+
+fun FragmentManager.performTransactionWithBackStack(destinationFragment: Fragment, @IdRes containerViewId: Int = android.R.id.content) {
+ this.commit {
+ beginTransaction()
+ addToBackStack(destinationFragment.tag)
+ setCustomAnimations(
+ R.animator.slide_in_left,
+ R.animator.slide_out_left,
+ R.animator.slide_in_right,
+ R.animator.slide_out_right)
+ replace(containerViewId, destinationFragment)
+ }
+}
+
+fun FragmentManager.performSharedElementTransaction(destinationFragment: Fragment, views: List, @IdRes containerViewId: Int = android.R.id.content) {
+ this.commit {
+ beginTransaction()
+ for (view in views) {
+ addSharedElement(view, view.transitionName)
+ }
+ addToBackStack(destinationFragment.tag)
+ replace(containerViewId, destinationFragment)
+ }
+}
diff --git a/app/src/main/res/animator/slide_in_left.xml b/app/src/main/res/animator/slide_in_left.xml
new file mode 100644
index 00000000..5f5498e5
--- /dev/null
+++ b/app/src/main/res/animator/slide_in_left.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/animator/slide_in_right.xml b/app/src/main/res/animator/slide_in_right.xml
new file mode 100644
index 00000000..34420581
--- /dev/null
+++ b/app/src/main/res/animator/slide_in_right.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/animator/slide_out_left.xml b/app/src/main/res/animator/slide_out_left.xml
new file mode 100644
index 00000000..5bc22aa9
--- /dev/null
+++ b/app/src/main/res/animator/slide_out_left.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/animator/slide_out_right.xml b/app/src/main/res/animator/slide_out_right.xml
new file mode 100644
index 00000000..8447ae76
--- /dev/null
+++ b/app/src/main/res/animator/slide_out_right.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_onboarding.xml b/app/src/main/res/layout/activity_onboarding.xml
index f2c6f797..6d880c31 100644
--- a/app/src/main/res/layout/activity_onboarding.xml
+++ b/app/src/main/res/layout/activity_onboarding.xml
@@ -1,73 +1,13 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
diff --git a/app/src/main/res/layout/fragment_clone.xml b/app/src/main/res/layout/fragment_clone.xml
new file mode 100644
index 00000000..0e173428
--- /dev/null
+++ b/app/src/main/res/layout/fragment_clone.xml
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_repo_location.xml b/app/src/main/res/layout/fragment_repo_location.xml
new file mode 100644
index 00000000..cc987d8c
--- /dev/null
+++ b/app/src/main/res/layout/fragment_repo_location.xml
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_welcome.xml b/app/src/main/res/layout/fragment_welcome.xml
new file mode 100644
index 00000000..98a0475d
--- /dev/null
+++ b/app/src/main/res/layout/fragment_welcome.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 53140e6c..90295413 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -77,7 +77,7 @@
Clone from server
Use local directory
Repository location
- Select where to create your password repository
+ Select where do you want to create your password repository
SD-Card
Hidden (Preferred)
Choose where to store the passwords
@@ -418,4 +418,22 @@
A folder by that name already exists
Digits/Symbols (d/s)
ds
+
+
+ Repository \nLocation
+ Select \nOpenPGP Provider
+ Let\'s Go
+ Select \nRepository Type
+ Select if you want to create a local repo or clone a remote repo.
+ Clone Remote Repo
+ Create Local Repo
+ Error getting directory uri
+ Select a directory to store passwords
+ Select \nStore Name
+ Select a name for your password store.
+ Store Name
+ Select an empty directory for password store
+ Enter a store name to continue
+ Cannot create new directory.
+