summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt28
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/ui/dialogs/ItemCreationBottomSheet.kt76
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/widget/fab/EmitExpandableTransformationBehaviour.kt141
-rw-r--r--app/src/main/res/layout/item_create_sheet.xml39
-rw-r--r--app/src/main/res/layout/password_recycler_view.xml31
-rw-r--r--app/src/main/res/values/dimens.xml2
-rw-r--r--app/src/main/res/values/strings.xml2
-rw-r--r--app/src/main/res/values/styles.xml13
8 files changed, 141 insertions, 191 deletions
diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt b/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt
index 5e12697d..2b1551fa 100644
--- a/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt
@@ -22,13 +22,13 @@ import androidx.lifecycle.observe
import androidx.recyclerview.widget.FixOnItemTouchDispatchRecyclerView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
-import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.snackbar.Snackbar
import com.zeapo.pwdstore.databinding.PasswordRecyclerViewBinding
import com.zeapo.pwdstore.git.BaseGitActivity
import com.zeapo.pwdstore.git.GitOperationActivity
import com.zeapo.pwdstore.ui.OnOffItemAnimator
import com.zeapo.pwdstore.ui.adapters.PasswordItemRecyclerAdapter
+import com.zeapo.pwdstore.ui.dialogs.ItemCreationBottomSheet
import com.zeapo.pwdstore.utils.PasswordItem
import com.zeapo.pwdstore.utils.PasswordRepository
import java.io.File
@@ -57,17 +57,10 @@ class PasswordFragment : Fragment() {
): View? {
_binding = PasswordRecyclerViewBinding.inflate(inflater, container, false)
initializePasswordList()
- val fab = binding.fab
- fab.setOnClickListener {
- toggleFabExpand(fab)
- }
- binding.createFolder.setOnClickListener {
- requireStore().createFolder()
- toggleFabExpand(fab)
- }
- binding.createPassword.setOnClickListener {
- requireStore().createPassword()
- toggleFabExpand(fab)
+ binding.fab.setOnClickListener {
+ ItemCreationBottomSheet().apply {
+ setTargetFragment(this@PasswordFragment, 1000)
+ }.show(parentFragmentManager, "BOTTOM_SHEET")
}
return binding.root
}
@@ -145,14 +138,7 @@ class PasswordFragment : Fragment() {
super.onDestroyView()
}
- private fun toggleFabExpand(fab: FloatingActionButton) = with(fab) {
- isExpanded = !isExpanded
- isActivated = isExpanded
- animate().rotationBy(if (isExpanded) -45f else 45f).setDuration(100).start()
- }
-
private val actionModeCallback = object : ActionMode.Callback {
-
// Called when the action mode is created; startActionMode() was called
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
// Inflate a menu resource providing context menu items
@@ -282,6 +268,10 @@ class PasswordFragment : Fragment() {
actionMode?.finish()
}
+ fun createFolder() = requireStore().createFolder()
+
+ fun createPassword() = requireStore().createPassword()
+
interface OnFragmentInteractionListener {
fun onFragmentInteraction(item: PasswordItem)
}
diff --git a/app/src/main/java/com/zeapo/pwdstore/ui/dialogs/ItemCreationBottomSheet.kt b/app/src/main/java/com/zeapo/pwdstore/ui/dialogs/ItemCreationBottomSheet.kt
new file mode 100644
index 00000000..55a767d8
--- /dev/null
+++ b/app/src/main/java/com/zeapo/pwdstore/ui/dialogs/ItemCreationBottomSheet.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+package com.zeapo.pwdstore.ui.dialogs
+
+import android.graphics.drawable.GradientDrawable
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewTreeObserver
+import android.widget.FrameLayout
+import androidx.fragment.app.Fragment
+import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.google.android.material.bottomsheet.BottomSheetDialog
+import com.google.android.material.bottomsheet.BottomSheetDialogFragment
+import com.zeapo.pwdstore.PasswordFragment
+import com.zeapo.pwdstore.R
+import com.zeapo.pwdstore.utils.resolveAttribute
+
+class ItemCreationBottomSheet : BottomSheetDialogFragment() {
+ private var behavior: BottomSheetBehavior<FrameLayout>? = null
+ private val bottomSheetCallback = object : BottomSheetBehavior.BottomSheetCallback() {
+ override fun onSlide(bottomSheet: View, slideOffset: Float) {
+ }
+
+ override fun onStateChanged(bottomSheet: View, newState: Int) {
+ if (newState == BottomSheetBehavior.STATE_COLLAPSED) {
+ dismiss()
+ }
+ }
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ if (savedInstanceState != null) dismiss()
+ return inflater.inflate(R.layout.item_create_sheet, container, false)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ view.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
+ override fun onGlobalLayout() {
+ view.viewTreeObserver.removeOnGlobalLayoutListener(this)
+ val dialog = dialog as BottomSheetDialog? ?: return
+ behavior = dialog.behavior
+ behavior?.apply {
+ state = BottomSheetBehavior.STATE_EXPANDED
+ peekHeight = 0
+ addBottomSheetCallback(bottomSheetCallback)
+ }
+ dialog.findViewById<View>(R.id.create_folder)?.setOnClickListener {
+ (requireTargetFragment() as PasswordFragment).createFolder()
+ dismiss()
+ }
+ dialog.findViewById<View>(R.id.create_password)?.setOnClickListener {
+ (requireTargetFragment() as PasswordFragment).createPassword()
+ dismiss()
+ }
+ }
+ })
+ val gradientDrawable = GradientDrawable().apply {
+ setColor(requireContext().resolveAttribute(android.R.attr.windowBackground))
+ }
+ view.background = gradientDrawable
+ }
+
+ override fun dismiss() {
+ super.dismiss()
+ behavior?.removeBottomSheetCallback(bottomSheetCallback)
+ }
+
+ private fun requireTargetFragment(): Fragment = requireNotNull(targetFragment) {
+ "A target fragment must be set for $this"
+ }
+}
diff --git a/app/src/main/java/com/zeapo/pwdstore/widget/fab/EmitExpandableTransformationBehaviour.kt b/app/src/main/java/com/zeapo/pwdstore/widget/fab/EmitExpandableTransformationBehaviour.kt
deleted file mode 100644
index 6cefda14..00000000
--- a/app/src/main/java/com/zeapo/pwdstore/widget/fab/EmitExpandableTransformationBehaviour.kt
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-package com.zeapo.pwdstore.widget.fab
-
-import android.animation.Animator
-import android.animation.AnimatorSet
-import android.animation.ObjectAnimator
-import android.animation.PropertyValuesHolder
-import android.content.Context
-import android.util.AttributeSet
-import android.view.View
-import android.view.ViewGroup
-import androidx.coordinatorlayout.widget.CoordinatorLayout
-import androidx.core.animation.addListener
-import androidx.core.view.children
-import androidx.core.view.isInvisible
-import androidx.core.view.isVisible
-import com.google.android.material.floatingactionbutton.FloatingActionButton
-import com.google.android.material.transformation.ExpandableTransformationBehavior
-import java.util.ArrayList
-
-/**
- * Taken from Mao Yufeng's excellent example at https://git.io/Jvml9, all credits to him for this.
- * It's hard to create per-file copyright rules for Spotless so I'm choosing to credit him here.
- */
-class EmitExpandableTransformationBehavior @JvmOverloads constructor(
- context: Context? = null,
- attrs: AttributeSet? = null
-) : ExpandableTransformationBehavior(context, attrs) {
-
- companion object {
- private const val EXPAND_DELAY = 60L
- private const val EXPAND_DURATION = 150L
- private const val COLLAPSE_DELAY = 60L
- private const val COLLAPSE_DURATION = 150L
- }
-
- override fun layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
- return dependency is FloatingActionButton && child is ViewGroup
- }
-
- override fun onCreateExpandedStateChangeAnimation(
- dependency: View,
- child: View,
- expanded: Boolean,
- isAnimating: Boolean
- ): AnimatorSet {
-
- if (child !is ViewGroup) {
- return AnimatorSet()
- }
-
- val animations = ArrayList<Animator>()
-
- if (expanded) {
- createExpandAnimation(child, isAnimating, animations)
- } else {
- createCollapseAnimation(child, animations)
- }
-
- val set = AnimatorSet()
- set.playTogether(animations)
- set.addListener(
- onStart = {
- if (expanded) {
- child.isVisible = true
- }
- },
- onEnd = {
- if (!expanded) {
- child.isInvisible = true
- }
- }
- )
- return set
- }
-
- private fun createExpandAnimation(
- child: ViewGroup,
- currentlyAnimating: Boolean,
- animations: MutableList<Animator>
- ) {
- if (!currentlyAnimating) {
- child.children.forEach {
- it.alpha = 0f
- it.scaleX = 0.4f
- it.scaleY = 0.4f
- }
- }
- val delays = List(child.childCount) {
- it * EXPAND_DELAY
- }.reversed().asSequence()
- val scaleXHolder = PropertyValuesHolder.ofFloat(View.SCALE_X, 1f)
- val scaleYHolder = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1f)
- val alphaHolder = PropertyValuesHolder.ofFloat(View.ALPHA, 1f)
- val animators = child.children.zip(delays) { view, delay ->
- ObjectAnimator.ofPropertyValuesHolder(
- view,
- scaleXHolder,
- scaleYHolder,
- alphaHolder
- ).apply {
- duration = EXPAND_DURATION
- startDelay = delay
- }
- }.toList()
- val animatorSet = AnimatorSet().apply {
- playTogether(animators)
- }
- animations.add(animatorSet)
- }
-
- private fun createCollapseAnimation(
- child: ViewGroup,
- animations: MutableList<Animator>
- ) {
- val delays = List(child.childCount) {
- it * COLLAPSE_DELAY
- }.asSequence()
- val scaleXHolder = PropertyValuesHolder.ofFloat(View.SCALE_X, 0.4f)
- val scaleYHolder = PropertyValuesHolder.ofFloat(View.SCALE_Y, 0.4f)
- val alphaHolder = PropertyValuesHolder.ofFloat(View.ALPHA, 0f)
- val animators = child.children.zip(delays) { view, delay ->
- ObjectAnimator.ofPropertyValuesHolder(
- view,
- scaleXHolder,
- scaleYHolder,
- alphaHolder
- ).apply {
- duration = COLLAPSE_DURATION
- startDelay = delay
- }
- }.toList()
- val animatorSet = AnimatorSet().apply {
- playTogether(animators)
- }
- animations.add(animatorSet)
- }
-}
diff --git a/app/src/main/res/layout/item_create_sheet.xml b/app/src/main/res/layout/item_create_sheet.xml
new file mode 100644
index 00000000..244537ad
--- /dev/null
+++ b/app/src/main/res/layout/item_create_sheet.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <com.google.android.material.button.MaterialButton
+ style="@style/Widget.MaterialComponents.Button.TextButton"
+ android:id="@+id/create_folder"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/bottom_sheet_item_height"
+ android:text="@string/bottom_sheet_create_new_folder"
+ android:textAlignment="viewStart"
+ android:textColor="?attr/colorOnSurface"
+ android:layout_margin="@dimen/normal_margin"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:icon="@drawable/ic_action_new_folder"
+ app:iconPadding="@dimen/normal_margin"
+ app:iconTint="?attr/colorSecondary"
+ app:rippleColor="?attr/colorSecondary" />
+
+ <com.google.android.material.button.MaterialButton
+ style="@style/Widget.MaterialComponents.Button.TextButton"
+ android:id="@+id/create_password"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/bottom_sheet_item_height"
+ android:text="@string/bottom_sheet_create_new_password"
+ android:textAlignment="viewStart"
+ android:textColor="?attr/colorOnSurface"
+ android:layout_margin="@dimen/normal_margin"
+ app:layout_constraintTop_toBottomOf="@id/create_folder"
+ app:icon="@drawable/ic_action_new_password"
+ app:iconPadding="@dimen/normal_margin"
+ app:iconTint="?attr/colorSecondary"
+ app:rippleColor="?attr/colorSecondary" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/app/src/main/res/layout/password_recycler_view.xml b/app/src/main/res/layout/password_recycler_view.xml
index 8348bea2..ca2b6550 100644
--- a/app/src/main/res/layout/password_recycler_view.xml
+++ b/app/src/main/res/layout/password_recycler_view.xml
@@ -24,37 +24,6 @@
tools:listitem="@layout/password_row_layout" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top|center_horizontal"
- android:layout_marginBottom="16dp"
- android:orientation="vertical"
- android:visibility="invisible"
- android:clipChildren="false"
- android:clipToPadding="false"
- app:layout_anchor="@id/fab"
- app:layout_anchorGravity="top|center_horizontal"
- app:layout_behavior="com.zeapo.pwdstore.widget.fab.EmitExpandableTransformationBehavior"
- tools:visibility="visible">
-
- <com.google.android.material.floatingactionbutton.FloatingActionButton
- android:id="@+id/create_password"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/fab_margin"
- app:fabSize="mini"
- app:srcCompat="@drawable/ic_action_new_password" />
-
- <com.google.android.material.floatingactionbutton.FloatingActionButton
- android:id="@+id/create_folder"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/fab_margin"
- app:fabSize="mini"
- app:srcCompat="@drawable/ic_action_new_folder" />
- </LinearLayout>
-
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:src="@drawable/ic_add_white_48dp"
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 15c6287c..0e24bea2 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -4,4 +4,6 @@
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="fab_compat_margin">16dp</dimen>
<dimen name="fab_margin">8dp</dimen>
+ <dimen name="normal_margin">8dp</dimen>
+ <dimen name="bottom_sheet_item_height">56dp</dimen>
</resources>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index acedf8e3..87357f86 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -360,4 +360,6 @@
<string name="git_operation_unable_to_open_ssh_key_title">Unable to open the ssh-key</string>
<string name="git_operation_unable_to_open_ssh_key_message">Please check that it was imported.</string>
<string name="git_operation_wrong_passphrase">Wrong passphrase</string>
+ <string name="bottom_sheet_create_new_folder">Create new folder</string>
+ <string name="bottom_sheet_create_new_password">Create new password</string>
</resources>
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index b5a03bb4..7058dcff 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -21,6 +21,7 @@
<item name="materialAlertDialogTheme">@style/AppTheme.Dialog</item>
<item name="actionBarPopupTheme">@style/ThemeOverlay.MaterialComponents.ActionBar</item>
<item name="textInputStyle">@style/TextInputLayoutBase</item>
+ <item name="bottomSheetDialogTheme">@style/BottomSheetDialogTheme</item>
</style>
<style name="AppTheme.Dialog" parent="Theme.MaterialComponents.DayNight.Dialog.Alert">
@@ -33,6 +34,18 @@
<item name="background">@color/primary_color</item>
</style>
+ <style name="BottomSheetDialogTheme" parent="ThemeOverlay.MaterialComponents.BottomSheetDialog">
+ <item name="android:windowIsFloating">false</item>
+ <item name="android:navigationBarColor">@android:color/transparent</item>
+ <item name="android:statusBarColor">@android:color/transparent</item>
+ <item name="android:windowTranslucentNavigation">false</item>
+ <item name="android:windowIsTranslucent">false</item>
+ <item name="android:backgroundDimEnabled">true</item>
+ <item name="android:backgroundDimAmount">0.5</item>
+ <item name="android:windowTranslucentStatus">false</item>
+ <item name="android:colorBackground">@android:color/transparent</item>
+ </style>
+
<style name="NoBackgroundTheme" parent="@style/AppTheme">
<item name="android:background">@android:color/transparent</item>
<item name="android:backgroundDimEnabled">true</item>