feat: raise min SDK to 26
Autofill is only available on API 26 and above and I do not want to deal with bugs on these older Android releases.
This commit is contained in:
parent
ee9d77eafd
commit
ec696c1d8d
27 changed files with 63 additions and 164 deletions
|
@ -35,7 +35,8 @@ All notable changes to this project will be documented in this file.
|
||||||
- **BREAKING**: The app's package name has been changed to `app.passwordstore` so users are aware that this is a new project with no compatibility guarantees with Password Store 1.x.y.
|
- **BREAKING**: The app's package name has been changed to `app.passwordstore` so users are aware that this is a new project with no compatibility guarantees with Password Store 1.x.y.
|
||||||
- **BREAKING**: Introduce a new PGP backend powered by [PGPainless](https://github.com/pgpainless/pgpainless) which completely replaces OpenKeychain
|
- **BREAKING**: Introduce a new PGP backend powered by [PGPainless](https://github.com/pgpainless/pgpainless) which completely replaces OpenKeychain
|
||||||
- **BREAKING**: Accessibility autofill has been removed completely due to being buggy, insecure and lacking in features. Upgrade to Android 8 or preferably later to gain access to our advanced Autofill implementation.
|
- **BREAKING**: Accessibility autofill has been removed completely due to being buggy, insecure and lacking in features. Upgrade to Android 8 or preferably later to gain access to our advanced Autofill implementation.
|
||||||
- **BREAKING***: Support for stores outside the hidden app directory has been removed due to technical restrictions, see [this issue](https://github.com/Android-Password-Store/Android-Password-Store/issues/1849) for details.
|
- **BREAKING**: Support for stores outside the hidden app directory has been removed due to technical restrictions, see [this issue](https://github.com/Android-Password-Store/Android-Password-Store/issues/1849) for details.
|
||||||
|
- **BREAKING**: The app's minimum supported Android version has been raised to Android Oreo (API level 26).
|
||||||
- The settings UI has been completely re-done to dramatically improve discoverability and navigation for users
|
- The settings UI has been completely re-done to dramatically improve discoverability and navigation for users
|
||||||
- Using the `git://` protocol in the server URL now presents an explicit discouragement rather than a generic error
|
- Using the `git://` protocol in the server URL now presents an explicit discouragement rather than a generic error
|
||||||
- Encrypted data is no longer ASCII armored, bringing it in line with `pass`
|
- Encrypted data is no longer ASCII armored, bringing it in line with `pass`
|
||||||
|
|
|
@ -6,11 +6,8 @@ package app.passwordstore.autofill.oreo.ui
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.IntentSender
|
import android.content.IntentSender
|
||||||
import android.os.Build
|
|
||||||
import androidx.annotation.RequiresApi
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.O)
|
|
||||||
@Suppress("UNUSED_PARAMETER")
|
@Suppress("UNUSED_PARAMETER")
|
||||||
class AutofillSmsActivity : AppCompatActivity() {
|
class AutofillSmsActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
|
|
@ -96,9 +96,7 @@ class Application : android.app.Application(), SharedPreferences.OnSharedPrefere
|
||||||
.detectLeakedRegistrationObjects()
|
.detectLeakedRegistrationObjects()
|
||||||
.detectLeakedSqlLiteObjects()
|
.detectLeakedSqlLiteObjects()
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
builder.detectContentUriWithoutPermission()
|
||||||
builder.detectContentUriWithoutPermission()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
builder.detectCredentialProtectedWhileLocked().detectImplicitDirectBoot()
|
builder.detectCredentialProtectedWhileLocked().detectImplicitDirectBoot()
|
||||||
|
|
|
@ -11,7 +11,6 @@ import android.content.IntentSender
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.autofill.AutofillManager
|
import android.view.autofill.AutofillManager
|
||||||
import androidx.annotation.RequiresApi
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import app.passwordstore.data.crypto.GPGPassphraseCache
|
import app.passwordstore.data.crypto.GPGPassphraseCache
|
||||||
import app.passwordstore.data.passfile.PasswordEntry
|
import app.passwordstore.data.passfile.PasswordEntry
|
||||||
|
@ -41,7 +40,6 @@ import kotlinx.coroutines.withContext
|
||||||
import logcat.LogPriority.ERROR
|
import logcat.LogPriority.ERROR
|
||||||
import logcat.logcat
|
import logcat.logcat
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.O)
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class AutofillDecryptActivity : BasePgpActivity() {
|
class AutofillDecryptActivity : BasePgpActivity() {
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
*/
|
*/
|
||||||
package app.passwordstore.ui.autofill
|
package app.passwordstore.ui.autofill
|
||||||
|
|
||||||
import android.annotation.TargetApi
|
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
@ -43,7 +42,6 @@ import kotlinx.coroutines.flow.onEach
|
||||||
import logcat.LogPriority.ERROR
|
import logcat.LogPriority.ERROR
|
||||||
import logcat.logcat
|
import logcat.logcat
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.O)
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class AutofillFilterView : AppCompatActivity() {
|
class AutofillFilterView : AppCompatActivity() {
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
*/
|
*/
|
||||||
package app.passwordstore.ui.autofill
|
package app.passwordstore.ui.autofill
|
||||||
|
|
||||||
import android.annotation.TargetApi
|
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
@ -33,7 +32,6 @@ import com.github.michaelbull.result.runCatching
|
||||||
import logcat.LogPriority.ERROR
|
import logcat.LogPriority.ERROR
|
||||||
import logcat.logcat
|
import logcat.logcat
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.O)
|
|
||||||
class AutofillPublisherChangedActivity : AppCompatActivity() {
|
class AutofillPublisherChangedActivity : AppCompatActivity() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -8,11 +8,9 @@ import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.IntentSender
|
import android.content.IntentSender
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.autofill.AutofillManager
|
import android.view.autofill.AutofillManager
|
||||||
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
|
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
|
||||||
import androidx.annotation.RequiresApi
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import app.passwordstore.data.repo.PasswordRepository
|
import app.passwordstore.data.repo.PasswordRepository
|
||||||
|
@ -29,7 +27,6 @@ import java.io.File
|
||||||
import logcat.LogPriority.ERROR
|
import logcat.LogPriority.ERROR
|
||||||
import logcat.logcat
|
import logcat.logcat
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.O)
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class AutofillSaveActivity : AppCompatActivity() {
|
class AutofillSaveActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@ open class BasePgpActivity : AppCompatActivity() {
|
||||||
val name: String by unsafeLazy { File(fullPath).nameWithoutExtension }
|
val name: String by unsafeLazy { File(fullPath).nameWithoutExtension }
|
||||||
|
|
||||||
/** Action to invoke if [keyImportAction] succeeds. */
|
/** Action to invoke if [keyImportAction] succeeds. */
|
||||||
var onKeyImport: (() -> Unit)? = null
|
private var onKeyImport: (() -> Unit)? = null
|
||||||
private val keyImportAction =
|
private val keyImportAction =
|
||||||
registerForActivityResult(StartActivityForResult()) {
|
registerForActivityResult(StartActivityForResult()) {
|
||||||
if (it.resultCode == RESULT_OK) {
|
if (it.resultCode == RESULT_OK) {
|
||||||
|
@ -94,10 +94,8 @@ open class BasePgpActivity : AppCompatActivity() {
|
||||||
) {
|
) {
|
||||||
val clipboard = clipboard ?: return
|
val clipboard = clipboard ?: return
|
||||||
val clip = ClipData.newPlainText("pgp_handler_result_pm", text)
|
val clip = ClipData.newPlainText("pgp_handler_result_pm", text)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
clip.description.extras =
|
||||||
clip.description.extras =
|
PersistableBundle().apply { putBoolean("android.content.extra.IS_SENSITIVE", true) }
|
||||||
PersistableBundle().apply { putBoolean("android.content.extra.IS_SENSITIVE", true) }
|
|
||||||
}
|
|
||||||
clipboard.setPrimaryClip(clip)
|
clipboard.setPrimaryClip(clip)
|
||||||
if (showSnackbar && Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
|
if (showSnackbar && Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
|
||||||
snackbar(message = resources.getString(snackbarTextRes))
|
snackbar(message = resources.getString(snackbarTextRes))
|
||||||
|
@ -144,11 +142,7 @@ open class BasePgpActivity : AppCompatActivity() {
|
||||||
action = ClipboardService.ACTION_START
|
action = ClipboardService.ACTION_START
|
||||||
putExtra(ClipboardService.EXTRA_NOTIFICATION_TIME, clearAfter)
|
putExtra(ClipboardService.EXTRA_NOTIFICATION_TIME, clearAfter)
|
||||||
}
|
}
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
startForegroundService(service)
|
||||||
startForegroundService(service)
|
|
||||||
} else {
|
|
||||||
startService(service)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,7 @@ package app.passwordstore.ui.settings
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import androidx.annotation.RequiresApi
|
|
||||||
import androidx.appcompat.widget.AppCompatTextView
|
import androidx.appcompat.widget.AppCompatTextView
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
|
@ -35,11 +33,9 @@ class AutofillSettings(private val activity: FragmentActivity) : SettingsProvide
|
||||||
|
|
||||||
private val isAutofillServiceEnabled: Boolean
|
private val isAutofillServiceEnabled: Boolean
|
||||||
get() {
|
get() {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return false
|
|
||||||
return activity.autofillManager?.hasEnabledAutofillServices() == true
|
return activity.autofillManager?.hasEnabledAutofillServices() == true
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.O)
|
|
||||||
private fun showAutofillDialog(pref: SwitchPreference) {
|
private fun showAutofillDialog(pref: SwitchPreference) {
|
||||||
val observer = LifecycleEventObserver { _, event ->
|
val observer = LifecycleEventObserver { _, event ->
|
||||||
when (event) {
|
when (event) {
|
||||||
|
@ -95,10 +91,8 @@ class AutofillSettings(private val activity: FragmentActivity) : SettingsProvide
|
||||||
builder.apply {
|
builder.apply {
|
||||||
switch(PreferenceKeys.AUTOFILL_ENABLE) {
|
switch(PreferenceKeys.AUTOFILL_ENABLE) {
|
||||||
titleRes = R.string.pref_autofill_enable_title
|
titleRes = R.string.pref_autofill_enable_title
|
||||||
visible = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
|
||||||
defaultValue = isAutofillServiceEnabled
|
defaultValue = isAutofillServiceEnabled
|
||||||
onClick {
|
onClick {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return@onClick true
|
|
||||||
if (isAutofillServiceEnabled) {
|
if (isAutofillServiceEnabled) {
|
||||||
activity.autofillManager?.disableAutofillServices()
|
activity.autofillManager?.disableAutofillServices()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
package app.passwordstore.ui.settings
|
package app.passwordstore.ui.settings
|
||||||
|
|
||||||
import android.content.pm.ShortcutManager
|
import android.content.pm.ShortcutManager
|
||||||
import android.os.Build
|
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
@ -97,10 +96,8 @@ class GeneralSettings(private val activity: FragmentActivity) : SettingsProvider
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
activity.getSystemService<ShortcutManager>()?.apply {
|
||||||
activity.getSystemService<ShortcutManager>()?.apply {
|
removeDynamicShortcuts(dynamicShortcuts.map { it.id }.toMutableList())
|
||||||
removeDynamicShortcuts(dynamicShortcuts.map { it.id }.toMutableList())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ package app.passwordstore.ui.settings
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
@ -47,11 +46,7 @@ class MiscSettings(activity: FragmentActivity) : SettingsProvider {
|
||||||
putExtra("uri", uri)
|
putExtra("uri", uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
activity.startForegroundService(service)
|
||||||
activity.startForegroundService(service)
|
|
||||||
} else {
|
|
||||||
activity.startService(service)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ package app.passwordstore.ui.settings
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.pm.ShortcutManager
|
import android.content.pm.ShortcutManager
|
||||||
import android.os.Build
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
|
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
|
@ -172,10 +171,8 @@ class RepositorySettings(
|
||||||
}
|
}
|
||||||
.onFailure { it.message?.let { message -> activity.snackbar(message = message) } }
|
.onFailure { it.message?.let { message -> activity.snackbar(message = message) } }
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
activity.getSystemService<ShortcutManager>()?.apply {
|
||||||
activity.getSystemService<ShortcutManager>()?.apply {
|
removeDynamicShortcuts(dynamicShortcuts.map { it.id }.toMutableList())
|
||||||
removeDynamicShortcuts(dynamicShortcuts.map { it.id }.toMutableList())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
activity.sharedPrefs.edit { putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, false) }
|
activity.sharedPrefs.edit { putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, false) }
|
||||||
dialogInterface.cancel()
|
dialogInterface.cancel()
|
||||||
|
|
|
@ -5,8 +5,6 @@
|
||||||
package app.passwordstore.util.autofill
|
package app.passwordstore.util.autofill
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
|
||||||
import androidx.annotation.RequiresApi
|
|
||||||
import app.passwordstore.data.passfile.PasswordEntry
|
import app.passwordstore.data.passfile.PasswordEntry
|
||||||
import app.passwordstore.util.extensions.getString
|
import app.passwordstore.util.extensions.getString
|
||||||
import app.passwordstore.util.extensions.sharedPrefs
|
import app.passwordstore.util.extensions.sharedPrefs
|
||||||
|
@ -101,7 +99,6 @@ enum class DirectoryStructure(val value: String) {
|
||||||
?: file.nameWithoutExtension
|
?: file.nameWithoutExtension
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.O)
|
|
||||||
fun getSaveFolderName(sanitizedIdentifier: String, username: String?) =
|
fun getSaveFolderName(sanitizedIdentifier: String, username: String?) =
|
||||||
when (this) {
|
when (this) {
|
||||||
EncryptedUsername -> "/"
|
EncryptedUsername -> "/"
|
||||||
|
|
|
@ -12,7 +12,6 @@ import android.service.autofill.Dataset
|
||||||
import android.service.autofill.FillCallback
|
import android.service.autofill.FillCallback
|
||||||
import android.service.autofill.FillResponse
|
import android.service.autofill.FillResponse
|
||||||
import android.service.autofill.SaveInfo
|
import android.service.autofill.SaveInfo
|
||||||
import androidx.annotation.RequiresApi
|
|
||||||
import app.passwordstore.autofill.oreo.ui.AutofillSmsActivity
|
import app.passwordstore.autofill.oreo.ui.AutofillSmsActivity
|
||||||
import app.passwordstore.ui.autofill.AutofillDecryptActivity
|
import app.passwordstore.ui.autofill.AutofillDecryptActivity
|
||||||
import app.passwordstore.ui.autofill.AutofillFilterView
|
import app.passwordstore.ui.autofill.AutofillFilterView
|
||||||
|
@ -32,7 +31,6 @@ import logcat.LogPriority.ERROR
|
||||||
import logcat.asLog
|
import logcat.asLog
|
||||||
import logcat.logcat
|
import logcat.logcat
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.O)
|
|
||||||
class AutofillResponseBuilder
|
class AutofillResponseBuilder
|
||||||
@AssistedInject
|
@AssistedInject
|
||||||
constructor(
|
constructor(
|
||||||
|
|
|
@ -21,7 +21,6 @@ import android.util.TypedValue
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.autofill.AutofillManager
|
import android.view.autofill.AutofillManager
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.annotation.RequiresApi
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
|
@ -39,7 +38,7 @@ import logcat.logcat
|
||||||
|
|
||||||
/** Get an instance of [AutofillManager]. Only available on Android Oreo and above */
|
/** Get an instance of [AutofillManager]. Only available on Android Oreo and above */
|
||||||
val Context.autofillManager: AutofillManager?
|
val Context.autofillManager: AutofillManager?
|
||||||
@RequiresApi(Build.VERSION_CODES.O) get() = getSystemService()
|
get() = getSystemService()
|
||||||
|
|
||||||
/** Get an instance of [ClipboardManager] */
|
/** Get an instance of [ClipboardManager] */
|
||||||
val Context.clipboard
|
val Context.clipboard
|
||||||
|
|
|
@ -14,7 +14,6 @@ import android.content.ClipData
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import androidx.annotation.RequiresApi
|
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import app.passwordstore.R
|
import app.passwordstore.R
|
||||||
|
@ -40,11 +39,7 @@ class ClipboardService : Service() {
|
||||||
when (intent.action) {
|
when (intent.action) {
|
||||||
ACTION_CLEAR -> {
|
ACTION_CLEAR -> {
|
||||||
clearClipboard()
|
clearClipboard()
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||||
stopForeground(STOP_FOREGROUND_REMOVE)
|
|
||||||
} else {
|
|
||||||
@Suppress("DEPRECATION") stopForeground(true)
|
|
||||||
}
|
|
||||||
stopSelf()
|
stopSelf()
|
||||||
return super.onStartCommand(intent, flags, startId)
|
return super.onStartCommand(intent, flags, startId)
|
||||||
}
|
}
|
||||||
|
@ -60,11 +55,7 @@ class ClipboardService : Service() {
|
||||||
withContext(Dispatchers.IO) { startTimer(time) }
|
withContext(Dispatchers.IO) { startTimer(time) }
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
clearClipboard()
|
clearClipboard()
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||||
stopForeground(STOP_FOREGROUND_REMOVE)
|
|
||||||
} else {
|
|
||||||
@Suppress("DEPRECATION") stopForeground(true)
|
|
||||||
}
|
|
||||||
stopSelf()
|
stopSelf()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,52 +113,23 @@ class ClipboardService : Service() {
|
||||||
val clearTimeMs = clearTime * 1000L
|
val clearTimeMs = clearTime * 1000L
|
||||||
val clearIntent = Intent(this, ClipboardService::class.java).apply { action = ACTION_CLEAR }
|
val clearIntent = Intent(this, ClipboardService::class.java).apply { action = ACTION_CLEAR }
|
||||||
val pendingIntent =
|
val pendingIntent =
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
PendingIntent.getForegroundService(
|
||||||
PendingIntent.getForegroundService(
|
this,
|
||||||
this,
|
0,
|
||||||
0,
|
clearIntent,
|
||||||
clearIntent,
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
|
} else {
|
||||||
} else {
|
PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT
|
}
|
||||||
}
|
)
|
||||||
)
|
val notification = createNotification(pendingIntent, clearTimeMs)
|
||||||
} else {
|
|
||||||
PendingIntent.getService(
|
|
||||||
this,
|
|
||||||
0,
|
|
||||||
clearIntent,
|
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val notification =
|
|
||||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O) {
|
|
||||||
createNotificationApi23(pendingIntent)
|
|
||||||
} else {
|
|
||||||
createNotificationApi24(pendingIntent, clearTimeMs)
|
|
||||||
}
|
|
||||||
|
|
||||||
createNotificationChannel()
|
createNotificationChannel()
|
||||||
startForeground(1, notification)
|
startForeground(1, notification)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createNotificationApi23(pendingIntent: PendingIntent): Notification {
|
private fun createNotification(pendingIntent: PendingIntent, clearTimeMs: Long): Notification {
|
||||||
return NotificationCompat.Builder(this, CHANNEL_ID)
|
|
||||||
.setContentTitle(getString(R.string.app_name))
|
|
||||||
.setContentText(getString(R.string.tap_clear_clipboard))
|
|
||||||
.setSmallIcon(R.drawable.ic_action_secure_24dp)
|
|
||||||
.setContentIntent(pendingIntent)
|
|
||||||
.setUsesChronometer(true)
|
|
||||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.N)
|
|
||||||
private fun createNotificationApi24(
|
|
||||||
pendingIntent: PendingIntent,
|
|
||||||
clearTimeMs: Long
|
|
||||||
): Notification {
|
|
||||||
return NotificationCompat.Builder(this, CHANNEL_ID)
|
return NotificationCompat.Builder(this, CHANNEL_ID)
|
||||||
.setContentTitle(getString(R.string.app_name))
|
.setContentTitle(getString(R.string.app_name))
|
||||||
.setContentText(getString(R.string.tap_clear_clipboard))
|
.setContentText(getString(R.string.tap_clear_clipboard))
|
||||||
|
@ -182,19 +144,17 @@ class ClipboardService : Service() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createNotificationChannel() {
|
private fun createNotificationChannel() {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
val serviceChannel =
|
||||||
val serviceChannel =
|
NotificationChannel(
|
||||||
NotificationChannel(
|
CHANNEL_ID,
|
||||||
CHANNEL_ID,
|
getString(R.string.app_name),
|
||||||
getString(R.string.app_name),
|
NotificationManager.IMPORTANCE_LOW
|
||||||
NotificationManager.IMPORTANCE_LOW
|
)
|
||||||
)
|
val manager = getSystemService<NotificationManager>()
|
||||||
val manager = getSystemService<NotificationManager>()
|
if (manager != null) {
|
||||||
if (manager != null) {
|
manager.createNotificationChannel(serviceChannel)
|
||||||
manager.createNotificationChannel(serviceChannel)
|
} else {
|
||||||
} else {
|
logcat { "Failed to create notification channel" }
|
||||||
logcat { "Failed to create notification channel" }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ import android.service.autofill.FillRequest
|
||||||
import android.service.autofill.FillResponse
|
import android.service.autofill.FillResponse
|
||||||
import android.service.autofill.SaveCallback
|
import android.service.autofill.SaveCallback
|
||||||
import android.service.autofill.SaveRequest
|
import android.service.autofill.SaveRequest
|
||||||
import androidx.annotation.RequiresApi
|
|
||||||
import app.passwordstore.BuildConfig
|
import app.passwordstore.BuildConfig
|
||||||
import app.passwordstore.R
|
import app.passwordstore.R
|
||||||
import app.passwordstore.ui.autofill.AutofillSaveActivity
|
import app.passwordstore.ui.autofill.AutofillSaveActivity
|
||||||
|
@ -37,7 +36,6 @@ import javax.inject.Inject
|
||||||
import logcat.LogPriority.ERROR
|
import logcat.LogPriority.ERROR
|
||||||
import logcat.logcat
|
import logcat.logcat
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.O)
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class OreoAutofillService : AutofillService() {
|
class OreoAutofillService : AutofillService() {
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ import android.app.NotificationManager
|
||||||
import android.app.Service
|
import android.app.Service
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.content.IntentCompat
|
import androidx.core.content.IntentCompat
|
||||||
|
@ -20,8 +19,6 @@ import app.passwordstore.R
|
||||||
import app.passwordstore.data.repo.PasswordRepository
|
import app.passwordstore.data.repo.PasswordRepository
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import java.util.Calendar
|
|
||||||
import java.util.TimeZone
|
|
||||||
import logcat.logcat
|
import logcat.logcat
|
||||||
|
|
||||||
class PasswordExportService : Service() {
|
class PasswordExportService : Service() {
|
||||||
|
@ -65,13 +62,7 @@ class PasswordExportService : Service() {
|
||||||
|
|
||||||
logcat { "Copying ${repositoryDirectory.path} to $targetDirectory" }
|
logcat { "Copying ${repositoryDirectory.path} to $targetDirectory" }
|
||||||
|
|
||||||
val dateString =
|
val dateString = LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME)
|
|
||||||
} else {
|
|
||||||
String.format("%tFT%<tRZ", Calendar.getInstance(TimeZone.getTimeZone("Z")))
|
|
||||||
}
|
|
||||||
|
|
||||||
val passDir = targetDirectory.createDirectory("password_store_$dateString")
|
val passDir = targetDirectory.createDirectory("password_store_$dateString")
|
||||||
|
|
||||||
if (passDir != null) {
|
if (passDir != null) {
|
||||||
|
@ -136,19 +127,17 @@ class PasswordExportService : Service() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createNotificationChannel() {
|
private fun createNotificationChannel() {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
val serviceChannel =
|
||||||
val serviceChannel =
|
NotificationChannel(
|
||||||
NotificationChannel(
|
CHANNEL_ID,
|
||||||
CHANNEL_ID,
|
getString(R.string.app_name),
|
||||||
getString(R.string.app_name),
|
NotificationManager.IMPORTANCE_LOW
|
||||||
NotificationManager.IMPORTANCE_LOW
|
)
|
||||||
)
|
val manager = getSystemService<NotificationManager>()
|
||||||
val manager = getSystemService<NotificationManager>()
|
if (manager != null) {
|
||||||
if (manager != null) {
|
manager.createNotificationChannel(serviceChannel)
|
||||||
manager.createNotificationChannel(serviceChannel)
|
} else {
|
||||||
} else {
|
logcat { "Failed to create notification channel" }
|
||||||
logcat { "Failed to create notification channel" }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,6 @@ import android.content.Intent
|
||||||
import android.content.pm.ShortcutInfo
|
import android.content.pm.ShortcutInfo
|
||||||
import android.content.pm.ShortcutManager
|
import android.content.pm.ShortcutManager
|
||||||
import android.graphics.drawable.Icon
|
import android.graphics.drawable.Icon
|
||||||
import android.os.Build
|
|
||||||
import androidx.annotation.RequiresApi
|
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import app.passwordstore.R
|
import app.passwordstore.R
|
||||||
import app.passwordstore.data.password.PasswordItem
|
import app.passwordstore.data.password.PasswordItem
|
||||||
|
@ -42,7 +40,6 @@ constructor(
|
||||||
* [MAX_SHORTCUT_COUNT] and older items are removed by a simple LRU sweep.
|
* [MAX_SHORTCUT_COUNT] and older items are removed by a simple LRU sweep.
|
||||||
*/
|
*/
|
||||||
fun addDynamicShortcut(item: PasswordItem, intent: Intent) {
|
fun addDynamicShortcut(item: PasswordItem, intent: Intent) {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) return
|
|
||||||
val shortcutManager: ShortcutManager = context.getSystemService() ?: return
|
val shortcutManager: ShortcutManager = context.getSystemService() ?: return
|
||||||
val shortcut = buildShortcut(item, intent)
|
val shortcut = buildShortcut(item, intent)
|
||||||
val shortcuts = shortcutManager.dynamicShortcuts
|
val shortcuts = shortcutManager.dynamicShortcuts
|
||||||
|
@ -67,7 +64,6 @@ constructor(
|
||||||
* a no-op if the user's default launcher does not support pinned shortcuts.
|
* a no-op if the user's default launcher does not support pinned shortcuts.
|
||||||
*/
|
*/
|
||||||
fun addPinnedShortcut(item: PasswordItem, intent: Intent) {
|
fun addPinnedShortcut(item: PasswordItem, intent: Intent) {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
|
|
||||||
val shortcutManager: ShortcutManager = context.getSystemService() ?: return
|
val shortcutManager: ShortcutManager = context.getSystemService() ?: return
|
||||||
if (!shortcutManager.isRequestPinShortcutSupported) {
|
if (!shortcutManager.isRequestPinShortcutSupported) {
|
||||||
logcat { "addPinnedShortcut: pin shortcuts unsupported" }
|
logcat { "addPinnedShortcut: pin shortcuts unsupported" }
|
||||||
|
@ -78,7 +74,6 @@ constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a [ShortcutInfo] from [item] and assigns [intent] to it. */
|
/** Creates a [ShortcutInfo] from [item] and assigns [intent] to it. */
|
||||||
@RequiresApi(Build.VERSION_CODES.N_MR1)
|
|
||||||
private fun buildShortcut(item: PasswordItem, intent: Intent): ShortcutInfo {
|
private fun buildShortcut(item: PasswordItem, intent: Intent): ShortcutInfo {
|
||||||
return ShortcutInfo.Builder(context, item.fullPathToParent)
|
return ShortcutInfo.Builder(context, item.fullPathToParent)
|
||||||
.setShortLabel(item.toString())
|
.setShortLabel(item.toString())
|
||||||
|
@ -93,7 +88,6 @@ constructor(
|
||||||
* data, which ensures that the get/set dance in [addDynamicShortcut] does not cause invalidation
|
* data, which ensures that the get/set dance in [addDynamicShortcut] does not cause invalidation
|
||||||
* of icon assets, resulting in invisible icons in all but the newest launcher shortcut.
|
* of icon assets, resulting in invisible icons in all but the newest launcher shortcut.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(Build.VERSION_CODES.N_MR1)
|
|
||||||
private fun rebuildShortcut(shortcut: ShortcutInfo): ShortcutInfo {
|
private fun rebuildShortcut(shortcut: ShortcutInfo): ShortcutInfo {
|
||||||
// Non-null assertions are fine since we know these values aren't null.
|
// Non-null assertions are fine since we know these values aren't null.
|
||||||
return ShortcutInfo.Builder(context, shortcut.id)
|
return ShortcutInfo.Builder(context, shortcut.id)
|
||||||
|
|
|
@ -14,7 +14,6 @@ import android.content.IntentSender
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.autofill.AutofillManager
|
import android.view.autofill.AutofillManager
|
||||||
import androidx.annotation.RequiresApi
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import app.passwordstore.databinding.ActivityOreoAutofillSmsBinding
|
import app.passwordstore.databinding.ActivityOreoAutofillSmsBinding
|
||||||
|
@ -52,7 +51,6 @@ suspend fun <T> Task<T>.suspendableAwait() =
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.O)
|
|
||||||
class AutofillSmsActivity : AppCompatActivity() {
|
class AutofillSmsActivity : AppCompatActivity() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
|
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
|
||||||
* SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception
|
* SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception
|
||||||
*/
|
*/
|
||||||
|
@file:Suppress("UnstableApiUsage")
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("com.github.android-password-store.published-android-library")
|
id("com.github.android-password-store.published-android-library")
|
||||||
id("com.github.android-password-store.kotlin-android")
|
id("com.github.android-password-store.kotlin-android")
|
||||||
|
@ -9,7 +11,10 @@ plugins {
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
defaultConfig { consumerProguardFiles("consumer-rules.pro") }
|
defaultConfig {
|
||||||
|
minSdk = 23
|
||||||
|
consumerProguardFiles("consumer-rules.pro")
|
||||||
|
}
|
||||||
sourceSets { getByName("test") { resources.srcDir("src/main/assets") } }
|
sourceSets { getByName("test") { resources.srcDir("src/main/assets") } }
|
||||||
namespace = "com.github.androidpasswordstore.autofillparser"
|
namespace = "com.github.androidpasswordstore.autofillparser"
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,14 +19,8 @@ object AndroidCommon {
|
||||||
project.extensions.configure<TestedExtension> {
|
project.extensions.configure<TestedExtension> {
|
||||||
setCompileSdkVersion(33)
|
setCompileSdkVersion(33)
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdk = 23
|
minSdk = 26
|
||||||
targetSdk = 31
|
targetSdk = 33
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
|
||||||
named("main") { java.srcDirs("src/main/kotlin") }
|
|
||||||
named("test") { java.srcDirs("src/test/kotlin") }
|
|
||||||
named("androidTest") { java.srcDirs("src/androidTest/kotlin") }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
packagingOptions {
|
packagingOptions {
|
||||||
|
@ -47,7 +41,8 @@ object AndroidCommon {
|
||||||
animationsDisabled = true
|
animationsDisabled = true
|
||||||
unitTests.isReturnDefaultValues = true
|
unitTests.isReturnDefaultValues = true
|
||||||
}
|
}
|
||||||
project.tasks.withType<Test> {
|
|
||||||
|
project.tasks.withType<Test>().configureEach {
|
||||||
jvmArgs(
|
jvmArgs(
|
||||||
"--add-opens=java.base/java.lang=ALL-UNNAMED",
|
"--add-opens=java.base/java.lang=ALL-UNNAMED",
|
||||||
"--add-opens=java.base/java.util=ALL-UNNAMED",
|
"--add-opens=java.base/java.util=ALL-UNNAMED",
|
||||||
|
|
|
@ -23,7 +23,9 @@ class PublishedAndroidLibraryPlugin : Plugin<Project> {
|
||||||
}
|
}
|
||||||
project.extensions.configure<MavenPublishBaseExtension> {
|
project.extensions.configure<MavenPublishBaseExtension> {
|
||||||
publishToMavenCentral(SonatypeHost.DEFAULT, true)
|
publishToMavenCentral(SonatypeHost.DEFAULT, true)
|
||||||
signAllPublications()
|
if (project.providers.environmentVariable("CI").isPresent) {
|
||||||
|
signAllPublications()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
project.extensions.configure<MetalavaExtension> {
|
project.extensions.configure<MetalavaExtension> {
|
||||||
documentation.set(Documentation.PUBLIC)
|
documentation.set(Documentation.PUBLIC)
|
||||||
|
|
Loading…
Reference in a new issue