diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ac2a4665..4a4351e6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -105,7 +105,6 @@ + android:label="@string/recovery_code_title" /> + android:permission="com.stevesoltys.seedvault.RESTORE_BACKUP"> diff --git a/app/src/main/java/com/stevesoltys/seedvault/App.kt b/app/src/main/java/com/stevesoltys/seedvault/App.kt index 053af85f..9389eb05 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/App.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/App.kt @@ -20,6 +20,7 @@ import android.os.UserManager import android.provider.Settings import androidx.work.ExistingPeriodicWorkPolicy.UPDATE import androidx.work.WorkManager +import com.google.android.material.color.DynamicColors import com.stevesoltys.seedvault.crypto.cryptoModule import com.stevesoltys.seedvault.header.headerModule import com.stevesoltys.seedvault.metadata.MetadataManager @@ -114,6 +115,7 @@ open class App : Application() { override fun onCreate() { super.onCreate() + DynamicColors.applyToActivitiesIfAvailable(this) startKoin() if (isDebugBuild()) { StrictMode.setThreadPolicy( diff --git a/app/src/main/java/com/stevesoltys/seedvault/restore/RestoreProgressFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/restore/RestoreProgressFragment.kt index 21b2f228..1f32eb76 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/restore/RestoreProgressFragment.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/restore/RestoreProgressFragment.kt @@ -14,12 +14,12 @@ import android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON import android.widget.Button import android.widget.ProgressBar import android.widget.TextView -import androidx.appcompat.app.AlertDialog import androidx.core.content.ContextCompat.getColor import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.ui.AppBackupState.FAILED_NOT_INSTALLED import org.koin.androidx.viewmodel.ext.android.sharedViewModel @@ -99,7 +99,7 @@ class RestoreProgressFragment : Fragment() { // check if any restore failed, because the app is not installed val failed = viewModel.restoreProgress.value?.any { it.state == FAILED_NOT_INSTALLED } if (failed != true) return // nothing left to do if there's no failures due to not installed - AlertDialog.Builder(requireContext()) + MaterialAlertDialogBuilder(requireContext()) .setTitle(R.string.restore_restoring_error_title) .setMessage(R.string.restore_restoring_error_message) .setPositiveButton(android.R.string.ok) { dialog, _ -> diff --git a/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallProgressFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallProgressFragment.kt index 926dab69..014f198c 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallProgressFragment.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallProgressFragment.kt @@ -19,11 +19,11 @@ import android.widget.TextView import android.widget.Toast import android.widget.Toast.LENGTH_LONG import androidx.activity.result.contract.ActivityResultContract -import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.restore.RestoreViewModel import org.koin.androidx.viewmodel.ext.android.sharedViewModel @@ -96,7 +96,7 @@ class InstallProgressFragment : Fragment(), InstallItemListener { adapter.setFinished() button.isEnabled = true if (!hasShownFailDialog && installResult.hasFailed) { - AlertDialog.Builder(requireContext()) + MaterialAlertDialogBuilder(requireContext()) .setIcon(R.drawable.ic_warning) .setTitle(R.string.restore_installing_error_title) .setMessage(R.string.restore_installing_error_message) diff --git a/app/src/main/java/com/stevesoltys/seedvault/settings/SchedulingFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/settings/SchedulingFragment.kt index 4309ecf2..ea901b4b 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/settings/SchedulingFragment.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/settings/SchedulingFragment.kt @@ -8,6 +8,8 @@ package com.stevesoltys.seedvault.settings import android.content.SharedPreferences import android.os.Bundle import android.view.View +import androidx.preference.ListPreference +import androidx.preference.Preference import androidx.preference.PreferenceCategory import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceManager @@ -16,6 +18,7 @@ import androidx.work.ExistingPeriodicWorkPolicy.UPDATE import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.permitDiskReads import com.stevesoltys.seedvault.plugins.StoragePluginManager +import com.stevesoltys.seedvault.settings.preference.M3ListPreference import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.sharedViewModel @@ -42,6 +45,21 @@ class SchedulingFragment : PreferenceFragmentCompat(), } } + override fun onDisplayPreferenceDialog(preference: Preference?) { + when (preference) { + is ListPreference -> { + val dialogFragment = M3ListPreference.newInstance(preference.getKey()) + dialogFragment.setTargetFragment(this, 0) + dialogFragment.show( + parentFragmentManager, + M3ListPreference.PREFERENCE_DIALOG_FRAGMENT_TAG + ) + } + + else -> super.onDisplayPreferenceDialog(preference) + } + } + override fun onStart() { super.onStart() diff --git a/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsActivity.kt b/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsActivity.kt index 436a040b..6697e3f4 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsActivity.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsActivity.kt @@ -34,6 +34,7 @@ class SettingsActivity : RequireProvisioningActivity(), OnPreferenceStartFragmen setContentView(R.layout.activity_fragment_container) + setSupportActionBar(requireViewById(R.id.toolbar)) supportActionBar!!.setDisplayHomeAsUpEnabled(true) // always start with settings fragment as a base (when fresh start) diff --git a/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsFragment.kt index e7a98ff2..e4096d20 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsFragment.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsFragment.kt @@ -19,12 +19,12 @@ import android.view.MenuItem import android.view.View import android.widget.Toast import android.widget.Toast.LENGTH_LONG -import androidx.appcompat.app.AlertDialog import androidx.preference.Preference import androidx.preference.Preference.OnPreferenceChangeListener import androidx.preference.PreferenceFragmentCompat import androidx.preference.TwoStatePreference import androidx.work.WorkInfo +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.permitDiskReads import com.stevesoltys.seedvault.plugins.StoragePluginManager @@ -77,7 +77,7 @@ class SettingsFragment : PreferenceFragmentCompat() { when (enabled) { true -> return@OnPreferenceChangeListener trySetBackupEnabled(true) false -> { - AlertDialog.Builder(requireContext()) + MaterialAlertDialogBuilder(requireContext()) .setIcon(R.drawable.ic_warning) .setTitle(R.string.settings_backup_dialog_title) .setMessage(R.string.settings_backup_dialog_message) @@ -123,7 +123,7 @@ class SettingsFragment : PreferenceFragmentCompat() { apkBackup.onPreferenceChangeListener = OnPreferenceChangeListener { _, newValue -> val enable = newValue as Boolean if (enable) return@OnPreferenceChangeListener true - AlertDialog.Builder(requireContext()) + MaterialAlertDialogBuilder(requireContext()) .setIcon(R.drawable.ic_warning) .setTitle(R.string.settings_backup_apk_dialog_title) .setMessage(R.string.settings_backup_apk_dialog_message) @@ -313,7 +313,7 @@ class SettingsFragment : PreferenceFragmentCompat() { } private fun onEnablingStorageBackup() { - AlertDialog.Builder(requireContext()) + MaterialAlertDialogBuilder(requireContext()) .setIcon(R.drawable.ic_warning) .setTitle(R.string.settings_backup_storage_dialog_title) .setMessage(R.string.settings_backup_storage_dialog_message) @@ -341,7 +341,7 @@ class SettingsFragment : PreferenceFragmentCompat() { } private fun showCodeRegenerationNeededDialog() { - AlertDialog.Builder(requireContext()) + MaterialAlertDialogBuilder(requireContext()) .setIcon(R.drawable.ic_vpn_key) .setTitle(R.string.settings_backup_new_code_dialog_title) .setMessage(R.string.settings_backup_new_code_dialog_message) diff --git a/app/src/main/java/com/stevesoltys/seedvault/settings/preference/M3ListPreference.kt b/app/src/main/java/com/stevesoltys/seedvault/settings/preference/M3ListPreference.kt new file mode 100644 index 00000000..45e5cc9d --- /dev/null +++ b/app/src/main/java/com/stevesoltys/seedvault/settings/preference/M3ListPreference.kt @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2024 The Calyx Institute + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.stevesoltys.seedvault.settings.preference + +import android.app.Dialog +import android.os.Bundle +import androidx.preference.ListPreferenceDialogFragmentCompat +import com.google.android.material.dialog.MaterialAlertDialogBuilder + +class M3ListPreference : ListPreferenceDialogFragmentCompat() { + + companion object { + const val PREFERENCE_DIALOG_FRAGMENT_TAG = "androidx.preference.PreferenceFragment.DIALOG" + + fun newInstance(key: String?): M3ListPreference { + val fragment = M3ListPreference() + val bundle = Bundle(1) + bundle.putString(ARG_KEY, key) + fragment.arguments = bundle + return fragment + } + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val builder = MaterialAlertDialogBuilder(requireContext()) + .setTitle(preference.dialogTitle) + .setIcon(preference.dialogIcon) + .setPositiveButton(preference.positiveButtonText, this) + .setNegativeButton(preference.negativeButtonText, this) + + val contentView = onCreateDialogView(requireContext()) + if (contentView != null) { + onBindDialogView(contentView) + builder.setView(contentView) + } else { + builder.setMessage(preference.dialogMessage) + } + + onPrepareDialogBuilder(builder) + return builder.create() + } +} diff --git a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeInputFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeInputFragment.kt index 5a0e4607..6cd76ba5 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeInputFragment.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeInputFragment.kt @@ -21,20 +21,20 @@ import android.view.View.VISIBLE import android.view.ViewGroup import android.view.WindowManager.LayoutParams.FLAG_SECURE import android.widget.ArrayAdapter -import android.widget.AutoCompleteTextView import android.widget.Button import android.widget.TextView import android.widget.Toast import android.widget.Toast.LENGTH_LONG import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult -import androidx.appcompat.app.AlertDialog import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.ContextCompat.getMainExecutor import androidx.fragment.app.Fragment import cash.z.ecc.android.bip39.Mnemonics import cash.z.ecc.android.bip39.Mnemonics.ChecksumException import cash.z.ecc.android.bip39.Mnemonics.InvalidWordException +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar +import com.google.android.material.textfield.MaterialAutoCompleteTextView import com.google.android.material.textfield.TextInputLayout import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.isDebugBuild @@ -118,7 +118,7 @@ class RecoveryCodeInputFragment : Fragment() { for (i in 0 until WORD_NUM) { val wordLayout = getWordLayout(i) - val editText = wordLayout.editText as AutoCompleteTextView + val editText = wordLayout.editText as MaterialAutoCompleteTextView editText.onFocusChangeListener = OnFocusChangeListener { _, focus -> if (!focus) wordLayout.isErrorEnabled = false } @@ -207,7 +207,7 @@ class RecoveryCodeInputFragment : Fragment() { } private fun onExistingCodeChecked(verified: Boolean) { - AlertDialog.Builder(requireContext()).apply { + MaterialAlertDialogBuilder(requireContext()).apply { if (verified) { setTitle(R.string.recovery_code_verification_ok_title) setMessage(R.string.recovery_code_verification_ok_message) @@ -237,7 +237,7 @@ class RecoveryCodeInputFragment : Fragment() { } private fun generateNewCode() { - AlertDialog.Builder(requireContext()) + MaterialAlertDialogBuilder(requireContext()) .setIcon(R.drawable.ic_warning) .setTitle(R.string.recovery_code_verification_new_dialog_title) .setMessage(R.string.recovery_code_verification_new_dialog_message) diff --git a/app/src/main/java/com/stevesoltys/seedvault/ui/storage/StorageActivity.kt b/app/src/main/java/com/stevesoltys/seedvault/ui/storage/StorageActivity.kt index 3542235c..1e0b78e1 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/ui/storage/StorageActivity.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/ui/storage/StorageActivity.kt @@ -16,7 +16,7 @@ import android.net.Uri import android.os.Bundle import androidx.activity.result.contract.ActivityResultContracts.OpenDocumentTree import androidx.annotation.CallSuper -import androidx.appcompat.app.AlertDialog +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.plugins.saf.StorageRootResolver import com.stevesoltys.seedvault.ui.BackupActivity @@ -89,7 +89,7 @@ class StorageActivity : BackupActivity() { private fun onInvalidLocation(errorMsg: String) { if (viewModel.isRestoreOperation) { - val dialog = AlertDialog.Builder(this) + val dialog = MaterialAlertDialogBuilder(this) .setTitle(getString(R.string.restore_invalid_location_title)) .setMessage(errorMsg) .setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() } diff --git a/app/src/main/java/com/stevesoltys/seedvault/ui/storage/StorageOptionAdapter.kt b/app/src/main/java/com/stevesoltys/seedvault/ui/storage/StorageOptionAdapter.kt index 91f6551d..4c3c716b 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/ui/storage/StorageOptionAdapter.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/ui/storage/StorageOptionAdapter.kt @@ -15,9 +15,9 @@ import android.view.View.VISIBLE import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView -import androidx.appcompat.app.AlertDialog import androidx.recyclerview.widget.RecyclerView.Adapter import androidx.recyclerview.widget.RecyclerView.ViewHolder +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.ui.storage.StorageOption.SafOption import com.stevesoltys.seedvault.ui.storage.StorageOptionAdapter.StorageOptionViewHolder @@ -91,7 +91,7 @@ internal class StorageOptionAdapter( } private fun showWarningDialog(context: Context, item: StorageOption) { - AlertDialog.Builder(context) + MaterialAlertDialogBuilder(context) .setTitle(R.string.storage_internal_warning_title) .setMessage(R.string.storage_internal_warning_message) .setPositiveButton(R.string.storage_internal_warning_choose_other) { dialog, _ -> diff --git a/app/src/main/res/drawable/ic_app_settings.xml b/app/src/main/res/drawable/ic_app_settings.xml index be2e27ef..567eb667 100644 --- a/app/src/main/res/drawable/ic_app_settings.xml +++ b/app/src/main/res/drawable/ic_app_settings.xml @@ -1,7 +1,7 @@ diff --git a/app/src/main/res/drawable/ic_call.xml b/app/src/main/res/drawable/ic_call.xml index b67ffd6b..8d9dfc5d 100644 --- a/app/src/main/res/drawable/ic_call.xml +++ b/app/src/main/res/drawable/ic_call.xml @@ -6,7 +6,7 @@ - + android:layout_height="match_parent" + android:orientation="vertical"> + + + + + diff --git a/app/src/main/res/layout/fragment_restore_app_selection.xml b/app/src/main/res/layout/fragment_restore_app_selection.xml index 36f68bf5..07f215d3 100644 --- a/app/src/main/res/layout/fragment_restore_app_selection.xml +++ b/app/src/main/res/layout/fragment_restore_app_selection.xml @@ -16,7 +16,8 @@ + android:layout_height="wrap_content" + android:background="@color/background"> + diff --git a/app/src/main/res/layout/recovery_code_input.xml b/app/src/main/res/layout/recovery_code_input.xml index da9adf30..c3ef42e6 100644 --- a/app/src/main/res/layout/recovery_code_input.xml +++ b/app/src/main/res/layout/recovery_code_input.xml @@ -23,8 +23,9 @@ app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_chainStyle="spread_inside"> - - - - - - - - - - - - - @android:color/system_accent1_100 - - @android:color/system_neutral1_900 + @android:color/system_accent1_100 @android:color/system_neutral1_900 diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 30dbd156..77b9a59b 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -6,22 +6,14 @@ - @android:color/system_accent1_600 - - @android:color/system_neutral1_50 - - @color/primary + @android:color/system_accent1_600 @android:color/system_neutral1_50 - - @color/primary - - @color/primary - - @*android:color/error_color_device_default_dark + + #ff7043 - @color/accent + @color/accent_primary #20ffffff diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index f5e767a9..ef5a92b2 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -2,30 +2,19 @@ SPDX-FileCopyrightText: 2020 The Calyx Institute SPDX-License-Identifier: Apache-2.0 --> - + - - - - - @@ -85,7 +74,7 @@ gravity - - + + diff --git a/storage/demo/src/main/java/de/grobox/storagebackuptester/settings/SettingsFragment.kt b/storage/demo/src/main/java/de/grobox/storagebackuptester/settings/SettingsFragment.kt index 1028fa83..940a8331 100644 --- a/storage/demo/src/main/java/de/grobox/storagebackuptester/settings/SettingsFragment.kt +++ b/storage/demo/src/main/java/de/grobox/storagebackuptester/settings/SettingsFragment.kt @@ -19,6 +19,7 @@ import android.widget.Toast import android.widget.Toast.LENGTH_SHORT import androidx.appcompat.app.AlertDialog import androidx.fragment.app.activityViewModels +import com.google.android.material.dialog.MaterialAlertDialogBuilder import de.grobox.storagebackuptester.MainViewModel import de.grobox.storagebackuptester.R import de.grobox.storagebackuptester.restore.DemoSnapshotFragment @@ -133,7 +134,7 @@ class SettingsFragment : BackupContentFragment() { } private fun onRestoreClicked() { - AlertDialog.Builder(requireContext()) + MaterialAlertDialogBuilder(requireContext()) .setIcon(android.R.drawable.stat_sys_warning) .setTitle("Warning") .setMessage("This will override data and should only be used on a clean phone. Not the one you just made the backup on.") diff --git a/storage/lib/src/main/java/org/calyxos/backup/storage/ui/backup/BackupContentAdapter.kt b/storage/lib/src/main/java/org/calyxos/backup/storage/ui/backup/BackupContentAdapter.kt index 854df079..50d8fd35 100644 --- a/storage/lib/src/main/java/org/calyxos/backup/storage/ui/backup/BackupContentAdapter.kt +++ b/storage/lib/src/main/java/org/calyxos/backup/storage/ui/backup/BackupContentAdapter.kt @@ -12,9 +12,9 @@ import android.view.ViewGroup import android.widget.ImageButton import android.widget.ImageView import android.widget.TextView -import androidx.appcompat.widget.SwitchCompat import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.Adapter +import com.google.android.material.materialswitch.MaterialSwitch import org.calyxos.backup.storage.R import org.calyxos.backup.storage.api.EXTERNAL_STORAGE_PROVIDER_AUTHORITY @@ -65,7 +65,7 @@ internal class BackupContentAdapter(private val listener: ContentClickListener) } internal inner class MediaHolder(view: View) : ViewHolder(view) { - private val switch: SwitchCompat = view.findViewById(R.id.switchView) + private val switch: MaterialSwitch = view.findViewById(R.id.switchView) override fun bind(item: BackupContentItem) { super.bind(item) diff --git a/storage/lib/src/main/res/layout/item_media.xml b/storage/lib/src/main/res/layout/item_media.xml index 7c357472..71db589b 100644 --- a/storage/lib/src/main/res/layout/item_media.xml +++ b/storage/lib/src/main/res/layout/item_media.xml @@ -42,7 +42,7 @@ app:layout_constraintTop_toTopOf="parent" tools:text="@string/content_videos" /> -