diff options
author | Aditya Wasan <adityawasan55@gmail.com> | 2020-09-15 21:53:12 +0530 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-09-15 21:53:12 +0530 |
commit | 4ba3b75f858251099f18f07be700f9900128f8e1 (patch) | |
tree | a3a689288253484817f868d15025fbbaf4a8d122 | |
parent | a34f749e9ac1d6112a42b0f4ded3ae801d8c583c (diff) |
Update on-boarding UI (#1099)
* Add onboarding flow from v2
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
* Minor fixes
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
* Add changelog entry
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
* Remove old activity from manifest
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
* Remove view type prefix from view ids
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
* Review fixes
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
* Treewide: Reformat code
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
* Moar review fixes
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
* Revert "Treewide: Reformat code"
This reverts commit 348ef0050942526a55890b245afec8d7fee4d81e.
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
* onboarding: cleanup OnboardingActivity init
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
* Remove unused layout
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
* Remove unnecessary ConstraintLayout
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
* Shorten animation duration
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
* onboarding: use viewBinding extension in fragments
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
18 files changed, 517 insertions, 142 deletions
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" /> <activity - android:name=".OnboardingActivity" + android:name=".ui.onboarding.activity.OnboardingActivity" android:configChanges="orientation|screenSize" /> <activity diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt b/app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt index 93dfad56..4e4bf85e 100644 --- a/app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt +++ b/app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt @@ -52,6 +52,7 @@ import com.zeapo.pwdstore.git.BaseGitActivity import com.zeapo.pwdstore.git.config.AuthMode import com.zeapo.pwdstore.git.config.GitSettings import com.zeapo.pwdstore.ui.dialogs.FolderCreationDialogFragment +import com.zeapo.pwdstore.ui.onboarding.activity.OnboardingActivity import com.zeapo.pwdstore.utils.PasswordItem import com.zeapo.pwdstore.utils.PasswordRepository import com.zeapo.pwdstore.utils.PasswordRepository.Companion.getRepository diff --git a/app/src/main/java/com/zeapo/pwdstore/ui/onboarding/activity/OnboardingActivity.kt b/app/src/main/java/com/zeapo/pwdstore/ui/onboarding/activity/OnboardingActivity.kt new file mode 100644 index 00000000..b81efb4c --- /dev/null +++ b/app/src/main/java/com/zeapo/pwdstore/ui/onboarding/activity/OnboardingActivity.kt @@ -0,0 +1,26 @@ +/* + * Copyright © 2019-2020 The Android Password Store Authors. All Rights Reserved. + * SPDX-License-Identifier: GPL-3.0-only + */ + +package com.zeapo.pwdstore.ui.onboarding.activity + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.zeapo.pwdstore.R + +class OnboardingActivity : AppCompatActivity(R.layout.activity_onboarding) { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + supportActionBar?.hide() + } + + override fun onBackPressed() { + if (supportFragmentManager.backStackEntryCount == 0) { + finishAffinity() + } else { + super.onBackPressed() + } + } +} diff --git a/app/src/main/java/com/zeapo/pwdstore/ui/onboarding/fragments/CloneFragment.kt b/app/src/main/java/com/zeapo/pwdstore/ui/onboarding/fragments/CloneFragment.kt new file mode 100644 index 00000000..19a14695 --- /dev/null +++ b/app/src/main/java/com/zeapo/pwdstore/ui/onboarding/fragments/CloneFragment.kt @@ -0,0 +1,59 @@ +/* + * 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.activity.result.contract.ActivityResultContracts +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.edit +import androidx.fragment.app.Fragment +import com.zeapo.pwdstore.R +import com.zeapo.pwdstore.databinding.FragmentCloneBinding +import com.zeapo.pwdstore.git.GitServerConfigActivity +import com.zeapo.pwdstore.utils.PreferenceKeys +import com.zeapo.pwdstore.utils.finish +import com.zeapo.pwdstore.utils.performTransactionWithBackStack +import com.zeapo.pwdstore.utils.sharedPrefs +import com.zeapo.pwdstore.utils.viewBinding + +class CloneFragment : Fragment(R.layout.fragment_clone) { + + private val binding by viewBinding(FragmentCloneBinding::bind) + + private val settings by lazy { requireActivity().applicationContext.sharedPrefs } + + private val cloneAction = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + 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 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<ClipboardManager>() +val Context.clipboard + get() = getSystemService<ClipboardManager>() 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<View>, @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 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + + <objectAnimator + android:duration="250" + android:propertyName="x" + android:valueFrom="1000" + android:valueTo="0" + android:valueType="floatType" /> + +</set> 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + + <objectAnimator + android:duration="250" + android:propertyName="x" + android:valueFrom="-1000" + android:valueTo="0" + android:valueType="floatType" /> + +</set> 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + + <objectAnimator + android:duration="250" + android:propertyName="x" + android:valueFrom="0" + android:valueTo="-1000" + android:valueType="floatType" /> + +</set> 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + + <objectAnimator + android:duration="250" + android:propertyName="x" + android:valueFrom="0" + android:valueTo="1000" + android:valueType="floatType" /> + +</set> 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 @@ -<!-- - ~ Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved. - ~ SPDX-License-Identifier: GPL-3.0-only - --> - -<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="?attr/colorPrimary" android:orientation="vertical"> - <androidx.appcompat.widget.AppCompatImageView - android:id="@+id/app_icon" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:contentDescription="@string/app_icon_hint" - android:src="@mipmap/ic_launcher" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> - - <androidx.appcompat.widget.AppCompatTextView - android:id="@+id/app_name" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_below="@+id/app_icon" - android:layout_centerHorizontal="true" - android:text="@string/app_name" - android:textAppearance="?android:attr/textAppearanceLarge" - android:textColor="?attr/colorOnPrimary" - android:textStyle="bold" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/app_icon" /> - - <com.google.android.material.button.MaterialButton - android:id="@+id/settings_button" - style="@style/Widget.MaterialComponents.Button.TextButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentTop="true" - android:layout_alignParentEnd="true" - android:layout_marginEnd="@dimen/activity_horizontal_margin" - android:text="@string/action_settings" - android:textColor="?attr/colorOnPrimary" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="parent" /> - - <com.google.android.material.button.MaterialButton - android:id="@+id/local_directory_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="48dp" - android:text="@string/initialize" - android:textSize="12sp" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/app_name" /> - - <com.google.android.material.button.MaterialButton - android:id="@+id/clone_from_server_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/clone" - android:textSize="12sp" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/local_directory_button" /> - -</androidx.constraintlayout.widget.ConstraintLayout> + <androidx.fragment.app.FragmentContainerView + android:id="@+id/fragment_first_run" + android:name="com.zeapo.pwdstore.ui.onboarding.fragments.WelcomeFragment" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:tag="welcome_fragment" /> +</LinearLayout> 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 @@ +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="?attr/colorPrimary" + android:orientation="vertical"> + + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/app_icon" + android:layout_width="64dp" + android:layout_height="64dp" + android:layout_marginStart="32dp" + android:layout_marginTop="100dp" + android:contentDescription="@string/app_icon_hint" + android:src="@mipmap/ic_launcher" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <androidx.appcompat.widget.AppCompatTextView + android:id="@+id/app_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:layout_marginStart="16dp" + android:text="@string/app_name" + android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5" + android:textColor="@color/color_control_normal" + android:textStyle="bold" + app:layout_constraintBottom_toBottomOf="@id/app_icon" + app:layout_constraintStart_toEndOf="@id/app_icon" + app:layout_constraintTop_toTopOf="@+id/app_icon" /> + + <TextView + android:id="@+id/repo_type" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="48dp" + android:text="@string/select_n_repository_type" + android:textAppearance="@style/TextAppearance.MaterialComponents.Headline4" + android:textColor="@color/color_control_normal" + android:textStyle="bold" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0" + app:layout_constraintStart_toStartOf="@id/app_icon" + app:layout_constraintTop_toBottomOf="@id/app_icon" /> + + <TextView + android:id="@+id/tv_repo_type_text" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="48dp" + android:layout_marginEnd="16dp" + android:text="@string/select_repo_type_text" + android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" + android:textColor="@color/color_control_normal" + android:textStyle="bold" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="@id/repo_type" + app:layout_constraintTop_toBottomOf="@id/repo_type" /> + + <com.google.android.material.button.MaterialButton + android:id="@+id/clone_remote" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginEnd="16dp" + android:maxWidth="300dp" + android:minWidth="100dp" + android:text="@string/clone_remote_repo" + app:layout_constraintBottom_toTopOf="@id/create_local" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" /> + + <com.google.android.material.button.MaterialButton + android:id="@+id/create_local" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginBottom="128dp" + android:maxWidth="300dp" + android:minWidth="100dp" + android:text="@string/create_local_repo" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="@id/clone_remote" + app:layout_constraintStart_toStartOf="@id/clone_remote" /> + +</androidx.constraintlayout.widget.ConstraintLayout> 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 @@ +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="?attr/colorPrimary" + android:orientation="vertical"> + + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/app_icon" + android:layout_width="64dp" + android:layout_height="64dp" + android:layout_marginStart="32dp" + android:layout_marginTop="100dp" + android:contentDescription="@string/app_icon_hint" + android:src="@mipmap/ic_launcher" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <androidx.appcompat.widget.AppCompatTextView + android:id="@+id/app_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:layout_marginStart="16dp" + android:text="@string/app_name" + android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5" + android:textColor="@color/color_control_normal" + android:textStyle="bold" + app:layout_constraintBottom_toBottomOf="@id/app_icon" + app:layout_constraintStart_toEndOf="@id/app_icon" + app:layout_constraintTop_toTopOf="@+id/app_icon" /> + + <TextView + android:id="@+id/repo_location" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="48dp" + android:text="@string/repository_n_location" + android:textAppearance="@style/TextAppearance.MaterialComponents.Headline4" + android:textColor="@color/color_control_normal" + android:textStyle="bold" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0" + app:layout_constraintStart_toStartOf="@id/app_icon" + app:layout_constraintTop_toBottomOf="@id/app_icon" /> + + <TextView + android:id="@+id/repo_location_text" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="48dp" + android:layout_marginEnd="16dp" + android:text="@string/location_dialog_create_text" + android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" + android:textColor="@color/color_control_normal" + android:textStyle="bold" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="@id/repo_location" + app:layout_constraintTop_toBottomOf="@id/repo_location" /> + + <com.google.android.material.button.MaterialButton + android:id="@+id/hidden" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginEnd="16dp" + android:maxWidth="300dp" + android:minWidth="100dp" + android:text="@string/location_hidden" + app:layout_constraintBottom_toTopOf="@id/sdcard" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" /> + + <com.google.android.material.button.MaterialButton + android:id="@+id/sdcard" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginBottom="128dp" + android:maxWidth="300dp" + android:minWidth="100dp" + android:text="@string/location_sdcard" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="@id/hidden" + app:layout_constraintStart_toStartOf="@id/hidden" /> + +</androidx.constraintlayout.widget.ConstraintLayout> 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 @@ +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="?attr/colorPrimary" + android:orientation="vertical"> + + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/app_icon" + android:layout_width="100dp" + android:layout_height="100dp" + android:contentDescription="@string/app_icon_hint" + android:src="@mipmap/ic_launcher" + android:transitionName="transition_first_app_icon" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <androidx.appcompat.widget.AppCompatTextView + android:id="@+id/app_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:layout_marginTop="16dp" + android:text="@string/app_name" + android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5" + android:textColor="@color/color_control_normal" + android:textStyle="bold" + android:transitionName="transition_first_run_app_name" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/app_icon" /> + + + <com.google.android.material.button.MaterialButton + android:id="@+id/lets_go" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginTop="48dp" + android:layout_marginEnd="16dp" + android:maxWidth="300dp" + android:minWidth="100dp" + android:text="@string/let_s_go" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/app_name" /> +</androidx.constraintlayout.widget.ConstraintLayout> 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 @@ <string name="clone">Clone from server</string> <string name="initialize">Use local directory</string> <string name="location_dialog_title">Repository location</string> - <string name="location_dialog_create_text">Select where to create your password repository</string> + <string name="location_dialog_create_text">Select where do you want to create your password repository</string> <string name="location_sdcard">SD-Card</string> <string name="location_hidden">Hidden (Preferred)</string> <string name="external_repository_dialog_title">Choose where to store the passwords</string> @@ -418,4 +418,22 @@ <string name="folder_creation_err_folder_exists">A folder by that name already exists</string> <string name="xkpwgen_extrachars_label">Digits/Symbols (d/s)</string> <string name="xk_numbers_symbols_append_default">ds</string> + + <!-- Onboarding flow --> + <string name="repository_n_location">Repository \nLocation</string> + <string name="select_n_openpgp_provider">Select \nOpenPGP Provider</string> + <string name="let_s_go">Let\'s Go</string> + <string name="select_n_repository_type">Select \nRepository Type</string> + <string name="select_repo_type_text">Select if you want to create a local repo or clone a remote repo.</string> + <string name="clone_remote_repo">Clone Remote Repo</string> + <string name="create_local_repo">Create Local Repo</string> + <string name="error_directory_uri">Error getting directory uri</string> + <string name="select_directory_passwords">Select a directory to store passwords</string> + <string name="select_n_store_name">Select \nStore Name</string> + <string name="select_n_store_text">Select a name for your password store.</string> + <string name="store_name">Store Name</string> + <string name="select_empty_directory">Select an empty directory for password store</string> + <string name="err_enter_store_name">Enter a store name to continue</string> + <string name="exception_cannot_create_directory">Cannot create new directory.</string> + </resources> |