summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/ui/dialogs/BasicBottomSheet.kt145
-rw-r--r--app/src/main/res/layout/basic_bottom_sheet.xml61
2 files changed, 206 insertions, 0 deletions
diff --git a/app/src/main/java/com/zeapo/pwdstore/ui/dialogs/BasicBottomSheet.kt b/app/src/main/java/com/zeapo/pwdstore/ui/dialogs/BasicBottomSheet.kt
new file mode 100644
index 00000000..5f79baf8
--- /dev/null
+++ b/app/src/main/java/com/zeapo/pwdstore/ui/dialogs/BasicBottomSheet.kt
@@ -0,0 +1,145 @@
+/*
+ * 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.content.Context
+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.annotation.StringRes
+import androidx.core.view.isVisible
+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.R
+import com.zeapo.pwdstore.databinding.BasicBottomSheetBinding
+import com.zeapo.pwdstore.utils.resolveAttribute
+import com.zeapo.pwdstore.utils.viewBinding
+
+/**
+ * [BottomSheetDialogFragment] that exposes a simple [androidx.appcompat.app.AlertDialog] like
+ * API through [Builder] to create a similar UI, just at the bottom of the screen.
+ */
+class BasicBottomSheet private constructor(
+ val title: String,
+ val message: String,
+ val positiveButtonClickListener: View.OnClickListener?,
+ val negativeButtonClickListener: View.OnClickListener?,
+) : BottomSheetDialogFragment() {
+
+ private val binding by viewBinding(BasicBottomSheetBinding::bind)
+
+ 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 layoutInflater.inflate(R.layout.basic_bottom_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)
+ }
+ binding.bottomSheetTitle.text = title
+ binding.bottomSheetMessage.text = message
+ if (positiveButtonClickListener != null) {
+ binding.bottomSheetOkButton.isVisible = true
+ binding.bottomSheetOkButton.setOnClickListener {
+ positiveButtonClickListener.onClick(it)
+ dismiss()
+ }
+ }
+ if (negativeButtonClickListener != null) {
+ binding.bottomSheetCancelButton.isVisible = true
+ binding.bottomSheetCancelButton.setOnClickListener {
+ negativeButtonClickListener.onClick(it)
+ dismiss()
+ }
+ }
+ }
+ })
+ val gradientDrawable = GradientDrawable().apply {
+ setColor(requireContext().resolveAttribute(android.R.attr.windowBackground))
+ }
+ view.background = gradientDrawable
+ }
+
+ override fun dismiss() {
+ super.dismiss()
+ behavior?.removeBottomSheetCallback(bottomSheetCallback)
+ }
+
+ class Builder(val context: Context) {
+
+ private var title: String? = null
+ private var message: String? = null
+ private var positiveButtonClickListener: View.OnClickListener? = null
+ private var negativeButtonClickListener: View.OnClickListener? = null
+
+ fun setTitleRes(@StringRes titleRes: Int): Builder {
+ this.title = context.resources.getString(titleRes)
+ return this
+ }
+
+ fun setTitle(title: String): Builder {
+ this.title = title
+ return this
+ }
+
+ fun setMessageRes(@StringRes messageRes: Int): Builder {
+ this.message = context.resources.getString(messageRes)
+ return this
+ }
+
+ fun setMessage(message: String): Builder {
+ this.message = message
+ return this
+ }
+
+ fun setPositiveButtonClickListener(listener: View.OnClickListener): Builder {
+ this.positiveButtonClickListener = listener
+ return this
+ }
+
+ fun setNegativeButtonClickListener(listener: View.OnClickListener): Builder {
+ this.negativeButtonClickListener = listener
+ return this
+ }
+
+ fun build(): BasicBottomSheet {
+ require(title != null) { "Title needs to be set" }
+ require(message != null) { "Message needs to be set" }
+ return BasicBottomSheet(
+ title!!,
+ message!!,
+ positiveButtonClickListener,
+ negativeButtonClickListener
+ )
+ }
+ }
+}
diff --git a/app/src/main/res/layout/basic_bottom_sheet.xml b/app/src/main/res/layout/basic_bottom_sheet.xml
new file mode 100644
index 00000000..9e05aca4
--- /dev/null
+++ b/app/src/main/res/layout/basic_bottom_sheet.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingBottom="24dp">
+
+ <com.google.android.material.textview.MaterialTextView
+ android:id="@+id/bottom_sheet_title"
+ style="@style/TextAppearance.MaterialComponents.Headline6"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
+ android:layout_marginTop="8dp"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:text="Bottom sheet title" />
+
+ <com.google.android.material.textview.MaterialTextView
+ android:id="@+id/bottom_sheet_message"
+ style="@style/TextAppearance.MaterialComponents.Body1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
+ android:layout_marginTop="16dp"
+ android:layout_marginEnd="8dp"
+ app:layout_constraintTop_toBottomOf="@id/bottom_sheet_title"
+ tools:text="A long body of text that serves as the bottom sheet message" />
+
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/bottom_sheet_cancel_button"
+ style="@style/AppTheme.OutlinedButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="24dp"
+ android:layout_marginEnd="8dp"
+ android:text="@string/dialog_cancel"
+ android:visibility="gone"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/bottom_sheet_message"
+ tools:visibility="visible" />
+
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/bottom_sheet_ok_button"
+ style="@style/AppTheme.OutlinedButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="24dp"
+ android:layout_marginEnd="8dp"
+ android:text="@string/dialog_ok"
+ android:visibility="gone"
+ app:layout_constraintEnd_toStartOf="@id/bottom_sheet_cancel_button"
+ app:layout_constraintTop_toBottomOf="@id/bottom_sheet_message"
+ tools:visibility="visible" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>