diff options
Diffstat (limited to 'app/src/main')
-rw-r--r-- | app/src/main/java/com/zeapo/pwdstore/ui/dialogs/BasicBottomSheet.kt | 145 | ||||
-rw-r--r-- | app/src/main/res/layout/basic_bottom_sheet.xml | 61 |
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> |