mirror of
https://github.com/ProtonDriveApps/android-drive.git
synced 2026-05-15 09:50:34 +00:00
1.1.0
This commit is contained in:
+14
-15
@@ -1,5 +1,5 @@
|
||||
default:
|
||||
image: ${CI_REGISTRY}/android/shared/docker-android:v1.0.0
|
||||
image: ${CI_REGISTRY}/android/shared/docker-android:v1.0.3
|
||||
|
||||
variables:
|
||||
# Use fastzip to improve cache times
|
||||
@@ -111,25 +111,25 @@ deploy:review:
|
||||
build dev debug:
|
||||
extends: [.build]
|
||||
needs:
|
||||
- job: "detekt analysis"
|
||||
- job: "prepare-environment"
|
||||
- job: "prepare-build"
|
||||
script:
|
||||
- ./gradlew assembleDevDebug
|
||||
artifacts:
|
||||
paths:
|
||||
- ./app/**/*.apk
|
||||
rules:
|
||||
- if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event" || $CI_COMMIT_REF_NAME =~ /^test/
|
||||
when: manual
|
||||
allow_failure: true
|
||||
|
||||
build dynamic debug:
|
||||
extends: [.build]
|
||||
needs:
|
||||
- job: "prepare-environment"
|
||||
- job: "prepare-build"
|
||||
script:
|
||||
- export $(cat deploy.env)
|
||||
- echo HOST="$DYNAMIC_DOMAIN" >> private.properties
|
||||
- ./gradlew assembleDynamicDebug assembleDynamicDebugAndroidTest assembleDebugAndroidTest
|
||||
- ./gradlew assembleDynamicDebug --max-workers=4
|
||||
- ./gradlew assembleDynamicDebugAndroidTest --max-workers=4
|
||||
- ./gradlew assembleDebugAndroidTest --max-workers=4
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event" || $CI_COMMIT_REF_NAME =~ /^test/
|
||||
- if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
|
||||
@@ -253,6 +253,7 @@ upload to firebase:
|
||||
--platform android
|
||||
--job-name $CI_JOB_NAME
|
||||
--slack-channel drive-android-ci-reports
|
||||
--push-metrics
|
||||
rules:
|
||||
# allow failure so non-run tests don't block pipeline
|
||||
- allow_failure: true
|
||||
@@ -380,13 +381,11 @@ publish to firebase app distribution:
|
||||
- if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
|
||||
|
||||
startReview:
|
||||
rules:
|
||||
- if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
|
||||
variables:
|
||||
PRODUCT_FLAVOR: "dev"
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
variables:
|
||||
PRODUCT_FLAVOR: "dynamic"
|
||||
needs:
|
||||
- job: "prepare-build"
|
||||
- job: "build dev debug"
|
||||
variables:
|
||||
PRODUCT_FLAVOR: "dev"
|
||||
before_script:
|
||||
- if [[ -f /load-env.sh ]]; then source /load-env.sh; fi
|
||||
- export REVIEW_APP_ARTIFACT_PATH="app/build/outputs/apk/$PRODUCT_FLAVOR/debug/"${ARCHIVES_BASE_NAME}-${PRODUCT_FLAVOR}-debug.apk
|
||||
|
||||
Generated
+1
-1
@@ -1,3 +1,3 @@
|
||||
<component name="DependencyValidationManager">
|
||||
<scope name="proton-core" pattern="[ProtonDrive.drive*]:*..*||file[ProtonDrive.drive*]:AndroidManifest.xml" />
|
||||
<scope name="proton-core" pattern="[ProtonDrive.drive*]:*..*||file[ProtonDrive.drive*]:AndroidManifest.xml||file[ProtonDrive.drive*]:*.properties" />
|
||||
</component>
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
}
|
||||
|
||||
driveModule(includeSubmodules = true)
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
}
|
||||
|
||||
driveModule(
|
||||
hilt = true,
|
||||
room = true,
|
||||
serialization = true,
|
||||
workManager = true,
|
||||
) {
|
||||
api(project(":app-lock:domain"))
|
||||
api(project(":drive:base"))
|
||||
api(libs.androidx.biometric)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2023 Proton AG.
|
||||
~ This file is part of Proton Drive.
|
||||
~
|
||||
~ Proton Drive is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ Proton Drive is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<manifest package="me.proton.android.drive.lock.data" />
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.data.crypto
|
||||
|
||||
import android.os.Build
|
||||
import android.security.keystore.KeyGenParameterSpec
|
||||
import android.security.keystore.KeyProperties
|
||||
import me.proton.core.drive.base.domain.log.LogTag
|
||||
import me.proton.core.util.kotlin.CoreLogger
|
||||
import java.security.KeyStore
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.KeyGenerator
|
||||
import javax.crypto.SecretKey
|
||||
import javax.crypto.spec.GCMParameterSpec
|
||||
|
||||
object Config {
|
||||
const val KEY_STORE_TYPE = "AndroidKeyStore"
|
||||
const val DEFAULT_CIPHER_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES
|
||||
const val DEFAULT_CIPHER_BLOCK_MODE = KeyProperties.BLOCK_MODE_GCM
|
||||
const val DEFAULT_CIPHER_PADDING = KeyProperties.ENCRYPTION_PADDING_NONE
|
||||
const val DEFAULT_CIPHER_GCM_TAG_LENGTH = 128
|
||||
const val DEFAULT_CIPHER_IV_BYTES = 12
|
||||
const val DEFAULT_KEY_SIZE = 256
|
||||
const val DEFAULT_USER_AUTHENTICATION_REQUIRED = true
|
||||
}
|
||||
|
||||
data class SecretKeyProperties(
|
||||
val keyAlias: String,
|
||||
val keyStoreType: String = Config.KEY_STORE_TYPE,
|
||||
val cipherAlgorithm: String = Config.DEFAULT_CIPHER_ALGORITHM,
|
||||
val cipherBlockMode: String = Config.DEFAULT_CIPHER_BLOCK_MODE,
|
||||
val cipherPadding: String = Config.DEFAULT_CIPHER_PADDING,
|
||||
val cipherKeySize: Int = Config.DEFAULT_KEY_SIZE,
|
||||
val userAuthenticationRequired: Boolean = Config.DEFAULT_USER_AUTHENTICATION_REQUIRED,
|
||||
)
|
||||
|
||||
val SecretKeyProperties.transformation: String get() = "$cipherAlgorithm/$cipherBlockMode/$cipherPadding"
|
||||
|
||||
fun SecretKeyProperties.getOrCreateSecretKey(
|
||||
invalidateKeyByBiometricEnrollment: Boolean = true,
|
||||
): SecretKey {
|
||||
val keyStore = KeyStore.getInstance(keyStoreType)
|
||||
keyStore.load(null)
|
||||
return if (keyStore.containsAlias(keyAlias)) {
|
||||
keyStore.getKey(keyAlias, null) as SecretKey
|
||||
} else {
|
||||
KeyGenerator.getInstance(
|
||||
cipherAlgorithm,
|
||||
keyStoreType,
|
||||
).let { keyGenerator ->
|
||||
keyGenerator.init(
|
||||
KeyGenParameterSpec.Builder(
|
||||
keyAlias,
|
||||
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
|
||||
)
|
||||
.setBlockModes(cipherBlockMode)
|
||||
.setEncryptionPaddings(cipherPadding)
|
||||
.setKeySize(cipherKeySize)
|
||||
.defaultKeyGenParameterSpecBuilder(
|
||||
userAuthenticationRequired = userAuthenticationRequired,
|
||||
invalidateKeyByBiometricEnrollment = invalidateKeyByBiometricEnrollment,
|
||||
)
|
||||
.build()
|
||||
)
|
||||
keyGenerator.generateKey()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun KeyGenParameterSpec.Builder.defaultKeyGenParameterSpecBuilder(
|
||||
userAuthenticationRequired: Boolean,
|
||||
invalidateKeyByBiometricEnrollment: Boolean,
|
||||
): KeyGenParameterSpec.Builder {
|
||||
setUserAuthenticationRequired(userAuthenticationRequired)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
setUserAuthenticationParameters(
|
||||
0,
|
||||
KeyProperties.AUTH_BIOMETRIC_STRONG or KeyProperties.AUTH_DEVICE_CREDENTIAL,
|
||||
)
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
setInvalidatedByBiometricEnrollment(invalidateKeyByBiometricEnrollment)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun SecretKeyProperties.getInitializedCipherForDecryption(
|
||||
initializationVector: ByteArray? = null,
|
||||
invalidateKeyByBiometricEnrollment: Boolean = true,
|
||||
cipherGcmTagLength: Int = Config.DEFAULT_CIPHER_GCM_TAG_LENGTH,
|
||||
): Cipher = getCipher(transformation).apply {
|
||||
init(
|
||||
Cipher.DECRYPT_MODE,
|
||||
getOrCreateSecretKey(
|
||||
invalidateKeyByBiometricEnrollment = invalidateKeyByBiometricEnrollment,
|
||||
),
|
||||
GCMParameterSpec(cipherGcmTagLength, initializationVector),
|
||||
)
|
||||
}
|
||||
|
||||
fun SecretKeyProperties.getInitializedCipherForEncryption(
|
||||
invalidateKeyByBiometricEnrollment: Boolean = true,
|
||||
): Cipher = getCipher(transformation).apply {
|
||||
init(
|
||||
Cipher.ENCRYPT_MODE,
|
||||
getOrCreateSecretKey(
|
||||
invalidateKeyByBiometricEnrollment = invalidateKeyByBiometricEnrollment,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun getCipher(transformation: String): Cipher = Cipher.getInstance(transformation)
|
||||
|
||||
fun removeKey(keyProperties: SecretKeyProperties) {
|
||||
try {
|
||||
KeyStore.getInstance(keyProperties.keyStoreType).run {
|
||||
load(null)
|
||||
deleteEntry(keyProperties.keyAlias)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
CoreLogger.d(LogTag.DEFAULT, e, e.message.orEmpty())
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.data.crypto
|
||||
|
||||
import android.util.Base64
|
||||
import me.proton.android.drive.lock.domain.entity.SecretKey
|
||||
import me.proton.core.crypto.common.keystore.EncryptedByteArray
|
||||
import me.proton.core.crypto.common.keystore.EncryptedString
|
||||
import me.proton.core.crypto.common.keystore.PlainByteArray
|
||||
import me.proton.core.crypto.common.keystore.use
|
||||
|
||||
class KeyStoreSecretKey(
|
||||
private val keyProperties: SecretKeyProperties,
|
||||
private val invalidateKeyByBiometricEnrollment: Boolean = true,
|
||||
) : SecretKey {
|
||||
|
||||
override fun encrypt(value: String): EncryptedString =
|
||||
value.encodeToByteArray().use { plainByteArray ->
|
||||
Base64.encodeToString(
|
||||
encrypt(plainByteArray).array,
|
||||
Base64.NO_WRAP,
|
||||
)
|
||||
}
|
||||
|
||||
override fun encrypt(value: PlainByteArray): EncryptedByteArray {
|
||||
val cipher = keyProperties.getInitializedCipherForEncryption(invalidateKeyByBiometricEnrollment)
|
||||
val cipherByteArray = cipher.doFinal(value.array)
|
||||
return EncryptedByteArray(cipher.iv + cipherByteArray)
|
||||
}
|
||||
|
||||
override fun decrypt(value: EncryptedString): String {
|
||||
val encryptedByteArray = Base64.decode(value, Base64.NO_WRAP)
|
||||
return decrypt(EncryptedByteArray(encryptedByteArray)).use { plainByteArray ->
|
||||
plainByteArray.array.decodeToString()
|
||||
}
|
||||
}
|
||||
|
||||
override fun decrypt(value: EncryptedByteArray): PlainByteArray {
|
||||
val initializationVector = value.array.copyOf(Config.DEFAULT_CIPHER_IV_BYTES)
|
||||
val cipher = keyProperties.getInitializedCipherForDecryption(
|
||||
initializationVector = initializationVector,
|
||||
invalidateKeyByBiometricEnrollment = invalidateKeyByBiometricEnrollment,
|
||||
)
|
||||
val cipherByteArray = value.array.copyOfRange(Config.DEFAULT_CIPHER_IV_BYTES, value.array.size)
|
||||
return PlainByteArray(cipher.doFinal(cipherByteArray))
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.data.crypto
|
||||
|
||||
import me.proton.android.drive.lock.domain.entity.SecretKey
|
||||
import me.proton.core.crypto.common.context.CryptoContext
|
||||
import me.proton.core.crypto.common.keystore.EncryptedByteArray
|
||||
import me.proton.core.crypto.common.keystore.EncryptedString
|
||||
import me.proton.core.crypto.common.keystore.PlainByteArray
|
||||
import me.proton.core.crypto.common.pgp.Armored
|
||||
import me.proton.core.crypto.common.pgp.Unarmored
|
||||
|
||||
class PgpSecretKey(
|
||||
passphrase: PlainByteArray,
|
||||
val lockedKey: Armored,
|
||||
cryptoContext: CryptoContext,
|
||||
) : SecretKey {
|
||||
private val pgpCrypto = cryptoContext.pgpCrypto
|
||||
private val unlockedKey: Unarmored
|
||||
private val publicKey: Armored
|
||||
|
||||
init {
|
||||
passphrase.use {
|
||||
unlockedKey = pgpCrypto.unlock(lockedKey, passphrase.array).value
|
||||
publicKey = pgpCrypto.getPublicKey(lockedKey)
|
||||
}
|
||||
}
|
||||
|
||||
override fun encrypt(value: String): EncryptedString = pgpCrypto.encryptText(value, publicKey)
|
||||
|
||||
override fun decrypt(value: EncryptedString): String = pgpCrypto.decryptText(value, unlockedKey)
|
||||
|
||||
override fun encrypt(value: PlainByteArray): EncryptedByteArray =
|
||||
EncryptedByteArray(pgpCrypto.encryptData(value.array, publicKey).toByteArray())
|
||||
|
||||
override fun decrypt(value: EncryptedByteArray): PlainByteArray =
|
||||
PlainByteArray(pgpCrypto.decryptData(pgpCrypto.getArmored(value.array), unlockedKey))
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.data.db
|
||||
|
||||
import me.proton.android.drive.lock.data.db.dao.AppLockDao
|
||||
import me.proton.android.drive.lock.data.db.dao.AutoLockDurationDao
|
||||
import me.proton.android.drive.lock.data.db.dao.EnableAppLockDao
|
||||
import me.proton.android.drive.lock.data.db.dao.LockDao
|
||||
import me.proton.core.data.room.db.Database
|
||||
|
||||
interface AppLockDatabase : Database {
|
||||
val appLockDao: AppLockDao
|
||||
val lockDao: LockDao
|
||||
val autoLockDurationDao: AutoLockDurationDao
|
||||
val enableAppLockDao: EnableAppLockDao
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.data.db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import me.proton.android.drive.lock.data.db.entity.AppLockEntity
|
||||
import me.proton.core.data.room.db.BaseDao
|
||||
|
||||
@Dao
|
||||
abstract class AppLockDao : BaseDao<AppLockEntity>() {
|
||||
@Query("""
|
||||
SELECT EXISTS(SELECT * FROM AppLockEntity)
|
||||
""")
|
||||
abstract suspend fun hasAppLock(): Boolean
|
||||
|
||||
@Query("""
|
||||
SELECT EXISTS(SELECT * FROM AppLockEntity)
|
||||
""")
|
||||
abstract fun hasAppLockFlow(): Flow<Boolean>
|
||||
|
||||
@Query("""
|
||||
SELECT * FROM AppLockEntity LIMIT 1
|
||||
""")
|
||||
abstract suspend fun getAppLock(): AppLockEntity
|
||||
|
||||
@Query("""
|
||||
DELETE FROM AppLockEntity
|
||||
""")
|
||||
abstract suspend fun deleteAppLock()
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.android.drive.lock.data.db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import me.proton.android.drive.lock.data.db.entity.AutoLockDurationEntity
|
||||
import me.proton.core.data.room.db.BaseDao
|
||||
|
||||
@Dao
|
||||
abstract class AutoLockDurationDao : BaseDao<AutoLockDurationEntity>() {
|
||||
@Query("""
|
||||
SELECT EXISTS(SELECT * FROM AutoLockDurationEntity WHERE `key` = :key)
|
||||
""")
|
||||
abstract suspend fun hasAutoLockDuration(key: String): Boolean
|
||||
|
||||
@Query("""
|
||||
SELECT * FROM AutoLockDurationEntity WHERE `key` = :key
|
||||
""")
|
||||
abstract fun getAutoLockDuration(key: String): Flow<AutoLockDurationEntity?>
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.android.drive.lock.data.db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import me.proton.android.drive.lock.data.db.entity.EnableAppLockEntity
|
||||
import me.proton.core.data.room.db.BaseDao
|
||||
|
||||
@Dao
|
||||
abstract class EnableAppLockDao : BaseDao<EnableAppLockEntity>() {
|
||||
@Query(
|
||||
"""
|
||||
SELECT EXISTS(SELECT * FROM EnableAppLockEntity WHERE `key` = :key)
|
||||
"""
|
||||
)
|
||||
abstract suspend fun hasEnableAppLock(key: String): Boolean
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.data.db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import me.proton.android.drive.lock.data.db.entity.LockEntity
|
||||
import me.proton.android.drive.lock.domain.entity.AppLockType
|
||||
import me.proton.core.data.room.db.BaseDao
|
||||
|
||||
@Dao
|
||||
abstract class LockDao : BaseDao<LockEntity>() {
|
||||
@Query("""
|
||||
SELECT EXISTS(SELECT * FROM LockEntity WHERE type = :type)
|
||||
""")
|
||||
abstract suspend fun hasLock(type: AppLockType): Boolean
|
||||
|
||||
@Query("""
|
||||
SELECT * FROM LockEntity WHERE type = :type LIMIT 1
|
||||
""")
|
||||
abstract suspend fun getLock(type: AppLockType): LockEntity
|
||||
|
||||
@Query("""
|
||||
DELETE FROM LockEntity WHERE type = :type
|
||||
""")
|
||||
abstract suspend fun deleteLock(type: AppLockType)
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.data.db.entity
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import me.proton.android.drive.lock.domain.entity.AppLockType
|
||||
import me.proton.core.drive.base.data.db.Column.KEY
|
||||
import me.proton.core.drive.base.data.db.Column.TYPE
|
||||
|
||||
@Entity(
|
||||
primaryKeys = [KEY],
|
||||
)
|
||||
data class AppLockEntity(
|
||||
@ColumnInfo(name = KEY)
|
||||
val key: String,
|
||||
@ColumnInfo(name = TYPE)
|
||||
val type: AppLockType,
|
||||
)
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.android.drive.lock.data.db.entity
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import me.proton.core.drive.base.data.db.Column.DURATION
|
||||
import me.proton.core.drive.base.data.db.Column.KEY
|
||||
|
||||
@Entity(
|
||||
primaryKeys = [KEY],
|
||||
)
|
||||
data class AutoLockDurationEntity(
|
||||
@ColumnInfo(name = KEY)
|
||||
val key: String,
|
||||
@ColumnInfo(name = DURATION)
|
||||
val durationInSeconds: Long,
|
||||
)
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.android.drive.lock.data.db.entity
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import me.proton.core.drive.base.data.db.Column.KEY
|
||||
import me.proton.core.drive.base.data.db.Column.LAST_ACCESS_TIME
|
||||
|
||||
@Entity(
|
||||
primaryKeys = [KEY],
|
||||
)
|
||||
data class EnableAppLockEntity(
|
||||
@ColumnInfo(name = KEY)
|
||||
val key: String,
|
||||
@ColumnInfo(name = LAST_ACCESS_TIME)
|
||||
val timestamp: Long,
|
||||
)
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.data.db.entity
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.Index
|
||||
import me.proton.android.drive.lock.domain.entity.AppLockType
|
||||
import me.proton.core.drive.base.data.db.Column.KEY
|
||||
import me.proton.core.drive.base.data.db.Column.PASSPHRASE
|
||||
import me.proton.core.drive.base.data.db.Column.TYPE
|
||||
|
||||
@Entity(
|
||||
primaryKeys = [PASSPHRASE],
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = AppLockEntity::class,
|
||||
parentColumns = [KEY],
|
||||
childColumns = [KEY],
|
||||
onDelete = ForeignKey.CASCADE
|
||||
)
|
||||
],
|
||||
indices = [
|
||||
Index(value = [KEY]),
|
||||
],
|
||||
)
|
||||
data class LockEntity(
|
||||
@ColumnInfo(name = PASSPHRASE)
|
||||
val appKeyPassphrase: String,
|
||||
@ColumnInfo(name = KEY)
|
||||
val appKey: String,
|
||||
@ColumnInfo(name = TYPE)
|
||||
val type: AppLockType,
|
||||
)
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.data.di
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import me.proton.android.drive.lock.data.usecase.BuildAppKeyImpl
|
||||
import me.proton.android.drive.lock.data.usecase.GeneratePgpSecretKey
|
||||
import me.proton.android.drive.lock.data.usecase.GetAppLockImpl
|
||||
import me.proton.android.drive.lock.domain.usecase.BuildAppKey
|
||||
import me.proton.android.drive.lock.domain.usecase.GenerateSecretKey
|
||||
import me.proton.android.drive.lock.domain.usecase.GetAppLock
|
||||
import javax.inject.Singleton
|
||||
|
||||
@InstallIn(SingletonComponent::class)
|
||||
@Module
|
||||
interface AppLockBindModule {
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindsGetAppLockImpl(impl: GetAppLockImpl): GetAppLock
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindsGeneratePgpSecretKey(impl: GeneratePgpSecretKey): GenerateSecretKey
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindsBuildAppKeyImpl(impl: BuildAppKeyImpl): BuildAppKey
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Proton Technologies AG
|
||||
* This file is part of Proton Technologies AG and ProtonCore.
|
||||
*
|
||||
* ProtonCore is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ProtonCore is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ProtonCore. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.data.di
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import androidx.biometric.BiometricManager
|
||||
import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_WEAK
|
||||
import androidx.work.WorkManager
|
||||
import dagger.MapKey
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import dagger.multibindings.IntoMap
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import me.proton.android.drive.lock.data.db.AppLockDatabase
|
||||
import me.proton.android.drive.lock.data.lock.CryptoSystemLock
|
||||
import me.proton.android.drive.lock.data.lock.SystemLock
|
||||
import me.proton.android.drive.lock.data.manager.AppLockManagerImpl
|
||||
import me.proton.android.drive.lock.data.manager.AutoLockManagerImpl
|
||||
import me.proton.android.drive.lock.data.provider.BiometricPromptProvider
|
||||
import me.proton.android.drive.lock.data.provider.BiometricPromptProviderImpl
|
||||
import me.proton.android.drive.lock.data.repository.AppLockRepositoryImpl
|
||||
import me.proton.android.drive.lock.domain.entity.AppLockType
|
||||
import me.proton.android.drive.lock.domain.lock.Lock
|
||||
import me.proton.android.drive.lock.domain.manager.AppLockManager
|
||||
import me.proton.android.drive.lock.domain.manager.AutoLockManager
|
||||
import me.proton.android.drive.lock.domain.repository.AppLockRepository
|
||||
import me.proton.android.drive.lock.domain.usecase.GetAutoLockDuration
|
||||
import me.proton.android.drive.lock.domain.usecase.LockApp
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object AppLockModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideBiometricManager(@ApplicationContext context: Context): BiometricManager =
|
||||
BiometricManager.from(context)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideBiometricPromptProvider(biometricManager: BiometricManager): BiometricPromptProvider =
|
||||
BiometricPromptProviderImpl(biometricManager)
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideAppLockKeyRepository(
|
||||
appLockDatabase: AppLockDatabase,
|
||||
): AppLockRepository =
|
||||
AppLockRepositoryImpl(appLockDatabase)
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideAppLockManager(
|
||||
appLockRepository: AppLockRepository,
|
||||
): AppLockManager =
|
||||
AppLockManagerImpl(appLockRepository, Dispatchers.Main + Job())
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideAutoLockManager(
|
||||
workManager: WorkManager,
|
||||
lockApp: LockApp,
|
||||
getAutoLockDuration: GetAutoLockDuration,
|
||||
): AutoLockManager =
|
||||
AutoLockManagerImpl(workManager, lockApp, getAutoLockDuration)
|
||||
|
||||
@MapKey
|
||||
annotation class AppLockTypeKey(val value: AppLockType)
|
||||
|
||||
@Singleton
|
||||
@Provides @IntoMap
|
||||
@AppLockTypeKey(AppLockType.SYSTEM)
|
||||
fun provideSystemLock(
|
||||
@ApplicationContext appContext: Context,
|
||||
appLockRepository: AppLockRepository,
|
||||
biometricPromptProvider: BiometricPromptProvider,
|
||||
biometricManager: BiometricManager,
|
||||
): Lock = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && biometricManager.hasBiometricHardware) {
|
||||
CryptoSystemLock(
|
||||
appContext = appContext,
|
||||
appLockRepository = appLockRepository,
|
||||
biometricPromptProvider = biometricPromptProvider,
|
||||
)
|
||||
} else {
|
||||
SystemLock(
|
||||
appContext = appContext,
|
||||
appLockRepository = appLockRepository,
|
||||
biometricPromptProvider = biometricPromptProvider,
|
||||
)
|
||||
}
|
||||
|
||||
private val BiometricManager.hasBiometricHardware: Boolean get() =
|
||||
canAuthenticate(BIOMETRIC_WEAK) != BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.data.extension
|
||||
|
||||
import me.proton.android.drive.lock.data.db.entity.AppLockEntity
|
||||
import me.proton.android.drive.lock.domain.entity.AppLock
|
||||
|
||||
fun AppLock.toAppLockEntity(): AppLockEntity = AppLockEntity(
|
||||
key = this.key,
|
||||
type = this.type,
|
||||
)
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.data.extension
|
||||
|
||||
import me.proton.android.drive.lock.data.db.entity.AppLockEntity
|
||||
import me.proton.android.drive.lock.domain.entity.AppLock
|
||||
|
||||
fun AppLockEntity.toAppLock() = AppLock(
|
||||
key = this.key,
|
||||
type = this.type,
|
||||
)
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.data.extension
|
||||
|
||||
import android.util.Base64
|
||||
import me.proton.android.drive.lock.data.db.entity.LockEntity
|
||||
import me.proton.android.drive.lock.domain.entity.LockKey
|
||||
|
||||
fun LockEntity.toLock(): LockKey = LockKey(
|
||||
appKeyPassphrase = Base64.decode(this.appKeyPassphrase, Base64.NO_WRAP),
|
||||
appKey = this.appKey,
|
||||
type = this.type,
|
||||
)
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.data.extension
|
||||
|
||||
import android.util.Base64
|
||||
import me.proton.android.drive.lock.data.db.entity.LockEntity
|
||||
import me.proton.android.drive.lock.domain.entity.LockKey
|
||||
|
||||
fun LockKey.toLockEntity(): LockEntity = LockEntity(
|
||||
appKeyPassphrase = Base64.encodeToString(this.appKeyPassphrase, Base64.NO_WRAP),
|
||||
appKey = this.appKey,
|
||||
type = this.type,
|
||||
)
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.android.drive.lock.data.extension
|
||||
|
||||
import me.proton.android.drive.lock.domain.lock.LockState
|
||||
|
||||
|
||||
fun LockState.onNotAvailable(action: () -> Unit): LockState = this.also {
|
||||
if (this is LockState.NotAvailable) action()
|
||||
}
|
||||
|
||||
fun LockState.onSetupRequired(action: () -> Unit): LockState = this.also {
|
||||
if (this is LockState.SetupRequired) action()
|
||||
}
|
||||
|
||||
fun LockState.onReady(action: () -> Unit): LockState = this.also {
|
||||
if (this is LockState.Ready) action()
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.data.lock
|
||||
|
||||
import android.content.Context
|
||||
import androidx.biometric.BiometricPrompt
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import me.proton.android.drive.lock.data.crypto.Config
|
||||
import me.proton.android.drive.lock.data.crypto.SecretKeyProperties
|
||||
import me.proton.android.drive.lock.data.crypto.getInitializedCipherForDecryption
|
||||
import me.proton.android.drive.lock.data.crypto.getInitializedCipherForEncryption
|
||||
import me.proton.android.drive.lock.data.crypto.removeKey
|
||||
import me.proton.android.drive.lock.data.provider.BiometricPromptProvider
|
||||
import me.proton.android.drive.lock.domain.entity.AppLockType
|
||||
import me.proton.android.drive.lock.domain.entity.LockKey
|
||||
import me.proton.android.drive.lock.domain.lock.Lock
|
||||
import me.proton.android.drive.lock.domain.lock.LockState
|
||||
import me.proton.android.drive.lock.domain.repository.AppLockRepository
|
||||
import me.proton.core.drive.base.domain.util.coRunCatching
|
||||
import javax.inject.Inject
|
||||
import me.proton.core.drive.base.presentation.R as BasePresentation
|
||||
|
||||
class CryptoSystemLock @Inject constructor(
|
||||
@ApplicationContext private val appContext: Context,
|
||||
private val appLockRepository: AppLockRepository,
|
||||
private val biometricPromptProvider: BiometricPromptProvider,
|
||||
) : Lock {
|
||||
private val keyProperties = SecretKeyProperties(keyAlias = SYSTEM_KEY_ALIAS)
|
||||
|
||||
override suspend fun <T> unlock(
|
||||
key: String,
|
||||
block: suspend (passphrase: ByteArray) -> T,
|
||||
): Result<T> = coRunCatching {
|
||||
val systemLockKey = appLockRepository.getLockKey(AppLockType.SYSTEM)
|
||||
require(systemLockKey.appKey == key)
|
||||
val initializationVector = systemLockKey.appKeyPassphrase.copyOf(Config.DEFAULT_CIPHER_IV_BYTES)
|
||||
val cipher = biometricPromptProvider.authenticate(
|
||||
title = appContext.getString(BasePresentation.string.app_lock_biometric_title_app_locked),
|
||||
subtitle = appContext.getString(BasePresentation.string.app_lock_biometric_subtitle_app_locked),
|
||||
cryptoObject = BiometricPrompt.CryptoObject(
|
||||
keyProperties.getInitializedCipherForDecryption(
|
||||
initializationVector = initializationVector,
|
||||
)
|
||||
)
|
||||
).getOrThrow().cryptoObject?.cipher
|
||||
block(
|
||||
requireNotNull(cipher).doFinal(
|
||||
systemLockKey.appKeyPassphrase.copyOfRange(
|
||||
Config.DEFAULT_CIPHER_IV_BYTES,
|
||||
systemLockKey.appKeyPassphrase.size,
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun lock(passphrase: ByteArray): Result<ByteArray> = coRunCatching {
|
||||
val cipher = requireNotNull(
|
||||
biometricPromptProvider.authenticate(
|
||||
title = appContext.getString(BasePresentation.string.app_lock_biometric_title_confirmation),
|
||||
subtitle = appContext.getString(
|
||||
BasePresentation.string.app_lock_biometric_subtitle_confirmation_enable
|
||||
),
|
||||
cryptoObject = BiometricPrompt.CryptoObject(
|
||||
keyProperties.getInitializedCipherForEncryption()
|
||||
)
|
||||
).getOrThrow().cryptoObject?.cipher
|
||||
)
|
||||
cipher.iv + cipher.doFinal(passphrase)
|
||||
}
|
||||
|
||||
override fun getLockState(): LockState = biometricPromptProvider.getLockState()
|
||||
|
||||
override suspend fun disable(userAuthenticationRequired: Boolean) {
|
||||
if (userAuthenticationRequired) {
|
||||
biometricPromptProvider.authenticate(
|
||||
title = appContext.getString(BasePresentation.string.app_lock_biometric_title_confirmation),
|
||||
subtitle = appContext.getString(
|
||||
BasePresentation.string.app_lock_biometric_subtitle_confirmation_disable
|
||||
),
|
||||
cryptoObject = null,
|
||||
).getOrThrow()
|
||||
}
|
||||
appLockRepository.deleteLockKey(AppLockType.SYSTEM)
|
||||
removeKey(keyProperties)
|
||||
}
|
||||
|
||||
override suspend fun enable(lockKey: LockKey) {
|
||||
appLockRepository.insertLockKey(lockKey)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val SYSTEM_KEY_ALIAS = "system_lock_key"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.data.lock
|
||||
|
||||
import android.content.Context
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import me.proton.android.drive.lock.data.crypto.KeyStoreSecretKey
|
||||
import me.proton.android.drive.lock.data.crypto.SecretKeyProperties
|
||||
import me.proton.android.drive.lock.data.crypto.removeKey
|
||||
import me.proton.android.drive.lock.data.provider.BiometricPromptProvider
|
||||
import me.proton.android.drive.lock.domain.entity.AppLockType
|
||||
import me.proton.android.drive.lock.domain.entity.LockKey
|
||||
import me.proton.android.drive.lock.domain.entity.SecretKey
|
||||
import me.proton.android.drive.lock.domain.lock.Lock
|
||||
import me.proton.android.drive.lock.domain.lock.LockState
|
||||
import me.proton.android.drive.lock.domain.repository.AppLockRepository
|
||||
import me.proton.core.crypto.common.keystore.EncryptedByteArray
|
||||
import me.proton.core.crypto.common.keystore.PlainByteArray
|
||||
import me.proton.core.drive.base.domain.util.coRunCatching
|
||||
import javax.inject.Inject
|
||||
import me.proton.core.drive.base.presentation.R as BasePresentation
|
||||
|
||||
class SystemLock @Inject constructor(
|
||||
@ApplicationContext private val appContext: Context,
|
||||
private val appLockRepository: AppLockRepository,
|
||||
private val biometricPromptProvider: BiometricPromptProvider,
|
||||
) : Lock {
|
||||
private val keyProperties = SecretKeyProperties(
|
||||
keyAlias = SYSTEM_KEY_ALIAS,
|
||||
userAuthenticationRequired = false,
|
||||
)
|
||||
private val secretKey: SecretKey = KeyStoreSecretKey(keyProperties)
|
||||
|
||||
override suspend fun <T> unlock(
|
||||
key: String,
|
||||
block: suspend (passphrase: ByteArray) -> T,
|
||||
): Result<T> = coRunCatching {
|
||||
val lockKey = appLockRepository.getLockKey(AppLockType.SYSTEM)
|
||||
require(lockKey.appKey == key)
|
||||
biometricPromptProvider.authenticate(
|
||||
title = appContext.getString(BasePresentation.string.app_lock_biometric_title_app_locked),
|
||||
subtitle = appContext.getString(BasePresentation.string.app_lock_biometric_subtitle_app_locked),
|
||||
cryptoObject = null,
|
||||
).getOrThrow()
|
||||
block(secretKey.decrypt(EncryptedByteArray(lockKey.appKeyPassphrase)).array)
|
||||
}
|
||||
|
||||
override suspend fun lock(passphrase: ByteArray): Result<ByteArray> = coRunCatching {
|
||||
biometricPromptProvider.authenticate(
|
||||
title = appContext.getString(BasePresentation.string.app_lock_biometric_title_confirmation),
|
||||
subtitle = appContext.getString(BasePresentation.string.app_lock_biometric_subtitle_confirmation_enable),
|
||||
cryptoObject = null,
|
||||
).getOrThrow()
|
||||
secretKey.encrypt(PlainByteArray(passphrase)).array
|
||||
}
|
||||
|
||||
override suspend fun disable(userAuthenticationRequired: Boolean) {
|
||||
if (userAuthenticationRequired) {
|
||||
biometricPromptProvider.authenticate(
|
||||
title = appContext.getString(BasePresentation.string.app_lock_biometric_title_confirmation),
|
||||
subtitle = appContext.getString(
|
||||
BasePresentation.string.app_lock_biometric_subtitle_confirmation_disable
|
||||
),
|
||||
cryptoObject = null,
|
||||
).getOrThrow()
|
||||
}
|
||||
appLockRepository.deleteLockKey(AppLockType.SYSTEM)
|
||||
removeKey(keyProperties)
|
||||
}
|
||||
|
||||
override suspend fun enable(lockKey: LockKey) {
|
||||
appLockRepository.insertLockKey(lockKey)
|
||||
}
|
||||
|
||||
override fun getLockState(): LockState = biometricPromptProvider.getLockState()
|
||||
|
||||
companion object {
|
||||
private const val SYSTEM_KEY_ALIAS = "system_lock_key"
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.data.manager
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import me.proton.android.drive.lock.domain.entity.AppLock
|
||||
import me.proton.android.drive.lock.domain.entity.SecretKey
|
||||
import me.proton.android.drive.lock.domain.manager.AppLockManager
|
||||
import me.proton.android.drive.lock.domain.repository.AppLockRepository
|
||||
import me.proton.core.drive.base.domain.util.coRunCatching
|
||||
import javax.inject.Inject
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class AppLockManagerImpl @Inject constructor(
|
||||
private val appLockRepository: AppLockRepository,
|
||||
coroutineContext: CoroutineContext,
|
||||
) : AppLockManager {
|
||||
private val coroutineScope = CoroutineScope(coroutineContext)
|
||||
override val enabled: StateFlow<Boolean> = appLockRepository.hasAppLockKeyFlow()
|
||||
.stateIn(coroutineScope, SharingStarted.Eagerly, false)
|
||||
private val appKey = MutableStateFlow<SecretKey?>(null)
|
||||
private val _locked: Flow<Boolean> = appKey.map { secretKey -> secretKey == null }
|
||||
override val locked: StateFlow<Boolean> = combine(enabled, _locked) {
|
||||
_, isLocked ->
|
||||
appLockRepository.hasAppLockKey() && isLocked
|
||||
}.distinctUntilChanged().stateIn(coroutineScope, SharingStarted.Eagerly, false)
|
||||
|
||||
override suspend fun isLocked(): Boolean = isEnabled() && appKey.value == null
|
||||
|
||||
override suspend fun isEnabled(): Boolean = appLockRepository.hasAppLockKey()
|
||||
|
||||
override suspend fun unlock(appKey: SecretKey) {
|
||||
this.appKey.value = appKey
|
||||
}
|
||||
|
||||
override suspend fun lock() {
|
||||
appKey.value = null
|
||||
}
|
||||
|
||||
override suspend fun enable(
|
||||
secretKey: SecretKey,
|
||||
appLock: AppLock,
|
||||
): Result<Boolean> = coRunCatching {
|
||||
unlock(secretKey)
|
||||
appLockRepository.insertAppLockKey(appLock)
|
||||
appLockRepository.insertOrUpdateEnableAppLockTimestamp(System.currentTimeMillis())
|
||||
true
|
||||
}
|
||||
|
||||
override suspend fun disable(): Result<Boolean> = coRunCatching {
|
||||
appLockRepository.deleteAppLockKey()
|
||||
lock()
|
||||
true
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.android.drive.lock.data.manager
|
||||
|
||||
import android.os.Build
|
||||
import androidx.work.WorkManager
|
||||
import kotlinx.coroutines.flow.first
|
||||
import me.proton.android.drive.lock.data.worker.AppLockWorker
|
||||
import me.proton.android.drive.lock.domain.manager.AutoLockManager
|
||||
import me.proton.android.drive.lock.domain.usecase.GetAutoLockDuration
|
||||
import me.proton.android.drive.lock.domain.usecase.LockApp
|
||||
import javax.inject.Inject
|
||||
|
||||
class AutoLockManagerImpl @Inject constructor(
|
||||
private val workManager: WorkManager,
|
||||
private val lockApp: LockApp,
|
||||
private val getAutoLockDuration: GetAutoLockDuration,
|
||||
) : AutoLockManager {
|
||||
|
||||
override suspend fun autoLock() {
|
||||
val lockAfter = getAutoLockDuration().first()
|
||||
if (lockAfter.inWholeSeconds == 0L || Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
lockApp()
|
||||
} else {
|
||||
workManager.enqueue(
|
||||
AppLockWorker.getWorkRequest(lockAfter, listOf(TAG))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun cancelAutoLock() {
|
||||
workManager.cancelAllWorkByTag(TAG)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "auto-lock"
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.data.provider
|
||||
|
||||
import androidx.biometric.BiometricPrompt
|
||||
import androidx.biometric.BiometricPrompt.CryptoObject
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import me.proton.android.drive.lock.domain.lock.LockState
|
||||
|
||||
interface BiometricPromptProvider {
|
||||
fun bindToActivity(activity: FragmentActivity)
|
||||
suspend fun authenticate(
|
||||
title: String,
|
||||
subtitle: String,
|
||||
cryptoObject: CryptoObject?,
|
||||
): Result<BiometricPrompt.AuthenticationResult>
|
||||
fun getLockState(): LockState
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.data.provider
|
||||
|
||||
import android.os.Build
|
||||
import androidx.biometric.BiometricManager
|
||||
import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG
|
||||
import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_WEAK
|
||||
import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
|
||||
import androidx.biometric.BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE
|
||||
import androidx.biometric.BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED
|
||||
import androidx.biometric.BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE
|
||||
import androidx.biometric.BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED
|
||||
import androidx.biometric.BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED
|
||||
import androidx.biometric.BiometricManager.BIOMETRIC_STATUS_UNKNOWN
|
||||
import androidx.biometric.BiometricManager.BIOMETRIC_SUCCESS
|
||||
import androidx.biometric.BiometricPrompt
|
||||
import androidx.biometric.BiometricPrompt.CryptoObject
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import kotlin.coroutines.resume
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import me.proton.android.drive.lock.domain.exception.LockException
|
||||
import me.proton.android.drive.lock.domain.lock.LockState
|
||||
import me.proton.core.drive.base.domain.util.coRunCatching
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.inject.Inject
|
||||
|
||||
class BiometricPromptProviderImpl @Inject constructor(
|
||||
private val biometricManager: BiometricManager,
|
||||
) : BiometricPromptProvider {
|
||||
private val listeners = ConcurrentHashMap<AuthenticationListener, Unit>()
|
||||
private var activity: WeakReference<FragmentActivity> = WeakReference(null)
|
||||
private val callback = object : BiometricPrompt.AuthenticationCallback() {
|
||||
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
|
||||
super.onAuthenticationError(errorCode, errString)
|
||||
listeners.keys().toList().forEach { listener ->
|
||||
listener.onError(LockException.BiometricAuthenticationError(errString.toString()))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAuthenticationFailed() {
|
||||
super.onAuthenticationFailed()
|
||||
listeners.keys().toList().forEach { listener ->
|
||||
listener.onError(LockException.BiometricAuthenticationFailed)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
|
||||
super.onAuthenticationSucceeded(result)
|
||||
listeners.keys().toList().forEach { listener -> listener.onSuccess(result) }
|
||||
}
|
||||
}
|
||||
|
||||
interface AuthenticationListener {
|
||||
fun onSuccess(result: BiometricPrompt.AuthenticationResult)
|
||||
fun onError(error: LockException)
|
||||
}
|
||||
|
||||
override fun bindToActivity(activity: FragmentActivity) {
|
||||
this.activity = WeakReference(activity)
|
||||
}
|
||||
|
||||
private fun buildBiometricPrompt(): BiometricPrompt {
|
||||
val activity = this.activity.get()
|
||||
requireNotNull(activity)
|
||||
val executor = ContextCompat.getMainExecutor(activity)
|
||||
return BiometricPrompt(
|
||||
activity,
|
||||
executor,
|
||||
callback,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun authenticate(
|
||||
title: String,
|
||||
subtitle: String,
|
||||
cryptoObject: CryptoObject?
|
||||
): Result<BiometricPrompt.AuthenticationResult> = coRunCatching {
|
||||
cryptoObject?.let {
|
||||
buildBiometricPrompt().authenticate(biometricPromptInfo(title, subtitle), cryptoObject)
|
||||
} ?: buildBiometricPrompt().authenticate(biometricPromptInfo(title, subtitle))
|
||||
|
||||
// await result
|
||||
suspendCancellableCoroutine<Result<BiometricPrompt.AuthenticationResult>> { continuation ->
|
||||
val listener = object : AuthenticationListener {
|
||||
override fun onSuccess(result: BiometricPrompt.AuthenticationResult) {
|
||||
listeners.remove(this)
|
||||
continuation.resume(Result.success(result))
|
||||
}
|
||||
|
||||
override fun onError(error: LockException) {
|
||||
listeners.remove(this)
|
||||
continuation.resume(Result.failure(error))
|
||||
}
|
||||
|
||||
}
|
||||
listeners[listener] = Unit
|
||||
continuation.invokeOnCancellation {
|
||||
listeners.remove(listener)
|
||||
}
|
||||
}.getOrThrow()
|
||||
}
|
||||
|
||||
override fun getLockState(): LockState =
|
||||
when (val result = biometricManager.canAuthenticate(allowedAuthenticators)) {
|
||||
BIOMETRIC_SUCCESS -> LockState.Ready
|
||||
BIOMETRIC_STATUS_UNKNOWN -> LockState.Ready
|
||||
BIOMETRIC_ERROR_UNSUPPORTED -> LockState.NotAvailable
|
||||
BIOMETRIC_ERROR_HW_UNAVAILABLE -> LockState.Ready
|
||||
BIOMETRIC_ERROR_NONE_ENROLLED -> LockState.SetupRequired
|
||||
BIOMETRIC_ERROR_NO_HARDWARE -> LockState.NotAvailable
|
||||
BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED -> LockState.NotAvailable
|
||||
else -> error("Unhandled BiometricManager.canAuthenticate result $result")
|
||||
}
|
||||
|
||||
private fun biometricPromptInfo(title: String, subtitle: String): BiometricPrompt.PromptInfo {
|
||||
return BiometricPrompt.PromptInfo.Builder()
|
||||
.setTitle(title)
|
||||
.setSubtitle(subtitle)
|
||||
.setAllowedAuthenticators(allowedAuthenticators)
|
||||
.build()
|
||||
}
|
||||
|
||||
private val allowedAuthenticators: Int get() = when (Build.VERSION.SDK_INT) {
|
||||
Build.VERSION_CODES.P, Build.VERSION_CODES.Q -> BIOMETRIC_WEAK or DEVICE_CREDENTIAL
|
||||
else -> BIOMETRIC_STRONG or DEVICE_CREDENTIAL
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.data.repository
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
import me.proton.android.drive.lock.data.db.AppLockDatabase
|
||||
import me.proton.android.drive.lock.data.db.entity.AutoLockDurationEntity
|
||||
import me.proton.android.drive.lock.data.db.entity.EnableAppLockEntity
|
||||
import me.proton.android.drive.lock.data.extension.toAppLock
|
||||
import me.proton.android.drive.lock.data.extension.toAppLockEntity
|
||||
import me.proton.android.drive.lock.data.extension.toLock
|
||||
import me.proton.android.drive.lock.data.extension.toLockEntity
|
||||
import me.proton.android.drive.lock.domain.entity.AppLock
|
||||
import me.proton.android.drive.lock.domain.entity.AppLockType
|
||||
import me.proton.android.drive.lock.domain.entity.LockKey
|
||||
import me.proton.android.drive.lock.domain.repository.AppLockRepository
|
||||
import javax.inject.Inject
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
class AppLockRepositoryImpl @Inject constructor(
|
||||
private val db: AppLockDatabase,
|
||||
) : AppLockRepository {
|
||||
|
||||
override fun hasAppLockKeyFlow(): Flow<Boolean> = db.appLockDao.hasAppLockFlow()
|
||||
override suspend fun hasAppLockKey(): Boolean = db.appLockDao.hasAppLock()
|
||||
override suspend fun getAppLockKey(): AppLock = db.appLockDao.getAppLock().toAppLock()
|
||||
override suspend fun insertAppLockKey(appLock: AppLock) = db.appLockDao.insertOrUpdate(appLock.toAppLockEntity())
|
||||
override suspend fun deleteAppLockKey() = db.appLockDao.deleteAppLock()
|
||||
|
||||
override suspend fun hasLockKey(lockType: AppLockType): Boolean = db.lockDao.hasLock(lockType)
|
||||
override suspend fun getLockKey(lockType: AppLockType): LockKey = db.lockDao.getLock(lockType).toLock()
|
||||
override suspend fun insertLockKey(lockKey: LockKey) = db.lockDao.insertOrUpdate(lockKey.toLockEntity())
|
||||
override suspend fun deleteLockKey(lockType: AppLockType) = db.lockDao.deleteLock(lockType)
|
||||
|
||||
override suspend fun hasAutoLockDuration(): Boolean = db.autoLockDurationDao.hasAutoLockDuration(
|
||||
AUTO_LOCK_DURATION_KEY
|
||||
)
|
||||
|
||||
override fun getAutoLockDuration(): Flow<Duration> = db.autoLockDurationDao.getAutoLockDuration(
|
||||
AUTO_LOCK_DURATION_KEY
|
||||
).filterNotNull().map { autoLockDurationEntity ->
|
||||
autoLockDurationEntity.durationInSeconds.seconds
|
||||
}
|
||||
|
||||
override suspend fun insertOrUpdateAutoLockDuration(duration: Duration) = db.autoLockDurationDao.insertOrUpdate(
|
||||
AutoLockDurationEntity(
|
||||
key = AUTO_LOCK_DURATION_KEY,
|
||||
durationInSeconds = duration.inWholeSeconds,
|
||||
)
|
||||
)
|
||||
|
||||
override suspend fun hasEnableAppLockTimestamp(): Boolean = db.enableAppLockDao.hasEnableAppLock(ENABLE_LOCK_KEY)
|
||||
|
||||
override suspend fun insertOrUpdateEnableAppLockTimestamp(timestamp: Long) = db.enableAppLockDao.insertOrUpdate(
|
||||
EnableAppLockEntity(
|
||||
key = ENABLE_LOCK_KEY,
|
||||
timestamp = timestamp,
|
||||
)
|
||||
)
|
||||
|
||||
companion object {
|
||||
private const val AUTO_LOCK_DURATION_KEY = "auto-lock"
|
||||
private const val ENABLE_LOCK_KEY = "enable-lock"
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.data.usecase
|
||||
|
||||
import me.proton.android.drive.lock.data.crypto.PgpSecretKey
|
||||
import me.proton.android.drive.lock.domain.entity.SecretKey
|
||||
import me.proton.android.drive.lock.domain.lock.Lock
|
||||
import me.proton.android.drive.lock.domain.usecase.BuildAppKey
|
||||
import me.proton.core.crypto.common.context.CryptoContext
|
||||
import me.proton.core.crypto.common.keystore.PlainByteArray
|
||||
import me.proton.core.drive.base.domain.util.coRunCatching
|
||||
import javax.inject.Inject
|
||||
|
||||
class BuildAppKeyImpl @Inject constructor(
|
||||
private val cryptoContext: CryptoContext,
|
||||
) : BuildAppKey {
|
||||
override suspend operator fun invoke(key: String, lock: Lock): Result<SecretKey> = coRunCatching {
|
||||
lock.unlock(key) { passphrase ->
|
||||
PgpSecretKey(
|
||||
passphrase = PlainByteArray(passphrase),
|
||||
lockedKey = key,
|
||||
cryptoContext = cryptoContext,
|
||||
)
|
||||
}.getOrThrow()
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.data.usecase
|
||||
|
||||
import me.proton.android.drive.lock.data.crypto.PgpSecretKey
|
||||
import me.proton.android.drive.lock.domain.entity.SecretKey
|
||||
import me.proton.android.drive.lock.domain.usecase.GenerateSecretKey
|
||||
import me.proton.core.crypto.common.context.CryptoContext
|
||||
import me.proton.core.crypto.common.keystore.PlainByteArray
|
||||
import me.proton.core.drive.base.domain.util.coRunCatching
|
||||
import javax.inject.Inject
|
||||
|
||||
class GeneratePgpSecretKey @Inject constructor(
|
||||
private val cryptoContext: CryptoContext,
|
||||
) : GenerateSecretKey {
|
||||
|
||||
override suspend fun invoke(
|
||||
passphrase: ByteArray,
|
||||
): Result<SecretKey> = coRunCatching {
|
||||
cryptoContext.pgpCrypto.generateRandomBytes()
|
||||
PgpSecretKey(
|
||||
passphrase = PlainByteArray(passphrase.clone()),
|
||||
lockedKey = cryptoContext.pgpCrypto.generateNewPrivateKey(DEFAULT_USERNAME, DEFAULT_DOMAIN, passphrase),
|
||||
cryptoContext = cryptoContext,
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val DEFAULT_USERNAME = "drive-app-lock-key"
|
||||
private const val DEFAULT_DOMAIN = "proton.me"
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.data.usecase
|
||||
|
||||
import me.proton.android.drive.lock.data.crypto.PgpSecretKey
|
||||
import me.proton.android.drive.lock.domain.entity.AppLock
|
||||
import me.proton.android.drive.lock.domain.entity.AppLockType
|
||||
import me.proton.android.drive.lock.domain.entity.SecretKey
|
||||
import me.proton.android.drive.lock.domain.usecase.GetAppLock
|
||||
import me.proton.core.drive.base.domain.util.coRunCatching
|
||||
import javax.inject.Inject
|
||||
|
||||
class GetAppLockImpl @Inject constructor() : GetAppLock {
|
||||
override operator fun invoke(secretKey: SecretKey, appLockType: AppLockType): Result<AppLock> = coRunCatching {
|
||||
require(secretKey is PgpSecretKey)
|
||||
AppLock(
|
||||
key = secretKey.lockedKey,
|
||||
type = appLockType,
|
||||
)
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.android.drive.lock.data.worker
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import androidx.hilt.work.HiltWorker
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.OneTimeWorkRequest
|
||||
import androidx.work.WorkerParameters
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import me.proton.android.drive.lock.domain.usecase.LockApp
|
||||
import me.proton.core.drive.base.data.workmanager.addTags
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.time.Duration
|
||||
|
||||
@HiltWorker
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
class AppLockWorker @AssistedInject constructor(
|
||||
@Assisted appContext: Context,
|
||||
@Assisted workerParams: WorkerParameters,
|
||||
private val lockApp: LockApp,
|
||||
) : CoroutineWorker(appContext, workerParams) {
|
||||
|
||||
override suspend fun doWork(): Result {
|
||||
lockApp()
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getWorkRequest(
|
||||
runAfter: Duration,
|
||||
tags: List<String> = emptyList(),
|
||||
): OneTimeWorkRequest =
|
||||
OneTimeWorkRequest.Builder(AppLockWorker::class.java)
|
||||
.setInitialDelay(runAfter.inWholeSeconds, TimeUnit.SECONDS)
|
||||
.addTags(tags)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
}
|
||||
|
||||
driveModule(
|
||||
hilt = true,
|
||||
serialization = true,
|
||||
) {
|
||||
api(project(":drive:link:domain"))
|
||||
api(project(":drive:crypto-base:domain"))
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2023 Proton AG.
|
||||
~ This file is part of Proton Drive.
|
||||
~
|
||||
~ Proton Drive is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ Proton Drive is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<manifest package="me.proton.android.drive.lock.domain" />
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.domain.entity
|
||||
|
||||
data class AppLock(
|
||||
val key: String,
|
||||
val type: AppLockType,
|
||||
)
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.domain.entity
|
||||
|
||||
enum class AppLockType {
|
||||
SYSTEM,
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.domain.entity
|
||||
|
||||
data class LockKey(
|
||||
val appKeyPassphrase: ByteArray,
|
||||
val appKey: String,
|
||||
val type: AppLockType,
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as LockKey
|
||||
|
||||
if (!appKeyPassphrase.contentEquals(other.appKeyPassphrase)) return false
|
||||
if (appKey != other.appKey) return false
|
||||
if (type != other.type) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = appKeyPassphrase.contentHashCode()
|
||||
result = 31 * result + appKey.hashCode()
|
||||
result = 31 * result + type.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.domain.entity
|
||||
|
||||
import me.proton.core.crypto.common.keystore.EncryptedByteArray
|
||||
import me.proton.core.crypto.common.keystore.EncryptedString
|
||||
import me.proton.core.crypto.common.keystore.PlainByteArray
|
||||
|
||||
interface SecretKey {
|
||||
/**
|
||||
* Encrypt a [String] [value] and return an [EncryptedString].
|
||||
*/
|
||||
fun encrypt(value: String): EncryptedString
|
||||
|
||||
/**
|
||||
* Decrypt an [EncryptedString] [value] and return a [String].
|
||||
*/
|
||||
fun decrypt(value: EncryptedString): String
|
||||
|
||||
/**
|
||||
* Encrypt a [PlainByteArray] [value] and return an [EncryptedByteArray].
|
||||
*/
|
||||
fun encrypt(value: PlainByteArray): EncryptedByteArray
|
||||
|
||||
/**
|
||||
* Decrypt an [EncryptedByteArray] [value] and return a [PlainByteArray].
|
||||
*/
|
||||
fun decrypt(value: EncryptedByteArray): PlainByteArray
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.android.drive.lock.domain.exception
|
||||
|
||||
import me.proton.core.drive.base.domain.exception.DriveException
|
||||
|
||||
sealed class LockException : DriveException() {
|
||||
object BiometricAuthenticationFailed : LockException()
|
||||
data class BiometricAuthenticationError(val errorMessage: String) : LockException()
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.android.drive.lock.domain.lock
|
||||
|
||||
import me.proton.android.drive.lock.domain.entity.LockKey
|
||||
|
||||
interface Lock {
|
||||
/**
|
||||
* Unlocks passphrase for a given [key] and provides it to the [block].
|
||||
* After [block] is done, passphrase should not be available anymore.
|
||||
*/
|
||||
suspend fun<T> unlock(key: String, block: suspend (passphrase: ByteArray) -> T): Result<T>
|
||||
|
||||
/**
|
||||
* Locks [passphrase] so that it's safe to store.
|
||||
*/
|
||||
suspend fun lock(passphrase: ByteArray): Result<ByteArray>
|
||||
|
||||
/**
|
||||
* Called when [Lock] should not be used anymore. If [userAuthenticationRequired] is true then user authentication
|
||||
* is required before disabling can be done.
|
||||
*/
|
||||
suspend fun disable(userAuthenticationRequired: Boolean)
|
||||
|
||||
/**
|
||||
* Called when [Lock] should protect [lockKey]
|
||||
*/
|
||||
suspend fun enable(lockKey: LockKey)
|
||||
|
||||
/**
|
||||
* Provides current [Lock] state. See [LockState].
|
||||
*/
|
||||
fun getLockState(): LockState
|
||||
}
|
||||
|
||||
sealed interface LockState {
|
||||
object NotAvailable : LockState
|
||||
object SetupRequired : LockState
|
||||
object Ready : LockState
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.domain.manager
|
||||
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import me.proton.android.drive.lock.domain.entity.AppLock
|
||||
import me.proton.android.drive.lock.domain.entity.SecretKey
|
||||
|
||||
interface AppLockManager {
|
||||
val locked: StateFlow<Boolean>
|
||||
val enabled: StateFlow<Boolean>
|
||||
suspend fun isLocked(): Boolean
|
||||
suspend fun isEnabled(): Boolean
|
||||
suspend fun unlock(appKey: SecretKey)
|
||||
suspend fun lock()
|
||||
suspend fun enable(secretKey: SecretKey, appLock: AppLock): Result<Boolean>
|
||||
suspend fun disable(): Result<Boolean>
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.android.drive.lock.domain.manager
|
||||
|
||||
interface AutoLockManager {
|
||||
suspend fun autoLock()
|
||||
fun cancelAutoLock()
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.domain.repository
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import me.proton.android.drive.lock.domain.entity.AppLock
|
||||
import me.proton.android.drive.lock.domain.entity.AppLockType
|
||||
import me.proton.android.drive.lock.domain.entity.LockKey
|
||||
import kotlin.time.Duration
|
||||
|
||||
interface AppLockRepository {
|
||||
fun hasAppLockKeyFlow(): Flow<Boolean>
|
||||
suspend fun hasAppLockKey(): Boolean
|
||||
suspend fun getAppLockKey(): AppLock
|
||||
suspend fun insertAppLockKey(appLock: AppLock)
|
||||
suspend fun deleteAppLockKey()
|
||||
|
||||
suspend fun hasLockKey(lockType: AppLockType): Boolean
|
||||
suspend fun getLockKey(lockType: AppLockType): LockKey
|
||||
suspend fun insertLockKey(lockKey: LockKey)
|
||||
suspend fun deleteLockKey(lockType: AppLockType)
|
||||
|
||||
suspend fun hasAutoLockDuration(): Boolean
|
||||
fun getAutoLockDuration(): Flow<Duration>
|
||||
suspend fun insertOrUpdateAutoLockDuration(duration: Duration)
|
||||
|
||||
suspend fun hasEnableAppLockTimestamp(): Boolean
|
||||
suspend fun insertOrUpdateEnableAppLockTimestamp(timestamp: Long)
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.domain.usecase
|
||||
|
||||
import me.proton.android.drive.lock.domain.entity.SecretKey
|
||||
import me.proton.android.drive.lock.domain.lock.Lock
|
||||
|
||||
interface BuildAppKey {
|
||||
suspend operator fun invoke(key: String, lock: Lock): Result<SecretKey>
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.domain.usecase
|
||||
|
||||
import me.proton.android.drive.lock.domain.entity.AppLockType
|
||||
import me.proton.android.drive.lock.domain.lock.Lock
|
||||
import me.proton.android.drive.lock.domain.manager.AppLockManager
|
||||
import me.proton.core.drive.base.domain.util.coRunCatching
|
||||
import me.proton.core.util.kotlin.CoreLogger
|
||||
import javax.inject.Inject
|
||||
|
||||
class DisableAppLock @Inject constructor(
|
||||
private val appLockManager: AppLockManager,
|
||||
private val locks: @JvmSuppressWildcards Map<AppLockType, Lock>,
|
||||
) {
|
||||
suspend operator fun invoke(
|
||||
lockType: AppLockType = AppLockType.SYSTEM,
|
||||
userAuthenticationRequired: Boolean = true,
|
||||
): Result<Unit> = coRunCatching {
|
||||
if (appLockManager.isEnabled()) {
|
||||
requireNotNull(locks[lockType]).disable(userAuthenticationRequired)
|
||||
appLockManager.disable().getOrThrow()
|
||||
}
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.domain.usecase
|
||||
|
||||
import me.proton.android.drive.lock.domain.entity.AppLockType
|
||||
import me.proton.android.drive.lock.domain.entity.LockKey
|
||||
import me.proton.android.drive.lock.domain.lock.Lock
|
||||
import me.proton.android.drive.lock.domain.manager.AppLockManager
|
||||
import me.proton.core.drive.base.domain.util.coRunCatching
|
||||
import me.proton.core.drive.cryptobase.domain.usecase.GeneratePassphrase
|
||||
import javax.inject.Inject
|
||||
|
||||
class EnableAppLock @Inject constructor(
|
||||
private val generatePassphrase: GeneratePassphrase,
|
||||
private val generateSecretKey: GenerateSecretKey,
|
||||
private val getAppLock: GetAppLock,
|
||||
private val appLockManager: AppLockManager,
|
||||
private val locks: @JvmSuppressWildcards Map<AppLockType, Lock>,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(lockType: AppLockType = AppLockType.SYSTEM) = coRunCatching {
|
||||
val passphrase = generatePassphrase()
|
||||
val secretKey = generateSecretKey(passphrase).getOrThrow()
|
||||
val appLock = getAppLock(secretKey, lockType).getOrThrow()
|
||||
val lock = requireNotNull(locks[lockType])
|
||||
val lockKey = LockKey(
|
||||
appKeyPassphrase = lock.lock(passphrase).getOrThrow(),
|
||||
appKey = appLock.key,
|
||||
type = appLock.type,
|
||||
)
|
||||
appLockManager.enable(secretKey, appLock).getOrThrow()
|
||||
lock.enable(lockKey)
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.domain.usecase
|
||||
|
||||
import me.proton.android.drive.lock.domain.entity.SecretKey
|
||||
|
||||
interface GenerateSecretKey {
|
||||
suspend operator fun invoke(passphrase: ByteArray): Result<SecretKey>
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.domain.usecase
|
||||
|
||||
import me.proton.android.drive.lock.domain.entity.AppLock
|
||||
import me.proton.android.drive.lock.domain.entity.AppLockType
|
||||
import me.proton.android.drive.lock.domain.entity.SecretKey
|
||||
|
||||
interface GetAppLock {
|
||||
operator fun invoke(secretKey: SecretKey, appLockType: AppLockType): Result<AppLock>
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.android.drive.lock.domain.usecase
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import me.proton.android.drive.lock.domain.repository.AppLockRepository
|
||||
import me.proton.core.drive.base.domain.provider.ConfigurationProvider
|
||||
import javax.inject.Inject
|
||||
import kotlin.time.Duration
|
||||
|
||||
class GetAutoLockDuration @Inject constructor(
|
||||
private val appLockRepository: AppLockRepository,
|
||||
private val configurationProvider: ConfigurationProvider,
|
||||
) {
|
||||
operator fun invoke(): Flow<Duration> = flow {
|
||||
if (appLockRepository.hasAutoLockDuration().not()) {
|
||||
emit(configurationProvider.autoLockDurations.first())
|
||||
}
|
||||
emitAll(appLockRepository.getAutoLockDuration())
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.android.drive.lock.domain.usecase
|
||||
|
||||
import me.proton.android.drive.lock.domain.entity.AppLockType
|
||||
import me.proton.android.drive.lock.domain.lock.Lock
|
||||
import me.proton.android.drive.lock.domain.lock.LockState
|
||||
import javax.inject.Inject
|
||||
|
||||
class GetLockState @Inject constructor(
|
||||
private val locks: @JvmSuppressWildcards Map<AppLockType, Lock>,
|
||||
) {
|
||||
operator fun invoke(appLockType: AppLockType = AppLockType.SYSTEM): LockState =
|
||||
requireNotNull(locks[appLockType]).getLockState()
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.android.drive.lock.domain.usecase
|
||||
|
||||
import me.proton.android.drive.lock.domain.repository.AppLockRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
class HasEnableAppLockTimestamp @Inject constructor(
|
||||
private val appLockRepository: AppLockRepository,
|
||||
) {
|
||||
suspend operator fun invoke(): Boolean =
|
||||
appLockRepository.hasEnableAppLockTimestamp()
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.domain.usecase
|
||||
|
||||
import me.proton.android.drive.lock.domain.manager.AppLockManager
|
||||
import javax.inject.Inject
|
||||
|
||||
class LockApp @Inject constructor(
|
||||
private val appLockManager: AppLockManager,
|
||||
) {
|
||||
suspend operator fun invoke() {
|
||||
if (appLockManager.isEnabled() && appLockManager.isLocked().not()) {
|
||||
appLockManager.lock()
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.domain.usecase
|
||||
|
||||
import me.proton.android.drive.lock.domain.entity.AppLockType
|
||||
import me.proton.android.drive.lock.domain.lock.Lock
|
||||
import me.proton.android.drive.lock.domain.manager.AppLockManager
|
||||
import me.proton.android.drive.lock.domain.repository.AppLockRepository
|
||||
import me.proton.core.drive.base.domain.util.coRunCatching
|
||||
import javax.inject.Inject
|
||||
|
||||
class UnlockApp @Inject constructor(
|
||||
private val appLockManager: AppLockManager,
|
||||
private val appLockRepository: AppLockRepository,
|
||||
private val locks: @JvmSuppressWildcards Map<AppLockType, Lock>,
|
||||
private val buildAppKey: BuildAppKey,
|
||||
) {
|
||||
suspend operator fun invoke(): Result<Unit> = coRunCatching {
|
||||
if (appLockManager.isEnabled() && appLockManager.isLocked()) {
|
||||
val appLockKey = appLockRepository.getAppLockKey()
|
||||
appLockManager.unlock(
|
||||
buildAppKey(appLockKey.key, requireNotNull(locks[appLockKey.type])).getOrThrow()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.android.drive.lock.domain.usecase
|
||||
|
||||
import me.proton.android.drive.lock.domain.repository.AppLockRepository
|
||||
import me.proton.core.drive.base.domain.provider.ConfigurationProvider
|
||||
import me.proton.core.drive.base.domain.util.coRunCatching
|
||||
import javax.inject.Inject
|
||||
import kotlin.time.Duration
|
||||
|
||||
class UpdateAutoLockDuration @Inject constructor(
|
||||
private val appLockRepository: AppLockRepository,
|
||||
private val configurationProvider: ConfigurationProvider,
|
||||
) {
|
||||
suspend operator fun invoke(duration: Duration): Result<Unit> = coRunCatching {
|
||||
require(configurationProvider.autoLockDurations.contains(duration)) {
|
||||
"Only values from ConfigurationProvider#autoLockDurations are allowed"
|
||||
}
|
||||
appLockRepository.insertOrUpdateAutoLockDuration(duration)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
}
|
||||
|
||||
driveModule(
|
||||
hilt = true,
|
||||
compose = true,
|
||||
) {
|
||||
api(project(":app-lock:domain"))
|
||||
implementation(project(":drive:base:presentation"))
|
||||
implementation(libs.accompanist.drawablepainter)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2023 Proton AG.
|
||||
~ This file is part of Proton Drive.
|
||||
~
|
||||
~ Proton Drive is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ Proton Drive is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<manifest package="me.proton.android.drive.lock.presentation" />
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.android.drive.lock.presentation.component
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import me.proton.core.account.domain.entity.Account
|
||||
import me.proton.core.domain.entity.UserId
|
||||
|
||||
@Composable
|
||||
fun AppLock(
|
||||
locked: Flow<Boolean>,
|
||||
primaryAccount: Flow<Account?>,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
var isLocked by remember { mutableStateOf(false) }
|
||||
var userId by remember { mutableStateOf<UserId?>(null) }
|
||||
LaunchedEffect(Unit) {
|
||||
locked
|
||||
.onEach { locked ->
|
||||
isLocked = locked
|
||||
}
|
||||
.launchIn(this)
|
||||
primaryAccount
|
||||
.onEach { account ->
|
||||
userId = account?.userId
|
||||
}
|
||||
.launchIn(this)
|
||||
}
|
||||
Crossfade(targetState = isLocked) { appLocked ->
|
||||
if (appLocked) {
|
||||
Unlock(
|
||||
userId = userId,
|
||||
modifier = Modifier,
|
||||
)
|
||||
} else {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
+251
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.presentation.component
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.google.accompanist.drawablepainter.rememberDrawablePainter
|
||||
import me.proton.android.drive.lock.presentation.R
|
||||
import me.proton.android.drive.lock.presentation.viewevent.UnlockViewEvent
|
||||
import me.proton.android.drive.lock.presentation.viewmodel.UnlockViewModel
|
||||
import me.proton.core.compose.component.ProtonSolidButton
|
||||
import me.proton.core.compose.component.ProtonTextButton
|
||||
import me.proton.core.compose.theme.ProtonDimens.LargeSpacing
|
||||
import me.proton.core.compose.theme.ProtonDimens.ListItemHeight
|
||||
import me.proton.core.compose.theme.ProtonDimens.SmallSpacing
|
||||
import me.proton.core.compose.theme.ProtonTheme
|
||||
import me.proton.core.domain.entity.UserId
|
||||
import me.proton.core.drive.base.presentation.extension.conditional
|
||||
import me.proton.core.drive.base.presentation.extension.isLandscape
|
||||
import me.proton.core.drive.base.presentation.extension.isPortrait
|
||||
import me.proton.core.drive.base.presentation.extension.shadow
|
||||
import me.proton.core.drive.base.presentation.R as BasePresentation
|
||||
import me.proton.core.presentation.R as CorePresentation
|
||||
|
||||
@Composable
|
||||
fun Unlock(
|
||||
userId: UserId?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val viewModel = hiltViewModel<UnlockViewModel>()
|
||||
Unlock(
|
||||
userId = userId,
|
||||
viewEvent = viewModel.viewEvent,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Unlock(
|
||||
userId: UserId?,
|
||||
viewEvent: UnlockViewEvent,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
LaunchedEffect(Unit) {
|
||||
viewEvent.onShowBiometric()
|
||||
}
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.conditional(isPortrait) {
|
||||
navigationBarsPadding()
|
||||
}
|
||||
) {
|
||||
LogoHeader(
|
||||
modifier = Modifier
|
||||
.weight(LogoHeaderWeight)
|
||||
)
|
||||
Actions(
|
||||
modifier = Modifier
|
||||
.weight(1f - LogoHeaderWeight),
|
||||
onUnlock = { viewEvent.onShowBiometric() },
|
||||
onSignOut = { viewEvent.onSignOut(userId) },
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LogoHeader(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Image(
|
||||
painter = rememberDrawablePainter(
|
||||
drawable = getDrawable(
|
||||
light = R.drawable.welcome_header_light,
|
||||
dark = R.drawable.welcome_header_dark,
|
||||
dayNight = R.drawable.welcome_header,
|
||||
)
|
||||
),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.None,
|
||||
)
|
||||
val y = LogoTranslationY
|
||||
Image(
|
||||
painter = painterResource(id = CorePresentation.drawable.ic_logo_drive),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(LogoSize)
|
||||
.align(Alignment.BottomCenter)
|
||||
.graphicsLayer {
|
||||
translationX = 0f
|
||||
translationY = y
|
||||
}
|
||||
.shadow(
|
||||
color = ShadowColor,
|
||||
alpha = DriveLogoShadowAlpha,
|
||||
cornersRadius = DriveLogoShadowCornerRadius,
|
||||
blurRadius = DriveLogoShadowBlurRadius,
|
||||
offsetY = DriveLogoShadowOffsetY,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Actions(
|
||||
modifier: Modifier = Modifier,
|
||||
onUnlock: () -> Unit,
|
||||
onSignOut: () -> Unit,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
Image(
|
||||
painter = rememberDrawablePainter(
|
||||
drawable = getDrawable(
|
||||
light = CorePresentation.drawable.logo_drive_dark,
|
||||
dark = CorePresentation.drawable.logo_drive_light,
|
||||
dayNight = CorePresentation.drawable.logo_drive_daylight,
|
||||
)
|
||||
),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.padding(top = DriveLogoTopPadding)
|
||||
.heightIn(max = DriveLogoHeight)
|
||||
.align(Alignment.TopCenter)
|
||||
)
|
||||
val buttonModifier = Modifier
|
||||
.conditional(isPortrait) {
|
||||
fillMaxWidth()
|
||||
}
|
||||
.conditional(isLandscape) {
|
||||
widthIn(min = ButtonMinWidth)
|
||||
}
|
||||
.heightIn(min = ListItemHeight)
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(all = LargeSpacing)
|
||||
.align(Alignment.BottomCenter)
|
||||
) {
|
||||
ProtonSolidButton(
|
||||
onClick = onUnlock,
|
||||
modifier = buttonModifier,
|
||||
) {
|
||||
Text(text = stringResource(id = BasePresentation.string.app_lock_unlock_the_app))
|
||||
}
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = SmallSpacing)
|
||||
)
|
||||
ProtonTextButton(
|
||||
onClick = onSignOut,
|
||||
modifier = buttonModifier,
|
||||
) {
|
||||
Text(text = stringResource(id = BasePresentation.string.title_sign_out))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun getDrawable(@DrawableRes light: Int, @DrawableRes dark: Int, @DrawableRes dayNight: Int): Drawable? =
|
||||
AppCompatResources.getDrawable(
|
||||
LocalContext.current,
|
||||
when (AppCompatDelegate.getDefaultNightMode()) {
|
||||
AppCompatDelegate.MODE_NIGHT_YES -> dark
|
||||
AppCompatDelegate.MODE_NIGHT_NO -> light
|
||||
else -> dayNight
|
||||
}
|
||||
)
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun UnlockPreview() {
|
||||
ProtonTheme {
|
||||
Unlock(
|
||||
userId = null,
|
||||
viewEvent = object : UnlockViewEvent {
|
||||
override val onShowBiometric = {}
|
||||
override val onSignOut: (UserId?) -> Unit = {}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val ShadowColor = Color(0xFF0D052E)
|
||||
private val LogoSize = 106.dp
|
||||
private val LogoTranslationY: Float @Composable get() = if (isPortrait) {
|
||||
LocalDensity.current.run { 44.dp.toPx() }
|
||||
} else {
|
||||
LocalDensity.current.run { 42.dp.toPx() }
|
||||
}
|
||||
private val LogoHeaderWeight: Float @Composable get() = if (isPortrait) 0.3f else 0.25f
|
||||
private val ButtonMinWidth = 300.dp
|
||||
private val DriveLogoHeight = 32.dp
|
||||
private val DriveLogoTopPadding = 60.dp
|
||||
private const val DriveLogoShadowAlpha = 0.07f
|
||||
private val DriveLogoShadowCornerRadius = 24.dp
|
||||
private val DriveLogoShadowBlurRadius = 16.dp
|
||||
private val DriveLogoShadowOffsetY = 8.dp
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.android.drive.lock.presentation.extension
|
||||
|
||||
import android.content.Context
|
||||
import me.proton.android.drive.lock.domain.exception.LockException
|
||||
import me.proton.core.drive.base.presentation.R as BasePresentation
|
||||
|
||||
fun LockException.getDefaultMessage(context: Context): String = when (this) {
|
||||
is LockException.BiometricAuthenticationFailed -> context.getString(
|
||||
BasePresentation.string.app_lock_system_biometrics_authentication_failed
|
||||
)
|
||||
is LockException.BiometricAuthenticationError -> errorMessage
|
||||
else -> error("Default message for exception is missing")
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.presentation.viewevent
|
||||
|
||||
import me.proton.core.domain.entity.UserId
|
||||
|
||||
interface UnlockViewEvent {
|
||||
val onShowBiometric: () -> Unit
|
||||
val onSignOut: (UserId?) -> Unit
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.lock.presentation.viewmodel
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import me.proton.android.drive.lock.domain.exception.LockException
|
||||
import me.proton.android.drive.lock.domain.usecase.UnlockApp
|
||||
import me.proton.android.drive.lock.presentation.viewevent.UnlockViewEvent
|
||||
import me.proton.core.domain.entity.UserId
|
||||
import me.proton.core.drive.base.domain.usecase.SignOut
|
||||
import me.proton.android.drive.lock.presentation.extension.getDefaultMessage
|
||||
import me.proton.core.accountmanager.domain.AccountManager
|
||||
import me.proton.core.drive.base.domain.provider.ConfigurationProvider
|
||||
import me.proton.core.drive.base.domain.usecase.BroadcastMessages
|
||||
import me.proton.core.drive.base.presentation.extension.getDefaultMessage
|
||||
import me.proton.core.drive.messagequeue.domain.entity.BroadcastMessage
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("StaticFieldLeak")
|
||||
@HiltViewModel
|
||||
class UnlockViewModel @Inject constructor(
|
||||
@ApplicationContext private val appContext: Context,
|
||||
private val signOut: SignOut,
|
||||
private val unlockApp: UnlockApp,
|
||||
private val configurationProvider: ConfigurationProvider,
|
||||
private val broadcastMessages: BroadcastMessages,
|
||||
private val accountManager: AccountManager,
|
||||
) : ViewModel() {
|
||||
|
||||
val viewEvent = object : UnlockViewEvent {
|
||||
override val onShowBiometric: () -> Unit = { showBiometrics() }
|
||||
override val onSignOut: (userId: UserId?) -> Unit = { userId -> userId?.let { doSignOut(userId) }}
|
||||
}
|
||||
|
||||
private fun showBiometrics() = viewModelScope.launch {
|
||||
unlockApp()
|
||||
.onFailure { error ->
|
||||
broadcastMessages(
|
||||
userId = accountManager.getAccounts().first().first().userId,
|
||||
message = when (error) {
|
||||
is LockException -> error.getDefaultMessage(appContext)
|
||||
else -> error.getDefaultMessage(appContext, configurationProvider.useExceptionMessage)
|
||||
},
|
||||
type = BroadcastMessage.Type.WARNING,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun doSignOut(userId: UserId) = viewModelScope.launch {
|
||||
signOut(userId)
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 200 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 411 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2023 Proton AG.
|
||||
~ This file is part of Proton Drive.
|
||||
~
|
||||
~ Proton Drive is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ Proton Drive is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<item name="welcome_header" type="drawable">@drawable/welcome_header_dark_land</item>
|
||||
</resources>
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2023 Proton AG.
|
||||
~ This file is part of Proton Drive.
|
||||
~
|
||||
~ Proton Drive is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ Proton Drive is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<item name="welcome_header" type="drawable">@drawable/welcome_header_light_land</item>
|
||||
<item name="welcome_header_light" type="drawable">@drawable/welcome_header_light_land</item>
|
||||
<item name="welcome_header_dark" type="drawable">@drawable/welcome_header_dark_land</item>
|
||||
</resources>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2023 Proton AG.
|
||||
~ This file is part of Proton Drive.
|
||||
~
|
||||
~ Proton Drive is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ Proton Drive is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<item name="welcome_header" type="drawable">@drawable/welcome_header_dark</item>
|
||||
</resources>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2023 Proton AG.
|
||||
~ This file is part of Proton Drive.
|
||||
~
|
||||
~ Proton Drive is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ Proton Drive is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<item name="welcome_header" type="drawable">@drawable/welcome_header_light</item>
|
||||
</resources>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2021 Proton Technologies AG
|
||||
~ This file is part of Proton Technologies AG and ProtonCore.
|
||||
~
|
||||
~ ProtonCore is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ ProtonCore is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with ProtonCore. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<manifest package="me.proton.android.drive.lock" />
|
||||
@@ -16,7 +16,6 @@
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import java.io.FileInputStream
|
||||
import java.util.Properties
|
||||
|
||||
plugins {
|
||||
@@ -49,6 +48,7 @@ driveModule(
|
||||
serialization = true,
|
||||
) {
|
||||
implementation(files("../../proton-libs/gopenpgp/gopenpgp.aar"))
|
||||
implementation(project(":app-lock"))
|
||||
implementation(project(":app-ui-settings"))
|
||||
implementation(project(":drive"))
|
||||
|
||||
@@ -67,13 +67,14 @@ driveModule(
|
||||
implementation(libs.timber)
|
||||
|
||||
androidTestImplementation(libs.androidx.navigation.compose)
|
||||
androidTestImplementation(libs.fusion)
|
||||
|
||||
coreLibraryDesugaring(libs.desugar.jdk.libs)
|
||||
}
|
||||
|
||||
val privateProperties = Properties().apply {
|
||||
try {
|
||||
load(FileInputStream("private.properties"))
|
||||
load(rootDir.resolve("private.properties").inputStream())
|
||||
} catch (exception: java.io.FileNotFoundException) {
|
||||
// Provide empty properties to allow the app to be built without secrets
|
||||
logger.warn("private.properties file not found", exception)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2023 Proton AG.
|
||||
~ This file is part of Proton Drive.
|
||||
~
|
||||
~ Proton Drive is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ Proton Drive is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
|
||||
<application
|
||||
android:requestLegacyExternalStorage="true"/>
|
||||
|
||||
</manifest>
|
||||
@@ -126,6 +126,17 @@
|
||||
android:authorities="${applicationId}.androidx-startup"
|
||||
android:exported="false"
|
||||
tools:node="merge">
|
||||
<meta-data
|
||||
android:name="me.proton.android.drive.initializer.MainInitializer"
|
||||
android:value="androidx.startup"
|
||||
tools:node="remove"/>
|
||||
<meta-data
|
||||
android:name="me.proton.android.drive.initializer.DocumentsProviderInitializer"
|
||||
android:value="androidx.startup" />
|
||||
<meta-data
|
||||
android:name="me.proton.android.drive.initializer.AutoLockInitializer"
|
||||
android:value="androidx.startup"
|
||||
tools:node="remove"/><!-- Initialized by MainInitializer -->
|
||||
<meta-data
|
||||
android:name="me.proton.android.drive.initializer.NotificationChannelInitializer"
|
||||
android:value="androidx.startup" />
|
||||
@@ -136,9 +147,18 @@
|
||||
android:name="androidx.work.WorkManagerInitializer"
|
||||
android:value="androidx.startup"
|
||||
tools:node="remove" />
|
||||
<meta-data
|
||||
android:name="me.proton.core.humanverification.presentation.HumanVerificationInitializer"
|
||||
android:value="androidx.startup"
|
||||
tools:node="remove" /><!-- Initialized by MainInitializer -->
|
||||
<meta-data
|
||||
android:name="me.proton.android.drive.initializer.WorkManagerInitializer"
|
||||
android:value="androidx.startup" />
|
||||
android:value="androidx.startup"
|
||||
tools:node="remove" /><!-- Initialized by MainInitializer -->
|
||||
<meta-data
|
||||
android:name="me.proton.core.plan.presentation.UnredeemedPurchaseInitializer"
|
||||
android:value="androidx.startup"
|
||||
tools:node="remove" /><!-- Initialized by MainInitializer -->
|
||||
<meta-data
|
||||
android:name="me.proton.android.drive.initializer.AccountStateHandlerInitializer"
|
||||
android:value="androidx.startup" />
|
||||
@@ -147,7 +167,16 @@
|
||||
android:value="androidx.startup" />
|
||||
<meta-data
|
||||
android:name="me.proton.android.drive.initializer.EventManagerInitializer"
|
||||
android:value="androidx.startup" />
|
||||
android:value="androidx.startup"
|
||||
tools:node="remove" /><!-- Initialized by MainInitializer -->
|
||||
<meta-data
|
||||
android:name="me.proton.core.auth.presentation.MissingScopeInitializer"
|
||||
android:value="androidx.startup"
|
||||
tools:node="remove" /><!-- Initialized by MainInitializer -->
|
||||
<meta-data
|
||||
android:name="me.proton.core.network.presentation.init.UnAuthSessionFetcherInitializer"
|
||||
android:value="androidx.startup"
|
||||
tools:node="remove" /><!-- Initialized by MainInitializer -->
|
||||
<meta-data
|
||||
android:name="me.proton.android.drive.initializer.LoggerInitializer"
|
||||
android:value="androidx.startup" />
|
||||
|
||||
@@ -20,6 +20,12 @@ package me.proton.android.drive
|
||||
|
||||
import android.app.Application
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
import me.proton.android.drive.initializer.MainInitializer
|
||||
|
||||
@HiltAndroidApp
|
||||
class App : Application()
|
||||
class App : Application() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
MainInitializer.init(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,247 +19,33 @@
|
||||
package me.proton.android.drive.db
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.AutoMigration
|
||||
import androidx.room.Database
|
||||
import androidx.room.TypeConverters
|
||||
import me.proton.core.account.data.db.AccountConverters
|
||||
import me.proton.core.account.data.db.AccountDatabase
|
||||
import me.proton.core.account.data.entity.AccountEntity
|
||||
import me.proton.core.account.data.entity.AccountMetadataEntity
|
||||
import me.proton.core.account.data.entity.SessionDetailsEntity
|
||||
import me.proton.core.account.data.entity.SessionEntity
|
||||
import me.proton.core.challenge.data.db.ChallengeConverters
|
||||
import me.proton.core.challenge.data.db.ChallengeDatabase
|
||||
import me.proton.core.challenge.data.entity.ChallengeFrameEntity
|
||||
import me.proton.core.crypto.android.keystore.CryptoConverters
|
||||
import androidx.room.migration.Migration
|
||||
import me.proton.android.drive.lock.data.db.AppLockDatabase
|
||||
import me.proton.android.drive.lock.data.db.entity.AppLockEntity
|
||||
import me.proton.android.drive.lock.data.db.entity.AutoLockDurationEntity
|
||||
import me.proton.android.drive.lock.data.db.entity.EnableAppLockEntity
|
||||
import me.proton.android.drive.lock.data.db.entity.LockEntity
|
||||
import me.proton.core.data.room.db.BaseDatabase
|
||||
import me.proton.core.data.room.db.CommonConverters
|
||||
import me.proton.core.drive.drivelink.data.db.DriveLinkDatabase
|
||||
import me.proton.core.drive.drivelink.download.data.db.DriveLinkDownloadDatabase
|
||||
import me.proton.core.drive.drivelink.offline.data.db.DriveLinkOfflineDatabase
|
||||
import me.proton.core.drive.drivelink.paged.data.db.DriveLinkPagedDatabase
|
||||
import me.proton.core.drive.drivelink.paged.data.db.entity.DriveLinkRemoteKeyEntity
|
||||
import me.proton.core.drive.drivelink.selection.data.db.DriveLinkSelectionDatabase
|
||||
import me.proton.core.drive.drivelink.shared.data.db.DriveLinkSharedDatabase
|
||||
import me.proton.core.drive.drivelink.trash.data.db.DriveLinkTrashDatabase
|
||||
import me.proton.core.drive.folder.data.db.FolderDatabase
|
||||
import me.proton.core.drive.folder.data.db.FolderMetadataEntity
|
||||
import me.proton.core.drive.link.data.db.LinkDatabase
|
||||
import me.proton.core.drive.link.data.db.entity.LinkEntity
|
||||
import me.proton.core.drive.link.data.db.entity.LinkFilePropertiesEntity
|
||||
import me.proton.core.drive.link.data.db.entity.LinkFolderPropertiesEntity
|
||||
import me.proton.core.drive.link.selection.data.db.LinkSelectionConverters
|
||||
import me.proton.core.drive.link.selection.data.db.LinkSelectionDatabase
|
||||
import me.proton.core.drive.link.selection.data.db.entity.LinkSelectionEntity
|
||||
import me.proton.core.drive.linkdownload.data.db.LinkDownloadDatabase
|
||||
import me.proton.core.drive.linkdownload.data.db.entity.DownloadBlockEntity
|
||||
import me.proton.core.drive.linkdownload.data.db.entity.LinkDownloadStateEntity
|
||||
import me.proton.core.drive.linknode.data.db.LinkAncestorDatabase
|
||||
import me.proton.core.drive.linkoffline.data.db.LinkOfflineDatabase
|
||||
import me.proton.core.drive.linkoffline.data.db.LinkOfflineEntity
|
||||
import me.proton.core.drive.linktrash.data.db.LinkTrashDatabase
|
||||
import me.proton.core.drive.linktrash.data.db.entity.LinkTrashStateEntity
|
||||
import me.proton.core.drive.linktrash.data.db.entity.TrashMetadataEntity
|
||||
import me.proton.core.drive.linktrash.data.db.entity.TrashWorkEntity
|
||||
import me.proton.core.drive.linkupload.data.db.LinkUploadDatabase
|
||||
import me.proton.core.drive.linkupload.data.db.entity.LinkUploadEntity
|
||||
import me.proton.core.drive.linkupload.data.db.entity.UploadBlockEntity
|
||||
import me.proton.core.drive.linkupload.data.db.entity.UploadBulkEntity
|
||||
import me.proton.core.drive.linkupload.data.db.entity.UploadBulkUriStringEntity
|
||||
import me.proton.core.drive.messagequeue.data.storage.db.MessageQueueDatabase
|
||||
import me.proton.core.drive.messagequeue.data.storage.db.entity.MessageEntity
|
||||
import me.proton.core.drive.notification.data.db.NotificationConverters
|
||||
import me.proton.core.drive.notification.data.db.NotificationDatabase
|
||||
import me.proton.core.drive.notification.data.db.entity.NotificationChannelEntity
|
||||
import me.proton.core.drive.notification.data.db.entity.NotificationEventEntity
|
||||
import me.proton.core.drive.share.data.db.ShareDatabase
|
||||
import me.proton.core.drive.share.data.db.ShareEntity
|
||||
import me.proton.core.drive.shareurl.base.data.db.ShareUrlDatabase
|
||||
import me.proton.core.drive.shareurl.base.data.db.entity.ShareUrlEntity
|
||||
import me.proton.core.drive.sorting.data.db.SortingDatabase
|
||||
import me.proton.core.drive.sorting.data.db.entity.SortingEntity
|
||||
import me.proton.core.drive.volume.data.db.VolumeDatabase
|
||||
import me.proton.core.drive.volume.data.db.VolumeEntity
|
||||
import me.proton.core.eventmanager.data.db.EventManagerConverters
|
||||
import me.proton.core.eventmanager.data.db.EventMetadataDatabase
|
||||
import me.proton.core.eventmanager.data.entity.EventMetadataEntity
|
||||
import me.proton.core.featureflag.data.db.FeatureFlagDatabase
|
||||
import me.proton.core.featureflag.data.entity.FeatureFlagEntity
|
||||
import me.proton.core.humanverification.data.db.HumanVerificationConverters
|
||||
import me.proton.core.humanverification.data.db.HumanVerificationDatabase
|
||||
import me.proton.core.humanverification.data.entity.HumanVerificationEntity
|
||||
import me.proton.core.key.data.db.KeySaltDatabase
|
||||
import me.proton.core.key.data.db.PublicAddressDatabase
|
||||
import me.proton.core.key.data.entity.KeySaltEntity
|
||||
import me.proton.core.key.data.entity.PublicAddressEntity
|
||||
import me.proton.core.key.data.entity.PublicAddressKeyEntity
|
||||
import me.proton.core.payment.data.local.db.PaymentDatabase
|
||||
import me.proton.core.payment.data.local.entity.GooglePurchaseEntity
|
||||
import me.proton.core.user.data.db.AddressDatabase
|
||||
import me.proton.core.user.data.db.UserConverters
|
||||
import me.proton.core.user.data.db.UserDatabase
|
||||
import me.proton.core.user.data.entity.AddressEntity
|
||||
import me.proton.core.user.data.entity.AddressKeyEntity
|
||||
import me.proton.core.user.data.entity.UserEntity
|
||||
import me.proton.core.user.data.entity.UserKeyEntity
|
||||
import me.proton.core.usersettings.data.db.OrganizationDatabase
|
||||
import me.proton.core.usersettings.data.db.UserSettingsConverters
|
||||
import me.proton.core.usersettings.data.db.UserSettingsDatabase
|
||||
import me.proton.core.usersettings.data.entity.OrganizationEntity
|
||||
import me.proton.core.usersettings.data.entity.OrganizationKeysEntity
|
||||
import me.proton.core.usersettings.data.entity.UserSettingsEntity
|
||||
import me.proton.drive.android.settings.data.db.AppUiSettingsDatabase
|
||||
import me.proton.drive.android.settings.data.db.entity.UiSettingsEntity
|
||||
|
||||
@Database(
|
||||
entities = [
|
||||
// Core
|
||||
AccountEntity::class,
|
||||
AccountMetadataEntity::class,
|
||||
SessionEntity::class,
|
||||
SessionDetailsEntity::class,
|
||||
UserEntity::class,
|
||||
UserKeyEntity::class,
|
||||
AddressEntity::class,
|
||||
AddressKeyEntity::class,
|
||||
KeySaltEntity::class,
|
||||
PublicAddressEntity::class,
|
||||
PublicAddressKeyEntity::class,
|
||||
HumanVerificationEntity::class,
|
||||
UserSettingsEntity::class,
|
||||
OrganizationEntity::class,
|
||||
OrganizationKeysEntity::class,
|
||||
EventMetadataEntity::class,
|
||||
FeatureFlagEntity::class,
|
||||
ChallengeFrameEntity::class,
|
||||
GooglePurchaseEntity::class,
|
||||
// Drive
|
||||
VolumeEntity::class,
|
||||
ShareEntity::class,
|
||||
ShareUrlEntity::class,
|
||||
LinkEntity::class,
|
||||
LinkFilePropertiesEntity::class,
|
||||
LinkFolderPropertiesEntity::class,
|
||||
LinkOfflineEntity::class,
|
||||
LinkDownloadStateEntity::class,
|
||||
DownloadBlockEntity::class,
|
||||
LinkTrashStateEntity::class,
|
||||
// Trash
|
||||
TrashWorkEntity::class,
|
||||
// MessageQueue
|
||||
MessageEntity::class,
|
||||
// AppUiSettings
|
||||
UiSettingsEntity::class,
|
||||
// DriveLinkPaged
|
||||
DriveLinkRemoteKeyEntity::class,
|
||||
// Sorting
|
||||
SortingEntity::class,
|
||||
// Upload
|
||||
LinkUploadEntity::class,
|
||||
UploadBlockEntity::class,
|
||||
UploadBulkEntity::class,
|
||||
UploadBulkUriStringEntity::class,
|
||||
FolderMetadataEntity::class,
|
||||
TrashMetadataEntity::class,
|
||||
// Notification
|
||||
NotificationChannelEntity::class,
|
||||
NotificationEventEntity::class,
|
||||
// Selection
|
||||
LinkSelectionEntity::class,
|
||||
// AppLock
|
||||
AppLockEntity::class,
|
||||
LockEntity::class,
|
||||
AutoLockDurationEntity::class,
|
||||
EnableAppLockEntity::class,
|
||||
],
|
||||
version = AppDatabase.VERSION,
|
||||
autoMigrations = [
|
||||
AutoMigration(from = 4, to = 5),
|
||||
AutoMigration(from = 5, to = 6),
|
||||
AutoMigration(from = 7, to = 8),
|
||||
AutoMigration(from = 9, to = 10),
|
||||
AutoMigration(from = 13, to = 14),
|
||||
AutoMigration(from = 15, to = 16),
|
||||
AutoMigration(from = 16, to = 17),
|
||||
AutoMigration(from = 17, to = 18, spec = ShareDatabase.DeleteBlockSizeFromShareEntity::class),
|
||||
AutoMigration(from = 18, to = 19),
|
||||
],
|
||||
exportSchema = true,
|
||||
)
|
||||
@TypeConverters(
|
||||
// Core
|
||||
CommonConverters::class,
|
||||
AccountConverters::class,
|
||||
UserConverters::class,
|
||||
CryptoConverters::class,
|
||||
HumanVerificationConverters::class,
|
||||
UserSettingsConverters::class,
|
||||
EventManagerConverters::class,
|
||||
ChallengeConverters::class,
|
||||
// Drive
|
||||
NotificationConverters::class,
|
||||
LinkSelectionConverters::class,
|
||||
)
|
||||
abstract class AppDatabase :
|
||||
BaseDatabase(),
|
||||
AccountDatabase,
|
||||
UserDatabase,
|
||||
AddressDatabase,
|
||||
KeySaltDatabase,
|
||||
HumanVerificationDatabase,
|
||||
PublicAddressDatabase,
|
||||
UserSettingsDatabase,
|
||||
OrganizationDatabase,
|
||||
FeatureFlagDatabase,
|
||||
VolumeDatabase,
|
||||
ShareDatabase,
|
||||
ShareUrlDatabase,
|
||||
LinkDatabase,
|
||||
FolderDatabase,
|
||||
LinkAncestorDatabase,
|
||||
LinkOfflineDatabase,
|
||||
LinkDownloadDatabase,
|
||||
LinkTrashDatabase,
|
||||
LinkSelectionDatabase,
|
||||
MessageQueueDatabase,
|
||||
AppUiSettingsDatabase,
|
||||
EventMetadataDatabase,
|
||||
ChallengeDatabase,
|
||||
SortingDatabase,
|
||||
LinkUploadDatabase,
|
||||
DriveLinkDatabase,
|
||||
DriveLinkPagedDatabase,
|
||||
DriveLinkTrashDatabase,
|
||||
DriveLinkOfflineDatabase,
|
||||
DriveLinkDownloadDatabase,
|
||||
DriveLinkSharedDatabase,
|
||||
DriveLinkSelectionDatabase,
|
||||
NotificationDatabase,
|
||||
PaymentDatabase {
|
||||
abstract class AppDatabase : BaseDatabase(), AppLockDatabase {
|
||||
|
||||
companion object {
|
||||
const val VERSION = 21
|
||||
|
||||
private val migrations = listOf(
|
||||
AppDatabaseMigrations.MIGRATION_1_2,
|
||||
AppDatabaseMigrations.MIGRATION_2_3,
|
||||
AppDatabaseMigrations.MIGRATION_3_4,
|
||||
//AutoMigration(from = 4, to = 5)
|
||||
//AutoMigration(from = 5, to = 6)
|
||||
AppDatabaseMigrations.MIGRATION_6_7,
|
||||
//AutoMigration(from = 7, to = 8)
|
||||
AppDatabaseMigrations.MIGRATION_8_9,
|
||||
//AutoMigration(from = 9, to = 10)
|
||||
AppDatabaseMigrations.MIGRATION_10_11,
|
||||
AppDatabaseMigrations.MIGRATION_11_12,
|
||||
AppDatabaseMigrations.MIGRATION_12_13,
|
||||
//AutoMigration(from = 13, to = 14)
|
||||
AppDatabaseMigrations.MIGRATION_14_15,
|
||||
//AutoMigration(from = 15, to = 16)
|
||||
//AutoMigration(from = 16, to = 17)
|
||||
//AutoMigration(from = 17, to = 18)
|
||||
//AutoMigration(from = 18, to = 19)
|
||||
AppDatabaseMigrations.MIGRATION_19_20,
|
||||
AppDatabaseMigrations.MIGRATION_20_21,
|
||||
)
|
||||
const val VERSION = 1
|
||||
private val migrations = listOf<Migration>()
|
||||
|
||||
fun buildDatabase(context: Context): AppDatabase =
|
||||
databaseBuilder<AppDatabase>(context, "db-drive")
|
||||
databaseBuilder<AppDatabase>(context, "db-app")
|
||||
.apply { migrations.forEach { addMigrations(it) } }
|
||||
.build()
|
||||
}
|
||||
|
||||
@@ -26,154 +26,23 @@ import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import me.proton.android.drive.db.AppDatabase
|
||||
import me.proton.core.account.data.db.AccountDatabase
|
||||
import me.proton.core.challenge.data.db.ChallengeDatabase
|
||||
import me.proton.core.drive.drivelink.data.db.DriveLinkDatabase
|
||||
import me.proton.core.drive.drivelink.download.data.db.DriveLinkDownloadDatabase
|
||||
import me.proton.core.drive.drivelink.offline.data.db.DriveLinkOfflineDatabase
|
||||
import me.proton.core.drive.drivelink.paged.data.db.DriveLinkPagedDatabase
|
||||
import me.proton.core.drive.drivelink.selection.data.db.DriveLinkSelectionDatabase
|
||||
import me.proton.core.drive.drivelink.shared.data.db.DriveLinkSharedDatabase
|
||||
import me.proton.core.drive.drivelink.trash.data.db.DriveLinkTrashDatabase
|
||||
import me.proton.core.drive.folder.data.db.FolderDatabase
|
||||
import me.proton.core.drive.link.data.db.LinkDatabase
|
||||
import me.proton.core.drive.link.selection.data.db.LinkSelectionDatabase
|
||||
import me.proton.core.drive.linkdownload.data.db.LinkDownloadDatabase
|
||||
import me.proton.core.drive.linknode.data.db.LinkAncestorDatabase
|
||||
import me.proton.core.drive.linkoffline.data.db.LinkOfflineDatabase
|
||||
import me.proton.core.drive.linktrash.data.db.LinkTrashDatabase
|
||||
import me.proton.core.drive.linkupload.data.db.LinkUploadDatabase
|
||||
import me.proton.core.drive.messagequeue.data.storage.db.MessageQueueDatabase
|
||||
import me.proton.core.drive.notification.data.db.NotificationDatabase
|
||||
import me.proton.core.drive.share.data.db.ShareDatabase
|
||||
import me.proton.core.drive.shareurl.base.data.db.ShareUrlDatabase
|
||||
import me.proton.core.drive.sorting.data.db.SortingDatabase
|
||||
import me.proton.core.drive.volume.data.db.VolumeDatabase
|
||||
import me.proton.core.eventmanager.data.db.EventMetadataDatabase
|
||||
import me.proton.core.featureflag.data.db.FeatureFlagDatabase
|
||||
import me.proton.core.humanverification.data.db.HumanVerificationDatabase
|
||||
import me.proton.core.key.data.db.KeySaltDatabase
|
||||
import me.proton.core.key.data.db.PublicAddressDatabase
|
||||
import me.proton.core.payment.data.local.db.PaymentDatabase
|
||||
import me.proton.core.user.data.db.AddressDatabase
|
||||
import me.proton.core.user.data.db.UserDatabase
|
||||
import me.proton.core.usersettings.data.db.OrganizationDatabase
|
||||
import me.proton.core.usersettings.data.db.UserSettingsDatabase
|
||||
import me.proton.drive.android.settings.data.db.AppUiSettingsDatabase
|
||||
import me.proton.android.drive.lock.data.db.AppLockDatabase
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object AppDatabaseModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideAppDatabase(@ApplicationContext context: Context): AppDatabase =
|
||||
AppDatabase.buildDatabase(context)
|
||||
}
|
||||
|
||||
}
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
abstract class AppDatabaseBindsModule {
|
||||
|
||||
@Binds
|
||||
abstract fun provideVolumeDatabase(db: AppDatabase): VolumeDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideShareDatabase(db: AppDatabase): ShareDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideShareUrlDatabase(db: AppDatabase): ShareUrlDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideLinkDatabase(db: AppDatabase): LinkDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideFolderDatabase(db: AppDatabase): FolderDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideLinkAncestorDatabase(db: AppDatabase): LinkAncestorDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideLinkOfflineDatabase(db: AppDatabase): LinkOfflineDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideLinkDownloadDatabase(db: AppDatabase): LinkDownloadDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideLinkTrashDatabase(db: AppDatabase): LinkTrashDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideLinkSelectionDatabase(db: AppDatabase): LinkSelectionDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideMessageQueueDatabase(db: AppDatabase): MessageQueueDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideSortingDatabase(db: AppDatabase): SortingDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideLinkUploadDatabase(db: AppDatabase): LinkUploadDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideAccountDatabase(db: AppDatabase): AccountDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideUserDatabase(db: AppDatabase): UserDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideAddressDatabase(db: AppDatabase): AddressDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideFeatureFlagDatabase(db: AppDatabase): FeatureFlagDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideKeySaltDatabase(db: AppDatabase): KeySaltDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun providePublicAddressDatabase(db: AppDatabase): PublicAddressDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideHumanVerificationDatabase(db: AppDatabase): HumanVerificationDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideUserSettingsDatabase(db: AppDatabase): UserSettingsDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideOrganizationDatabase(db: AppDatabase): OrganizationDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideAppUiSettingsDatabase(db: AppDatabase): AppUiSettingsDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideEventMetadataDatabase(db: AppDatabase): EventMetadataDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideChallengeDatabase(appDatabase: AppDatabase): ChallengeDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideDriveLinkDatabase(db: AppDatabase): DriveLinkDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideDriveLinkPagedDatabase(db: AppDatabase): DriveLinkPagedDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideDriveLinkTrashDatabase(db: AppDatabase): DriveLinkTrashDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideDriveLinkOfflineDatabase(db: AppDatabase): DriveLinkOfflineDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideDriveLinkDownloadDatabase(db: AppDatabase): DriveLinkDownloadDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideDriveLinkSharedDatabase(db: AppDatabase): DriveLinkSharedDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideDriveLinkSelectionDatabase(db: AppDatabase): DriveLinkSelectionDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideNotificationDatabase(db: AppDatabase): NotificationDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun providePaymentDatabase(db: AppDatabase): PaymentDatabase
|
||||
abstract fun provideAppLockDatabase(db: AppDatabase): AppLockDatabase
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
package me.proton.android.drive.di
|
||||
|
||||
import android.app.ActivityManager
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
@@ -29,15 +30,19 @@ import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import me.proton.android.drive.BuildConfig
|
||||
import me.proton.android.drive.lock.data.usecase.BuildAppKeyImpl
|
||||
import me.proton.android.drive.lock.domain.usecase.BuildAppKey
|
||||
import me.proton.android.drive.log.DriveLogger
|
||||
import me.proton.android.drive.notification.AppNotificationBuilderProvider
|
||||
import me.proton.android.drive.notification.AppNotificationEventHandler
|
||||
import me.proton.android.drive.provider.BuildConfigurationProvider
|
||||
import me.proton.android.drive.settings.DebugSettings
|
||||
import me.proton.android.drive.usecase.GetDocumentsProviderRootsImpl
|
||||
import me.proton.core.account.domain.entity.AccountType
|
||||
import me.proton.core.domain.entity.AppStore
|
||||
import me.proton.core.domain.entity.Product
|
||||
import me.proton.core.drive.base.domain.provider.ConfigurationProvider
|
||||
import me.proton.core.drive.documentsprovider.domain.usecase.GetDocumentsProviderRoots
|
||||
import me.proton.core.drive.notification.data.provider.NotificationBuilderProvider
|
||||
import me.proton.core.drive.notification.domain.handler.NotificationEventHandler
|
||||
import me.proton.drive.android.settings.data.datastore.AppUiSettingsDataStore
|
||||
@@ -79,7 +84,7 @@ object ApplicationModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideDriveLogger(): DriveLogger = DriveLogger()
|
||||
fun provideDriveLogger(@ApplicationContext context: Context): DriveLogger = DriveLogger(context)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@@ -105,6 +110,11 @@ object ApplicationModule {
|
||||
@Singleton
|
||||
fun provideClipboardManager(@ApplicationContext context: Context): ClipboardManager =
|
||||
context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideActivityManager(@ApplicationContext context: Context): ActivityManager =
|
||||
context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
||||
}
|
||||
|
||||
@Module
|
||||
@@ -119,4 +129,8 @@ abstract class ApplicationBindsModule {
|
||||
abstract fun bindsNotificationBuilderProvider(
|
||||
impl: AppNotificationBuilderProvider
|
||||
): NotificationBuilderProvider
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
abstract fun bindsGetDocumentsProviderRootsImpl(impl: GetDocumentsProviderRootsImpl): GetDocumentsProviderRoots
|
||||
}
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.android.drive.di
|
||||
|
||||
import android.content.Context
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import me.proton.android.drive.db.DriveDatabase
|
||||
import me.proton.core.account.data.db.AccountDatabase
|
||||
import me.proton.core.challenge.data.db.ChallengeDatabase
|
||||
import me.proton.core.drive.drivelink.data.db.DriveLinkDatabase
|
||||
import me.proton.core.drive.drivelink.download.data.db.DriveLinkDownloadDatabase
|
||||
import me.proton.core.drive.drivelink.offline.data.db.DriveLinkOfflineDatabase
|
||||
import me.proton.core.drive.drivelink.paged.data.db.DriveLinkPagedDatabase
|
||||
import me.proton.core.drive.drivelink.selection.data.db.DriveLinkSelectionDatabase
|
||||
import me.proton.core.drive.drivelink.shared.data.db.DriveLinkSharedDatabase
|
||||
import me.proton.core.drive.drivelink.trash.data.db.DriveLinkTrashDatabase
|
||||
import me.proton.core.drive.folder.data.db.FolderDatabase
|
||||
import me.proton.core.drive.link.data.db.LinkDatabase
|
||||
import me.proton.core.drive.link.selection.data.db.LinkSelectionDatabase
|
||||
import me.proton.core.drive.linkdownload.data.db.LinkDownloadDatabase
|
||||
import me.proton.core.drive.linknode.data.db.LinkAncestorDatabase
|
||||
import me.proton.core.drive.linkoffline.data.db.LinkOfflineDatabase
|
||||
import me.proton.core.drive.linktrash.data.db.LinkTrashDatabase
|
||||
import me.proton.core.drive.linkupload.data.db.LinkUploadDatabase
|
||||
import me.proton.core.drive.messagequeue.data.storage.db.MessageQueueDatabase
|
||||
import me.proton.core.drive.notification.data.db.NotificationDatabase
|
||||
import me.proton.core.drive.share.data.db.ShareDatabase
|
||||
import me.proton.core.drive.shareurl.base.data.db.ShareUrlDatabase
|
||||
import me.proton.core.drive.sorting.data.db.SortingDatabase
|
||||
import me.proton.core.drive.volume.data.db.VolumeDatabase
|
||||
import me.proton.core.eventmanager.data.db.EventMetadataDatabase
|
||||
import me.proton.core.featureflag.data.db.FeatureFlagDatabase
|
||||
import me.proton.core.humanverification.data.db.HumanVerificationDatabase
|
||||
import me.proton.core.key.data.db.KeySaltDatabase
|
||||
import me.proton.core.key.data.db.PublicAddressDatabase
|
||||
import me.proton.core.observability.data.db.ObservabilityDatabase
|
||||
import me.proton.core.payment.data.local.db.PaymentDatabase
|
||||
import me.proton.core.user.data.db.AddressDatabase
|
||||
import me.proton.core.user.data.db.UserDatabase
|
||||
import me.proton.core.usersettings.data.db.OrganizationDatabase
|
||||
import me.proton.core.usersettings.data.db.UserSettingsDatabase
|
||||
import me.proton.drive.android.settings.data.db.AppUiSettingsDatabase
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object DriveDatabaseModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideDriveDatabase(@ApplicationContext context: Context): DriveDatabase =
|
||||
DriveDatabase.buildDatabase(context)
|
||||
}
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
abstract class DriveDatabaseBindsModule {
|
||||
|
||||
@Binds
|
||||
abstract fun provideVolumeDatabase(db: DriveDatabase): VolumeDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideShareDatabase(db: DriveDatabase): ShareDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideShareUrlDatabase(db: DriveDatabase): ShareUrlDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideLinkDatabase(db: DriveDatabase): LinkDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideFolderDatabase(db: DriveDatabase): FolderDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideLinkAncestorDatabase(db: DriveDatabase): LinkAncestorDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideLinkOfflineDatabase(db: DriveDatabase): LinkOfflineDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideLinkDownloadDatabase(db: DriveDatabase): LinkDownloadDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideLinkTrashDatabase(db: DriveDatabase): LinkTrashDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideLinkSelectionDatabase(db: DriveDatabase): LinkSelectionDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideMessageQueueDatabase(db: DriveDatabase): MessageQueueDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideSortingDatabase(db: DriveDatabase): SortingDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideLinkUploadDatabase(db: DriveDatabase): LinkUploadDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideAccountDatabase(db: DriveDatabase): AccountDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideUserDatabase(db: DriveDatabase): UserDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideAddressDatabase(db: DriveDatabase): AddressDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideFeatureFlagDatabase(db: DriveDatabase): FeatureFlagDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideKeySaltDatabase(db: DriveDatabase): KeySaltDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun providePublicAddressDatabase(db: DriveDatabase): PublicAddressDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideHumanVerificationDatabase(db: DriveDatabase): HumanVerificationDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideUserSettingsDatabase(db: DriveDatabase): UserSettingsDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideOrganizationDatabase(db: DriveDatabase): OrganizationDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideAppUiSettingsDatabase(db: DriveDatabase): AppUiSettingsDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideEventMetadataDatabase(db: DriveDatabase): EventMetadataDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideChallengeDatabase(driveDatabase: DriveDatabase): ChallengeDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideDriveLinkDatabase(db: DriveDatabase): DriveLinkDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideDriveLinkPagedDatabase(db: DriveDatabase): DriveLinkPagedDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideDriveLinkTrashDatabase(db: DriveDatabase): DriveLinkTrashDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideDriveLinkOfflineDatabase(db: DriveDatabase): DriveLinkOfflineDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideDriveLinkDownloadDatabase(db: DriveDatabase): DriveLinkDownloadDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideDriveLinkSharedDatabase(db: DriveDatabase): DriveLinkSharedDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideDriveLinkSelectionDatabase(db: DriveDatabase): DriveLinkSelectionDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideNotificationDatabase(db: DriveDatabase): NotificationDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun providePaymentDatabase(db: DriveDatabase): PaymentDatabase
|
||||
|
||||
@Binds
|
||||
abstract fun provideObservabilityDatabase(db: DriveDatabase): ObservabilityDatabase
|
||||
}
|
||||
@@ -19,18 +19,19 @@
|
||||
package me.proton.android.drive.extension
|
||||
|
||||
import android.content.Context
|
||||
import me.proton.android.drive.lock.domain.exception.LockException
|
||||
import me.proton.core.drive.base.domain.exception.DriveException
|
||||
import me.proton.core.drive.base.presentation.R as BasePresentation
|
||||
import me.proton.core.drive.share.domain.exception.ShareException
|
||||
import me.proton.core.util.kotlin.CoreLogger
|
||||
import me.proton.android.drive.lock.presentation.extension.getDefaultMessage as lockGetDefaultMessage
|
||||
|
||||
fun DriveException.getDefaultMessage(context: Context): String = context.getString(
|
||||
when (this) {
|
||||
is ShareException.MainShareLocked -> BasePresentation.string.error_main_share_locked
|
||||
is ShareException.MainShareNotFound -> BasePresentation.string.error_main_share_not_found
|
||||
else -> throw IllegalStateException("Default message for exception is missing")
|
||||
}
|
||||
)
|
||||
fun DriveException.getDefaultMessage(context: Context): String = when (this) {
|
||||
is ShareException.MainShareLocked -> context.getString(BasePresentation.string.error_main_share_locked)
|
||||
is ShareException.MainShareNotFound -> context.getString(BasePresentation.string.error_main_share_not_found)
|
||||
is LockException -> lockGetDefaultMessage(context)
|
||||
else -> throw IllegalStateException("Default message for exception is missing")
|
||||
}
|
||||
|
||||
fun DriveException.log(tag: String, message: String = this.message.orEmpty()): DriveException = also {
|
||||
CoreLogger.d(tag, this, message)
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.proton.android.drive.initializer
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.coroutineScope
|
||||
import androidx.startup.Initializer
|
||||
import dagger.hilt.EntryPoint
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.EntryPointAccessors
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import me.proton.android.drive.lock.domain.manager.AppLockManager
|
||||
import me.proton.android.drive.lock.domain.manager.AutoLockManager
|
||||
import me.proton.core.presentation.app.AppLifecycleProvider
|
||||
|
||||
class AutoLockInitializer : Initializer<Unit> {
|
||||
|
||||
override fun create(context: Context) {
|
||||
with (
|
||||
EntryPointAccessors.fromApplication(
|
||||
context.applicationContext,
|
||||
AutoLockInitializerEntryPoint::class.java
|
||||
)
|
||||
) {
|
||||
appLifecycleProvider.state
|
||||
.onEach { state ->
|
||||
if (appLockManager.isEnabled()) {
|
||||
when (state) {
|
||||
AppLifecycleProvider.State.Background -> autoLockManager.autoLock()
|
||||
AppLifecycleProvider.State.Foreground -> autoLockManager.cancelAutoLock()
|
||||
}
|
||||
}
|
||||
}
|
||||
.launchIn(appLifecycleProvider.lifecycle.coroutineScope)
|
||||
}
|
||||
}
|
||||
|
||||
override fun dependencies(): List<Class<out Initializer<*>>> = listOf(
|
||||
LoggerInitializer::class.java,
|
||||
WorkManagerInitializer::class.java,
|
||||
)
|
||||
|
||||
@EntryPoint
|
||||
@InstallIn(SingletonComponent::class)
|
||||
interface AutoLockInitializerEntryPoint {
|
||||
val appLifecycleProvider: AppLifecycleProvider
|
||||
val appLockManager: AppLockManager
|
||||
val autoLockManager: AutoLockManager
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.android.drive.initializer
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.coroutineScope
|
||||
import androidx.startup.Initializer
|
||||
import dagger.hilt.EntryPoint
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.EntryPointAccessors
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import me.proton.android.drive.lock.domain.manager.AppLockManager
|
||||
import me.proton.core.drive.documentsprovider.data.DriveDocumentsProvider
|
||||
import me.proton.core.presentation.app.AppLifecycleProvider
|
||||
|
||||
class DocumentsProviderInitializer : Initializer<Unit> {
|
||||
|
||||
override fun create(context: Context) {
|
||||
with (
|
||||
EntryPointAccessors.fromApplication(
|
||||
context.applicationContext,
|
||||
AutoLockInitializerEntryPoint::class.java
|
||||
)
|
||||
) {
|
||||
appLockManager.enabled
|
||||
.onEach {
|
||||
DriveDocumentsProvider.notifyRootsHaveChanged(context)
|
||||
}
|
||||
.launchIn(appLifecycleProvider.lifecycle.coroutineScope)
|
||||
}
|
||||
}
|
||||
|
||||
override fun dependencies(): List<Class<out Initializer<*>>> = listOf(
|
||||
LoggerInitializer::class.java,
|
||||
)
|
||||
|
||||
@EntryPoint
|
||||
@InstallIn(SingletonComponent::class)
|
||||
interface AutoLockInitializerEntryPoint {
|
||||
val appLifecycleProvider: AppLifecycleProvider
|
||||
val appLockManager: AppLockManager
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.android.drive.initializer
|
||||
|
||||
import android.content.Context
|
||||
import androidx.startup.AppInitializer
|
||||
import androidx.startup.Initializer
|
||||
import me.proton.core.auth.presentation.MissingScopeInitializer
|
||||
import me.proton.core.humanverification.presentation.HumanVerificationInitializer
|
||||
import me.proton.core.network.presentation.init.UnAuthSessionFetcherInitializer
|
||||
import me.proton.core.plan.presentation.UnredeemedPurchaseInitializer
|
||||
|
||||
class MainInitializer : Initializer<Unit> {
|
||||
|
||||
override fun create(context: Context) {
|
||||
// No-op needed
|
||||
}
|
||||
|
||||
override fun dependencies() = listOf(
|
||||
EventManagerInitializer::class.java,
|
||||
HumanVerificationInitializer::class.java,
|
||||
UnredeemedPurchaseInitializer::class.java,
|
||||
MissingScopeInitializer::class.java,
|
||||
UnAuthSessionFetcherInitializer::class.java,
|
||||
AutoLockInitializer::class.java,
|
||||
)
|
||||
|
||||
companion object {
|
||||
|
||||
fun init(appContext: Context) {
|
||||
with(AppInitializer.getInstance(appContext)) {
|
||||
// WorkManager need to be initialized before any other dependant initializer.
|
||||
initializeComponent(WorkManagerInitializer::class.java)
|
||||
initializeComponent(MainInitializer::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,21 +20,28 @@
|
||||
|
||||
package me.proton.android.drive.log
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.LocaleList
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.sentry.Breadcrumb
|
||||
import io.sentry.Sentry
|
||||
import io.sentry.SentryEvent
|
||||
import io.sentry.SentryLevel
|
||||
import io.sentry.protocol.Message
|
||||
import me.proton.core.drive.base.presentation.extension.getDefaultMessage
|
||||
import me.proton.core.util.kotlin.Logger
|
||||
import me.proton.core.util.kotlin.LoggerLogTag
|
||||
import timber.log.Timber
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import me.proton.core.drive.base.presentation.R as BasePresentation
|
||||
|
||||
@Singleton
|
||||
class DriveLogger : Logger {
|
||||
class DriveLogger @Inject constructor(
|
||||
@ApplicationContext private val appContext: Context,
|
||||
) : Logger {
|
||||
|
||||
override fun v(tag: String, message: String) {
|
||||
DriveSentry.addBreadcrumb(tag, message)
|
||||
@@ -61,11 +68,11 @@ class DriveLogger : Logger {
|
||||
Timber.tag(tag).i(e, message)
|
||||
}
|
||||
override fun e(tag: String, e: Throwable) {
|
||||
DriveSentry.captureException(tag, e)
|
||||
DriveSentry.captureException(appContext, tag, e)
|
||||
Timber.tag(tag).e(e)
|
||||
}
|
||||
override fun e(tag: String, e: Throwable, message: String) {
|
||||
DriveSentry.captureException(tag, e, message)
|
||||
DriveSentry.captureException(appContext, tag, e, message)
|
||||
Timber.tag(tag).e(e, message)
|
||||
}
|
||||
override fun log(tag: LoggerLogTag, message: String) = i(tag.name, message)
|
||||
@@ -85,12 +92,24 @@ class DriveLogger : Logger {
|
||||
|
||||
private object DriveSentry {
|
||||
|
||||
fun captureException(tag: String, e: Throwable) {
|
||||
fun captureException(
|
||||
context: Context,
|
||||
tag: String,
|
||||
e: Throwable,
|
||||
) {
|
||||
setInternalErrorTag(context, e)
|
||||
Sentry.setTag("CoreLogger", tag)
|
||||
Sentry.captureException(e)
|
||||
}
|
||||
|
||||
fun captureException(tag: String, e: Throwable, message: String, level: SentryLevel = SentryLevel.ERROR) {
|
||||
fun captureException(
|
||||
context: Context,
|
||||
tag: String,
|
||||
e: Throwable,
|
||||
message: String,
|
||||
level: SentryLevel = SentryLevel.ERROR,
|
||||
) {
|
||||
setInternalErrorTag(context, e)
|
||||
Sentry.setTag("CoreLogger", tag)
|
||||
Sentry.captureEvent(
|
||||
SentryEvent(e).apply {
|
||||
@@ -122,6 +141,15 @@ class DriveLogger : Logger {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun setInternalErrorTag(context: Context, e: Throwable) {
|
||||
val internalErrorMessage = context.getString(BasePresentation.string.common_error_internal)
|
||||
val errorMessage = e.getDefaultMessage(
|
||||
context = context,
|
||||
useExceptionMessage = false,
|
||||
)
|
||||
Sentry.setTag("InternalError", (errorMessage == internalErrorMessage).toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,8 @@ import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import me.proton.android.drive.extension.deepLinkBaseUrl
|
||||
import me.proton.android.drive.lock.data.provider.BiometricPromptProvider
|
||||
import me.proton.android.drive.lock.domain.manager.AppLockManager
|
||||
import me.proton.android.drive.log.DriveLogTag
|
||||
import me.proton.android.drive.ui.navigation.AppNavGraph
|
||||
import me.proton.android.drive.ui.provider.LocalSnackbarPadding
|
||||
@@ -100,6 +102,8 @@ class MainActivity : FragmentActivity() {
|
||||
@Inject lateinit var actionProvider: ActionProvider
|
||||
@Inject lateinit var getThemeStyle: GetThemeStyle
|
||||
@Inject lateinit var processIntent: ProcessIntent
|
||||
@Inject lateinit var biometricPromptProvider: BiometricPromptProvider
|
||||
@Inject lateinit var appLockManager: AppLockManager
|
||||
lateinit var configurationProvider: ConfigurationProvider
|
||||
private val accountViewModel: AccountViewModel by viewModels()
|
||||
private val bugReportViewModel: BugReportViewModel by viewModels()
|
||||
@@ -121,6 +125,7 @@ class MainActivity : FragmentActivity() {
|
||||
applySecureFlag()
|
||||
setTheme(CorePresentation.style.ProtonTheme_Drive)
|
||||
super.onCreate(savedInstanceState)
|
||||
biometricPromptProvider.bindToActivity(this)
|
||||
setupAccountsViewModel()
|
||||
bugReportViewModel.initialize(this)
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
@@ -145,12 +150,13 @@ class MainActivity : FragmentActivity() {
|
||||
deepLinkBaseUrl = this@MainActivity.deepLinkBaseUrl,
|
||||
clearBackstackTrigger = clearBackstackTrigger,
|
||||
deepLinkIntent = deepLinkIntent,
|
||||
locked = appLockManager.locked,
|
||||
primaryAccount = accountViewModel.primaryAccount,
|
||||
exitApp = { finish() },
|
||||
sendBugReport = bugReportViewModel::sendBugReport,
|
||||
) { isOpen ->
|
||||
isDrawerOpen = isOpen
|
||||
}
|
||||
|
||||
HeadlessSnackBar(snackbarHostState)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.android.drive.ui.dialog
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.RadioButton
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import me.proton.android.drive.ui.viewevent.AutoLockDurationsViewEvent
|
||||
import me.proton.android.drive.ui.viewmodel.AutoLockDurationsViewModel
|
||||
import me.proton.android.drive.ui.viewstate.AutoLockDurationsViewState
|
||||
import me.proton.core.compose.component.bottomsheet.BottomSheetContent
|
||||
import me.proton.core.compose.component.bottomsheet.RunAction
|
||||
import me.proton.core.compose.flow.rememberFlowWithLifecycle
|
||||
import me.proton.core.compose.theme.ProtonDimens.DefaultSpacing
|
||||
import me.proton.core.drive.settings.presentation.extension.toString
|
||||
|
||||
@Composable
|
||||
fun AutoLockDurations(
|
||||
runAction: RunAction,
|
||||
dismiss: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val viewModel = hiltViewModel<AutoLockDurationsViewModel>()
|
||||
val viewState by rememberFlowWithLifecycle(flow = viewModel.viewState)
|
||||
.collectAsState(initial = viewModel.initialViewState)
|
||||
AutoLockDurations(
|
||||
viewState = viewState,
|
||||
viewEvent = viewModel.viewEvent(runAction, dismiss),
|
||||
modifier = modifier.navigationBarsPadding(),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AutoLockDurations(
|
||||
viewState: AutoLockDurationsViewState,
|
||||
viewEvent: AutoLockDurationsViewEvent,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
BottomSheetContent(
|
||||
modifier = modifier,
|
||||
header = {
|
||||
Text(text = viewState.title)
|
||||
},
|
||||
content = {
|
||||
viewState.durations.forEach { duration ->
|
||||
Duration(
|
||||
title = duration.toString(LocalContext.current),
|
||||
isSelected = duration == viewState.selected,
|
||||
) {
|
||||
viewEvent.onDuration(duration)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Duration(
|
||||
title: String,
|
||||
isSelected: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { onClick() },
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
modifier = Modifier
|
||||
.padding(start = DefaultSpacing)
|
||||
.weight(1f),
|
||||
)
|
||||
RadioButton(
|
||||
selected = isSelected,
|
||||
onClick = { onClick() },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PreviewSelectedDuration() {
|
||||
Duration(title = "Immediately", isSelected = true) {}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PreviewUnselectedDuration() {
|
||||
Duration(title = "15 minutes", isSelected = false) {}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.android.drive.ui.dialog
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import me.proton.android.drive.ui.viewmodel.SystemAccessDialogViewModel
|
||||
import me.proton.core.compose.component.ProtonAlertDialog
|
||||
import me.proton.core.compose.component.ProtonAlertDialogButton
|
||||
import me.proton.core.compose.component.ProtonAlertDialogText
|
||||
import me.proton.core.drive.base.presentation.R
|
||||
|
||||
@Composable
|
||||
fun SystemAccessDialog(
|
||||
modifier: Modifier = Modifier,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
val viewModel = hiltViewModel<SystemAccessDialogViewModel>()
|
||||
SystemAccessDialog(
|
||||
modifier = modifier,
|
||||
onDismiss = onDismiss,
|
||||
onSettings = viewModel.viewEvent(LocalContext.current, onDismiss).onSettings,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SystemAccessDialog(
|
||||
modifier: Modifier = Modifier,
|
||||
onDismiss: () -> Unit,
|
||||
onSettings: () -> Unit,
|
||||
) {
|
||||
ProtonAlertDialog(
|
||||
modifier = modifier,
|
||||
titleResId = R.string.app_lock_system_dialog_title,
|
||||
text = {
|
||||
Column {
|
||||
ProtonAlertDialogText(textResId = R.string.app_lock_system_dialog_description)
|
||||
}
|
||||
},
|
||||
onDismissRequest = onDismiss,
|
||||
dismissButton = {
|
||||
ProtonAlertDialogButton(
|
||||
titleResId = R.string.common_cancel_action,
|
||||
onClick = onDismiss,
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
ProtonAlertDialogButton(
|
||||
titleResId = R.string.app_lock_system_dialog_settings_button,
|
||||
onClick = onSettings,
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -43,6 +43,7 @@ import androidx.navigation.navArgument
|
||||
import androidx.navigation.navDeepLink
|
||||
import com.google.accompanist.navigation.animation.composable
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import me.proton.android.drive.extension.get
|
||||
@@ -50,7 +51,9 @@ import me.proton.android.drive.extension.popAllBackStack
|
||||
import me.proton.android.drive.extension.require
|
||||
import me.proton.android.drive.extension.requireArguments
|
||||
import me.proton.android.drive.extension.runFromRoute
|
||||
import me.proton.android.drive.lock.presentation.component.AppLock
|
||||
import me.proton.android.drive.log.DriveLogTag
|
||||
import me.proton.android.drive.ui.dialog.AutoLockDurations
|
||||
import me.proton.android.drive.ui.dialog.ConfirmDeletionDialog
|
||||
import me.proton.android.drive.ui.dialog.ConfirmEmptyTrashDialog
|
||||
import me.proton.android.drive.ui.dialog.ConfirmStopSharingDialog
|
||||
@@ -59,6 +62,7 @@ import me.proton.android.drive.ui.dialog.MultipleFileOrFolderOptions
|
||||
import me.proton.android.drive.ui.dialog.ParentFolderOptions
|
||||
import me.proton.android.drive.ui.dialog.SendFileDialog
|
||||
import me.proton.android.drive.ui.dialog.SortingList
|
||||
import me.proton.android.drive.ui.dialog.SystemAccessDialog
|
||||
import me.proton.android.drive.ui.navigation.animation.defaultEnterSlideTransition
|
||||
import me.proton.android.drive.ui.navigation.animation.defaultPopExitSlideTransition
|
||||
import me.proton.android.drive.ui.navigation.animation.slideComposable
|
||||
@@ -67,6 +71,7 @@ import me.proton.android.drive.ui.navigation.internal.MutableNavControllerSaver
|
||||
import me.proton.android.drive.ui.navigation.internal.createNavController
|
||||
import me.proton.android.drive.ui.navigation.internal.modalBottomSheet
|
||||
import me.proton.android.drive.ui.navigation.internal.rememberAnimatedNavController
|
||||
import me.proton.android.drive.ui.screen.AppAccessScreen
|
||||
import me.proton.android.drive.ui.screen.FileInfoScreen
|
||||
import me.proton.android.drive.ui.screen.HomeScreen
|
||||
import me.proton.android.drive.ui.screen.LauncherScreen
|
||||
@@ -78,6 +83,7 @@ import me.proton.android.drive.ui.screen.SigningOutScreen
|
||||
import me.proton.android.drive.ui.screen.TrashScreen
|
||||
import me.proton.android.drive.ui.screen.UploadToScreen
|
||||
import me.proton.android.drive.ui.screen.WelcomeScreen
|
||||
import me.proton.core.account.domain.entity.Account
|
||||
import me.proton.core.compose.component.bottomsheet.ModalBottomSheetViewState
|
||||
import me.proton.core.crypto.common.keystore.KeyStoreCrypto
|
||||
import me.proton.core.domain.entity.UserId
|
||||
@@ -101,6 +107,8 @@ fun AppNavGraph(
|
||||
deepLinkBaseUrl: String,
|
||||
clearBackstackTrigger: SharedFlow<Unit>,
|
||||
deepLinkIntent: SharedFlow<Intent>,
|
||||
locked: Flow<Boolean>,
|
||||
primaryAccount: Flow<Account?>,
|
||||
exitApp: () -> Unit,
|
||||
sendBugReport: () -> Unit,
|
||||
onDrawerStateChanged: (Boolean) -> Unit,
|
||||
@@ -136,14 +144,16 @@ fun AppNavGraph(
|
||||
homeNavController = createNavController(localContext)
|
||||
}
|
||||
}
|
||||
AppNavGraph(
|
||||
navController = navController,
|
||||
homeNavController = homeNavController,
|
||||
deepLinkBaseUrl = deepLinkBaseUrl,
|
||||
exitApp = exitApp,
|
||||
sendBugReport = sendBugReport,
|
||||
onDrawerStateChanged = onDrawerStateChanged,
|
||||
)
|
||||
AppLock(locked = locked, primaryAccount = primaryAccount) {
|
||||
AppNavGraph(
|
||||
navController = navController,
|
||||
homeNavController = homeNavController,
|
||||
deepLinkBaseUrl = deepLinkBaseUrl,
|
||||
exitApp = exitApp,
|
||||
sendBugReport = sendBugReport,
|
||||
onDrawerStateChanged = onDrawerStateChanged,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -188,6 +198,9 @@ fun AppNavGraph(
|
||||
addShareViaLink(navController)
|
||||
addDiscardShareViaLinkChanges(navController)
|
||||
addUploadTo(navController, deepLinkBaseUrl, exitApp)
|
||||
addAppAccess(navController)
|
||||
addSystemAccessDialog(navController)
|
||||
addAutoLockDurations(navController)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -673,9 +686,16 @@ fun NavGraphBuilder.addSettings(navController: NavHostController) = composable(
|
||||
arguments = listOf(
|
||||
navArgument(Screen.PagerPreview.USER_ID) { type = NavType.StringType },
|
||||
),
|
||||
) {
|
||||
) { navBackStackEntry ->
|
||||
val userId = UserId(navBackStackEntry.require(Screen.Files.USER_ID))
|
||||
SettingsScreen(
|
||||
navigateBack = { navController.popBackStack() },
|
||||
navigateToAppAccess = {
|
||||
navController.navigate(Screen.Settings.AppAccess(userId))
|
||||
},
|
||||
navigateToAutoLockDurations = {
|
||||
navController.navigate(Screen.Settings.AutoLockDurations(userId))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -872,3 +892,47 @@ fun NavGraphBuilder.addUploadTo(
|
||||
exitApp = exitApp,
|
||||
)
|
||||
}
|
||||
|
||||
@ExperimentalAnimationApi
|
||||
fun NavGraphBuilder.addAppAccess(navController: NavHostController) = composable(
|
||||
route = Screen.Settings.AppAccess.route,
|
||||
enterTransition = defaultEnterSlideTransition { true },
|
||||
exitTransition = { ExitTransition.None },
|
||||
popEnterTransition = { EnterTransition.None },
|
||||
popExitTransition = defaultPopExitSlideTransition { true },
|
||||
arguments = listOf(
|
||||
navArgument(Screen.Settings.USER_ID) { type = NavType.StringType },
|
||||
),
|
||||
) { navBackStackEntry ->
|
||||
val userId = UserId(navBackStackEntry.require(Screen.Settings.USER_ID))
|
||||
AppAccessScreen(
|
||||
navigateToSystemAccess = {
|
||||
navController.navigate(Screen.Settings.AppAccess.Dialogs.SystemAccess(userId))
|
||||
},
|
||||
navigateBack = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
fun NavGraphBuilder.addSystemAccessDialog(navController: NavHostController) = dialog(
|
||||
route = Screen.Settings.AppAccess.Dialogs.SystemAccess.route,
|
||||
arguments = listOf(
|
||||
navArgument(Screen.Settings.USER_ID) { type = NavType.StringType },
|
||||
),
|
||||
) {
|
||||
SystemAccessDialog(onDismiss = { navController.popBackStack() })
|
||||
}
|
||||
|
||||
fun NavGraphBuilder.addAutoLockDurations(
|
||||
navController: NavHostController,
|
||||
) = modalBottomSheet(
|
||||
route = Screen.Settings.AutoLockDurations.route,
|
||||
viewState = ModalBottomSheetViewState(dismissOnAction = false),
|
||||
) { _, runAction ->
|
||||
AutoLockDurations(
|
||||
runAction = runAction,
|
||||
dismiss = { navController.popBackStack() }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -271,6 +271,22 @@ sealed class Screen(val route: String) {
|
||||
operator fun invoke(userId: UserId) = "settings/${userId.id}"
|
||||
|
||||
const val USER_ID = Screen.USER_ID
|
||||
|
||||
object AppAccess : Screen("settings/{userId}/appAccess") {
|
||||
|
||||
operator fun invoke(userId: UserId) = "settings/${userId.id}/appAccess"
|
||||
|
||||
object Dialogs {
|
||||
|
||||
object SystemAccess : Screen("settings/{userId}/appAccess/systemAccess") {
|
||||
operator fun invoke(userId: UserId) = "settings/${userId.id}/appAccess/systemAccess"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object AutoLockDurations : Screen("settings/{userId}/autoLockDurations") {
|
||||
operator fun invoke(userId: UserId) = "settings/${userId.id}/autoLockDurations"
|
||||
}
|
||||
}
|
||||
|
||||
object SendFile : Screen("send/{userId}/shares/{shareId}/files/{fileId}") {
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.android.drive.ui.screen
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.RadioButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import me.proton.android.drive.ui.viewevent.AppAccessViewEvent
|
||||
import me.proton.android.drive.ui.viewmodel.AppAccessViewModel
|
||||
import me.proton.android.drive.ui.viewstate.AccessOption
|
||||
import me.proton.android.drive.ui.viewstate.AppAccessViewState
|
||||
import me.proton.core.compose.flow.rememberFlowWithLifecycle
|
||||
import me.proton.core.compose.theme.ProtonDimens.DefaultSpacing
|
||||
import me.proton.core.compose.theme.ProtonTheme
|
||||
import me.proton.core.drive.base.presentation.component.ProtonListItem
|
||||
import me.proton.core.drive.base.presentation.component.TopAppBar
|
||||
import me.proton.core.drive.base.presentation.R as BasePresentation
|
||||
import me.proton.core.presentation.R as CorePresentation
|
||||
|
||||
@Composable
|
||||
fun AppAccessScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
navigateToSystemAccess: () -> Unit,
|
||||
navigateBack: () -> Unit,
|
||||
) {
|
||||
val viewModel = hiltViewModel<AppAccessViewModel>()
|
||||
val viewState by rememberFlowWithLifecycle(flow = viewModel.viewState)
|
||||
.collectAsState(initial = viewModel.initialViewState)
|
||||
AppAccess(
|
||||
viewState = viewState,
|
||||
viewEvent = viewModel.viewEvent(navigateToSystemAccess, navigateBack),
|
||||
modifier = modifier.fillMaxSize(),
|
||||
navigateBack = navigateBack,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AppAccess(
|
||||
viewState: AppAccessViewState,
|
||||
viewEvent: AppAccessViewEvent,
|
||||
modifier: Modifier = Modifier,
|
||||
navigateBack: () -> Unit,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
TopAppBar(
|
||||
navigationIcon = painterResource(id = CorePresentation.drawable.ic_arrow_back),
|
||||
onNavigationIcon = navigateBack,
|
||||
title = viewState.title,
|
||||
modifier = Modifier.statusBarsPadding()
|
||||
)
|
||||
AppAccessOptions(viewState.enabledOption, viewEvent)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AppAccessOptions(
|
||||
enabledOption: AccessOption,
|
||||
viewEvent: AppAccessViewEvent,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
AppAccessOption(
|
||||
iconResId = BasePresentation.drawable.ic_proton_lock_open,
|
||||
titleResId = BasePresentation.string.app_lock_option_none,
|
||||
isSelected = enabledOption == AccessOption.NONE,
|
||||
) {
|
||||
viewEvent.onDisable()
|
||||
}
|
||||
AppAccessOption(
|
||||
iconResId = CorePresentation.drawable.ic_proton_fingerprint,
|
||||
titleResId = BasePresentation.string.app_lock_option_system,
|
||||
isSelected = enabledOption == AccessOption.SYSTEM,
|
||||
) {
|
||||
viewEvent.onSystem()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AppAccessOption(
|
||||
iconResId: Int,
|
||||
titleResId: Int,
|
||||
isSelected: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
onClick()
|
||||
},
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
ProtonListItem(
|
||||
icon = painterResource(id = iconResId),
|
||||
title = stringResource(id = titleResId),
|
||||
modifier = modifier
|
||||
.weight(1f)
|
||||
.padding(start = DefaultSpacing),
|
||||
)
|
||||
RadioButton(
|
||||
selected = isSelected,
|
||||
onClick = { onClick() },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun AppAccessPreview() {
|
||||
ProtonTheme {
|
||||
AppAccess(
|
||||
viewState = AppAccessViewState(
|
||||
title = "Title",
|
||||
enabledOption = AccessOption.NONE,
|
||||
),
|
||||
viewEvent = object : AppAccessViewEvent {
|
||||
override val onDisable = {}
|
||||
override val onSystem = {}
|
||||
},
|
||||
navigateBack = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,15 +66,18 @@ fun FilesScreen(
|
||||
val selected by rememberFlowWithLifecycle(flow = viewState.selected)
|
||||
.collectAsState(initial = null)
|
||||
val inMultiselect = remember(selected) { selected?.isNotEmpty() ?: false }
|
||||
val viewEvent = viewModel.viewEvent(
|
||||
navigateToFiles,
|
||||
navigateToPreview,
|
||||
navigateToSortingDialog,
|
||||
navigateToFileOrFolderOptions,
|
||||
navigateToMultipleFileOrFolderOptions,
|
||||
navigateToParentFolderOptions,
|
||||
navigateBack,
|
||||
)
|
||||
|
||||
val viewEvent = remember {
|
||||
viewModel.viewEvent(
|
||||
navigateToFiles,
|
||||
navigateToPreview,
|
||||
navigateToSortingDialog,
|
||||
navigateToFileOrFolderOptions,
|
||||
navigateToMultipleFileOrFolderOptions,
|
||||
navigateToParentFolderOptions,
|
||||
navigateBack,
|
||||
)
|
||||
}
|
||||
BackHandler(enabled = inMultiselect) { viewEvent.onBack() }
|
||||
LaunchedEffect(viewState) {
|
||||
homeScaffoldState.topAppBar.value = {
|
||||
|
||||
@@ -98,6 +98,7 @@ fun MoveToFolder(
|
||||
modifier
|
||||
.systemBarsPadding()
|
||||
.padding(vertical = DefaultSpacing)
|
||||
.testTag(MoveToFolderScreenTestTag.screen)
|
||||
) {
|
||||
Column {
|
||||
Title(
|
||||
@@ -198,5 +199,5 @@ fun TitleText(
|
||||
|
||||
object MoveToFolderScreenTestTag {
|
||||
const val screen = "move to folder screen"
|
||||
const val plusFolderButton = "plus folder button"
|
||||
const val plusFolderButton = "move to folder plus folder button"
|
||||
}
|
||||
|
||||
@@ -29,12 +29,12 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import me.proton.android.drive.ui.navigation.PagerType
|
||||
import me.proton.android.drive.ui.viewmodel.OfflineViewModel
|
||||
import me.proton.core.compose.flow.rememberFlowWithLifecycle
|
||||
import me.proton.core.drive.files.presentation.component.DriveLinksFlow
|
||||
import me.proton.core.drive.files.presentation.component.Files
|
||||
import me.proton.core.drive.link.domain.entity.FileId
|
||||
import me.proton.core.drive.link.domain.entity.FolderId
|
||||
import me.proton.core.drive.link.domain.entity.LinkId
|
||||
import me.proton.core.compose.flow.rememberFlowWithLifecycle
|
||||
import me.proton.core.drive.sorting.domain.entity.Sorting
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -107,7 +107,6 @@ fun PreviewScreen(
|
||||
navigateBack = navigateBack,
|
||||
navigateToFileOrFolderOptions = navigateToFileOrFolderOptions,
|
||||
),
|
||||
zoomEffect = viewModel.zoomEffect,
|
||||
modifier = modifier,
|
||||
) { page ->
|
||||
viewModel.onPageChanged(page)
|
||||
|
||||
@@ -41,6 +41,8 @@ import me.proton.core.drive.settings.presentation.Settings
|
||||
@Composable
|
||||
fun SettingsScreen(
|
||||
navigateBack: () -> Unit,
|
||||
navigateToAppAccess: () -> Unit,
|
||||
navigateToAutoLockDurations: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val viewModel = hiltViewModel<SettingsViewModel>()
|
||||
@@ -54,15 +56,19 @@ fun SettingsScreen(
|
||||
}.launchIn(this)
|
||||
}
|
||||
|
||||
settingsViewState?.let { viewState ->
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.systemBarsPadding()
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.systemBarsPadding()
|
||||
) {
|
||||
settingsViewState?.let { viewState ->
|
||||
Settings(
|
||||
viewState = viewState,
|
||||
viewEvent = viewModel.viewEvent(navigateBack),
|
||||
viewEvent = viewModel.viewEvent(
|
||||
navigateBack = navigateBack,
|
||||
navigateToAppAccess = navigateToAppAccess,
|
||||
navigateToAutoLockDurations = navigateToAutoLockDurations,
|
||||
),
|
||||
)
|
||||
|
||||
ProtonSnackbarHost(
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.android.drive.ui.viewevent
|
||||
|
||||
interface AppAccessViewEvent {
|
||||
val onDisable: () -> Unit
|
||||
val onSystem: () -> Unit
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Proton AG.
|
||||
* This file is part of Proton Drive.
|
||||
*
|
||||
* Proton Drive is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Proton Drive is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Proton Drive. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.android.drive.ui.viewevent
|
||||
|
||||
import kotlin.time.Duration
|
||||
|
||||
interface AutoLockDurationsViewEvent {
|
||||
val onDuration: (Duration) -> Unit
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user