Merge pull request #776 from theimpulson/menu

Switch to toolbar APIs for working with menu
This commit is contained in:
Torsten Grote 2024-10-17 13:41:18 -03:00 committed by GitHub
commit 3fd615477d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 478 additions and 361 deletions

View file

@ -11,6 +11,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.Fragment
import com.stevesoltys.seedvault.R
import com.stevesoltys.seedvault.transport.backup.PackageService
@ -41,12 +42,11 @@ class AboutDialogFragment : Fragment() {
contributorsView.movementMethod = linkMovementMethod
orgsView.movementMethod = linkMovementMethod
v.requireViewById<Toolbar>(R.id.toolbar).setNavigationOnClickListener {
requireActivity().onBackPressedDispatcher.onBackPressed()
}
return v
}
override fun onStart() {
super.onStart()
activity?.setTitle(R.string.about_title)
}
}

View file

@ -7,14 +7,13 @@ package com.stevesoltys.seedvault.settings
import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import android.view.ViewGroup
import android.widget.ProgressBar
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
@ -32,7 +31,6 @@ class AppStatusFragment : Fragment(), AppStatusToggleListener {
private val layoutManager = LinearLayoutManager(context)
private val adapter = AppStatusAdapter(this)
private lateinit var appEditMenuItem: MenuItem
private lateinit var list: RecyclerView
private lateinit var progressBar: ProgressBar
@ -41,7 +39,6 @@ class AppStatusFragment : Fragment(), AppStatusToggleListener {
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
setHasOptionsMenu(true)
val v: View = inflater.inflate(R.layout.fragment_app_status, container, false)
progressBar = v.requireViewById(R.id.progressBar)
@ -53,7 +50,17 @@ class AppStatusFragment : Fragment(), AppStatusToggleListener {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
activity?.setTitle(R.string.settings_backup_status_title)
val toolbar = view.requireViewById<Toolbar>(R.id.toolbar).apply {
setOnMenuItemClickListener(::onMenuItemSelected)
setNavigationOnClickListener {
requireActivity().onBackPressedDispatcher.onBackPressed()
}
}
viewModel.appEditMode.observe(viewLifecycleOwner) { enabled ->
toolbar.menu.findItem(R.id.edit_app_blacklist)?.isChecked = enabled
adapter.setEditMode(enabled)
}
list.apply {
layoutManager = this@AppStatusFragment.layoutManager
@ -67,24 +74,12 @@ class AppStatusFragment : Fragment(), AppStatusToggleListener {
}
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.app_status_menu, menu)
appEditMenuItem = menu.findItem(R.id.edit_app_blacklist)
// observe edit mode changes here where we are sure to have the MenuItem
viewModel.appEditMode.observe(viewLifecycleOwner) { enabled ->
appEditMenuItem.isChecked = enabled
adapter.setEditMode(enabled)
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) {
private fun onMenuItemSelected(item: MenuItem): Boolean = when (item.itemId) {
R.id.edit_app_blacklist -> {
viewModel.setEditMode(!item.isChecked)
true
}
else -> super.onOptionsItemSelected(item)
else -> false
}
override fun onAppStatusToggled(status: AppStatus) {

View file

@ -7,7 +7,9 @@ package com.stevesoltys.seedvault.settings
import android.app.backup.IBackupManager
import android.os.Bundle
import android.view.View
import androidx.activity.result.contract.ActivityResultContracts.CreateDocument
import androidx.appcompat.widget.Toolbar
import androidx.preference.Preference
import androidx.preference.Preference.OnPreferenceChangeListener
import androidx.preference.PreferenceFragmentCompat
@ -66,9 +68,19 @@ class ExpertSettingsFragment : PreferenceFragmentCompat() {
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.requireViewById<Toolbar>(R.id.toolbar).apply {
title = getString(R.string.settings_expert_title)
setNavigationOnClickListener {
requireActivity().onBackPressedDispatcher.onBackPressed()
}
}
}
override fun onStart() {
super.onStart()
activity?.setTitle(R.string.settings_expert_title)
apkBackup.isEnabled = backupManager.isBackupEnabled
}
}

View file

@ -8,6 +8,7 @@ package com.stevesoltys.seedvault.settings
import android.content.SharedPreferences
import android.os.Bundle
import android.view.View
import androidx.appcompat.widget.Toolbar
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
@ -39,6 +40,13 @@ class SchedulingFragment : PreferenceFragmentCompat(),
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.requireViewById<Toolbar>(R.id.toolbar).apply {
title = getString(R.string.settings_backup_scheduling_title)
setNavigationOnClickListener {
requireActivity().onBackPressedDispatcher.onBackPressed()
}
}
val storage = backendManager.backendProperties
if (storage?.isUsb == true) {
findPreference<PreferenceCategory>("scheduling_category_conditions")?.isEnabled = false
@ -60,12 +68,6 @@ class SchedulingFragment : PreferenceFragmentCompat(),
}
}
override fun onStart() {
super.onStart()
activity?.setTitle(R.string.settings_backup_scheduling_title)
}
override fun onResume() {
super.onResume()
settingsManager.registerOnSharedPreferenceChangeListener(this)

View file

@ -30,9 +30,6 @@ class SettingsActivity : RequireProvisioningActivity(), OnPreferenceStartFragmen
setContentView(R.layout.activity_fragment_container)
setSupportActionBar(requireViewById(R.id.toolbar))
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
if (savedInstanceState == null && viewModel.isFirstStart) {
// let user choose whether to restore on first start
FirstRunFragment().show(supportFragmentManager, null)

View file

@ -11,12 +11,11 @@ import android.os.Bundle
import android.os.PowerManager
import android.os.RemoteException
import android.util.Log
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.widget.Toast
import android.widget.Toast.LENGTH_LONG
import androidx.appcompat.widget.Toolbar
import androidx.preference.Preference
import androidx.preference.Preference.OnPreferenceChangeListener
import androidx.preference.PreferenceFragmentCompat
@ -53,9 +52,6 @@ class SettingsFragment : PreferenceFragmentCompat() {
private lateinit var backupStorage: TwoStatePreference
private lateinit var backupRecoveryCode: Preference
private var menuBackupNow: MenuItem? = null
private var menuRestore: MenuItem? = null
private val backendProperties: BackendProperties<*>?
get() = backendManager.backendProperties
@ -63,7 +59,6 @@ class SettingsFragment : PreferenceFragmentCompat() {
permitDiskReads {
setPreferencesFromResource(R.xml.settings, rootKey)
}
setHasOptionsMenu(true)
backup = findPreference("backup")!!
backup.onPreferenceChangeListener = OnPreferenceChangeListener { _, newValue ->
@ -141,6 +136,20 @@ class SettingsFragment : PreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val toolbar = view.requireViewById<Toolbar>(R.id.toolbar).apply {
title = getString(R.string.backup)
inflateMenu(R.menu.settings_menu)
setOnMenuItemClickListener(::onMenuItemSelected)
setNavigationOnClickListener {
requireActivity().onBackPressedDispatcher.onBackPressed()
}
}
viewModel.backupPossible.observe(viewLifecycleOwner) { possible ->
toolbar.menu.findItem(R.id.action_backup)?.isEnabled = possible
toolbar.menu.findItem(R.id.action_restore)?.isEnabled = possible
}
viewModel.lastBackupTime.observe(viewLifecycleOwner) { time ->
setAppBackupStatusSummary(time)
}
@ -157,9 +166,6 @@ class SettingsFragment : PreferenceFragmentCompat() {
override fun onStart() {
super.onStart()
// we need to re-set the title when returning to this fragment
activity?.setTitle(R.string.backup)
setBackupEnabledState()
setBackupLocationSummary()
setAutoRestoreState()
@ -185,18 +191,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
}
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.settings_menu, menu)
menuBackupNow = menu.findItem(R.id.action_backup)
menuRestore = menu.findItem(R.id.action_restore)
viewModel.backupPossible.observe(viewLifecycleOwner) { possible ->
menuBackupNow?.isEnabled = possible
menuRestore?.isEnabled = possible
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) {
private fun onMenuItemSelected(item: MenuItem): Boolean = when (item.itemId) {
R.id.action_backup -> {
viewModel.backupNow()
true
@ -219,7 +214,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
.commit()
true
}
else -> super.onOptionsItemSelected(item)
else -> false
}
private fun trySetBackupEnabled(enabled: Boolean): Boolean {

View file

@ -6,8 +6,6 @@
package com.stevesoltys.seedvault.ui
import android.os.Bundle
import android.view.MenuItem
import androidx.annotation.CallSuper
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import com.stevesoltys.seedvault.R
@ -19,15 +17,6 @@ abstract class BackupActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
}
@CallSuper
override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) {
android.R.id.home -> {
onBackPressed()
true
}
else -> super.onOptionsItemSelected(item)
}
protected fun showFragment(f: Fragment, addToBackStack: Boolean = false, tag: String? = null) {
supportFragmentManager.beginTransaction().apply {
replace(R.id.fragment, f, tag)

View file

@ -6,9 +6,8 @@
package com.stevesoltys.seedvault.ui.files
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.Toolbar
import com.stevesoltys.seedvault.R
import com.stevesoltys.seedvault.settings.SettingsViewModel
import org.calyxos.backup.storage.ui.backup.BackupContentFragment
@ -20,13 +19,15 @@ class FileSelectionFragment : BackupContentFragment() {
override val viewModel by viewModel<FileSelectionViewModel>()
private val settingsViewModel by sharedViewModel<SettingsViewModel>()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
requireActivity().setTitle(R.string.settings_backup_files_title)
return super.onCreateView(inflater, container, savedInstanceState)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.requireViewById<Toolbar>(R.id.toolbar).apply {
title = getString(R.string.settings_backup_files_title)
setNavigationOnClickListener {
requireActivity().onBackPressedDispatcher.onBackPressed()
}
}
}
override fun onDestroy() {

View file

@ -6,7 +6,6 @@
package com.stevesoltys.seedvault.ui.recoverycode
import android.os.Bundle
import android.view.MenuItem
import android.view.WindowManager.LayoutParams.FLAG_SECURE
import com.stevesoltys.seedvault.R
import com.stevesoltys.seedvault.isDebugBuild
@ -42,16 +41,6 @@ class RecoveryCodeActivity : BackupActivity() {
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
android.R.id.home -> {
onBackPressed()
true
}
else -> super.onOptionsItemSelected(item)
}
}
private fun showOutput() {
supportFragmentManager.beginTransaction()
.add(R.id.fragment, RecoveryCodeOutputFragment(), "Code")

View file

@ -26,6 +26,7 @@ import android.widget.TextView
import android.widget.Toast
import android.widget.Toast.LENGTH_LONG
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.appcompat.widget.Toolbar
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat.getMainExecutor
import androidx.fragment.app.Fragment
@ -105,7 +106,11 @@ class RecoveryCodeInputFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
activity?.setTitle(R.string.recovery_code_title)
view.requireViewById<Toolbar>(R.id.toolbar).apply {
setNavigationOnClickListener {
requireActivity().onBackPressedDispatcher.onBackPressed()
}
}
if (viewModel.isRestore) {
introText.setText(R.string.recovery_code_input_intro)

View file

@ -12,6 +12,7 @@ import android.view.View
import android.view.ViewGroup
import android.view.WindowManager.LayoutParams.FLAG_SECURE
import android.widget.Button
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
@ -44,6 +45,12 @@ class RecoveryCodeOutputFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.requireViewById<Toolbar>(R.id.toolbar).apply {
setNavigationOnClickListener {
requireActivity().onBackPressedDispatcher.onBackPressed()
}
}
setGridParameters(wordList)
wordList.adapter = RecoveryCodeAdapter(viewModel.wordList)

View file

@ -8,13 +8,6 @@
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:theme="?attr/actionBarTheme" />
<FrameLayout
android:id="@+id/fragment"
android:layout_width="match_parent"

View file

@ -3,151 +3,165 @@
SPDX-FileCopyrightText: 2020 The Calyx Institute
SPDX-License-Identifier: Apache-2.0
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout 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:layout_height="match_parent"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
app:navigationIcon="?attr/homeAsUpIndicator"
app:title="@string/about_title" />
<ImageView
android:id="@+id/logoView"
android:layout_width="128dp"
android:layout_height="128dp"
android:layout_marginStart="16dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@mipmap/ic_launcher"
tools:ignore="ContentDescription" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/appNameView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:text="@string/app_name"
android:textColor="?android:textColorPrimary"
android:textSize="26sp"
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@+id/backupNameView"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/logoView" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/backupNameView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="@string/backup"
android:textColor="?android:textColorPrimary"
android:textSize="26sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/appNameView"
app:layout_constraintTop_toBottomOf="@+id/logoView" />
<ImageView
android:id="@+id/logoView"
android:layout_width="128dp"
android:layout_height="128dp"
android:layout_marginStart="16dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@mipmap/ic_launcher"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/versionView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:gravity="center_horizontal"
android:text="@string/about_version"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appNameView" />
<TextView
android:id="@+id/appNameView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:text="@string/app_name"
android:textColor="?android:textColorPrimary"
android:textSize="26sp"
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@+id/backupNameView"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/logoView" />
<TextView
android:id="@+id/summaryView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="@string/about_summary"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/versionView" />
<TextView
android:id="@+id/backupNameView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="@string/backup"
android:textColor="?android:textColorPrimary"
android:textSize="26sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/appNameView"
app:layout_constraintTop_toBottomOf="@+id/logoView" />
<TextView
android:id="@+id/contributorHeadline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="@string/about_contributor_headline"
android:textColor="?android:textColorPrimary"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/summaryView" />
<TextView
android:id="@+id/versionView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:gravity="center_horizontal"
android:text="@string/about_version"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appNameView" />
<TextView
android:id="@+id/contributorView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:lineSpacingExtra="8dp"
android:text="@string/about_contributor_content"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/contributorHeadline" />
<TextView
android:id="@+id/summaryView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="@string/about_summary"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/versionView" />
<TextView
android:id="@+id/about_contributing_organizations_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="@string/about_contributing_organizations_title"
android:textColor="?android:textColorPrimary"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/contributorView" />
<TextView
android:id="@+id/contributorHeadline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="@string/about_contributor_headline"
android:textColor="?android:textColorPrimary"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/summaryView" />
<TextView
android:id="@+id/about_contributing_organizations_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:lineSpacingExtra="8dp"
android:text="@string/about_contributing_organizations_content"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/about_contributing_organizations_title"
app:layout_constraintVertical_bias="0.0" />
<TextView
android:id="@+id/contributorView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:lineSpacingExtra="8dp"
android:text="@string/about_contributor_content"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/contributorHeadline" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
<TextView
android:id="@+id/about_contributing_organizations_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="@string/about_contributing_organizations_title"
android:textColor="?android:textColorPrimary"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/contributorView" />
<TextView
android:id="@+id/about_contributing_organizations_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:lineSpacingExtra="8dp"
android:text="@string/about_contributing_organizations_content"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/about_contributing_organizations_title"
app:layout_constraintVertical_bias="0.0" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</LinearLayout>

View file

@ -8,6 +8,18 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:menu="@menu/app_status_menu"
app:navigationIcon="?attr/homeAsUpIndicator"
app:title="@string/settings_backup_status_title" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list"
android:layout_width="0dp"
@ -15,7 +27,7 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toBottomOf="@id/toolbar" />
<ProgressBar
android:id="@+id/progressBar"

View file

@ -3,87 +3,100 @@
SPDX-FileCopyrightText: 2020 The Calyx Institute
SPDX-License-Identifier: Apache-2.0
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout 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:fillViewport="true">
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context=".ui.recoverycode.RecoveryCodeInputFragment">
android:minHeight="?attr/actionBarSize"
app:navigationIcon="?attr/homeAsUpIndicator"
app:title="@string/recovery_code_title" />
<ImageView
android:id="@+id/introIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="24dp"
android:src="@drawable/ic_info_outline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/introText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="@string/recovery_code_confirm_intro"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/introIcon"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/divider"
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_marginTop="16dp"
android:background="@color/divider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/introText" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/wordList"
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/doneButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/divider">
tools:context=".ui.recoverycode.RecoveryCodeInputFragment">
<include layout="@layout/recovery_code_input" />
<ImageView
android:id="@+id/introIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="24dp"
android:src="@drawable/ic_info_outline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/introText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="@string/recovery_code_confirm_intro"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/introIcon"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/divider"
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_marginTop="16dp"
android:background="@color/divider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/introText" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/wordList"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/doneButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/divider">
<include layout="@layout/recovery_code_input" />
</androidx.constraintlayout.widget.ConstraintLayout>
<Button
android:id="@+id/doneButton"
style="@style/SudPrimaryButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="40dp"
android:text="@string/recovery_code_done_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<Button
android:id="@+id/newCodeButton"
style="@style/SudSecondaryButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="40dp"
android:text="@string/recovery_code_verification_generate_new"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
<Button
android:id="@+id/doneButton"
style="@style/SudPrimaryButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="40dp"
android:text="@string/recovery_code_done_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<Button
android:id="@+id/newCodeButton"
style="@style/SudSecondaryButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="40dp"
android:text="@string/recovery_code_verification_generate_new"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</ScrollView>
</LinearLayout>

View file

@ -10,6 +10,17 @@
android:layout_height="match_parent"
tools:context=".ui.recoverycode.RecoveryCodeActivity">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navigationIcon="?attr/homeAsUpIndicator"
app:title="@string/recovery_code_title" />
<ImageView
android:id="@+id/introIcon"
android:layout_width="wrap_content"
@ -18,7 +29,7 @@
android:layout_marginTop="24dp"
android:src="@drawable/ic_info_outline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar"
tools:ignore="ContentDescription" />
<TextView
@ -32,7 +43,7 @@
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/introIcon"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toBottomOf="@id/toolbar" />
<TextView
android:id="@+id/introText2"
@ -78,8 +89,8 @@
style="@style/SudPrimaryButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/recovery_code_confirm_button"
android:layout_marginEnd="40dp"
android:text="@string/recovery_code_confirm_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/wordList" />

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
SPDX-FileCopyrightText: 2024 The Calyx Institute
SPDX-License-Identifier: Apache-2.0
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
tools:context=".settings.SettingsFragment">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
app:navigationIcon="?attr/homeAsUpIndicator" />
<!-- Required ViewGroup for PreferenceFragmentCompat -->
<FrameLayout
android:id="@android:id/list_container"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>

View file

@ -10,13 +10,17 @@
<item name="colorError">@color/red</item>
<item name="android:windowBackground">@color/background</item>
<item name="fontFamily">@*android:string/config_bodyFontFamily</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
<item name="preferenceTheme">@style/PreferenceTheme</item>
<item name="android:windowLightStatusBar">@bool/isLight</item>
<item name="android:statusBarColor">@color/background</item>
<item name="android:windowLightNavigationBar">@bool/isLight</item>
<item name="android:navigationBarColor">@color/background</item>
</style>
<style name="PreferenceTheme" parent="PreferenceThemeOverlay">
<item name="android:layout">@layout/fragment_settings</item>
</style>
<!-- Copied from Settings -->
<style name="ActionPrimaryButton" parent="android:Widget.DeviceDefault.Button.Colored" />

View file

@ -9,6 +9,7 @@ import android.app.Application
import android.os.StrictMode
import android.os.StrictMode.VmPolicy
import android.util.Log
import com.google.android.material.color.DynamicColors
import de.grobox.storagebackuptester.crypto.KeyManager
import de.grobox.storagebackuptester.plugin.TestSafBackend
import de.grobox.storagebackuptester.settings.SettingsManager
@ -26,6 +27,7 @@ class App : Application() {
override fun onCreate() {
super.onCreate()
DynamicColors.applyToActivitiesIfAvailable(this)
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectAll()

View file

@ -11,8 +11,6 @@ import android.os.Bundle
import android.os.Environment
import android.provider.Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.View.INVISIBLE
@ -22,6 +20,7 @@ import android.widget.Button
import android.widget.ProgressBar
import android.widget.Toast
import android.widget.Toast.LENGTH_SHORT
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.recyclerview.widget.RecyclerView
@ -48,7 +47,6 @@ open class LogFragment : Fragment() {
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
setHasOptionsMenu(true)
val v = inflater.inflate(R.layout.fragment_log, container, false)
list = v.findViewById(R.id.listView)
list.adapter = adapter
@ -75,16 +73,16 @@ open class LogFragment : Fragment() {
return v
}
override fun onStart() {
super.onStart()
requireActivity().setTitle(R.string.app_name)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.requireViewById<Toolbar>(R.id.toolbar).apply {
title = getString(R.string.app_name)
setOnMenuItemClickListener(::onMenuItemSelected)
}
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.fragment_main, menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
private fun onMenuItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.settings -> {
if (!checkPermission()) return false
@ -108,7 +106,7 @@ open class LogFragment : Fragment() {
startActivity(shareIntent)
true
}
else -> super.onOptionsItemSelected(item)
else -> false
}
}

View file

@ -8,6 +8,11 @@ package de.grobox.storagebackuptester
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat.setOnApplyWindowInsetsListener
import androidx.core.view.WindowCompat.setDecorFitsSystemWindows
import androidx.core.view.WindowInsetsCompat.CONSUMED
import androidx.core.view.WindowInsetsCompat.Type.displayCutout
import androidx.core.view.WindowInsetsCompat.Type.systemBars
import de.grobox.storagebackuptester.crypto.KeyManager
class MainActivity : AppCompatActivity() {
@ -16,6 +21,13 @@ class MainActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setDecorFitsSystemWindows(window, false)
setOnApplyWindowInsetsListener(window.decorView.rootView) { v, windowInsets ->
val insets = windowInsets.getInsets(systemBars() or displayCutout())
v.setPadding(insets.left, insets.top, insets.right, insets.bottom)
CONSUMED
}
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, LogFragment.newInstance())

View file

@ -31,7 +31,7 @@ class DemoSnapshotFragment : SnapshotFragment() {
bottomStub.layoutResource = R.layout.footer_snapshot
val footer = bottomStub.inflate()
footer.findViewById<Button>(R.id.button).setOnClickListener {
requireActivity().onBackPressed()
requireActivity().onBackPressedDispatcher.onBackPressed()
}
return v
}

View file

@ -10,8 +10,6 @@ import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.View.FOCUS_DOWN
@ -21,6 +19,7 @@ import android.view.ViewGroup
import android.widget.ProgressBar
import android.widget.TextView
import androidx.annotation.UiThread
import androidx.appcompat.widget.Toolbar
import androidx.core.widget.NestedScrollView
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
@ -53,8 +52,6 @@ open class MediaScanFragment : Fragment() {
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
setHasOptionsMenu(true)
requireActivity().title = arguments?.getString("name")
val v = inflater.inflate(R.layout.fragment_scan, container, false)
scrollView = v.findViewById(R.id.scrollView)
logView = v.findViewById(R.id.logView)
@ -63,11 +60,19 @@ open class MediaScanFragment : Fragment() {
return v
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.fragment_scan, menu)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.requireViewById<Toolbar>(R.id.toolbar).apply {
title = arguments?.getString("name")
setOnMenuItemClickListener(::onMenuItemSelected)
setNavigationOnClickListener {
requireActivity().onBackPressedDispatcher.onBackPressed()
}
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
private fun onMenuItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.refresh -> {
loadText()
@ -85,7 +90,7 @@ open class MediaScanFragment : Fragment() {
startActivity(shareIntent)
true
}
else -> super.onOptionsItemSelected(item)
else -> false
}
}

View file

@ -9,14 +9,12 @@ import android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION
import android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import android.widget.Toast.LENGTH_SHORT
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.activityViewModels
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import de.grobox.storagebackuptester.MainViewModel
@ -44,18 +42,21 @@ class SettingsFragment : BackupContentFragment() {
onBackupUriReceived(uri)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
setHasOptionsMenu(true)
requireActivity().title = "Settings"
return super.onCreateView(inflater, container, savedInstanceState)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.requireViewById<Toolbar>(R.id.toolbar).apply {
title = "Settings"
inflateMenu(R.menu.fragment_settings)
onCreateMenu(menu)
setOnMenuItemClickListener(::onMenuItemSelected)
setNavigationOnClickListener {
requireActivity().onBackPressedDispatcher.onBackPressed()
}
}
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.fragment_settings, menu)
private fun onCreateMenu(menu: Menu) {
backupLocationItem = menu.findItem(R.id.backup_location)
backupLocationItem.isChecked = viewModel.hasBackupLocation()
jobItem = menu.findItem(R.id.backup_job)
@ -65,7 +66,7 @@ class SettingsFragment : BackupContentFragment() {
restoreItem.isEnabled = backupLocationItem.isChecked
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
private fun onMenuItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.info -> {
parentFragmentManager.beginTransaction()
@ -102,7 +103,7 @@ class SettingsFragment : BackupContentFragment() {
Toast.makeText(requireContext(), "Cache cleared", LENGTH_SHORT).show()
true
}
else -> super.onOptionsItemSelected(item)
else -> false
}
}

View file

@ -9,6 +9,16 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:menu="@menu/fragment_main" />
<ProgressBar
android:id="@+id/horizontalProgressBar"
style="?android:attr/progressBarStyleHorizontal"
@ -16,7 +26,7 @@
android:layout_height="5dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar"
tools:max="10"
tools:progress="7" />

View file

@ -9,13 +9,24 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:menu="@menu/fragment_scan"
app:navigationIcon="?attr/homeAsUpIndicator" />
<androidx.core.widget.NestedScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="0dp"
android:fadeScrollbars="false"
android:scrollbars="vertical"
app:layout_constraintTop_toTopOf="parent">
app:layout_constraintTop_toBottomOf="@id/toolbar">
<TextView
android:id="@+id/logView"

View file

@ -4,7 +4,7 @@
-->
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.StorageBackupTester" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<style name="Theme.StorageBackupTester" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/matrix</item>
<item name="colorPrimaryVariant">@color/purple_700</item>

View file

@ -4,7 +4,7 @@
-->
<resources>
<style name="Theme.StorageBackupTester" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<style name="Theme.StorageBackupTester" parent="Theme.Material3.DayNight.NoActionBar">
<item name="colorPrimary">@color/matrix</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/white</item>

View file

@ -3,28 +3,40 @@
SPDX-FileCopyrightText: 2021 The Calyx Institute
SPDX-License-Identifier: Apache-2.0
-->
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout 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:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list"
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
app:layoutManager="LinearLayoutManager"
tools:listitem="@layout/item_media" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:contentDescription="@string/content_add"
android:src="@drawable/ic_add"
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior" />
android:minHeight="?attr/actionBarSize"
app:navigationIcon="?attr/homeAsUpIndicator" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
app:layoutManager="LinearLayoutManager"
tools:listitem="@layout/item_media" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:contentDescription="@string/content_add"
android:src="@drawable/ic_add"
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</LinearLayout>