Code style and lint after Android 12 version bumps

This commit is contained in:
Torsten Grote 2021-10-07 17:54:35 -03:00 committed by Chirayu Desai
parent e80d89f0a2
commit 579919d5e7
116 changed files with 254 additions and 252 deletions

View file

@ -6,6 +6,7 @@
</option> </option>
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" /> <option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" /> <option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
<option name="ALLOW_TRAILING_COMMA" value="true" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings> </JetCodeStyleSettings>
<codeStyleSettings language="XML"> <codeStyleSettings language="XML">

View file

@ -5,8 +5,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry
import com.stevesoltys.seedvault.plugins.LegacyStoragePlugin import com.stevesoltys.seedvault.plugins.LegacyStoragePlugin
import com.stevesoltys.seedvault.plugins.StoragePlugin import com.stevesoltys.seedvault.plugins.StoragePlugin
import com.stevesoltys.seedvault.plugins.saf.DocumentsProviderStoragePlugin
import com.stevesoltys.seedvault.plugins.saf.DocumentsProviderLegacyPlugin import com.stevesoltys.seedvault.plugins.saf.DocumentsProviderLegacyPlugin
import com.stevesoltys.seedvault.plugins.saf.DocumentsProviderStoragePlugin
import com.stevesoltys.seedvault.plugins.saf.DocumentsStorage import com.stevesoltys.seedvault.plugins.saf.DocumentsStorage
import com.stevesoltys.seedvault.plugins.saf.FILE_BACKUP_METADATA import com.stevesoltys.seedvault.plugins.saf.FILE_BACKUP_METADATA
import com.stevesoltys.seedvault.plugins.saf.deleteContents import com.stevesoltys.seedvault.plugins.saf.deleteContents
@ -36,6 +36,7 @@ class PluginTest : KoinComponent {
private val storage = DocumentsStorage(context, mockedSettingsManager) private val storage = DocumentsStorage(context, mockedSettingsManager)
private val storagePlugin: StoragePlugin = DocumentsProviderStoragePlugin(context, storage) private val storagePlugin: StoragePlugin = DocumentsProviderStoragePlugin(context, storage)
@Suppress("Deprecation") @Suppress("Deprecation")
private val legacyStoragePlugin: LegacyStoragePlugin = private val legacyStoragePlugin: LegacyStoragePlugin =
DocumentsProviderLegacyPlugin(context, storage) DocumentsProviderLegacyPlugin(context, storage)

View file

@ -72,8 +72,8 @@
<activity <activity
android:name=".settings.SettingsActivity" android:name=".settings.SettingsActivity"
android:permission="com.stevesoltys.seedvault.OPEN_SETTINGS" android:exported="true"
android:exported="true" /> android:permission="com.stevesoltys.seedvault.OPEN_SETTINGS" />
<activity <activity
android:name=".ui.storage.StorageActivity" android:name=".ui.storage.StorageActivity"
@ -91,9 +91,9 @@
<activity <activity
android:name=".restore.RestoreActivity" android:name=".restore.RestoreActivity"
android:permission="com.stevesoltys.seedvault.RESTORE_BACKUP"
android:exported="true" android:exported="true"
android:label="@string/restore_title" android:label="@string/restore_title"
android:permission="com.stevesoltys.seedvault.RESTORE_BACKUP"
android:theme="@style/AppTheme.NoActionBar"> android:theme="@style/AppTheme.NoActionBar">
<intent-filter> <intent-filter>
<action android:name="com.stevesoltys.seedvault.RESTORE_BACKUP" /> <action android:name="com.stevesoltys.seedvault.RESTORE_BACKUP" />
@ -134,7 +134,9 @@
<intent-filter> <intent-filter>
<action android:name="android.telephony.action.SECRET_CODE" /> <action android:name="android.telephony.action.SECRET_CODE" />
<!-- *#*#RESTORE#*#* --> <!-- *#*#RESTORE#*#* -->
<data android:scheme="android_secret_code" android:host="7378673" /> <data
android:host="7378673"
android:scheme="android_secret_code" />
</intent-filter> </intent-filter>
</receiver> </receiver>

View file

@ -7,7 +7,6 @@ import android.content.Context.BACKUP_SERVICE
import android.os.Build import android.os.Build
import android.os.ServiceManager.getService import android.os.ServiceManager.getService
import android.os.StrictMode import android.os.StrictMode
import org.koin.core.logger.Level
import com.stevesoltys.seedvault.crypto.cryptoModule import com.stevesoltys.seedvault.crypto.cryptoModule
import com.stevesoltys.seedvault.header.headerModule import com.stevesoltys.seedvault.header.headerModule
import com.stevesoltys.seedvault.metadata.MetadataManager import com.stevesoltys.seedvault.metadata.MetadataManager
@ -31,6 +30,7 @@ import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger import org.koin.android.ext.koin.androidLogger
import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.core.context.startKoin import org.koin.core.context.startKoin
import org.koin.core.logger.Level
import org.koin.dsl.module import org.koin.dsl.module
/** /**

View file

@ -8,13 +8,13 @@ import android.util.Log
import com.stevesoltys.seedvault.restore.RestoreActivity import com.stevesoltys.seedvault.restore.RestoreActivity
private val TAG = BroadcastReceiver::class.java.simpleName private val TAG = BroadcastReceiver::class.java.simpleName
private val RESTORE_SECRET_CODE = "7378673" private const val RESTORE_SECRET_CODE = "7378673"
class SecretCodeReceiver : BroadcastReceiver() { class SecretCodeReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
val host = intent.data.host val host = intent.data.host
if (!RESTORE_SECRET_CODE.equals(host)) return if (RESTORE_SECRET_CODE != host) return
Log.d(TAG, "Restore secret code received.") Log.d(TAG, "Restore secret code received.")
val i = Intent(context, RestoreActivity::class.java).apply { val i = Intent(context, RestoreActivity::class.java).apply {
flags = FLAG_ACTIVITY_NEW_TASK flags = FLAG_ACTIVITY_NEW_TASK

View file

@ -57,7 +57,7 @@ internal interface Crypto {
@Throws(IOException::class, GeneralSecurityException::class) @Throws(IOException::class, GeneralSecurityException::class)
fun newEncryptingStream( fun newEncryptingStream(
outputStream: OutputStream, outputStream: OutputStream,
associatedData: ByteArray associatedData: ByteArray,
): OutputStream ): OutputStream
/** /**
@ -67,7 +67,7 @@ internal interface Crypto {
@Throws(IOException::class, GeneralSecurityException::class) @Throws(IOException::class, GeneralSecurityException::class)
fun newDecryptingStream( fun newDecryptingStream(
inputStream: InputStream, inputStream: InputStream,
associatedData: ByteArray associatedData: ByteArray,
): InputStream ): InputStream
/** /**
@ -78,13 +78,14 @@ internal interface Crypto {
* *
* @return The read [VersionHeader] present in the beginning of the given [InputStream]. * @return The read [VersionHeader] present in the beginning of the given [InputStream].
*/ */
@Suppress("Deprecation")
@Deprecated("Use newDecryptingStream instead") @Deprecated("Use newDecryptingStream instead")
@Throws(IOException::class, SecurityException::class) @Throws(IOException::class, SecurityException::class)
fun decryptHeader( fun decryptHeader(
inputStream: InputStream, inputStream: InputStream,
expectedVersion: Byte, expectedVersion: Byte,
expectedPackageName: String, expectedPackageName: String,
expectedKey: String? = null expectedKey: String? = null,
): VersionHeader ): VersionHeader
/** /**
@ -118,7 +119,7 @@ internal const val TYPE_BACKUP_FULL: Byte = 0x02
internal class CryptoImpl( internal class CryptoImpl(
private val keyManager: KeyManager, private val keyManager: KeyManager,
private val cipherFactory: CipherFactory, private val cipherFactory: CipherFactory,
private val headerReader: HeaderReader private val headerReader: HeaderReader,
) : Crypto { ) : Crypto {
private val key: ByteArray by lazy { private val key: ByteArray by lazy {
@ -151,7 +152,7 @@ internal class CryptoImpl(
@Throws(IOException::class, GeneralSecurityException::class) @Throws(IOException::class, GeneralSecurityException::class)
override fun newEncryptingStream( override fun newEncryptingStream(
outputStream: OutputStream, outputStream: OutputStream,
associatedData: ByteArray associatedData: ByteArray,
): OutputStream { ): OutputStream {
return StreamCrypto.newEncryptingStream(key, outputStream, associatedData) return StreamCrypto.newEncryptingStream(key, outputStream, associatedData)
} }
@ -159,18 +160,19 @@ internal class CryptoImpl(
@Throws(IOException::class, GeneralSecurityException::class) @Throws(IOException::class, GeneralSecurityException::class)
override fun newDecryptingStream( override fun newDecryptingStream(
inputStream: InputStream, inputStream: InputStream,
associatedData: ByteArray associatedData: ByteArray,
): InputStream { ): InputStream {
return StreamCrypto.newDecryptingStream(key, inputStream, associatedData) return StreamCrypto.newDecryptingStream(key, inputStream, associatedData)
} }
@Suppress("Deprecation")
@Throws(IOException::class, SecurityException::class) @Throws(IOException::class, SecurityException::class)
@Deprecated("Use newDecryptingStream instead") @Deprecated("Use newDecryptingStream instead")
override fun decryptHeader( override fun decryptHeader(
inputStream: InputStream, inputStream: InputStream,
expectedVersion: Byte, expectedVersion: Byte,
expectedPackageName: String, expectedPackageName: String,
expectedKey: String? expectedKey: String?,
): VersionHeader { ): VersionHeader {
val decrypted = decryptSegment(inputStream, MAX_VERSION_HEADER_SIZE) val decrypted = decryptSegment(inputStream, MAX_VERSION_HEADER_SIZE)
val header = headerReader.getVersionHeader(decrypted) val header = headerReader.getVersionHeader(decrypted)
@ -214,6 +216,7 @@ internal class CryptoImpl(
} }
} }
@Suppress("Deprecation")
@Throws(EOFException::class, IOException::class, SecurityException::class) @Throws(EOFException::class, IOException::class, SecurityException::class)
private fun decryptSegment(inputStream: InputStream, maxSegmentLength: Int): ByteArray { private fun decryptSegment(inputStream: InputStream, maxSegmentLength: Int): ByteArray {
val segmentHeader = headerReader.readSegmentHeader(inputStream) val segmentHeader = headerReader.readSegmentHeader(inputStream)

View file

@ -63,7 +63,7 @@ interface KeyManager {
} }
internal class KeyManagerImpl( internal class KeyManagerImpl(
private val keyStore: KeyStore private val keyStore: KeyStore,
) : KeyManager { ) : KeyManager {
override fun storeBackupKey(seed: ByteArray) { override fun storeBackupKey(seed: ByteArray) {

View file

@ -19,7 +19,7 @@ internal const val MAX_VERSION_HEADER_SIZE =
internal data class VersionHeader( internal data class VersionHeader(
internal val version: Byte = VERSION, // 1 byte internal val version: Byte = VERSION, // 1 byte
internal val packageName: String, // ?? bytes (max 255) internal val packageName: String, // ?? bytes (max 255)
internal val key: String? = null // ?? bytes internal val key: String? = null, // ?? bytes
) { ) {
init { init {
check(packageName.length <= MAX_PACKAGE_LENGTH_SIZE) { check(packageName.length <= MAX_PACKAGE_LENGTH_SIZE) {
@ -64,7 +64,7 @@ internal const val SEGMENT_HEADER_SIZE = SEGMENT_LENGTH_SIZE + IV_SIZE
@Deprecated("Don't do manual segments, use Crypto interface instead.") @Deprecated("Don't do manual segments, use Crypto interface instead.")
class SegmentHeader( class SegmentHeader(
internal val segmentLength: Short, // 2 bytes internal val segmentLength: Short, // 2 bytes
internal val nonce: ByteArray // 12 bytes internal val nonce: ByteArray, // 12 bytes
) { ) {
init { init {
check(nonce.size == IV_SIZE) { check(nonce.size == IV_SIZE) {

View file

@ -11,11 +11,13 @@ internal interface HeaderReader {
@Throws(IOException::class, UnsupportedVersionException::class) @Throws(IOException::class, UnsupportedVersionException::class)
fun readVersion(inputStream: InputStream, expectedVersion: Byte): Byte fun readVersion(inputStream: InputStream, expectedVersion: Byte): Byte
@Deprecated("") @Suppress("Deprecation")
@Deprecated("For restoring v0 backups only")
@Throws(SecurityException::class) @Throws(SecurityException::class)
fun getVersionHeader(byteArray: ByteArray): VersionHeader fun getVersionHeader(byteArray: ByteArray): VersionHeader
@Deprecated("") @Suppress("Deprecation")
@Deprecated("For restoring v0 backups only")
@Throws(EOFException::class, IOException::class) @Throws(EOFException::class, IOException::class)
fun readSegmentHeader(inputStream: InputStream): SegmentHeader fun readSegmentHeader(inputStream: InputStream): SegmentHeader
} }
@ -33,6 +35,7 @@ internal class HeaderReaderImpl : HeaderReader {
return version return version
} }
@Suppress("OverridingDeprecatedMember", "Deprecation")
override fun getVersionHeader(byteArray: ByteArray): VersionHeader { override fun getVersionHeader(byteArray: ByteArray): VersionHeader {
val buffer = ByteBuffer.wrap(byteArray) val buffer = ByteBuffer.wrap(byteArray)
val version = buffer.get() val version = buffer.get()
@ -65,6 +68,7 @@ internal class HeaderReaderImpl : HeaderReader {
} }
@Throws(EOFException::class, IOException::class) @Throws(EOFException::class, IOException::class)
@Suppress("OverridingDeprecatedMember", "Deprecation")
override fun readSegmentHeader(inputStream: InputStream): SegmentHeader { override fun readSegmentHeader(inputStream: InputStream): SegmentHeader {
val buffer = ByteArray(SEGMENT_HEADER_SIZE) val buffer = ByteArray(SEGMENT_HEADER_SIZE)
val bytesRead = inputStream.read(buffer) val bytesRead = inputStream.read(buffer)

View file

@ -18,7 +18,7 @@ data class BackupMetadata(
internal val androidVersion: Int = Build.VERSION.SDK_INT, internal val androidVersion: Int = Build.VERSION.SDK_INT,
internal val androidIncremental: String = Build.VERSION.INCREMENTAL, internal val androidIncremental: String = Build.VERSION.INCREMENTAL,
internal val deviceName: String = "${Build.MANUFACTURER} ${Build.MODEL}", internal val deviceName: String = "${Build.MANUFACTURER} ${Build.MODEL}",
internal val packageMetadataMap: PackageMetadataMap = PackageMetadataMap() internal val packageMetadataMap: PackageMetadataMap = PackageMetadataMap(),
) )
internal const val JSON_METADATA = "@meta@" internal const val JSON_METADATA = "@meta@"
@ -77,7 +77,7 @@ data class PackageMetadata(
internal val installer: String? = null, internal val installer: String? = null,
internal val splits: List<ApkSplit>? = null, internal val splits: List<ApkSplit>? = null,
internal val sha256: String? = null, internal val sha256: String? = null,
internal val signatures: List<String>? = null internal val signatures: List<String>? = null,
) { ) {
fun hasApk(): Boolean { fun hasApk(): Boolean {
return version != null && sha256 != null && signatures != null return version != null && sha256 != null && signatures != null
@ -86,7 +86,7 @@ data class PackageMetadata(
data class ApkSplit( data class ApkSplit(
val name: String, val name: String,
val sha256: String val sha256: String,
// There's also a revisionCode, but it doesn't seem to be used just yet // There's also a revisionCode, but it doesn't seem to be used just yet
) )

View file

@ -34,7 +34,7 @@ internal class MetadataManager(
private val clock: Clock, private val clock: Clock,
private val crypto: Crypto, private val crypto: Crypto,
private val metadataWriter: MetadataWriter, private val metadataWriter: MetadataWriter,
private val metadataReader: MetadataReader private val metadataReader: MetadataReader,
) { ) {
private val uninitializedMetadata = BackupMetadata(token = 0L, salt = "") private val uninitializedMetadata = BackupMetadata(token = 0L, salt = "")
@ -80,7 +80,7 @@ internal class MetadataManager(
fun onApkBackedUp( fun onApkBackedUp(
packageInfo: PackageInfo, packageInfo: PackageInfo,
packageMetadata: PackageMetadata, packageMetadata: PackageMetadata,
metadataOutputStream: OutputStream metadataOutputStream: OutputStream,
) { ) {
val packageName = packageInfo.packageName val packageName = packageInfo.packageName
metadata.packageMetadataMap[packageName]?.let { metadata.packageMetadataMap[packageName]?.let {
@ -129,7 +129,7 @@ internal class MetadataManager(
fun onPackageBackedUp( fun onPackageBackedUp(
packageInfo: PackageInfo, packageInfo: PackageInfo,
type: BackupType, type: BackupType,
metadataOutputStream: OutputStream metadataOutputStream: OutputStream,
) { ) {
val packageName = packageInfo.packageName val packageName = packageInfo.packageName
modifyMetadata(metadataOutputStream) { modifyMetadata(metadataOutputStream) {
@ -162,7 +162,7 @@ internal class MetadataManager(
packageInfo: PackageInfo, packageInfo: PackageInfo,
packageState: PackageState, packageState: PackageState,
metadataOutputStream: OutputStream, metadataOutputStream: OutputStream,
backupType: BackupType? = null backupType: BackupType? = null,
) { ) {
check(packageState != APK_AND_DATA) { "Backup Error with non-error package state." } check(packageState != APK_AND_DATA) { "Backup Error with non-error package state." }
val packageName = packageInfo.packageName val packageName = packageInfo.packageName

View file

@ -31,7 +31,7 @@ interface MetadataReader {
fun decode( fun decode(
bytes: ByteArray, bytes: ByteArray,
expectedVersion: Byte? = null, expectedVersion: Byte? = null,
expectedToken: Long? = null expectedToken: Long? = null,
): BackupMetadata ): BackupMetadata
} }
@ -64,6 +64,7 @@ internal class MetadataReaderImpl(private val crypto: Crypto) : MetadataReader {
UnsupportedVersionException::class, UnsupportedVersionException::class,
IOException::class IOException::class
) )
@Suppress("Deprecation")
private fun readMetadataV0(inputStream: InputStream, expectedToken: Long): BackupMetadata { private fun readMetadataV0(inputStream: InputStream, expectedToken: Long): BackupMetadata {
val metadataBytes = try { val metadataBytes = try {
crypto.decryptMultipleSegments(inputStream) crypto.decryptMultipleSegments(inputStream)
@ -77,7 +78,7 @@ internal class MetadataReaderImpl(private val crypto: Crypto) : MetadataReader {
override fun decode( override fun decode(
bytes: ByteArray, bytes: ByteArray,
expectedVersion: Byte?, expectedVersion: Byte?,
expectedToken: Long? expectedToken: Long?,
): BackupMetadata { ): BackupMetadata {
// NOTE: We don't do extensive validation of the parsed input here, // NOTE: We don't do extensive validation of the parsed input here,
// because it was encrypted with authentication, so we should be able to trust it. // because it was encrypted with authentication, so we should be able to trust it.

View file

@ -34,7 +34,7 @@ interface LegacyStoragePlugin {
suspend fun getInputStreamForRecord( suspend fun getInputStreamForRecord(
token: Long, token: Long,
packageInfo: PackageInfo, packageInfo: PackageInfo,
key: String key: String,
): InputStream ): InputStream
/** /**

View file

@ -13,7 +13,7 @@ import java.io.InputStream
@Suppress("BlockingMethodInNonBlockingContext", "Deprecation") // all methods do I/O @Suppress("BlockingMethodInNonBlockingContext", "Deprecation") // all methods do I/O
internal class DocumentsProviderLegacyPlugin( internal class DocumentsProviderLegacyPlugin(
private val context: Context, private val context: Context,
private val storage: DocumentsStorage private val storage: DocumentsStorage,
) : LegacyStoragePlugin { ) : LegacyStoragePlugin {
private var packageDir: DocumentFile? = null private var packageDir: DocumentFile? = null
@ -50,7 +50,7 @@ internal class DocumentsProviderLegacyPlugin(
override suspend fun getInputStreamForRecord( override suspend fun getInputStreamForRecord(
token: Long, token: Long,
packageInfo: PackageInfo, packageInfo: PackageInfo,
key: String key: String,
): InputStream { ): InputStream {
val packageDir = this.packageDir val packageDir = this.packageDir
?: throw AssertionError("No cached packageDir for ${packageInfo.packageName}") ?: throw AssertionError("No cached packageDir for ${packageInfo.packageName}")
@ -70,7 +70,7 @@ internal class DocumentsProviderLegacyPlugin(
@Throws(IOException::class) @Throws(IOException::class)
override suspend fun getInputStreamForPackage( override suspend fun getInputStreamForPackage(
token: Long, token: Long,
packageInfo: PackageInfo packageInfo: PackageInfo,
): InputStream { ): InputStream {
val backupDir = storage.getFullBackupDir(token) ?: throw IOException() val backupDir = storage.getFullBackupDir(token) ?: throw IOException()
val packageFile = val packageFile =
@ -82,7 +82,7 @@ internal class DocumentsProviderLegacyPlugin(
override suspend fun getApkInputStream( override suspend fun getApkInputStream(
token: Long, token: Long,
packageName: String, packageName: String,
suffix: String suffix: String,
): InputStream { ): InputStream {
val setDir = storage.getSetDir(token) ?: throw IOException() val setDir = storage.getSetDir(token) ?: throw IOException()
val file = setDir.findFileBlocking(context, "$packageName$suffix.apk") val file = setDir.findFileBlocking(context, "$packageName$suffix.apk")

View file

@ -17,7 +17,7 @@ private val TAG = DocumentsProviderStoragePlugin::class.java.simpleName
@Suppress("BlockingMethodInNonBlockingContext") @Suppress("BlockingMethodInNonBlockingContext")
internal class DocumentsProviderStoragePlugin( internal class DocumentsProviderStoragePlugin(
private val context: Context, private val context: Context,
private val storage: DocumentsStorage private val storage: DocumentsStorage,
) : StoragePlugin { ) : StoragePlugin {
private val packageManager: PackageManager = context.packageManager private val packageManager: PackageManager = context.packageManager

View file

@ -27,8 +27,10 @@ import java.io.OutputStream
import kotlin.coroutines.resume import kotlin.coroutines.resume
const val DIRECTORY_ROOT = ".SeedVaultAndroidBackup" const val DIRECTORY_ROOT = ".SeedVaultAndroidBackup"
@Deprecated("") @Deprecated("")
const val DIRECTORY_FULL_BACKUP = "full" const val DIRECTORY_FULL_BACKUP = "full"
@Deprecated("") @Deprecated("")
const val DIRECTORY_KEY_VALUE_BACKUP = "kv" const val DIRECTORY_KEY_VALUE_BACKUP = "kv"
const val FILE_BACKUP_METADATA = ".backup.metadata" const val FILE_BACKUP_METADATA = ".backup.metadata"
@ -39,7 +41,7 @@ private val TAG = DocumentsStorage::class.java.simpleName
internal class DocumentsStorage( internal class DocumentsStorage(
private val context: Context, private val context: Context,
private val settingsManager: SettingsManager private val settingsManager: SettingsManager,
) { ) {
private val contentResolver = context.contentResolver private val contentResolver = context.contentResolver
@ -143,7 +145,7 @@ internal class DocumentsStorage(
internal suspend fun DocumentFile.createOrGetFile( internal suspend fun DocumentFile.createOrGetFile(
context: Context, context: Context,
name: String, name: String,
mimeType: String = MIME_TYPE mimeType: String = MIME_TYPE,
): DocumentFile { ): DocumentFile {
return try { return try {
findFileBlocking(context, name) ?: createFile(mimeType, name)?.apply { findFileBlocking(context, name) ?: createFile(mimeType, name)?.apply {

View file

@ -8,7 +8,6 @@ import com.stevesoltys.seedvault.restore.DisplayFragment.RESTORE_BACKUP
import com.stevesoltys.seedvault.restore.DisplayFragment.RESTORE_FILES import com.stevesoltys.seedvault.restore.DisplayFragment.RESTORE_FILES
import com.stevesoltys.seedvault.restore.DisplayFragment.RESTORE_FILES_STARTED import com.stevesoltys.seedvault.restore.DisplayFragment.RESTORE_FILES_STARTED
import com.stevesoltys.seedvault.restore.install.InstallProgressFragment import com.stevesoltys.seedvault.restore.install.InstallProgressFragment
import com.stevesoltys.seedvault.ui.LiveEventHandler
import com.stevesoltys.seedvault.ui.RequireProvisioningActivity import com.stevesoltys.seedvault.ui.RequireProvisioningActivity
import com.stevesoltys.seedvault.ui.RequireProvisioningViewModel import com.stevesoltys.seedvault.ui.RequireProvisioningViewModel
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
@ -26,7 +25,7 @@ class RestoreActivity : RequireProvisioningActivity() {
setContentView(R.layout.activity_fragment_container) setContentView(R.layout.activity_fragment_container)
viewModel.displayFragment.observeEvent(this, LiveEventHandler { fragment -> viewModel.displayFragment.observeEvent(this, { fragment ->
when (fragment) { when (fragment) {
RESTORE_APPS -> showFragment(InstallProgressFragment()) RESTORE_APPS -> showFragment(InstallProgressFragment())
RESTORE_BACKUP -> showFragment(RestoreProgressFragment()) RESTORE_BACKUP -> showFragment(RestoreProgressFragment())

View file

@ -15,9 +15,7 @@ internal const val REQUEST_CODE_UNINSTALL = 4576841
class RestoreErrorBroadcastReceiver : BroadcastReceiver() { class RestoreErrorBroadcastReceiver : BroadcastReceiver() {
// using KoinComponent would crash robolectric tests :( // using KoinComponent would crash robolectric tests :(
private val notificationManager: BackupNotificationManager by lazy { private val notificationManager: BackupNotificationManager by lazy { get().get() }
get().get<BackupNotificationManager>()
}
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
if (intent.action != ACTION_RESTORE_ERROR_UNINSTALL) return if (intent.action != ACTION_RESTORE_ERROR_UNINSTALL) return

View file

@ -20,7 +20,7 @@ internal class RestoreFilesFragment : SnapshotFragment() {
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?,
): View { ): View {
val v = super.onCreateView(inflater, container, savedInstanceState) val v = super.onCreateView(inflater, container, savedInstanceState)
@ -50,7 +50,7 @@ internal class RestoreFilesStartedFragment : Fragment() {
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?,
): View { ): View {
val v: View = inflater.inflate(R.layout.fragment_restore_files_started, container, false) val v: View = inflater.inflate(R.layout.fragment_restore_files_started, container, false)

View file

@ -38,7 +38,7 @@ internal class RestoreProgressAdapter : Adapter<PackageViewHolder>() {
private class Diff( private class Diff(
private val oldItems: LinkedList<AppRestoreResult>, private val oldItems: LinkedList<AppRestoreResult>,
private val newItems: LinkedList<AppRestoreResult> private val newItems: LinkedList<AppRestoreResult>,
) : DiffUtil.Callback() { ) : DiffUtil.Callback() {
override fun getOldListSize() = oldItems.size override fun getOldListSize() = oldItems.size
@ -74,5 +74,5 @@ internal class RestoreProgressAdapter : Adapter<PackageViewHolder>() {
internal data class AppRestoreResult( internal data class AppRestoreResult(
val packageName: String, val packageName: String,
val name: CharSequence, val name: CharSequence,
val state: AppBackupState val state: AppBackupState,
) )

View file

@ -11,7 +11,6 @@ import android.widget.TextView
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat.getColor import androidx.core.content.ContextCompat.getColor
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager.VERTICAL import androidx.recyclerview.widget.LinearLayoutManager.VERTICAL
@ -36,7 +35,7 @@ class RestoreProgressFragment : Fragment() {
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?,
): View { ): View {
val v: View = inflater.inflate(R.layout.fragment_restore_progress, container, false) val v: View = inflater.inflate(R.layout.fragment_restore_progress, container, false)
@ -66,17 +65,17 @@ class RestoreProgressFragment : Fragment() {
// decryption will fail when the device is locked, so keep the screen on to prevent locking // decryption will fail when the device is locked, so keep the screen on to prevent locking
requireActivity().window.addFlags(FLAG_KEEP_SCREEN_ON) requireActivity().window.addFlags(FLAG_KEEP_SCREEN_ON)
viewModel.chosenRestorableBackup.observe(viewLifecycleOwner, Observer { restorableBackup -> viewModel.chosenRestorableBackup.observe(viewLifecycleOwner, { restorableBackup ->
backupNameView.text = restorableBackup.name backupNameView.text = restorableBackup.name
progressBar.max = restorableBackup.packageMetadataMap.size progressBar.max = restorableBackup.packageMetadataMap.size
}) })
viewModel.restoreProgress.observe(viewLifecycleOwner, Observer { list -> viewModel.restoreProgress.observe(viewLifecycleOwner, { list ->
stayScrolledAtTop { adapter.update(list) } stayScrolledAtTop { adapter.update(list) }
progressBar.progress = list.size progressBar.progress = list.size
}) })
viewModel.restoreBackupResult.observe(viewLifecycleOwner, Observer { finished -> viewModel.restoreBackupResult.observe(viewLifecycleOwner, { finished ->
button.isEnabled = true button.isEnabled = true
if (finished.hasError()) { if (finished.hasError()) {
backupNameView.text = finished.errorMsg backupNameView.text = finished.errorMsg

View file

@ -14,7 +14,7 @@ import com.stevesoltys.seedvault.restore.RestoreSetAdapter.RestoreSetViewHolder
internal class RestoreSetAdapter( internal class RestoreSetAdapter(
private val listener: RestorableBackupClickListener, private val listener: RestorableBackupClickListener,
private val items: List<RestorableBackup> private val items: List<RestorableBackup>,
) : Adapter<RestoreSetViewHolder>() { ) : Adapter<RestoreSetViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RestoreSetViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RestoreSetViewHolder {

View file

@ -10,7 +10,6 @@ import android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
import android.widget.ProgressBar import android.widget.ProgressBar
import android.widget.TextView import android.widget.TextView
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.R
import org.koin.androidx.viewmodel.ext.android.sharedViewModel import org.koin.androidx.viewmodel.ext.android.sharedViewModel
@ -27,7 +26,7 @@ class RestoreSetFragment : Fragment() {
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?,
): View { ): View {
val v: View = inflater.inflate(R.layout.fragment_restore_set, container, false) val v: View = inflater.inflate(R.layout.fragment_restore_set, container, false)
@ -45,7 +44,7 @@ class RestoreSetFragment : Fragment() {
// decryption will fail when the device is locked, so keep the screen on to prevent locking // decryption will fail when the device is locked, so keep the screen on to prevent locking
requireActivity().window.addFlags(FLAG_KEEP_SCREEN_ON) requireActivity().window.addFlags(FLAG_KEEP_SCREEN_ON)
viewModel.restoreSetResults.observe(viewLifecycleOwner, Observer { result -> viewModel.restoreSetResults.observe(viewLifecycleOwner, { result ->
onRestoreResultsLoaded(result) onRestoreResultsLoaded(result)
}) })

View file

@ -75,7 +75,7 @@ internal class RestoreViewModel(
private val restoreCoordinator: RestoreCoordinator, private val restoreCoordinator: RestoreCoordinator,
private val apkRestore: ApkRestore, private val apkRestore: ApkRestore,
storageBackup: StorageBackup, storageBackup: StorageBackup,
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO,
) : RequireProvisioningViewModel(app, settingsManager, keyManager), ) : RequireProvisioningViewModel(app, settingsManager, keyManager),
RestorableBackupClickListener, SnapshotViewModel { RestorableBackupClickListener, SnapshotViewModel {
@ -247,7 +247,7 @@ internal class RestoreViewModel(
@WorkerThread @WorkerThread
private fun getFailedStatus( private fun getFailedStatus(
packageName: String, packageName: String,
restorableBackup: RestorableBackup = chosenRestorableBackup.value!! restorableBackup: RestorableBackup = chosenRestorableBackup.value!!,
): AppBackupState { ): AppBackupState {
val metadata = restorableBackup.packageMetadataMap[packageName] ?: return FAILED val metadata = restorableBackup.packageMetadataMap[packageName] ?: return FAILED
return when (metadata.state) { return when (metadata.state) {
@ -369,7 +369,7 @@ internal class RestoreViewModel(
internal class RestoreSetResult( internal class RestoreSetResult(
internal val restorableBackups: List<RestorableBackup>, internal val restorableBackups: List<RestorableBackup>,
internal val errorMsg: String? internal val errorMsg: String?,
) { ) {
internal constructor(restorableBackups: List<RestorableBackup>) : this(restorableBackups, null) internal constructor(restorableBackups: List<RestorableBackup>) : this(restorableBackups, null)

View file

@ -1,6 +1,7 @@
package com.stevesoltys.seedvault.restore.install package com.stevesoltys.seedvault.restore.install
import android.app.PendingIntent import android.app.PendingIntent
import android.app.PendingIntent.FLAG_MUTABLE
import android.app.PendingIntent.FLAG_UPDATE_CURRENT import android.app.PendingIntent.FLAG_UPDATE_CURRENT
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
@ -41,7 +42,7 @@ internal class ApkInstaller(private val context: Context) {
cachedApks: List<File>, cachedApks: List<File>,
packageName: String, packageName: String,
installerPackageName: String?, installerPackageName: String?,
installResult: MutableInstallResult installResult: MutableInstallResult,
) = suspendCancellableCoroutine<InstallResult> { cont -> ) = suspendCancellableCoroutine<InstallResult> { cont ->
val broadcastReceiver = object : BroadcastReceiver() { val broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, i: Intent) { override fun onReceive(context: Context, i: Intent) {
@ -84,8 +85,12 @@ internal class ApkInstaller(private val context: Context) {
flags = FLAG_RECEIVER_FOREGROUND flags = FLAG_RECEIVER_FOREGROUND
setPackage(context.packageName) setPackage(context.packageName)
} }
val pendingIntent = val pendingIntent = PendingIntent.getBroadcast(
PendingIntent.getBroadcast(context, 0, broadcastIntent, FLAG_UPDATE_CURRENT) context,
0,
broadcastIntent,
FLAG_UPDATE_CURRENT or FLAG_MUTABLE, // needs to be mutable, otherwise no extras
)
return pendingIntent.intentSender return pendingIntent.intentSender
} }
@ -93,7 +98,7 @@ internal class ApkInstaller(private val context: Context) {
i: Intent, i: Intent,
expectedPackageName: String, expectedPackageName: String,
cachedApks: List<File>, cachedApks: List<File>,
installResult: MutableInstallResult installResult: MutableInstallResult,
): InstallResult { ): InstallResult {
val packageName = i.getStringExtra(EXTRA_PACKAGE_NAME)!! val packageName = i.getStringExtra(EXTRA_PACKAGE_NAME)!!
val success = i.getIntExtra(EXTRA_STATUS, -1) == STATUS_SUCCESS val success = i.getIntExtra(EXTRA_STATUS, -1) == STATUS_SUCCESS

View file

@ -33,7 +33,7 @@ internal class ApkRestore(
private val legacyStoragePlugin: LegacyStoragePlugin, private val legacyStoragePlugin: LegacyStoragePlugin,
private val crypto: Crypto, private val crypto: Crypto,
private val splitCompatChecker: ApkSplitCompatibilityChecker, private val splitCompatChecker: ApkSplitCompatibilityChecker,
private val apkInstaller: ApkInstaller private val apkInstaller: ApkInstaller,
) { ) {
private val pm = context.packageManager private val pm = context.packageManager
@ -84,7 +84,7 @@ internal class ApkRestore(
backup: RestorableBackup, backup: RestorableBackup,
packageName: String, packageName: String,
metadata: PackageMetadata, metadata: PackageMetadata,
installResult: MutableInstallResult installResult: MutableInstallResult,
) { ) {
// cache the APK and get its hash // cache the APK and get its hash
val (cachedApk, sha256) = cacheApk(backup.version, backup.token, backup.salt, packageName) val (cachedApk, sha256) = cacheApk(backup.version, backup.token, backup.salt, packageName)
@ -169,7 +169,7 @@ internal class ApkRestore(
backup: RestorableBackup, backup: RestorableBackup,
packageName: String, packageName: String,
cachedApk: File, cachedApk: File,
splits: List<ApkSplit>? splits: List<ApkSplit>?,
): List<File>? { ): List<File>? {
// if null, there are no splits, so we just have a single base APK to consider // if null, there are no splits, so we just have a single base APK to consider
val splitNames = splits?.map { it.name } ?: return listOf(cachedApk) val splitNames = splits?.map { it.name } ?: return listOf(cachedApk)
@ -208,7 +208,7 @@ internal class ApkRestore(
token: Long, token: Long,
salt: String, salt: String,
packageName: String, packageName: String,
suffix: String = "" suffix: String = "",
): Pair<File, String> { ): Pair<File, String> {
// create a cache file to write the APK into // create a cache file to write the APK into
val cachedApk = File.createTempFile(packageName + suffix, ".apk", context.cacheDir) val cachedApk = File.createTempFile(packageName + suffix, ".apk", context.cacheDir)
@ -231,7 +231,7 @@ internal class ApkRestore(
private fun shouldInstallSystemApp( private fun shouldInstallSystemApp(
packageName: String, packageName: String,
metadata: PackageMetadata, metadata: PackageMetadata,
installResult: MutableInstallResult installResult: MutableInstallResult,
): InstallResult? { ): InstallResult? {
val installedPackageInfo = try { val installedPackageInfo = try {
pm.getPackageInfo(packageName, 0) pm.getPackageInfo(packageName, 0)

View file

@ -58,7 +58,7 @@ class ApkSplitCompatibilityChecker(private val deviceInfo: DeviceInfo) {
private fun isCompatible( private fun isCompatible(
deviceName: String, deviceName: String,
unknownAllowed: Boolean, unknownAllowed: Boolean,
splitName: String splitName: String,
): Boolean { ): Boolean {
val index = splitName.indexOf(CONFIG_PREFIX) val index = splitName.indexOf(CONFIG_PREFIX)
// If this is not a standardized config split // If this is not a standardized config split

View file

@ -8,7 +8,7 @@ import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
internal class InstallIntentCreator( internal class InstallIntentCreator(
private val packageManager: PackageManager private val packageManager: PackageManager,
) { ) {
private val installerToPackage = mapOf( private val installerToPackage = mapOf(

View file

@ -23,12 +23,12 @@ internal interface InstallItemListener {
} }
internal class InstallProgressAdapter( internal class InstallProgressAdapter(
private val listener: InstallItemListener private val listener: InstallItemListener,
) : Adapter<InstallProgressAdapter.AppInstallViewHolder>() { ) : Adapter<InstallProgressAdapter.AppInstallViewHolder>() {
private var finished = false private var finished = false
private val finishedComparator = FailedFirstComparator() private val finishedComparator = FailedFirstComparator()
private val items = SortedList<ApkInstallResult>( private val items = SortedList(
ApkInstallResult::class.java, ApkInstallResult::class.java,
object : SortedListAdapterCallback<ApkInstallResult>(this) { object : SortedListAdapterCallback<ApkInstallResult>(this) {
override fun areItemsTheSame(item1: ApkInstallResult, item2: ApkInstallResult) = override fun areItemsTheSame(item1: ApkInstallResult, item2: ApkInstallResult) =

View file

@ -12,7 +12,6 @@ import android.widget.TextView
import androidx.activity.result.contract.ActivityResultContract import androidx.activity.result.contract.ActivityResultContract
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager.VERTICAL import androidx.recyclerview.widget.LinearLayoutManager.VERTICAL
@ -37,8 +36,8 @@ class InstallProgressFragment : Fragment(), InstallItemListener {
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?,
): View? { ): View {
val v: View = inflater.inflate(R.layout.fragment_restore_progress, container, false) val v: View = inflater.inflate(R.layout.fragment_restore_progress, container, false)
progressBar = v.findViewById(R.id.progressBar) progressBar = v.findViewById(R.id.progressBar)
@ -61,15 +60,15 @@ class InstallProgressFragment : Fragment(), InstallItemListener {
button.setText(R.string.restore_next) button.setText(R.string.restore_next)
button.setOnClickListener { viewModel.onNextClickedAfterInstallingApps() } button.setOnClickListener { viewModel.onNextClickedAfterInstallingApps() }
viewModel.chosenRestorableBackup.observe(viewLifecycleOwner, Observer { restorableBackup -> viewModel.chosenRestorableBackup.observe(viewLifecycleOwner, { restorableBackup ->
backupNameView.text = restorableBackup.name backupNameView.text = restorableBackup.name
}) })
viewModel.installResult.observe(viewLifecycleOwner, Observer { result -> viewModel.installResult.observe(viewLifecycleOwner, { result ->
onInstallResult(result) onInstallResult(result)
}) })
viewModel.nextButtonEnabled.observe(viewLifecycleOwner, Observer { enabled -> viewModel.nextButtonEnabled.observe(viewLifecycleOwner, { enabled ->
button.isEnabled = enabled button.isEnabled = enabled
}) })
} }

View file

@ -89,7 +89,7 @@ internal class MutableInstallResult(override val total: Int) : InstallResult {
fun update( fun update(
packageName: String, packageName: String,
updateFun: (ApkInstallResult) -> ApkInstallResult updateFun: (ApkInstallResult) -> ApkInstallResult,
): MutableInstallResult { ): MutableInstallResult {
val result = get(packageName) val result = get(packageName)
check(result != null) { "ApkRestoreResult for $packageName does not exist." } check(result != null) { "ApkRestoreResult for $packageName does not exist." }
@ -118,7 +118,7 @@ data class ApkInstallResult(
val state: ApkInstallState, val state: ApkInstallState,
val name: CharSequence? = null, val name: CharSequence? = null,
val icon: Drawable? = null, val icon: Drawable? = null,
val installerPackageName: CharSequence? = null val installerPackageName: CharSequence? = null,
) : Comparable<ApkInstallResult> { ) : Comparable<ApkInstallResult> {
override fun compareTo(other: ApkInstallResult): Int { override fun compareTo(other: ApkInstallResult): Int {
return other.progress.compareTo(progress) return other.progress.compareTo(progress)

View file

@ -28,8 +28,8 @@ class AboutDialogFragment : DialogFragment() {
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?,
): View? { ): View {
val v: View = inflater.inflate(R.layout.fragment_about, container, false) val v: View = inflater.inflate(R.layout.fragment_about, container, false)
versionView = v.findViewById(R.id.versionView) versionView = v.findViewById(R.id.versionView)

View file

@ -39,7 +39,7 @@ data class AppStatus(
val name: String, val name: String,
val time: Long, val time: Long,
val status: AppBackupState, val status: AppBackupState,
val isSpecial: Boolean = false val isSpecial: Boolean = false,
) : AppListItem() ) : AppListItem()
class AppSectionTitle(@StringRes val titleRes: Int) : AppListItem() class AppSectionTitle(@StringRes val titleRes: Int) : AppListItem()
@ -48,7 +48,7 @@ internal class AppListRetriever(
private val context: Context, private val context: Context,
private val packageService: PackageService, private val packageService: PackageService,
private val settingsManager: SettingsManager, private val settingsManager: SettingsManager,
private val metadataManager: MetadataManager private val metadataManager: MetadataManager,
) { ) {
private val pm: PackageManager = context.packageManager private val pm: PackageManager = context.packageManager

View file

@ -139,7 +139,7 @@ internal class AppStatusAdapter(private val toggleListener: AppStatusToggleListe
internal class AppStatusDiff( internal class AppStatusDiff(
private val oldItems: List<AppListItem>, private val oldItems: List<AppListItem>,
private val newItems: List<AppListItem> private val newItems: List<AppListItem>,
) : DiffUtil.Callback() { ) : DiffUtil.Callback() {
override fun getOldListSize() = oldItems.size override fun getOldListSize() = oldItems.size
@ -163,5 +163,5 @@ internal class AppStatusDiff(
internal class AppStatusResult( internal class AppStatusResult(
val appStatusList: List<AppListItem>, val appStatusList: List<AppListItem>,
val diff: DiffResult val diff: DiffResult,
) )

View file

@ -11,7 +11,6 @@ import android.view.View.VISIBLE
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ProgressBar import android.widget.ProgressBar
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.R
@ -35,7 +34,7 @@ class AppStatusFragment : Fragment(), AppStatusToggleListener {
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?,
): View { ): View {
setHasOptionsMenu(true) setHasOptionsMenu(true)
val v: View = inflater.inflate(R.layout.fragment_app_status, container, false) val v: View = inflater.inflate(R.layout.fragment_app_status, container, false)
@ -57,7 +56,7 @@ class AppStatusFragment : Fragment(), AppStatusToggleListener {
} }
progressBar.visibility = VISIBLE progressBar.visibility = VISIBLE
viewModel.appStatusList.observe(viewLifecycleOwner, Observer { result -> viewModel.appStatusList.observe(viewLifecycleOwner, { result ->
adapter.update(result.appStatusList, result.diff) adapter.update(result.appStatusList, result.diff)
progressBar.visibility = INVISIBLE progressBar.visibility = INVISIBLE
}) })
@ -69,7 +68,7 @@ class AppStatusFragment : Fragment(), AppStatusToggleListener {
appEditMenuItem = menu.findItem(R.id.edit_app_blacklist) appEditMenuItem = menu.findItem(R.id.edit_app_blacklist)
// observe edit mode changes here where we are sure to have the MenuItem // observe edit mode changes here where we are sure to have the MenuItem
viewModel.appEditMode.observe(viewLifecycleOwner, Observer { enabled -> viewModel.appEditMode.observe(viewLifecycleOwner, { enabled ->
appEditMenuItem.isChecked = enabled appEditMenuItem.isChecked = enabled
adapter.setEditMode(enabled) adapter.setEditMode(enabled)
}) })

View file

@ -55,7 +55,7 @@ class SettingsActivity : RequireProvisioningActivity(), OnPreferenceStartFragmen
override fun onPreferenceStartFragment( override fun onPreferenceStartFragment(
caller: PreferenceFragmentCompat, caller: PreferenceFragmentCompat,
pref: Preference pref: Preference,
): Boolean { ): Boolean {
val fragment = val fragment =
supportFragmentManager.fragmentFactory.instantiate(classLoader, pref.fragment) supportFragmentManager.fragmentFactory.instantiate(classLoader, pref.fragment)

View file

@ -12,7 +12,6 @@ import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.Observer
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.Preference.OnPreferenceChangeListener import androidx.preference.Preference.OnPreferenceChangeListener
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
@ -128,12 +127,12 @@ class SettingsFragment : PreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
viewModel.lastBackupTime.observe(viewLifecycleOwner, Observer { time -> viewModel.lastBackupTime.observe(viewLifecycleOwner, { time ->
setAppBackupStatusSummary(time) setAppBackupStatusSummary(time)
}) })
val backupFiles: Preference = findPreference("backup_files")!! val backupFiles: Preference = findPreference("backup_files")!!
viewModel.filesSummary.observe(viewLifecycleOwner, Observer { summary -> viewModel.filesSummary.observe(viewLifecycleOwner, { summary ->
backupFiles.summary = summary backupFiles.summary = summary
}) })
} }
@ -158,7 +157,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
if (resources.getBoolean(R.bool.show_restore_in_settings)) { if (resources.getBoolean(R.bool.show_restore_in_settings)) {
menuRestore?.isVisible = true menuRestore?.isVisible = true
} }
viewModel.backupPossible.observe(viewLifecycleOwner, Observer { possible -> viewModel.backupPossible.observe(viewLifecycleOwner, { possible ->
menuBackupNow?.isEnabled = possible menuBackupNow?.isEnabled = possible
menuRestore?.isEnabled = possible menuRestore?.isEnabled = possible
}) })

View file

@ -146,7 +146,7 @@ data class Storage(
val uri: Uri, val uri: Uri,
val name: String, val name: String,
val isUsb: Boolean, val isUsb: Boolean,
val requiresNetwork: Boolean val requiresNetwork: Boolean,
) { ) {
fun getDocumentFile(context: Context) = DocumentFile.fromTreeUri(context, uri) fun getDocumentFile(context: Context) = DocumentFile.fromTreeUri(context, uri)
?: throw AssertionError("Should only happen on API < 21.") ?: throw AssertionError("Should only happen on API < 21.")
@ -171,7 +171,7 @@ data class Storage(
private fun hasUnmeteredInternet(context: Context): Boolean { private fun hasUnmeteredInternet(context: Context): Boolean {
val cm = context.getSystemService(ConnectivityManager::class.java) val cm = context.getSystemService(ConnectivityManager::class.java)
val isMetered = cm.isActiveNetworkMetered() val isMetered = cm.isActiveNetworkMetered
val capabilities = cm.getNetworkCapabilities(cm.activeNetwork) ?: return false val capabilities = cm.getNetworkCapabilities(cm.activeNetwork) ?: return false
return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && !isMetered return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && !isMetered
} }
@ -181,7 +181,7 @@ data class FlashDrive(
val name: String, val name: String,
val serialNumber: String?, val serialNumber: String?,
val vendorId: Int, val vendorId: Int,
val productId: Int val productId: Int,
) { ) {
companion object { companion object {
fun from(device: UsbDevice) = FlashDrive( fun from(device: UsbDevice) = FlashDrive(

View file

@ -48,7 +48,7 @@ internal class SettingsViewModel(
private val notificationManager: BackupNotificationManager, private val notificationManager: BackupNotificationManager,
private val metadataManager: MetadataManager, private val metadataManager: MetadataManager,
private val appListRetriever: AppListRetriever, private val appListRetriever: AppListRetriever,
private val storageBackup: StorageBackup private val storageBackup: StorageBackup,
) : RequireProvisioningViewModel(app, settingsManager, keyManager) { ) : RequireProvisioningViewModel(app, settingsManager, keyManager) {
private val contentResolver = app.contentResolver private val contentResolver = app.contentResolver
@ -56,7 +56,7 @@ internal class SettingsViewModel(
override val isRestoreOperation = false override val isRestoreOperation = false
private val mBackupPossible = MutableLiveData<Boolean>(false) private val mBackupPossible = MutableLiveData(false)
val backupPossible: LiveData<Boolean> = mBackupPossible val backupPossible: LiveData<Boolean> = mBackupPossible
internal val lastBackupTime = metadataManager.lastBackupTime internal val lastBackupTime = metadataManager.lastBackupTime

View file

@ -10,7 +10,7 @@ import javax.crypto.SecretKey
internal class SeedvaultStoragePlugin( internal class SeedvaultStoragePlugin(
context: Context, context: Context,
private val storage: DocumentsStorage, private val storage: DocumentsStorage,
private val keyManager: KeyManager private val keyManager: KeyManager,
) : SafStoragePlugin(context) { ) : SafStoragePlugin(context) {
override val root: DocumentFile override val root: DocumentFile
get() = storage.rootBackupDir ?: error("No storage set") get() = storage.rootBackupDir ?: error("No storage set")

View file

@ -117,7 +117,7 @@ class ConfigurableBackupTransport internal constructor(private val context: Cont
override fun isAppEligibleForBackup( override fun isAppEligibleForBackup(
targetPackage: PackageInfo, targetPackage: PackageInfo,
isFullBackup: Boolean isFullBackup: Boolean,
): Boolean { ): Boolean {
return backupCoordinator.isAppEligibleForBackup(targetPackage, isFullBackup) return backupCoordinator.isAppEligibleForBackup(targetPackage, isFullBackup)
} }
@ -145,14 +145,14 @@ class ConfigurableBackupTransport internal constructor(private val context: Cont
override fun performBackup( override fun performBackup(
packageInfo: PackageInfo, packageInfo: PackageInfo,
inFd: ParcelFileDescriptor, inFd: ParcelFileDescriptor,
flags: Int flags: Int,
): Int = runBlocking { ): Int = runBlocking {
backupCoordinator.performIncrementalBackup(packageInfo, inFd, flags) backupCoordinator.performIncrementalBackup(packageInfo, inFd, flags)
} }
override fun performBackup( override fun performBackup(
targetPackage: PackageInfo, targetPackage: PackageInfo,
fileDescriptor: ParcelFileDescriptor fileDescriptor: ParcelFileDescriptor,
): Int { ): Int {
Log.w(TAG, "Warning: Legacy performBackup() method called.") Log.w(TAG, "Warning: Legacy performBackup() method called.")
return performBackup(targetPackage, fileDescriptor, 0) return performBackup(targetPackage, fileDescriptor, 0)
@ -173,14 +173,14 @@ class ConfigurableBackupTransport internal constructor(private val context: Cont
override fun performFullBackup( override fun performFullBackup(
targetPackage: PackageInfo, targetPackage: PackageInfo,
socket: ParcelFileDescriptor, socket: ParcelFileDescriptor,
flags: Int flags: Int,
): Int = runBlocking { ): Int = runBlocking {
backupCoordinator.performFullBackup(targetPackage, socket, flags) backupCoordinator.performFullBackup(targetPackage, socket, flags)
} }
override fun performFullBackup( override fun performFullBackup(
targetPackage: PackageInfo, targetPackage: PackageInfo,
fileDescriptor: ParcelFileDescriptor fileDescriptor: ParcelFileDescriptor,
): Int = runBlocking { ): Int = runBlocking {
Log.w(TAG, "Warning: Legacy performFullBackup() method called.") Log.w(TAG, "Warning: Legacy performFullBackup() method called.")
backupCoordinator.performFullBackup(targetPackage, fileDescriptor, 0) backupCoordinator.performFullBackup(targetPackage, fileDescriptor, 0)

View file

@ -30,7 +30,7 @@ internal class ApkBackup(
private val pm: PackageManager, private val pm: PackageManager,
private val crypto: Crypto, private val crypto: Crypto,
private val settingsManager: SettingsManager, private val settingsManager: SettingsManager,
private val metadataManager: MetadataManager private val metadataManager: MetadataManager,
) { ) {
/** /**
@ -46,7 +46,7 @@ internal class ApkBackup(
suspend fun backupApkIfNecessary( suspend fun backupApkIfNecessary(
packageInfo: PackageInfo, packageInfo: PackageInfo,
packageState: PackageState, packageState: PackageState,
streamGetter: suspend (name: String) -> OutputStream streamGetter: suspend (name: String) -> OutputStream,
): PackageMetadata? { ): PackageMetadata? {
// do not back up @pm@ // do not back up @pm@
val packageName = packageInfo.packageName val packageName = packageInfo.packageName
@ -126,7 +126,7 @@ internal class ApkBackup(
private fun signaturesChanged( private fun signaturesChanged(
packageMetadata: PackageMetadata, packageMetadata: PackageMetadata,
signatures: List<String> signatures: List<String>,
): Boolean { ): Boolean {
// no signatures in package metadata counts as them not having changed // no signatures in package metadata counts as them not having changed
if (packageMetadata.signatures == null) return false if (packageMetadata.signatures == null) return false
@ -151,7 +151,7 @@ internal class ApkBackup(
@Throws(IOException::class) @Throws(IOException::class)
private suspend fun backupSplitApks( private suspend fun backupSplitApks(
packageInfo: PackageInfo, packageInfo: PackageInfo,
streamGetter: suspend (name: String) -> OutputStream streamGetter: suspend (name: String) -> OutputStream,
): List<ApkSplit> { ): List<ApkSplit> {
check(packageInfo.splitNames != null) check(packageInfo.splitNames != null)
val splitSourceDirs = packageInfo.applicationInfo.splitSourceDirs val splitSourceDirs = packageInfo.applicationInfo.splitSourceDirs
@ -178,7 +178,7 @@ internal class ApkBackup(
packageName: String, packageName: String,
splitName: String, splitName: String,
sourceDir: String, sourceDir: String,
streamGetter: suspend (name: String) -> OutputStream streamGetter: suspend (name: String) -> OutputStream,
): ApkSplit { ): ApkSplit {
// Calculate sha256 hash first to determine file name suffix. // Calculate sha256 hash first to determine file name suffix.
// We could also just use the split name as a suffix, but there is a theoretical risk // We could also just use the split name as a suffix, but there is a theoretical risk

View file

@ -43,7 +43,7 @@ private val TAG = BackupCoordinator::class.java.simpleName
private class CoordinatorState( private class CoordinatorState(
var calledInitialize: Boolean, var calledInitialize: Boolean,
var calledClearBackupData: Boolean, var calledClearBackupData: Boolean,
var cancelReason: PackageState var cancelReason: PackageState,
) { ) {
val expectFinish: Boolean val expectFinish: Boolean
get() = calledInitialize || calledClearBackupData get() = calledInitialize || calledClearBackupData
@ -71,7 +71,7 @@ internal class BackupCoordinator(
private val packageService: PackageService, private val packageService: PackageService,
private val metadataManager: MetadataManager, private val metadataManager: MetadataManager,
private val settingsManager: SettingsManager, private val settingsManager: SettingsManager,
private val nm: BackupNotificationManager private val nm: BackupNotificationManager,
) { ) {
private val state = CoordinatorState( private val state = CoordinatorState(
@ -140,7 +140,7 @@ internal class BackupCoordinator(
fun isAppEligibleForBackup( fun isAppEligibleForBackup(
targetPackage: PackageInfo, targetPackage: PackageInfo,
@Suppress("UNUSED_PARAMETER") isFullBackup: Boolean @Suppress("UNUSED_PARAMETER") isFullBackup: Boolean,
): Boolean { ): Boolean {
val packageName = targetPackage.packageName val packageName = targetPackage.packageName
// Check that the app is not blacklisted by the user // Check that the app is not blacklisted by the user
@ -230,7 +230,7 @@ internal class BackupCoordinator(
suspend fun performIncrementalBackup( suspend fun performIncrementalBackup(
packageInfo: PackageInfo, packageInfo: PackageInfo,
data: ParcelFileDescriptor, data: ParcelFileDescriptor,
flags: Int flags: Int,
): Int { ): Int {
state.cancelReason = UNKNOWN_ERROR state.cancelReason = UNKNOWN_ERROR
if (metadataManager.requiresInit) { if (metadataManager.requiresInit) {
@ -280,7 +280,7 @@ internal class BackupCoordinator(
suspend fun performFullBackup( suspend fun performFullBackup(
targetPackage: PackageInfo, targetPackage: PackageInfo,
fileDescriptor: ParcelFileDescriptor, fileDescriptor: ParcelFileDescriptor,
flags: Int flags: Int,
): Int { ): Int {
state.cancelReason = UNKNOWN_ERROR state.cancelReason = UNKNOWN_ERROR
val token = settingsManager.getToken() ?: error("no token in performFullBackup") val token = settingsManager.getToken() ?: error("no token in performFullBackup")
@ -448,7 +448,7 @@ internal class BackupCoordinator(
*/ */
private suspend fun backUpApk( private suspend fun backUpApk(
packageInfo: PackageInfo, packageInfo: PackageInfo,
packageState: PackageState = UNKNOWN_ERROR packageState: PackageState = UNKNOWN_ERROR,
): Boolean { ): Boolean {
val packageName = packageInfo.packageName val packageName = packageInfo.packageName
return try { return try {

View file

@ -23,7 +23,7 @@ private class FullBackupState(
val packageInfo: PackageInfo, val packageInfo: PackageInfo,
val inputFileDescriptor: ParcelFileDescriptor, val inputFileDescriptor: ParcelFileDescriptor,
val inputStream: InputStream, val inputStream: InputStream,
var outputStreamInit: (suspend () -> OutputStream)? var outputStreamInit: (suspend () -> OutputStream)?,
) { ) {
/** /**
* This is an encrypted stream that can be written to directly. * This is an encrypted stream that can be written to directly.
@ -42,7 +42,7 @@ internal class FullBackup(
private val plugin: StoragePlugin, private val plugin: StoragePlugin,
private val settingsManager: SettingsManager, private val settingsManager: SettingsManager,
private val inputFactory: InputFactory, private val inputFactory: InputFactory,
private val crypto: Crypto private val crypto: Crypto,
) { ) {
private var state: FullBackupState? = null private var state: FullBackupState? = null
@ -104,7 +104,7 @@ internal class FullBackup(
socket: ParcelFileDescriptor, socket: ParcelFileDescriptor,
@Suppress("UNUSED_PARAMETER") flags: Int = 0, @Suppress("UNUSED_PARAMETER") flags: Int = 0,
token: Long, token: Long,
salt: String salt: String,
): Int { ): Int {
if (state != null) throw AssertionError() if (state != null) throw AssertionError()
val packageName = targetPackage.packageName val packageName = targetPackage.packageName

View file

@ -22,7 +22,7 @@ class KVBackupState(
internal val packageInfo: PackageInfo, internal val packageInfo: PackageInfo,
val token: Long, val token: Long,
val name: String, val name: String,
val db: KVDb val db: KVDb,
) { ) {
var needsUpload: Boolean = false var needsUpload: Boolean = false
} }
@ -37,7 +37,7 @@ internal class KVBackup(
private val settingsManager: SettingsManager, private val settingsManager: SettingsManager,
private val inputFactory: InputFactory, private val inputFactory: InputFactory,
private val crypto: Crypto, private val crypto: Crypto,
private val dbManager: KvDbManager private val dbManager: KvDbManager,
) { ) {
private var state: KVBackupState? = null private var state: KVBackupState? = null
@ -57,7 +57,7 @@ internal class KVBackup(
data: ParcelFileDescriptor, data: ParcelFileDescriptor,
flags: Int, flags: Int,
token: Long, token: Long,
salt: String salt: String,
): Int { ): Int {
val dataNotChanged = flags and FLAG_DATA_NOT_CHANGED != 0 val dataNotChanged = flags and FLAG_DATA_NOT_CHANGED != 0
val isIncremental = flags and FLAG_INCREMENTAL != 0 val isIncremental = flags and FLAG_INCREMENTAL != 0
@ -235,7 +235,7 @@ internal class KVBackup(
token: Long, token: Long,
name: String, name: String,
packageName: String, packageName: String,
db: KVDb db: KVDb,
) { ) {
db.vacuum() db.vacuum()
db.close() db.close()
@ -259,7 +259,7 @@ internal class KVBackup(
/** /**
* value is null when this is a deletion operation * value is null when this is a deletion operation
*/ */
val value: ByteArray? val value: ByteArray?,
) )
private sealed class Result<out T> { private sealed class Result<out T> {

View file

@ -28,7 +28,7 @@ private const val LOG_MAX_PACKAGES = 100
*/ */
internal class PackageService( internal class PackageService(
private val context: Context, private val context: Context,
private val backupManager: IBackupManager private val backupManager: IBackupManager,
) { ) {
private val packageManager: PackageManager = context.packageManager private val packageManager: PackageManager = context.packageManager
@ -144,7 +144,7 @@ internal data class ExpectedAppTotals(
/** /**
* The number of non-system apps that has opted-out of backup. * The number of non-system apps that has opted-out of backup.
*/ */
val appsOptOut: Int val appsOptOut: Int,
) )
internal fun PackageInfo.isUserVisible(context: Context): Boolean { internal fun PackageInfo.isUserVisible(context: Context): Boolean {

View file

@ -25,7 +25,7 @@ private class FullRestoreState(
val version: Byte, val version: Byte,
val token: Long, val token: Long,
val name: String, val name: String,
val packageInfo: PackageInfo val packageInfo: PackageInfo,
) { ) {
var inputStream: InputStream? = null var inputStream: InputStream? = null
} }
@ -39,7 +39,7 @@ internal class FullRestore(
private val legacyPlugin: LegacyStoragePlugin, private val legacyPlugin: LegacyStoragePlugin,
private val outputFactory: OutputFactory, private val outputFactory: OutputFactory,
private val headerReader: HeaderReader, private val headerReader: HeaderReader,
private val crypto: Crypto private val crypto: Crypto,
) { ) {
private var state: FullRestoreState? = null private var state: FullRestoreState? = null

View file

@ -34,7 +34,7 @@ private class KVRestoreState(
/** /**
* Optional [PackageInfo] for single package restore, optimizes restore of @pm@ * Optional [PackageInfo] for single package restore, optimizes restore of @pm@
*/ */
val autoRestorePackageInfo: PackageInfo? val autoRestorePackageInfo: PackageInfo?,
) )
private val TAG = KVRestore::class.java.simpleName private val TAG = KVRestore::class.java.simpleName
@ -47,7 +47,7 @@ internal class KVRestore(
private val outputFactory: OutputFactory, private val outputFactory: OutputFactory,
private val headerReader: HeaderReader, private val headerReader: HeaderReader,
private val crypto: Crypto, private val crypto: Crypto,
private val dbManager: KvDbManager private val dbManager: KvDbManager,
) { ) {
private var state: KVRestoreState? = null private var state: KVRestoreState? = null
@ -76,7 +76,7 @@ internal class KVRestore(
token: Long, token: Long,
name: String, name: String,
packageInfo: PackageInfo, packageInfo: PackageInfo,
autoRestorePackageInfo: PackageInfo? = null autoRestorePackageInfo: PackageInfo? = null,
) { ) {
state = KVRestoreState(version, token, name, packageInfo, autoRestorePackageInfo) state = KVRestoreState(version, token, name, packageInfo, autoRestorePackageInfo)
} }
@ -238,7 +238,7 @@ internal class KVRestore(
private suspend fun readAndWriteValueV0( private suspend fun readAndWriteValueV0(
state: KVRestoreState, state: KVRestoreState,
dKey: DecodedKey, dKey: DecodedKey,
out: BackupDataOutput out: BackupDataOutput,
) = legacyPlugin.getInputStreamForRecord(state.token, state.packageInfo, dKey.base64Key) ) = legacyPlugin.getInputStreamForRecord(state.token, state.packageInfo, dKey.base64Key)
.use { inputStream -> .use { inputStream ->
val version = headerReader.readVersion(inputStream, state.version) val version = headerReader.readVersion(inputStream, state.version)

View file

@ -33,7 +33,7 @@ private data class RestoreCoordinatorState(
* Optional [PackageInfo] for single package restore, to reduce data needed to read for @pm@ * Optional [PackageInfo] for single package restore, to reduce data needed to read for @pm@
*/ */
val autoRestorePackageInfo: PackageInfo?, val autoRestorePackageInfo: PackageInfo?,
val backupMetadata: BackupMetadata val backupMetadata: BackupMetadata,
) { ) {
var currentPackage: String? = null var currentPackage: String? = null
} }
@ -50,7 +50,7 @@ internal class RestoreCoordinator(
private val plugin: StoragePlugin, private val plugin: StoragePlugin,
private val kv: KVRestore, private val kv: KVRestore,
private val full: FullRestore, private val full: FullRestore,
private val metadataReader: MetadataReader private val metadataReader: MetadataReader,
) { ) {
private var state: RestoreCoordinatorState? = null private var state: RestoreCoordinatorState? = null
@ -249,7 +249,7 @@ internal class RestoreCoordinator(
@Suppress("deprecation") @Suppress("deprecation")
private suspend fun nextRestorePackageV0( private suspend fun nextRestorePackageV0(
state: RestoreCoordinatorState, state: RestoreCoordinatorState,
packageInfo: PackageInfo packageInfo: PackageInfo,
): RestoreDescription? { ): RestoreDescription? {
val packageName = packageInfo.packageName val packageName = packageInfo.packageName
val type = try { val type = try {

View file

@ -49,7 +49,7 @@ abstract class RequireProvisioningActivity : BackupActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
getViewModel().chooseBackupLocation.observeEvent(this, LiveEventHandler { show -> getViewModel().chooseBackupLocation.observeEvent(this, { show ->
if (show) showStorageActivity() if (show) showStorageActivity()
}) })
} }

View file

@ -9,7 +9,7 @@ import com.stevesoltys.seedvault.ui.storage.StorageViewModel
abstract class RequireProvisioningViewModel( abstract class RequireProvisioningViewModel(
protected val app: Application, protected val app: Application,
protected val settingsManager: SettingsManager, protected val settingsManager: SettingsManager,
protected val keyManager: KeyManager protected val keyManager: KeyManager,
) : AndroidViewModel(app) { ) : AndroidViewModel(app) {
abstract val isRestoreOperation: Boolean abstract val isRestoreOperation: Boolean

View file

@ -10,7 +10,7 @@ import org.calyxos.backup.storage.ui.backup.BackupContentFragment
import org.koin.androidx.viewmodel.ext.android.sharedViewModel import org.koin.androidx.viewmodel.ext.android.sharedViewModel
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
class FileSelectionFragment() : BackupContentFragment() { class FileSelectionFragment : BackupContentFragment() {
override val viewModel by viewModel<FileSelectionViewModel>() override val viewModel by viewModel<FileSelectionViewModel>()
private val settingsViewModel by sharedViewModel<SettingsViewModel>() private val settingsViewModel by sharedViewModel<SettingsViewModel>()
@ -18,7 +18,7 @@ class FileSelectionFragment() : BackupContentFragment() {
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?,
): View { ): View {
requireActivity().setTitle(R.string.settings_backup_files_title) requireActivity().setTitle(R.string.settings_backup_files_title)
return super.onCreateView(inflater, container, savedInstanceState) return super.onCreateView(inflater, container, savedInstanceState)

View file

@ -8,7 +8,7 @@ import org.calyxos.backup.storage.ui.backup.BackupContentViewModel
class FileSelectionViewModel( class FileSelectionViewModel(
app: Application, app: Application,
override val storageBackup: StorageBackup override val storageBackup: StorageBackup,
) : BackupContentViewModel(app) { ) : BackupContentViewModel(app) {
init { init {

View file

@ -70,7 +70,7 @@ internal class BackupNotificationManager(private val context: Context) {
*/ */
fun onBackupStarted( fun onBackupStarted(
expectedPackages: Int, expectedPackages: Int,
appTotals: ExpectedAppTotals appTotals: ExpectedAppTotals,
) { ) {
updateBackupNotification( updateBackupNotification(
infoText = "", // This passes quickly, no need to show something here infoText = "", // This passes quickly, no need to show something here
@ -112,7 +112,7 @@ internal class BackupNotificationManager(private val context: Context) {
private fun updateBackupNotification( private fun updateBackupNotification(
infoText: CharSequence, infoText: CharSequence,
transferred: Int, transferred: Int,
expected: Int expected: Int,
) { ) {
@Suppress("MagicNumber") @Suppress("MagicNumber")
val percentage = (transferred.toFloat() / expected) * 100 val percentage = (transferred.toFloat() / expected) * 100
@ -223,7 +223,7 @@ internal class BackupNotificationManager(private val context: Context) {
setPackage(context.packageName) setPackage(context.packageName)
putExtra(EXTRA_PACKAGE_NAME, packageName) putExtra(EXTRA_PACKAGE_NAME, packageName)
} }
val flags = FLAG_UPDATE_CURRENT and FLAG_IMMUTABLE val flags = FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE
val pendingIntent = val pendingIntent =
PendingIntent.getBroadcast(context, REQUEST_CODE_UNINSTALL, intent, flags) PendingIntent.getBroadcast(context, REQUEST_CODE_UNINSTALL, intent, flags)
val actionText = context.getString(R.string.notification_restore_error_action) val actionText = context.getString(R.string.notification_restore_error_action)

View file

@ -19,7 +19,7 @@ private val TAG = NotificationBackupObserver::class.java.simpleName
internal class NotificationBackupObserver( internal class NotificationBackupObserver(
private val context: Context, private val context: Context,
private val expectedPackages: Int, private val expectedPackages: Int,
appTotals: ExpectedAppTotals appTotals: ExpectedAppTotals,
) : IBackupObserver.Stub(), KoinComponent { ) : IBackupObserver.Stub(), KoinComponent {
private val nm: BackupNotificationManager by inject() private val nm: BackupNotificationManager by inject()

View file

@ -7,7 +7,6 @@ import com.stevesoltys.seedvault.R
import com.stevesoltys.seedvault.ui.BackupActivity import com.stevesoltys.seedvault.ui.BackupActivity
import com.stevesoltys.seedvault.ui.INTENT_EXTRA_IS_RESTORE import com.stevesoltys.seedvault.ui.INTENT_EXTRA_IS_RESTORE
import com.stevesoltys.seedvault.ui.INTENT_EXTRA_IS_SETUP_WIZARD import com.stevesoltys.seedvault.ui.INTENT_EXTRA_IS_SETUP_WIZARD
import com.stevesoltys.seedvault.ui.LiveEventHandler
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
class RecoveryCodeActivity : BackupActivity() { class RecoveryCodeActivity : BackupActivity() {
@ -23,10 +22,10 @@ class RecoveryCodeActivity : BackupActivity() {
setContentView(R.layout.activity_recovery_code) setContentView(R.layout.activity_recovery_code)
viewModel.isRestore = isRestore() viewModel.isRestore = isRestore()
viewModel.confirmButtonClicked.observeEvent(this, LiveEventHandler { clicked -> viewModel.confirmButtonClicked.observeEvent(this, { clicked ->
if (clicked) showInput(true) if (clicked) showInput(true)
}) })
viewModel.recoveryCodeSaved.observeEvent(this, LiveEventHandler { saved -> viewModel.recoveryCodeSaved.observeEvent(this, { saved ->
if (saved) { if (saved) {
setResult(RESULT_OK) setResult(RESULT_OK)
finishAfterTransition() finishAfterTransition()

View file

@ -34,7 +34,6 @@ import com.google.android.material.snackbar.Snackbar
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.R
import com.stevesoltys.seedvault.isDebugBuild import com.stevesoltys.seedvault.isDebugBuild
import com.stevesoltys.seedvault.ui.LiveEventHandler
import org.koin.androidx.viewmodel.ext.android.sharedViewModel import org.koin.androidx.viewmodel.ext.android.sharedViewModel
import java.util.Locale import java.util.Locale
@ -70,7 +69,7 @@ class RecoveryCodeInputFragment : Fragment() {
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?,
): View { ): View {
val v: View = inflater.inflate(R.layout.fragment_recovery_code_input, container, false) val v: View = inflater.inflate(R.layout.fragment_recovery_code_input, container, false)
@ -127,9 +126,9 @@ class RecoveryCodeInputFragment : Fragment() {
newCodeButton.visibility = if (forStoringNewCode) GONE else VISIBLE newCodeButton.visibility = if (forStoringNewCode) GONE else VISIBLE
newCodeButton.setOnClickListener { generateNewCode() } newCodeButton.setOnClickListener { generateNewCode() }
viewModel.existingCodeChecked.observeEvent(viewLifecycleOwner, viewModel.existingCodeChecked.observeEvent(viewLifecycleOwner, { verified ->
LiveEventHandler { verified -> onExistingCodeChecked(verified) } onExistingCodeChecked(verified)
) })
if (forStoringNewCode && isDebugBuild() && !viewModel.isRestore) debugPreFill() if (forStoringNewCode && isDebugBuild() && !viewModel.isRestore) debugPreFill()
} }

View file

@ -22,7 +22,7 @@ class RecoveryCodeOutputFragment : Fragment() {
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?,
): View { ): View {
val v: View = inflater.inflate(R.layout.fragment_recovery_code_output, container, false) val v: View = inflater.inflate(R.layout.fragment_recovery_code_output, container, false)

View file

@ -34,7 +34,7 @@ internal class RecoveryCodeViewModel(
private val backupManager: IBackupManager, private val backupManager: IBackupManager,
private val backupCoordinator: BackupCoordinator, private val backupCoordinator: BackupCoordinator,
private val notificationManager: BackupNotificationManager, private val notificationManager: BackupNotificationManager,
private val storageBackup: StorageBackup private val storageBackup: StorageBackup,
) : AndroidViewModel(app) { ) : AndroidViewModel(app) {
internal val wordList: List<CharArray> by lazy { internal val wordList: List<CharArray> by lazy {

View file

@ -26,7 +26,7 @@ internal class BackupStorageViewModel(
private val backupManager: IBackupManager, private val backupManager: IBackupManager,
private val backupCoordinator: BackupCoordinator, private val backupCoordinator: BackupCoordinator,
private val storageBackup: StorageBackup, private val storageBackup: StorageBackup,
settingsManager: SettingsManager settingsManager: SettingsManager,
) : StorageViewModel(app, settingsManager) { ) : StorageViewModel(app, settingsManager) {
override val isRestoreOperation = false override val isRestoreOperation = false

View file

@ -5,9 +5,9 @@ import android.net.Uri
import android.util.Log import android.util.Log
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.R
import com.stevesoltys.seedvault.plugins.StoragePlugin
import com.stevesoltys.seedvault.plugins.saf.DIRECTORY_ROOT import com.stevesoltys.seedvault.plugins.saf.DIRECTORY_ROOT
import com.stevesoltys.seedvault.settings.SettingsManager import com.stevesoltys.seedvault.settings.SettingsManager
import com.stevesoltys.seedvault.plugins.StoragePlugin
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.io.IOException import java.io.IOException
@ -17,7 +17,7 @@ private val TAG = RestoreStorageViewModel::class.java.simpleName
internal class RestoreStorageViewModel( internal class RestoreStorageViewModel(
private val app: Application, private val app: Application,
private val storagePlugin: StoragePlugin, private val storagePlugin: StoragePlugin,
settingsManager: SettingsManager settingsManager: SettingsManager,
) : StorageViewModel(app, settingsManager) { ) : StorageViewModel(app, settingsManager) {
override val isRestoreOperation = true override val isRestoreOperation = true

View file

@ -17,7 +17,6 @@ import com.stevesoltys.seedvault.R
import com.stevesoltys.seedvault.ui.BackupActivity import com.stevesoltys.seedvault.ui.BackupActivity
import com.stevesoltys.seedvault.ui.INTENT_EXTRA_IS_RESTORE import com.stevesoltys.seedvault.ui.INTENT_EXTRA_IS_RESTORE
import com.stevesoltys.seedvault.ui.INTENT_EXTRA_IS_SETUP_WIZARD import com.stevesoltys.seedvault.ui.INTENT_EXTRA_IS_SETUP_WIZARD
import com.stevesoltys.seedvault.ui.LiveEventHandler
import org.koin.androidx.viewmodel.ext.android.getViewModel import org.koin.androidx.viewmodel.ext.android.getViewModel
private val TAG = StorageActivity::class.java.name private val TAG = StorageActivity::class.java.name
@ -59,11 +58,11 @@ class StorageActivity : BackupActivity() {
} }
viewModel.isSetupWizard = isSetupWizard() viewModel.isSetupWizard = isSetupWizard()
viewModel.locationSet.observeEvent(this, LiveEventHandler { viewModel.locationSet.observeEvent(this, {
showFragment(StorageCheckFragment.newInstance(getCheckFragmentTitle()), true) showFragment(StorageCheckFragment.newInstance(getCheckFragmentTitle()), true)
}) })
viewModel.locationChecked.observeEvent(this, LiveEventHandler { result -> viewModel.locationChecked.observeEvent(this, { result ->
val errorMsg = result.errorMsg val errorMsg = result.errorMsg
if (errorMsg == null) { if (errorMsg == null) {
setResult(RESULT_OK) setResult(RESULT_OK)

View file

@ -37,7 +37,7 @@ class StorageCheckFragment : Fragment() {
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?,
): View { ): View {
val v: View = inflater.inflate(R.layout.fragment_storage_check, container, false) val v: View = inflater.inflate(R.layout.fragment_storage_check, container, false)

View file

@ -17,7 +17,7 @@ import com.stevesoltys.seedvault.ui.storage.StorageRootAdapter.StorageRootViewHo
internal class StorageRootAdapter( internal class StorageRootAdapter(
private val isRestore: Boolean, private val isRestore: Boolean,
private val listener: StorageRootClickedListener private val listener: StorageRootClickedListener,
) : Adapter<StorageRootViewHolder>() { ) : Adapter<StorageRootViewHolder>() {
private val items = ArrayList<StorageRoot>() private val items = ArrayList<StorageRoot>()

View file

@ -41,7 +41,7 @@ data class StorageRoot(
internal val isUsb: Boolean, internal val isUsb: Boolean,
internal val requiresNetwork: Boolean, internal val requiresNetwork: Boolean,
internal val enabled: Boolean = true, internal val enabled: Boolean = true,
internal val overrideClickListener: (() -> Unit)? = null internal val overrideClickListener: (() -> Unit)? = null,
) { ) {
internal val uri: Uri by lazy { internal val uri: Uri by lazy {

View file

@ -20,7 +20,6 @@ import android.widget.TextView
import androidx.activity.result.contract.ActivityResultContracts.OpenDocumentTree import androidx.activity.result.contract.ActivityResultContracts.OpenDocumentTree
import androidx.annotation.RequiresPermission import androidx.annotation.RequiresPermission
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.R
import com.stevesoltys.seedvault.ui.INTENT_EXTRA_IS_RESTORE import com.stevesoltys.seedvault.ui.INTENT_EXTRA_IS_RESTORE
@ -53,8 +52,8 @@ internal class StorageRootsFragment : Fragment(), StorageRootClickedListener {
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?,
): View? { ): View {
val v: View = inflater.inflate(R.layout.fragment_storage_root, container, false) val v: View = inflater.inflate(R.layout.fragment_storage_root, container, false)
titleView = v.findViewById(R.id.titleView) titleView = v.findViewById(R.id.titleView)
@ -92,7 +91,7 @@ internal class StorageRootsFragment : Fragment(), StorageRootClickedListener {
listView.adapter = adapter listView.adapter = adapter
viewModel.storageRoots.observe(viewLifecycleOwner, Observer { roots -> viewModel.storageRoots.observe(viewLifecycleOwner, { roots ->
onRootsLoaded(roots) onRootsLoaded(roots)
}) })
} }

View file

@ -25,7 +25,7 @@ private val TAG = StorageViewModel::class.java.simpleName
internal abstract class StorageViewModel( internal abstract class StorageViewModel(
private val app: Application, private val app: Application,
protected val settingsManager: SettingsManager protected val settingsManager: SettingsManager,
) : AndroidViewModel(app), RemovableStorageListener { ) : AndroidViewModel(app), RemovableStorageListener {
private val mStorageRoots = MutableLiveData<List<StorageRoot>>() private val mStorageRoots = MutableLiveData<List<StorageRoot>>()
@ -48,7 +48,7 @@ internal abstract class StorageViewModel(
companion object { companion object {
internal fun validLocationIsSet( internal fun validLocationIsSet(
context: Context, context: Context,
settingsManager: SettingsManager settingsManager: SettingsManager,
): Boolean { ): Boolean {
val storage = settingsManager.getStorage() ?: return false val storage = settingsManager.getStorage() ?: return false
if (storage.isUsb) return true if (storage.isUsb) return true

View file

@ -5,7 +5,8 @@
<color name="primaryDark">@*android:color/primary_dark_device_default_settings_light</color> <color name="primaryDark">@*android:color/primary_dark_device_default_settings_light</color>
<color name="background">@*android:color/background_device_default_light</color> <color name="background">@*android:color/background_device_default_light</color>
<color name="actionBarPrimary">@*android:color/primary_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> <color name="statusBarColor">@*android:color/primary_dark_material_light_light_status_bar
</color>
<color name="red">@*android:color/error_color_device_default_dark</color> <color name="red">@*android:color/error_color_device_default_dark</color>
<color name="divider">#20ffffff</color> <color name="divider">#20ffffff</color>
<color name="green">#558B2F</color> <color name="green">#558B2F</color>

View file

@ -48,14 +48,6 @@ fun ByteArray.toHexString(spacer: String = " "): String {
fun String.toByteArrayFromHex() = chunked(2).map { it.toInt(16).toByte() }.toByteArray() fun String.toByteArrayFromHex() = chunked(2).map { it.toInt(16).toByte() }.toByteArray()
fun ByteArray.toIntString(): String {
var str = ""
for (b in this) {
str += String.format("%02d ", b)
}
return str
}
fun OutputStream.writeAndClose(data: ByteArray) = use { fun OutputStream.writeAndClose(data: ByteArray) = use {
it.write(data) it.write(data)
} }

View file

@ -51,7 +51,7 @@ internal class MetadataReadWriteTest {
} }
private fun getMetadata( private fun getMetadata(
packageMetadata: HashMap<String, PackageMetadata> = HashMap() packageMetadata: HashMap<String, PackageMetadata> = HashMap(),
): BackupMetadata { ): BackupMetadata {
return BackupMetadata( return BackupMetadata(
version = VERSION, version = VERSION,

View file

@ -169,7 +169,7 @@ class MetadataReaderTest {
} }
private fun getMetadata( private fun getMetadata(
packageMetadata: PackageMetadataMap = PackageMetadataMap() packageMetadata: PackageMetadataMap = PackageMetadataMap(),
): BackupMetadata { ): BackupMetadata {
return BackupMetadata( return BackupMetadata(
version = 1.toByte(), version = 1.toByte(),

View file

@ -50,7 +50,7 @@ internal class MetadataV0ReadTest {
} }
private fun getMetadata( private fun getMetadata(
packageMetadata: HashMap<String, PackageMetadata> = HashMap() packageMetadata: HashMap<String, PackageMetadata> = HashMap(),
) = BackupMetadata( ) = BackupMetadata(
version = 0x00, version = 0x00,
token = 1337L, token = 1337L,

View file

@ -120,7 +120,7 @@ internal class MetadataWriterDecoderTest {
} }
private fun getMetadata( private fun getMetadata(
packageMetadata: HashMap<String, PackageMetadata> = HashMap() packageMetadata: HashMap<String, PackageMetadata> = HashMap(),
): BackupMetadata { ): BackupMetadata {
return BackupMetadata( return BackupMetadata(
version = Random.nextBytes(1)[0], version = Random.nextBytes(1)[0],

View file

@ -46,6 +46,7 @@ internal class ApkBackupRestoreTest : TransportTest() {
private val strictContext: Context = mockk<Context>().apply { private val strictContext: Context = mockk<Context>().apply {
every { packageManager } returns pm every { packageManager } returns pm
} }
@Suppress("Deprecation") @Suppress("Deprecation")
private val legacyStoragePlugin: LegacyStoragePlugin = mockk() private val legacyStoragePlugin: LegacyStoragePlugin = mockk()
private val storagePlugin: StoragePlugin = mockk() private val storagePlugin: StoragePlugin = mockk()

View file

@ -14,10 +14,10 @@ import com.stevesoltys.seedvault.header.VERSION
import com.stevesoltys.seedvault.metadata.BackupType import com.stevesoltys.seedvault.metadata.BackupType
import com.stevesoltys.seedvault.metadata.MetadataReader import com.stevesoltys.seedvault.metadata.MetadataReader
import com.stevesoltys.seedvault.metadata.PackageMetadata import com.stevesoltys.seedvault.metadata.PackageMetadata
import com.stevesoltys.seedvault.plugins.EncryptedMetadata
import com.stevesoltys.seedvault.plugins.StoragePlugin
import com.stevesoltys.seedvault.settings.Storage import com.stevesoltys.seedvault.settings.Storage
import com.stevesoltys.seedvault.transport.TransportTest import com.stevesoltys.seedvault.transport.TransportTest
import com.stevesoltys.seedvault.plugins.StoragePlugin
import com.stevesoltys.seedvault.plugins.EncryptedMetadata
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
import io.mockk.Runs import io.mockk.Runs
import io.mockk.coEvery import io.mockk.coEvery

View file

@ -14,10 +14,10 @@ import com.stevesoltys.seedvault.encodeBase64
import com.stevesoltys.seedvault.header.HeaderReaderImpl import com.stevesoltys.seedvault.header.HeaderReaderImpl
import com.stevesoltys.seedvault.metadata.MetadataReaderImpl import com.stevesoltys.seedvault.metadata.MetadataReaderImpl
import com.stevesoltys.seedvault.plugins.LegacyStoragePlugin import com.stevesoltys.seedvault.plugins.LegacyStoragePlugin
import com.stevesoltys.seedvault.plugins.StoragePlugin
import com.stevesoltys.seedvault.toByteArrayFromHex import com.stevesoltys.seedvault.toByteArrayFromHex
import com.stevesoltys.seedvault.transport.TransportTest import com.stevesoltys.seedvault.transport.TransportTest
import com.stevesoltys.seedvault.transport.backup.KvDbManager import com.stevesoltys.seedvault.transport.backup.KvDbManager
import com.stevesoltys.seedvault.plugins.StoragePlugin
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
import io.mockk.coEvery import io.mockk.coEvery
import io.mockk.every import io.mockk.every

View file

@ -1,5 +1,9 @@
package org.calyxos.backup.contacts; package org.calyxos.backup.contacts;
import static android.Manifest.permission.READ_CONTACTS;
import static android.Manifest.permission.WRITE_CONTACTS;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.app.backup.BackupAgent; import android.app.backup.BackupAgent;
import android.app.backup.BackupDataInput; import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput; import android.app.backup.BackupDataOutput;
@ -16,10 +20,6 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Optional; import java.util.Optional;
import static android.Manifest.permission.READ_CONTACTS;
import static android.Manifest.permission.WRITE_CONTACTS;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
public class ContactsBackupAgent extends BackupAgent implements FullBackupFileHandler { public class ContactsBackupAgent extends BackupAgent implements FullBackupFileHandler {
private final static String TAG = "ContactsBackupAgent"; private final static String TAG = "ContactsBackupAgent";

View file

@ -53,7 +53,7 @@ open class LogFragment : Fragment() {
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?,
): View { ): View {
setHasOptionsMenu(true) setHasOptionsMenu(true)
val v = inflater.inflate(R.layout.fragment_log, container, false) val v = inflater.inflate(R.layout.fragment_log, container, false)

View file

@ -37,7 +37,7 @@ internal class BackupStats(
totalSize: Long, totalSize: Long,
numFiles: Int, numFiles: Int,
numSmallFiles: Int, numSmallFiles: Int,
numLargeFiles: Int numLargeFiles: Int,
) { ) {
super.onBackupStart(totalSize, numFiles, numSmallFiles, numLargeFiles) super.onBackupStart(totalSize, numFiles, numSmallFiles, numLargeFiles)

View file

@ -19,7 +19,7 @@ class DemoSnapshotFragment : SnapshotFragment() {
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?,
): View { ): View {
val v = super.onCreateView(inflater, container, savedInstanceState) val v = super.onCreateView(inflater, container, savedInstanceState)
val bottomStub: ViewStub = v.findViewById(R.id.bottomStub) val bottomStub: ViewStub = v.findViewById(R.id.bottomStub)

View file

@ -33,7 +33,7 @@ class RestoreFragment : Fragment() {
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?,
): View { ): View {
setHasOptionsMenu(true) setHasOptionsMenu(true)
val v = inflater.inflate(R.layout.fragment_log, container, false) val v = inflater.inflate(R.layout.fragment_log, container, false)

View file

@ -29,7 +29,7 @@ private val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
fun scanDocument( fun scanDocument(
context: Context, context: Context,
documentFiles: List<BackupFile>, documentFiles: List<BackupFile>,
sb: StringBuilder? = null sb: StringBuilder? = null,
): ScanResult { ): ScanResult {
var totalSize = 0L var totalSize = 0L
val warnings = StringBuilder() val warnings = StringBuilder()

View file

@ -46,7 +46,7 @@ open class MediaScanFragment : Fragment() {
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?,
): View { ): View {
setHasOptionsMenu(true) setHasOptionsMenu(true)
requireActivity().title = arguments?.getString("name") requireActivity().title = arguments?.getString("name")

View file

@ -14,7 +14,7 @@ import kotlin.time.measureTimedValue
data class ScanResult( data class ScanResult(
var itemsFound: Long, var itemsFound: Long,
var totalSize: Long var totalSize: Long,
) { ) {
operator fun plusAssign(other: ScanResult) { operator fun plusAssign(other: ScanResult) {
itemsFound += other.itemsFound itemsFound += other.itemsFound
@ -57,7 +57,7 @@ fun appendStats(
context: Context, context: Context,
sb: StringBuilder, sb: StringBuilder,
timedResult: TimedValue<ScanResult>, timedResult: TimedValue<ScanResult>,
title: String? = null title: String? = null,
): String { ): String {
val result = timedResult.value val result = timedResult.value
if (title != null || sb.isNotEmpty()) { if (title != null || sb.isNotEmpty()) {

View file

@ -42,7 +42,7 @@ class SettingsFragment : BackupContentFragment() {
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?,
): View { ): View {
setHasOptionsMenu(true) setHasOptionsMenu(true)
requireActivity().title = "Settings" requireActivity().title = "Settings"

View file

@ -6,5 +6,5 @@
android:tint="?attr/colorControlNormal"> android:tint="?attr/colorControlNormal">
<path <path
android:fillColor="@android:color/white" android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/> android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z" />
</vector> </vector>

View file

@ -6,5 +6,5 @@
android:tint="?attr/colorControlNormal"> android:tint="?attr/colorControlNormal">
<path <path
android:fillColor="@android:color/white" android:fillColor="@android:color/white"
android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z"/> android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z" />
</vector> </vector>

View file

@ -6,12 +6,12 @@
android:id="@+id/refresh" android:id="@+id/refresh"
android:icon="@drawable/ic_refresh" android:icon="@drawable/ic_refresh"
android:title="Refresh" android:title="Refresh"
app:showAsAction="always"/> app:showAsAction="always" />
<item <item
android:id="@+id/share" android:id="@+id/share"
android:icon="@drawable/ic_share" android:icon="@drawable/ic_share"
android:title="Share" android:title="Share"
app:showAsAction="always"/> app:showAsAction="always" />
</menu> </menu>

View file

@ -1,4 +1,5 @@
<resources> <resources>
<style name="Theme.StorageBackupTester" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> <style name="Theme.StorageBackupTester" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<item name="colorPrimary">@color/matrix</item> <item name="colorPrimary">@color/matrix</item>
<item name="colorPrimaryVariant">@color/purple_700</item> <item name="colorPrimaryVariant">@color/purple_700</item>

View file

@ -28,7 +28,7 @@ public val mediaUris: List<Uri> = listOf(
public sealed class BackupContentType( public sealed class BackupContentType(
@DrawableRes @DrawableRes
public val drawableRes: Int public val drawableRes: Int,
) { ) {
public object Custom : BackupContentType(R.drawable.ic_folder) { public object Custom : BackupContentType(R.drawable.ic_folder) {
public fun getName(uri: Uri): String { public fun getName(uri: Uri): String {

View file

@ -119,7 +119,7 @@ internal class Backup(
private suspend fun backupFiles( private suspend fun backupFiles(
filesResult: FileScannerResult, filesResult: FileScannerResult,
availableChunkIds: HashSet<String>, availableChunkIds: HashSet<String>,
backupObserver: BackupObserver? backupObserver: BackupObserver?,
) { ) {
val startTime = System.currentTimeMillis() val startTime = System.currentTimeMillis()
val numSmallFiles = filesResult.smallFiles.size val numSmallFiles = filesResult.smallFiles.size

View file

@ -36,7 +36,7 @@ internal class ChunkWriter(
fun writeChunk( fun writeChunk(
inputStream: InputStream, inputStream: InputStream,
chunks: List<Chunk>, chunks: List<Chunk>,
missingChunkIds: List<String> missingChunkIds: List<String>,
): ChunkWriterResult { ): ChunkWriterResult {
var writtenChunks = 0 var writtenChunks = 0
var writtenBytes = 0L var writtenBytes = 0L
@ -77,7 +77,7 @@ internal class ChunkWriter(
private fun copyChunkFromInputStream( private fun copyChunkFromInputStream(
inputStream: InputStream, inputStream: InputStream,
chunk: Chunk, chunk: Chunk,
outputStream: OutputStream outputStream: OutputStream,
) { ) {
var totalBytesRead = 0L var totalBytesRead = 0L
do { do {
@ -101,7 +101,7 @@ internal class ChunkWriter(
fun writeZipChunk( fun writeZipChunk(
chunk: ZipChunk, chunk: ZipChunk,
zip: ByteArrayOutputStream, zip: ByteArrayOutputStream,
missingChunkIds: List<String> missingChunkIds: List<String>,
): Boolean { ): Boolean {
val cachedChunk = chunksCache.get(chunk.id) val cachedChunk = chunksCache.get(chunk.id)
val isMissing = chunk.id in missingChunkIds val isMissing = chunk.id in missingChunkIds

View file

@ -10,7 +10,7 @@ import kotlin.math.min
internal data class Chunk( internal data class Chunk(
val id: String, val id: String,
val offset: Long, val offset: Long,
val size: Long val size: Long,
) { ) {
fun toCachedChunk() = CachedChunk(id, 0, size) fun toCachedChunk() = CachedChunk(id, 0, size)
} }

View file

@ -35,7 +35,7 @@ internal class ChunksCacheRepopulater(
@OptIn(ExperimentalTime::class) @OptIn(ExperimentalTime::class)
private suspend fun repopulateInternal( private suspend fun repopulateInternal(
streamKey: ByteArray, streamKey: ByteArray,
availableChunkIds: HashSet<String> availableChunkIds: HashSet<String>,
) { ) {
val start = System.currentTimeMillis() val start = System.currentTimeMillis()
val snapshots = storagePlugin.getCurrentBackupSnapshots().mapNotNull { storedSnapshot -> val snapshots = storagePlugin.getCurrentBackupSnapshots().mapNotNull { storedSnapshot ->
@ -85,7 +85,7 @@ internal class ChunksCacheRepopulater(
snapshotTimeStamp: Long, snapshotTimeStamp: Long,
availableChunks: HashSet<String>, availableChunks: HashSet<String>,
chunkMap: HashMap<String, CachedChunk>, chunkMap: HashMap<String, CachedChunk>,
chunksInSnapshot: HashSet<String> chunksInSnapshot: HashSet<String>,
) = chunksInSnapshot.forEach { chunkId -> ) = chunksInSnapshot.forEach { chunkId ->
if (!availableChunks.contains(chunkId)) { if (!availableChunks.contains(chunkId)) {
Log.w(TAG, "ChunkId $chunkId referenced in $snapshotTimeStamp, but not in storage.") Log.w(TAG, "ChunkId $chunkId referenced in $snapshotTimeStamp, but not in storage.")

View file

@ -27,7 +27,7 @@ internal class FileBackup(
suspend fun backupFiles( suspend fun backupFiles(
files: List<ContentFile>, files: List<ContentFile>,
availableChunkIds: HashSet<String>, availableChunkIds: HashSet<String>,
backupObserver: BackupObserver? backupObserver: BackupObserver?,
): BackupResult { ): BackupResult {
val chunkIds = HashSet<String>() val chunkIds = HashSet<String>()
val backupMediaFiles = ArrayList<BackupMediaFile>() val backupMediaFiles = ArrayList<BackupMediaFile>()
@ -71,7 +71,7 @@ internal class FileBackup(
@Throws(IOException::class, GeneralSecurityException::class) @Throws(IOException::class, GeneralSecurityException::class)
private fun backupFile( private fun backupFile(
file: ContentFile, file: ContentFile,
availableChunkIds: HashSet<String> availableChunkIds: HashSet<String>,
): FileBackupResult { ): FileBackupResult {
val cachedFile = filesCache.getByUri(file.uri) val cachedFile = filesCache.getByUri(file.uri)
val missingChunkIds = cachedFile?.chunks?.minus(availableChunkIds) ?: emptyList() val missingChunkIds = cachedFile?.chunks?.minus(availableChunkIds) ?: emptyList()

View file

@ -25,7 +25,7 @@ public open class NotificationBackupObserver internal constructor(private val n:
totalSize: Long, totalSize: Long,
numFiles: Int, numFiles: Int,
numSmallFiles: Int, numSmallFiles: Int,
numLargeFiles: Int numLargeFiles: Int,
) { ) {
totalFiles = numFiles totalFiles = numFiles
n.updateBackupNotification( n.updateBackupNotification(
@ -40,7 +40,7 @@ public open class NotificationBackupObserver internal constructor(private val n:
wasUploaded: Boolean, wasUploaded: Boolean,
reusedChunks: Int, reusedChunks: Int,
bytesWritten: Long, bytesWritten: Long,
tag: String tag: String,
) { ) {
filesBackedUp++ filesBackedUp++
n.updateBackupNotification( n.updateBackupNotification(

View file

@ -27,7 +27,7 @@ internal class SmallFileBackup(
suspend fun backupFiles( suspend fun backupFiles(
files: List<ContentFile>, files: List<ContentFile>,
availableChunkIds: HashSet<String>, availableChunkIds: HashSet<String>,
backupObserver: BackupObserver? backupObserver: BackupObserver?,
): BackupResult { ): BackupResult {
val chunkIds = HashSet<String>() val chunkIds = HashSet<String>()
val missingChunkIds = ArrayList<String>() val missingChunkIds = ArrayList<String>()
@ -105,7 +105,7 @@ internal class SmallFileBackup(
@Throws(IOException::class, GeneralSecurityException::class) @Throws(IOException::class, GeneralSecurityException::class)
private fun makeZipChunk( private fun makeZipChunk(
window: List<ContentFile>, window: List<ContentFile>,
missingChunkIds: List<String> missingChunkIds: List<String>,
): SmallFileBackupResult? { ): SmallFileBackupResult? {
val file = window[0] val file = window[0]
val nextFile = window.getOrNull(1) val nextFile = window.getOrNull(1)
@ -124,7 +124,7 @@ internal class SmallFileBackup(
@Throws(IOException::class, GeneralSecurityException::class) @Throws(IOException::class, GeneralSecurityException::class)
private fun finalizeAndReset( private fun finalizeAndReset(
zipChunker: ZipChunker, zipChunker: ZipChunker,
missingChunkIds: List<String> missingChunkIds: List<String>,
): SmallFileBackupResult { ): SmallFileBackupResult {
val zipChunk = zipChunker.finalizeAndReset(missingChunkIds) val zipChunk = zipChunker.finalizeAndReset(missingChunkIds)
val chunkIds = listOf(zipChunk.id) val chunkIds = listOf(zipChunk.id)

View file

@ -21,7 +21,7 @@ internal object ChunkCrypto {
@Throws(GeneralSecurityException::class) @Throws(GeneralSecurityException::class)
fun deriveChunkIdKey( fun deriveChunkIdKey(
masterKey: SecretKey, masterKey: SecretKey,
info: ByteArray = INFO_CHUNK_ID.toByteArray() info: ByteArray = INFO_CHUNK_ID.toByteArray(),
): ByteArray = Hkdf.expand( ): ByteArray = Hkdf.expand(
secretKey = masterKey, secretKey = masterKey,
info = info, info = info,

View file

@ -22,7 +22,7 @@ public object StreamCrypto {
@Throws(GeneralSecurityException::class) @Throws(GeneralSecurityException::class)
public fun deriveStreamKey( public fun deriveStreamKey(
masterKey: SecretKey, masterKey: SecretKey,
info: ByteArray = INFO_STREAM_KEY.toByteArray() info: ByteArray = INFO_STREAM_KEY.toByteArray(),
): ByteArray = Hkdf.expand( ): ByteArray = Hkdf.expand(
secretKey = masterKey, secretKey = masterKey,
info = info, info = info,
@ -51,7 +51,7 @@ public object StreamCrypto {
public fun newEncryptingStream( public fun newEncryptingStream(
secret: ByteArray, secret: ByteArray,
outputStream: OutputStream, outputStream: OutputStream,
associatedData: ByteArray = ByteArray(0) associatedData: ByteArray = ByteArray(0),
): OutputStream { ): OutputStream {
return AesGcmHkdfStreaming( return AesGcmHkdfStreaming(
secret, secret,
@ -66,7 +66,7 @@ public object StreamCrypto {
public fun newDecryptingStream( public fun newDecryptingStream(
secret: ByteArray, secret: ByteArray,
inputStream: InputStream, inputStream: InputStream,
associatedData: ByteArray = ByteArray(0) associatedData: ByteArray = ByteArray(0),
): InputStream { ): InputStream {
return AesGcmHkdfStreaming( return AesGcmHkdfStreaming(
secret, secret,

View file

@ -23,7 +23,7 @@ internal data class CachedFile(
*/ */
@ColumnInfo(name = "zip_index") val zipIndex: Int? = null, @ColumnInfo(name = "zip_index") val zipIndex: Int? = null,
// TODO also purge files from the cache from time to time // TODO also purge files from the cache from time to time
@ColumnInfo(name = "last_seen") val lastSeen: Long @ColumnInfo(name = "last_seen") val lastSeen: Long,
) )
@Dao @Dao

Some files were not shown because too many files have changed in this diff Show more