Code style and lint after Android 12 version bumps
This commit is contained in:
parent
e80d89f0a2
commit
579919d5e7
116 changed files with 254 additions and 252 deletions
|
@ -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">
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
)
|
)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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) =
|
||||||
|
|
|
@ -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
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
)
|
)
|
||||||
|
|
|
@ -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)
|
||||||
})
|
})
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
})
|
})
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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>()
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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],
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.")
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
Loading…
Reference in a new issue