Merge branch 'android12' into patch-2

This commit is contained in:
Allan Nordhøy 2022-11-16 17:57:34 +00:00 committed by GitHub
commit 64c8430046
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 341 additions and 167 deletions

View file

@ -50,7 +50,7 @@ open class App : Application() {
factory<IBackupManager> { IBackupManager.Stub.asInterface(getService(BACKUP_SERVICE)) }
factory { AppListRetriever(this@App, get(), get(), get()) }
viewModel { SettingsViewModel(this@App, get(), get(), get(), get(), get(), get()) }
viewModel { SettingsViewModel(this@App, get(), get(), get(), get(), get(), get(), get()) }
viewModel { RecoveryCodeViewModel(this@App, get(), get(), get(), get(), get(), get()) }
viewModel { BackupStorageViewModel(this@App, get(), get(), get(), get()) }
viewModel { RestoreStorageViewModel(this@App, get(), get()) }

View file

@ -11,9 +11,7 @@ import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat.getColor
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager.VERTICAL
import androidx.recyclerview.widget.RecyclerView
import com.stevesoltys.seedvault.R
import com.stevesoltys.seedvault.ui.AppBackupState.FAILED_NOT_INSTALLED
@ -54,7 +52,6 @@ class RestoreProgressFragment : Fragment() {
appList.apply {
layoutManager = this@RestoreProgressFragment.layoutManager
adapter = this@RestoreProgressFragment.adapter
addItemDecoration(DividerItemDecoration(context, VERTICAL))
}
button.setText(R.string.restore_finished_button)

View file

@ -41,7 +41,12 @@ internal class ApkRestore(
@Suppress("BlockingMethodInNonBlockingContext")
fun restore(backup: RestorableBackup) = flow {
// filter out packages without APK and get total
val packages = backup.packageMetadataMap.filter { it.value.hasApk() }
val packages = backup.packageMetadataMap.filter {
// We also need to exclude the DocumentsProvider used to retrieve backup data.
// Otherwise, it gets killed when we install it, terminating our restoration.
val isStorageProvider = it.key == storagePlugin.providerPackageName
it.value.hasApk() && !isStorageProvider
}
val total = packages.size
var progress = 0

View file

@ -1,5 +1,6 @@
package com.stevesoltys.seedvault.restore.install
import android.os.Build.VERSION.SDK_INT
import android.view.LayoutInflater
import android.view.View
import android.view.View.GONE
@ -44,7 +45,8 @@ internal class InstallProgressAdapter(
return if (finished) finishedComparator.compare(item1, item2)
else item1.compareTo(item2)
}
})
}
)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AppInstallViewHolder {
val v = LayoutInflater.from(parent.context)
@ -79,15 +81,23 @@ internal class InstallProgressAdapter(
IN_PROGRESS -> {
appStatus.visibility = INVISIBLE
progressBar.visibility = VISIBLE
if (SDK_INT >= 30) {
progressBar.stateDescription =
context.getString(R.string.restore_app_status_installing)
}
}
SUCCEEDED -> {
appStatus.setImageResource(R.drawable.ic_check_green)
appStatus.visibility = VISIBLE
appStatus.contentDescription =
context.getString(R.string.restore_app_status_installed)
progressBar.visibility = INVISIBLE
}
FAILED -> {
appStatus.setImageResource(R.drawable.ic_error_red)
appStatus.visibility = VISIBLE
appStatus.contentDescription =
context.getString(R.string.restore_app_status_install_error)
progressBar.visibility = INVISIBLE
if (finished) {
v.background = clickableBackground
@ -100,6 +110,8 @@ internal class InstallProgressAdapter(
}
FAILED_SYSTEM_APP -> {
appStatus.setImageResource(R.drawable.ic_error_red)
appStatus.contentDescription =
context.getString(R.string.restore_app_status_install_error)
appStatus.visibility = VISIBLE
progressBar.visibility = INVISIBLE
}

View file

@ -15,9 +15,7 @@ import android.widget.Toast.LENGTH_LONG
import androidx.activity.result.contract.ActivityResultContract
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager.VERTICAL
import androidx.recyclerview.widget.RecyclerView
import com.stevesoltys.seedvault.R
import com.stevesoltys.seedvault.restore.RestoreViewModel
@ -58,22 +56,21 @@ class InstallProgressFragment : Fragment(), InstallItemListener {
appList.apply {
layoutManager = this@InstallProgressFragment.layoutManager
adapter = this@InstallProgressFragment.adapter
addItemDecoration(DividerItemDecoration(context, VERTICAL))
}
button.setText(R.string.restore_next)
button.setOnClickListener { viewModel.onNextClickedAfterInstallingApps() }
viewModel.chosenRestorableBackup.observe(viewLifecycleOwner, { restorableBackup ->
viewModel.chosenRestorableBackup.observe(viewLifecycleOwner) { restorableBackup ->
backupNameView.text = restorableBackup.name
})
}
viewModel.installResult.observe(viewLifecycleOwner, { result ->
viewModel.installResult.observe(viewLifecycleOwner) { result ->
onInstallResult(result)
})
}
viewModel.nextButtonEnabled.observe(viewLifecycleOwner, { enabled ->
viewModel.nextButtonEnabled.observe(viewLifecycleOwner) { enabled ->
button.isEnabled = enabled
})
}
}
private fun onInstallResult(installResult: InstallResult) {

View file

@ -1,6 +1,7 @@
package com.stevesoltys.seedvault.settings
import android.app.Application
import android.app.backup.IBackupManager
import android.app.job.JobInfo.NETWORK_TYPE_NONE
import android.app.job.JobInfo.NETWORK_TYPE_UNMETERED
import android.content.Intent
@ -49,6 +50,7 @@ internal class SettingsViewModel(
private val metadataManager: MetadataManager,
private val appListRetriever: AppListRetriever,
private val storageBackup: StorageBackup,
private val backupManager: IBackupManager,
) : RequireProvisioningViewModel(app, settingsManager, keyManager) {
private val contentResolver = app.contentResolver
@ -157,6 +159,8 @@ internal class SettingsViewModel(
// maybe replace the check below with one that checks if our transport service is running
if (notificationManager.hasActiveBackupNotifications()) {
Toast.makeText(app, R.string.notification_backup_already_running, LENGTH_LONG).show()
} else if (!backupManager.isBackupEnabled) {
Toast.makeText(app, R.string.notification_backup_disabled, LENGTH_LONG).show()
} else viewModelScope.launch(Dispatchers.IO) {
if (settingsManager.isStorageBackupEnabled()) {
val i = Intent(app, StorageBackupService::class.java)

View file

@ -2,6 +2,7 @@ package com.stevesoltys.seedvault.ui
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build.VERSION.SDK_INT
import android.view.View
import android.view.View.GONE
import android.view.View.INVISIBLE
@ -39,21 +40,45 @@ internal abstract class AppViewHolder(protected val v: View) : RecyclerView.View
appInfo.visibility = GONE
appStatus.visibility = INVISIBLE
progressBar.visibility = VISIBLE
if (SDK_INT >= 30) {
progressBar.stateDescription = context.getString(
if (isRestore) R.string.restore_restoring
else R.string.backup_app_in_progress
)
}
} else {
appStatus.visibility = VISIBLE
progressBar.visibility = INVISIBLE
appInfo.visibility = GONE
val contentDescription: String?
when (state) {
SUCCEEDED -> appStatus.setImageResource(R.drawable.ic_check_green)
FAILED -> appStatus.setImageResource(R.drawable.ic_error_red)
SUCCEEDED -> {
appStatus.setImageResource(R.drawable.ic_check_green)
contentDescription = context.getString(
if (isRestore) R.string.restore_app_status_restored
else R.string.backup_app_success
)
}
FAILED -> {
appStatus.setImageResource(R.drawable.ic_error_red)
contentDescription = context.getString(
if (isRestore) R.string.restore_app_status_failed
else R.string.notification_failed_title
)
}
else -> {
appStatus.setImageResource(R.drawable.ic_warning_yellow)
contentDescription = context.getString(
if (isRestore) R.string.restore_app_status_warning
else R.string.backup_app_warning
)
appInfo.text =
if (isRestore) state.getRestoreText(context)
else state.getBackupText(context)
appInfo.visibility = VISIBLE
}
}
appStatus.contentDescription = contentDescription
}
}

View file

@ -136,6 +136,7 @@ internal class BackupNotificationManager(private val context: Context) {
val notification = Builder(context, CHANNEL_ID_OBSERVER).apply {
setSmallIcon(R.drawable.ic_cloud_upload)
setContentTitle(context.getString(R.string.notification_title))
setOngoing(true)
setShowWhen(false)
setWhen(System.currentTimeMillis())
setProgress(0, 0, true)

View file

@ -44,7 +44,6 @@ internal class StorageOptionsFragment : Fragment(), StorageOptionClickedListener
private lateinit var titleView: TextView
private lateinit var warningIcon: ImageView
private lateinit var warningText: TextView
private lateinit var divider: View
private lateinit var listView: RecyclerView
private lateinit var progressBar: ProgressBar
private lateinit var skipView: TextView
@ -61,7 +60,6 @@ internal class StorageOptionsFragment : Fragment(), StorageOptionClickedListener
titleView = v.findViewById(R.id.titleView)
warningIcon = v.findViewById(R.id.warningIcon)
warningText = v.findViewById(R.id.warningText)
divider = v.findViewById(R.id.divider)
listView = v.findViewById(R.id.listView)
progressBar = v.findViewById(R.id.progressBar)
skipView = v.findViewById(R.id.skipView)
@ -93,7 +91,6 @@ internal class StorageOptionsFragment : Fragment(), StorageOptionClickedListener
warningText.setText(R.string.storage_fragment_warning_delete)
}
warningText.visibility = VISIBLE
divider.visibility = VISIBLE
}
listView.adapter = adapter

View file

@ -6,12 +6,12 @@
<TextView
android:id="@+id/skipView"
style="@style/SudSecondaryButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="40dp"
android:background="?android:selectableItemBackground"
android:padding="16dp"
android:text="@string/restore_storage_skip"
android:textColor="?android:colorAccent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

View file

@ -60,29 +60,24 @@
<Button
android:id="@+id/doneButton"
style="@style/ActionPrimaryButton"
style="@style/SudPrimaryButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="40dp"
android:text="@string/recovery_code_done_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/newCodeButton"
app:layout_constraintStart_toStartOf="parent" />
app:layout_constraintEnd_toEndOf="parent" />
<Button
android:id="@+id/newCodeButton"
style="@style/Widget.AppCompat.Button.Borderless"
style="@style/SudSecondaryButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:layout_marginStart="40dp"
android:text="@string/recovery_code_verification_generate_new"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/doneButton"
app:layout_constraintStart_toStartOf="parent"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -71,16 +71,13 @@
<Button
android:id="@+id/confirmCodeButton"
style="@style/ActionPrimaryButton"
style="@style/SudPrimaryButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:text="@string/recovery_code_confirm_button"
android:layout_marginEnd="40dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/wordList" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -7,10 +7,7 @@
<ImageView
android:id="@+id/imageView"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_margin="16dp"
app:layout_constraintEnd_toEndOf="parent"
style="@style/SudHeaderIcon"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_cloud_restore"
@ -19,21 +16,19 @@
<TextView
android:id="@+id/titleView"
style="@style/SudHeaderTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:gravity="center"
android:text="@string/restore_storage_in_progress_title"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView" />
<TextView
android:id="@+id/infoView"
style="@style/SudContent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="@string/restore_storage_in_progress_info"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@ -41,15 +36,13 @@
<Button
android:id="@+id/button"
style="@style/Widget.AppCompat.Button.Colored"
style="@style/SudPrimaryButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:layout_marginEnd="40dp"
android:text="@string/restore_storage_got_it"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/infoView"
app:layout_constraintVertical_bias="1.0" />

View file

@ -5,61 +5,60 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="4dp"
android:indeterminate="false"
android:padding="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:max="23"
tools:progress="5" />
<ImageView
android:id="@+id/imageView"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_margin="16dp"
app:layout_constraintEnd_toEndOf="parent"
style="@style/SudHeaderIcon"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/progressBar"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_cloud_download"
app:tint="?android:colorAccent"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/titleView"
android:layout_width="wrap_content"
style="@style/SudHeaderTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="@string/restore_installing_packages"
android:textColor="?android:textColorSecondary"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView" />
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="4dp"
android:layout_marginStart="40dp"
android:layout_marginEnd="40dp"
android:indeterminate="false"
app:layout_constraintBottom_toTopOf="@+id/appList"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/backupNameView"
tools:max="23"
tools:progress="5" />
<TextView
android:id="@+id/backupNameView"
style="@style/SudDescription"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:textColor="?android:textColorTertiary"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/titleView"
tools:text="Pixel 2 XL" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/appList"
style="@style/SudContent"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
app:layout_constraintBottom_toTopOf="@+id/button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@ -68,17 +67,14 @@
<Button
android:id="@+id/button"
style="@style/ActionPrimaryButton"
style="@style/SudPrimaryButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:layout_marginEnd="40dp"
android:enabled="false"
android:text="@string/restore_next"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent" />
app:layout_constraintTop_toBottomOf="@+id/appList" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -7,10 +7,7 @@
<ImageView
android:id="@+id/imageView"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_margin="16dp"
app:layout_constraintEnd_toEndOf="parent"
style="@style/SudHeaderIcon"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_cloud_download"
@ -19,22 +16,21 @@
<TextView
android:id="@+id/titleView"
android:layout_width="wrap_content"
style="@style/SudHeaderTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="@string/restore_choose_restore_set"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/listView"
style="@style/SudContent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toTopOf="@+id/skipView"
app:layout_constraintEnd_toEndOf="parent"
@ -54,9 +50,9 @@
<TextView
android:id="@+id/errorView"
style="@style/SudContent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:textColor="?android:colorError"
android:textSize="18sp"
android:visibility="invisible"
@ -69,15 +65,13 @@
<TextView
android:id="@+id/skipView"
style="@style/SudSecondaryButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:padding="16dp"
android:layout_marginStart="40dp"
android:text="@string/restore_skip_apps"
android:textColor="?android:colorAccent"
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/listView" />

View file

@ -7,10 +7,7 @@
<ImageView
android:id="@+id/imageView"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_margin="16dp"
app:layout_constraintEnd_toEndOf="parent"
style="@style/SudHeaderIcon"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_storage"
@ -19,11 +16,9 @@
<TextView
android:id="@+id/titleView"
style="@style/SudHeaderTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:gravity="center_horizontal"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView"
@ -31,10 +26,9 @@
<TextView
android:id="@+id/patienceView"
style="@style/SudContent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:gravity="center_horizontal"
android:text="@string/storage_check_fragment_patience"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@ -45,9 +39,9 @@
style="?android:progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginStart="40dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="16dp"
android:layout_marginEnd="40dp"
app:layout_constraintBottom_toTopOf="@+id/backButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@ -56,11 +50,9 @@
<TextView
android:id="@+id/errorView"
style="@style/SudContent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="16dp"
android:textColor="?android:colorError"
android:textSize="18sp"
android:visibility="invisible"
@ -74,10 +66,10 @@
<Button
android:id="@+id/backButton"
style="@style/ActionPrimaryButton"
style="@style/SudSecondaryButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:layout_marginStart="40dp"
android:text="@string/storage_check_fragment_error_button"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"

View file

@ -7,10 +7,7 @@
<ImageView
android:id="@+id/imageView"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_margin="16dp"
app:layout_constraintEnd_toEndOf="parent"
style="@style/SudHeaderIcon"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_storage"
@ -19,12 +16,10 @@
<TextView
android:id="@+id/titleView"
style="@style/SudHeaderTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:gravity="center"
android:text="@string/storage_fragment_backup_title"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView"
@ -34,7 +29,7 @@
android:id="@+id/warningIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginStart="40dp"
android:src="@drawable/ic_warning"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/warningText"
@ -49,7 +44,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginEnd="40dp"
android:text="@string/storage_fragment_warning"
android:textSize="16sp"
android:visibility="gone"
@ -58,27 +53,16 @@
app:layout_constraintTop_toBottomOf="@+id/titleView"
tools:visibility="visible" />
<View
android:id="@+id/divider"
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_marginTop="16dp"
android:background="@color/divider"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/warningText"
tools:visibility="visible" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/listView"
style="@style/SudContent"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toTopOf="@+id/skipView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/divider"
app:layout_constraintTop_toBottomOf="@+id/warningText"
app:layout_goneMarginTop="16dp"
tools:listitem="@layout/list_item_storage_root" />
@ -94,16 +78,15 @@
<TextView
android:id="@+id/skipView"
style="@style/SudSecondaryButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="40dp"
android:background="?android:selectableItemBackground"
android:padding="16dp"
android:text="@string/restore_skip"
android:textColor="?android:colorAccent"
android:visibility="gone"
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/listView"
tools:visibility="visible" />

View file

@ -7,10 +7,7 @@
<ImageView
android:id="@+id/imageView"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_margin="16dp"
app:layout_constraintEnd_toEndOf="parent"
style="@style/SudHeaderIcon"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_cloud_download"
@ -19,12 +16,10 @@
<TextView
android:id="@+id/titleView"
style="@style/SudHeaderTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:gravity="center"
android:text="@string/restore_storage_choose_snapshot"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"

View file

@ -3,6 +3,9 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:paddingStart="40dp"
android:paddingEnd="40dp"
android:textColor="?android:textColorPrimary"
tools:text="@string/backup_section_not_allowed" />

View file

@ -4,21 +4,22 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="40dp"
android:layout_marginEnd="40dp"
android:background="?android:selectableItemBackground"
android:paddingStart="16dp"
android:paddingTop="8dp"
android:paddingEnd="16dp"
android:paddingBottom="8dp">
android:paddingBottom="8dp"
android:screenReaderFocusable="true">
<ImageView
android:id="@+id/appIcon"
android:layout_width="42dp"
android:layout_height="42dp"
android:importantForAccessibility="no"
android:scaleType="fitCenter"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription"
tools:srcCompat="@tools:sample/avatars" />
<TextView
@ -72,6 +73,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
android:focusable="false"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"

View file

@ -4,6 +4,8 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="40dp"
android:layout_marginEnd="40dp"
android:background="?android:selectableItemBackground"
tools:showIn="@layout/fragment_restore_set">
@ -11,7 +13,6 @@
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"

View file

@ -10,7 +10,6 @@
android:id="@+id/iconView"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"
@ -25,7 +24,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginEnd="40dp"
android:textColor="?android:attr/textColorPrimary"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toTopOf="@+id/summaryView"
@ -41,7 +40,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:layout_marginEnd="40dp"
android:layout_marginBottom="16dp"
android:textColor="?android:attr/textColorTertiary"
android:textSize="12sp"

View file

@ -2,7 +2,7 @@
<resources>
<color name="accent">@*android:color/accent_device_default_dark</color>
<color name="primary">@*android:color/primary_device_default_settings</color>
<color name="primaryDark">@*android:color/primary_dark_device_default_settings</color>
<color name="primaryDark">@android:color/black</color>
<color name="background">@color/primaryDark</color>
<color name="actionBarPrimary">@color/background</color>
<color name="statusBarColor">@android:color/transparent</color>

View file

@ -5,7 +5,7 @@
<color name="primaryDark">@*android:color/primary_dark_device_default_settings_light</color>
<color name="background">@*android:color/background_device_default_light</color>
<color name="actionBarPrimary">@*android:color/primary_device_default_light</color>
<color name="statusBarColor">@*android:color/primary_dark_material_light_light_status_bar
<color name="statusBarColor">@*android:color/primary_device_default_settings_light
</color>
<color name="red">@*android:color/error_color_device_default_dark</color>
<color name="divider">#20ffffff</color>

View file

@ -111,6 +111,7 @@
<string name="notification_channel_title">Backup notification</string>
<string name="notification_title">Backup running…</string>
<string name="notification_backup_already_running">Backup already in progress…</string>
<string name="notification_backup_disabled">Auto-backups off</string>
<string name="notification_success_title">Backup finished</string>
<string name="notification_success_text">%1$d of %2$d apps backed up. Tap to learn more.</string>
@ -140,6 +141,9 @@
<!-- This text gets shown for apps that the OS did not try to back up for whatever reason e.g. no backup was run yet -->
<string name="backup_app_not_yet_backed_up">Waiting to back up…</string>
<string name="restore_app_not_yet_backed_up">Was not yet backed up</string>
<string name="backup_app_in_progress">Backing up</string>
<string name="backup_app_success">Backed up</string>
<string name="backup_app_warning">Backup warning</string>
<string name="backup_app_was_stopped">Not backed up as it wasn\'t used recently</string>
<string name="restore_app_was_stopped">Was not backed up as it hadn\'t been used recently</string>
<string name="backup_app_no_data">The app reported no data for backup</string>
@ -160,6 +164,12 @@
<string name="restore_set_error">An error occurred while loading the backups.</string>
<string name="restore_set_empty_result">No suitable backups found at the given location.\n\nMost likely the recovery code is wrong, or there is a storage error.</string>
<string name="restore_installing_packages">Re-installing apps</string>
<string name="restore_app_status_installing">Re-installing</string>
<string name="restore_app_status_installed">Re-installed</string>
<string name="restore_app_status_install_error">Could not install</string>
<string name="restore_app_status_restored">Data restored</string>
<string name="restore_app_status_failed">Restore failed</string>
<string name="restore_app_status_warning">Restore warning</string>
<string name="restore_installing_error_title">Some apps not installed</string>
<string name="restore_installing_error_message">Data can only be restored if an app is installed.\n\nTap failed apps to try to install them manually before proceeding.</string>
<string name="restore_installing_tap_to_install">Tap to install</string>

View file

@ -11,7 +11,8 @@
<item name="android:windowLightStatusBar">@bool/isLight</item>
<item name="android:statusBarColor">@color/statusBarColor</item>
<item name="actionBarTheme">@style/Theme.ActionBar</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:windowLightNavigationBar">@bool/isLight</item>
<item name="android:navigationBarColor">@color/primary</item>
</style>
<style name="Theme.ActionBar" parent="Theme.AppCompat.DayNight.DarkActionBar">
@ -26,4 +27,84 @@
<!-- Copied from Settings -->
<style name="ActionPrimaryButton" parent="android:Widget.DeviceDefault.Button.Colored" />
<style name="SudHeaderTitle">
<item name="android:ellipsize">end</item>
<item name="android:maxLines">3</item>
<item name="android:textSize">36sp</item>
<item name="android:layout_marginBottom">16dp</item>
<item name="android:layout_marginLeft">40dp</item>
<item name="android:layout_marginRight">40dp</item>
<item name="android:lineSpacingExtra">3.67sp</item>
<item name="android:paddingBottom">2dp</item>
<item name="android:paddingTop">16dp</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="SudIconContainer">
<item name="android:layout_marginLeft">40dp</item>
<item name="android:layout_marginRight">40dp</item>
<item name="android:layout_marginTop">56dp</item>
<item name="android:maxHeight">32dp</item>
</style>
<style name="SudHeaderIcon">
<item name="android:layout_marginLeft">40dp</item>
<item name="android:layout_marginRight">40dp</item>
<item name="android:layout_marginTop">32dp</item>
<item name="android:layout_width">48dp</item>
<item name="android:layout_height">48dp</item>
<item name="android:scaleType">fitCenter</item>
<item name="android:adjustViewBounds">true</item>
</style>
<style name="SudDescription">
<item name="android:layout_gravity">top</item>
<item name="android:layout_marginTop">3dp</item>
<item name="android:layout_marginBottom">12dp</item>
<item name="android:layout_marginLeft">40dp</item>
<item name="android:layout_marginStart">40dp</item>
<item name="android:layout_marginRight">40dp</item>
<item name="android:layout_marginEnd">40dp</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:textDirection">locale</item>
<item name="android:gravity">start</item>
<item name="android:textAlignment">gravity</item>
<item name="android:lineSpacingExtra">3.67sp</item>
</style>
<style name="SudContent" parent="SudDescription">
<item name="android:layout_marginTop">32dp</item>
<item name="android:layout_marginBottom">6dp</item>
<item name="android:layout_marginStart">40dp</item>
<item name="android:layout_marginEnd">40dp</item>
<item name="android:gravity">start</item>
<item name="android:textAlignment">gravity</item>
</style>
<style name="SudPrimaryButton" parent="Widget.AppCompat.Button.Colored">
<item name="android:buttonStyle">@style/SudPrimaryButton</item>
<item name="android:theme">@style/SudPrimaryButton</item>
<item name="buttonStyle">@style/SudPrimaryButton</item>
<item name="android:paddingLeft">16dp</item>
<item name="android:paddingRight">16dp</item>
<item name="android:layout_marginTop">16dp</item>
<item name="android:layout_marginBottom">16dp</item>
<item name="android:stateListAnimator">@null</item>
<item name="android:buttonCornerRadius">28dp</item>
<item name="android:textSize">14sp</item>
<item name="android:minHeight">56dp</item>
<item name="textAllCaps">false</item>
</style>
<style name="SudSecondaryButton" parent="Widget.AppCompat.Button.Borderless.Colored">
<item name="android:buttonStyle">@style/SudSecondaryButton</item>
<item name="android:theme">@style/SudSecondaryButton</item>
<item name="buttonStyle">@style/SudSecondaryButton</item>
<item name="android:paddingLeft">16dp</item>
<item name="android:paddingRight">16dp</item>
<item name="android:layout_marginTop">16dp</item>
<item name="android:layout_marginBottom">16dp</item>
<item name="android:minHeight">56dp</item>
<item name="textAllCaps">false</item>
</style>
</resources>

View file

@ -118,6 +118,7 @@ internal class ApkBackupRestoreTest : TransportTest() {
every { metadataManager.salt } returns salt
every { crypto.getNameForApk(salt, packageName) } returns name
every { crypto.getNameForApk(salt, packageName, splitName) } returns suffixName
every { storagePlugin.providerPackageName } returns storageProviderPackageName
apkBackup.backupApkIfNecessary(packageInfo, PackageState.APK_AND_DATA, outputStreamGetter)

View file

@ -97,6 +97,7 @@ internal class ApkRestoreTest : TransportTest() {
every { strictContext.cacheDir } returns File(tmpDir.toString())
every { crypto.getNameForApk(salt, packageName, "") } returns name
coEvery { storagePlugin.getInputStream(token, name) } returns apkInputStream
every { storagePlugin.providerPackageName } returns storageProviderPackageName
apkRestore.restore(backup).collectIndexed { i, value ->
assertQueuedFailFinished(i, value)
@ -112,6 +113,7 @@ internal class ApkRestoreTest : TransportTest() {
every { crypto.getNameForApk(salt, packageName, "") } returns name
coEvery { storagePlugin.getInputStream(token, name) } returns apkInputStream
every { pm.getPackageArchiveInfo(any(), any()) } returns packageInfo
every { storagePlugin.providerPackageName } returns storageProviderPackageName
apkRestore.restore(backup).collectIndexed { i, value ->
assertQueuedFailFinished(i, value)
@ -124,6 +126,7 @@ internal class ApkRestoreTest : TransportTest() {
coEvery {
apkInstaller.install(match { it.size == 1 }, packageName, installerName, any())
} throws SecurityException()
every { storagePlugin.providerPackageName } returns storageProviderPackageName
apkRestore.restore(backup).collectIndexed { i, value ->
assertQueuedProgressFailFinished(i, value)
@ -146,6 +149,7 @@ internal class ApkRestoreTest : TransportTest() {
coEvery {
apkInstaller.install(match { it.size == 1 }, packageName, installerName, any())
} returns installResult
every { storagePlugin.providerPackageName } returns storageProviderPackageName
apkRestore.restore(backup).collectIndexed { i, value ->
assertQueuedProgressSuccessFinished(i, value)
@ -184,6 +188,7 @@ internal class ApkRestoreTest : TransportTest() {
coEvery {
apkInstaller.install(match { it.size == 1 }, packageName, installerName, any())
} returns installResult
every { storagePlugin.providerPackageName } returns storageProviderPackageName
apkRestore.restore(backup).collectIndexed { i, value ->
assertQueuedProgressSuccessFinished(i, value)
@ -202,6 +207,7 @@ internal class ApkRestoreTest : TransportTest() {
cacheBaseApkAndGetInfo(tmpDir)
every { packageInfo.applicationInfo.loadIcon(pm) } returns icon
every { storagePlugin.providerPackageName } returns storageProviderPackageName
if (willFail) {
every {
@ -281,6 +287,7 @@ internal class ApkRestoreTest : TransportTest() {
every {
splitCompatChecker.isCompatible(deviceName, listOf(split1Name, split2Name))
} returns false
every { storagePlugin.providerPackageName } returns storageProviderPackageName
apkRestore.restore(backup).collectIndexed { i, value ->
assertQueuedProgressFailFinished(i, value)
@ -303,6 +310,7 @@ internal class ApkRestoreTest : TransportTest() {
coEvery {
storagePlugin.getInputStream(token, suffixName)
} returns ByteArrayInputStream(getRandomByteArray())
every { storagePlugin.providerPackageName } returns storageProviderPackageName
apkRestore.restore(backup).collectIndexed { i, value ->
assertQueuedProgressFailFinished(i, value)
@ -325,6 +333,7 @@ internal class ApkRestoreTest : TransportTest() {
every { splitCompatChecker.isCompatible(deviceName, listOf(splitName)) } returns true
every { crypto.getNameForApk(salt, packageName, splitName) } returns suffixName
coEvery { storagePlugin.getInputStream(token, suffixName) } throws IOException()
every { storagePlugin.providerPackageName } returns storageProviderPackageName
apkRestore.restore(backup).collectIndexed { i, value ->
assertQueuedProgressFailFinished(i, value)
@ -363,6 +372,7 @@ internal class ApkRestoreTest : TransportTest() {
coEvery { storagePlugin.getInputStream(token, suffixName1) } returns split1InputStream
every { crypto.getNameForApk(salt, packageName, split2Name) } returns suffixName2
coEvery { storagePlugin.getInputStream(token, suffixName2) } returns split2InputStream
every { storagePlugin.providerPackageName } returns storageProviderPackageName
coEvery {
apkInstaller.install(match { it.size == 3 }, packageName, installerName, any())
@ -381,6 +391,27 @@ internal class ApkRestoreTest : TransportTest() {
}
}
@Test
fun `storage provider app does not get reinstalled`(@TempDir tmpDir: Path) = runBlocking {
// set the storage provider package name to match our current package name,
// and ensure that the current package is therefore skipped.
every { storagePlugin.providerPackageName } returns packageName
apkRestore.restore(backup).collectIndexed { i, value ->
when (i) {
0 -> {
assertFalse(value.isFinished)
}
1 -> {
// the only package provided should have been filtered, leaving 0 packages.
assertEquals(0, value.total)
assertTrue(value.isFinished)
}
else -> fail("more values emitted")
}
}
}
private fun swapPackages(packageMetadataMap: PackageMetadataMap): RestorableBackup {
val metadata = metadata.copy(packageMetadataMap = packageMetadataMap)
return backup.copy(backupMetadata = metadata)

View file

@ -61,6 +61,7 @@ internal abstract class TransportTest {
protected val salt = metadata.salt
protected val name = getRandomString(12)
protected val name2 = getRandomString(23)
protected val storageProviderPackageName = getRandomString(23)
init {
mockkStatic(Log::class)

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" height="24" viewBox="0 0 24 24"
width="24" version="1.1" id="svg6" sodipodi:docname="cloud-24px.svg"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<metadata id="metadata12">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs id="defs10" />
<sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
inkscape:pageshadow="2" inkscape:window-width="1920" inkscape:window-height="958"
id="namedview8" showgrid="false" inkscape:zoom="13.007819" inkscape:cx="14.342117"
inkscape:cy="11.405653" inkscape:window-x="1920" inkscape:window-y="96"
inkscape:window-maximized="0" inkscape:current-layer="svg6" />
<path d="M0 0h24v24H0z" fill="none" id="path2" />
<path id="path4"
d="M 12 4 C 9.11 4 6.5996094 5.6390625 5.3496094 8.0390625 C 2.3396094 8.3590625 -1.1842379e-15 10.91 0 14 C 0 17.31 2.69 20 6 20 L 19 20 C 21.76 20 24 17.76 24 15 C 24 12.36 21.949609 10.219062 19.349609 10.039062 C 18.669609 6.5890625 15.64 4 12 4 z M 12.175781 8.1230469 C 14.93578 8.1230469 17.175781 10.363047 17.175781 13.123047 C 17.175781 15.883047 14.93578 18.123047 12.175781 18.123047 C 10.125781 18.123047 8.365703 16.883047 7.5957031 15.123047 L 9.3066406 15.123047 C 9.9366406 16.023047 10.985781 16.623047 12.175781 16.623047 C 14.105781 16.623047 15.675781 15.053047 15.675781 13.123047 C 15.675781 11.193047 14.105781 9.6230469 12.175781 9.6230469 C 10.825781 9.6230469 9.6561719 10.401484 9.0761719 11.521484 L 10.675781 13.123047 L 6.6757812 13.123047 L 6.6757812 9.1230469 L 7.9765625 10.421875 C 8.8665625 9.0418743 10.405781 8.1230469 12.175781 8.1230469 z " />
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.1" width="24"
height="24" viewBox="0 0 24 24" id="svg4" sodipodi:docname="image-multiple.svg"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<metadata id="metadata10">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs id="defs8" />
<sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1"
objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0"
inkscape:pageshadow="2" inkscape:window-width="1920" inkscape:window-height="958"
id="namedview6" showgrid="false" inkscape:zoom="13.007819" inkscape:cx="8.8546913"
inkscape:cy="7.0282265" inkscape:window-x="1920" inkscape:window-y="96"
inkscape:window-maximized="0" inkscape:current-layer="svg4" />
<path id="path1490"
d="M 2 6 L 2 20 C 2 21.104569 2.8954305 22 4 22 L 18 22 L 18 20 L 4 20 L 4 6 L 2 6 z " />
<path id="path1486"
d="M 8 2 C 6.8954305 2 6 2.8954305 6 4 L 6 16 C 6 17.104569 6.8954305 18 8 18 L 20 18 C 21.104569 18 22 17.104569 22 16 L 22 4 C 22 2.8954305 21.104569 2 20 2 L 8 2 z M 12.5 5 L 15.5 5 L 15.5 8 L 18 8 L 14 12 L 10 8 L 12.5 8 L 12.5 5 z M 10 13 L 18 13 L 18 15 L 10 15 L 10 13 z " />
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -57,9 +57,9 @@ public abstract class BackupContentFragment : Fragment(), ContentClickListener {
val adapter = BackupContentAdapter(this)
list.adapter = adapter
viewModel.content.observe(viewLifecycleOwner, {
viewModel.content.observe(viewLifecycleOwner) {
adapter.setItems(it)
})
}
v.findViewById<FloatingActionButton>(R.id.fab).setOnClickListener {
addRequest.launch(DocumentsContract.buildRootsUri(EXTERNAL_STORAGE_PROVIDER_AUTHORITY))
}

View file

@ -20,7 +20,10 @@
android:id="@+id/list"
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="16dp"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:paddingStart="40dp"
android:paddingEnd="40dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toTopOf="@+id/bottomStub"
app:layout_constraintEnd_toEndOf="parent"
@ -32,7 +35,7 @@
android:id="@+id/emptyStateView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:layout_margin="40dp"
android:gravity="center"
android:text="@string/snapshots_empty"
android:visibility="invisible"

View file

@ -5,6 +5,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:screenReaderFocusable="true"
app:cardCornerRadius="8dp">
<androidx.constraintlayout.widget.ConstraintLayout
@ -19,6 +20,7 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="8dp"
android:importantForAccessibility="no"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
@ -41,6 +43,7 @@
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/content_options"
android:src="@drawable/ic_more_vert"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"

View file

@ -5,6 +5,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:screenReaderFocusable="true"
app:cardCornerRadius="8dp">
<androidx.constraintlayout.widget.ConstraintLayout
@ -19,6 +20,7 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="8dp"
android:importantForAccessibility="no"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"

View file

@ -4,8 +4,8 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
app:cardCornerRadius="8dp"
android:layout_marginBottom="8dp"
app:cardCornerRadius="16dp"
tools:showIn="@layout/fragment_snapshot">
<androidx.constraintlayout.widget.ConstraintLayout
@ -13,14 +13,15 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:padding="8dp">
android:padding="16dp">
<TextView
android:id="@+id/nameView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItem"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="8dp"
app:layout_constraintEnd_toStartOf="@+id/sizeView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Backup name which might be quite long, who knows...?" />
@ -41,11 +42,10 @@
android:id="@+id/sizeView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/nameView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/timeView"
app:layout_constraintTop_toBottomOf="@+id/nameView"
app:layout_constraintTop_toTopOf="parent"
tools:text="35 GB" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -3,6 +3,7 @@
<string name="content_videos">Videos</string>
<string name="content_audio">Audio files</string>
<string name="content_downloads">Downloads</string>
<string name="content_options">Options</string>
<string name="content_add">Add</string>
<string name="notification_backup_title">Storage backup</string>