mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-02 12:07:28 +00:00
Merge pull request #7857 from element-hq/feature/bma/copyToClipboardNoDoubleToast
Mark the recovery key as sensitive when copying it, and do not show a copy confirmation on Android 13+
This commit is contained in:
+28
-2
@@ -9,8 +9,11 @@
|
||||
package io.element.android.libraries.androidutils.clipboard
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipDescription
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.PersistableBundle
|
||||
import androidx.core.content.getSystemService
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
@@ -24,7 +27,30 @@ class AndroidClipboardHelper(
|
||||
) : ClipboardHelper {
|
||||
private val clipboardManager = requireNotNull(context.getSystemService<ClipboardManager>())
|
||||
|
||||
override fun copyPlainText(text: String) {
|
||||
clipboardManager.setPrimaryClip(ClipData.newPlainText("", text))
|
||||
override fun copyPlainText(
|
||||
text: String,
|
||||
isSensitive: Boolean,
|
||||
onSuccessOnOldDevice: () -> Unit,
|
||||
) {
|
||||
val clipData = ClipData.newPlainText("", text)
|
||||
if (isSensitive) {
|
||||
clipData.description.extras = PersistableBundle().apply {
|
||||
putBoolean(EXTRA_IS_SENSITIVE, true)
|
||||
}
|
||||
}
|
||||
clipboardManager.setPrimaryClip(clipData)
|
||||
// On Android 13+, the system already displays a confirmation when content is copied to the clipboard.
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
onSuccessOnOldDevice()
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
// The constant only exists from API 33, but the documentation recommends using its value on older versions too.
|
||||
val EXTRA_IS_SENSITIVE = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
ClipDescription.EXTRA_IS_SENSITIVE
|
||||
} else {
|
||||
"android.content.extra.IS_SENSITIVE"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-4
@@ -8,9 +8,18 @@
|
||||
|
||||
package io.element.android.libraries.androidutils.clipboard
|
||||
|
||||
/**
|
||||
* Wrapper class for handling clipboard operations so it can be used in JVM environments.
|
||||
*/
|
||||
interface ClipboardHelper {
|
||||
fun copyPlainText(text: String)
|
||||
/**
|
||||
* Copy a text to the clipboard.
|
||||
*
|
||||
* @param text the text to copy
|
||||
* @param isSensitive if true, the clip is flagged so the system clipboard preview (Android 13+) and keyboards hide its content.
|
||||
* @param onSuccessOnOldDevice invoked after the copy only on Android versions older than 13, since newer versions already
|
||||
* display a confirmation. Can be used to render a Toast or a Snackbar.
|
||||
*/
|
||||
fun copyPlainText(
|
||||
text: String,
|
||||
isSensitive: Boolean = false,
|
||||
onSuccessOnOldDevice: () -> Unit,
|
||||
)
|
||||
}
|
||||
|
||||
+13
-2
@@ -8,10 +8,21 @@
|
||||
|
||||
package io.element.android.libraries.androidutils.clipboard
|
||||
|
||||
class FakeClipboardHelper : ClipboardHelper {
|
||||
class FakeClipboardHelper(
|
||||
private val isOldDevice: Boolean = true,
|
||||
) : ClipboardHelper {
|
||||
var clipboardContents: Any? = null
|
||||
var isSensitive: Boolean? = null
|
||||
|
||||
override fun copyPlainText(text: String) {
|
||||
override fun copyPlainText(
|
||||
text: String,
|
||||
isSensitive: Boolean,
|
||||
onSuccessOnOldDevice: () -> Unit,
|
||||
) {
|
||||
clipboardContents = text
|
||||
this.isSensitive = isSensitive
|
||||
if (isOldDevice) {
|
||||
onSuccessOnOldDevice()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2022-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.androidutils.system
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import androidx.core.content.getSystemService
|
||||
|
||||
class CopyToClipboardUseCase(
|
||||
private val context: Context,
|
||||
) {
|
||||
fun execute(text: CharSequence) {
|
||||
context.getSystemService<ClipboardManager>()
|
||||
?.setPrimaryClip(ClipData.newPlainText("", text))
|
||||
}
|
||||
}
|
||||
-19
@@ -50,25 +50,6 @@ fun Context.getVersionCodeFromManifest(): Long {
|
||||
)
|
||||
}
|
||||
|
||||
// ==============================================================================================================
|
||||
// Clipboard helper
|
||||
// ==============================================================================================================
|
||||
|
||||
/**
|
||||
* Copy a text to the clipboard, and display a Toast when done.
|
||||
*
|
||||
* @receiver the context
|
||||
* @param text the text to copy
|
||||
* @param toastMessage content of the toast message as a String resource. Null for no toast
|
||||
*/
|
||||
fun Context.copyToClipboard(
|
||||
text: CharSequence,
|
||||
toastMessage: String? = null
|
||||
) {
|
||||
CopyToClipboardUseCase(this).execute(text)
|
||||
toastMessage?.let { toast(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows notification settings for the current app.
|
||||
* In android O will directly opens the notification settings, in lower version it will show the App settings
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.androidutils.toast
|
||||
|
||||
import android.content.Context
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.libraries.androidutils.system.toast
|
||||
import io.element.android.libraries.di.annotations.ApplicationContext
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class AndroidToastHelper(
|
||||
@ApplicationContext private val context: Context,
|
||||
) : ToastHelper {
|
||||
override fun show(resId: Int) {
|
||||
context.toast(resId)
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.androidutils.toast
|
||||
|
||||
class FakeToastHelper : ToastHelper {
|
||||
val shownToasts = mutableListOf<Int>()
|
||||
|
||||
override fun show(resId: Int) {
|
||||
shownToasts.add(resId)
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.androidutils.toast
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
|
||||
interface ToastHelper {
|
||||
fun show(@StringRes resId: Int)
|
||||
}
|
||||
Reference in New Issue
Block a user