mirror of
https://github.com/bitwarden/android.git
synced 2026-10-22 11:02:51 -05:00
[PM-40488] fix: clear clipboard setting does not clear the clipboard on Android 13+ (#7169)
Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
This commit is contained in:
1 parent
e324c23353
commit
d70e13393e
2 files changed
+113
-2
No files matched your search
+21
-2
@@ -1,16 +1,19 @@
|
||||
package com.x8bit.bitwarden.data.platform.manager.clipboard
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipDescription
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Context.CLIPBOARD_SERVICE
|
||||
import android.os.Build
|
||||
import android.os.PersistableBundle
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
import com.bitwarden.annotation.OmitFromCoverage
|
||||
import com.bitwarden.core.util.isBuildVersionAtLeast
|
||||
|
||||
/**
|
||||
* A worker to clear the clipboard manager.
|
||||
*/
|
||||
@OmitFromCoverage
|
||||
class ClearClipboardWorker(
|
||||
appContext: Context,
|
||||
workerParams: WorkerParameters,
|
||||
@@ -20,6 +23,22 @@ class ClearClipboardWorker(
|
||||
appContext.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
||||
|
||||
override fun doWork(): Result {
|
||||
clipboardManager.setPrimaryClip(
|
||||
ClipData
|
||||
.newPlainText("", "")
|
||||
.apply {
|
||||
description.extras = PersistableBundle().apply {
|
||||
putBoolean(
|
||||
if (isBuildVersionAtLeast(version = Build.VERSION_CODES.TIRAMISU)) {
|
||||
ClipDescription.EXTRA_IS_SENSITIVE
|
||||
} else {
|
||||
"android.content.extra.IS_SENSITIVE"
|
||||
},
|
||||
true,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
clipboardManager.clearPrimaryClip()
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
package com.x8bit.bitwarden.data.platform.manager.clipboard
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipDescription
|
||||
import android.content.ClipboardManager
|
||||
import android.os.Build
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.work.ListenableWorker
|
||||
import com.bitwarden.ui.platform.base.BaseRobolectricTest
|
||||
import io.mockk.mockk
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.robolectric.RuntimeEnvironment
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
class ClearClipboardWorkerTest : BaseRobolectricTest() {
|
||||
|
||||
private val context = RuntimeEnvironment.getApplication()
|
||||
private val clipboardManager = requireNotNull(context.getSystemService<ClipboardManager>())
|
||||
private val worker: ClearClipboardWorker = ClearClipboardWorker(
|
||||
appContext = context,
|
||||
workerParams = mockk(relaxed = true),
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `doWork should clear a populated clipboard and return success`() {
|
||||
clipboardManager.setPrimaryClip(ClipData.newPlainText("", "password"))
|
||||
|
||||
val result = worker.doWork()
|
||||
|
||||
assertTrue(clipboardManager.primaryClip?.getItemAt(0)?.text.isNullOrEmpty())
|
||||
assertEquals(ListenableWorker.Result.success(), result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `doWork should return success when the clipboard is already empty`() {
|
||||
clipboardManager.clearPrimaryClip()
|
||||
|
||||
val result = worker.doWork()
|
||||
|
||||
assertFalse(clipboardManager.hasPrimaryClip())
|
||||
assertEquals(ListenableWorker.Result.success(), result)
|
||||
}
|
||||
|
||||
@Config(sdk = [Build.VERSION_CODES.TIRAMISU])
|
||||
@Test
|
||||
fun `doWork should overwrite the clipboard with sensitive empty content before clearing`() {
|
||||
val overwrittenClip = captureEmptyClipboardOverwrite()
|
||||
|
||||
assertTrue(
|
||||
overwrittenClip
|
||||
?.description
|
||||
?.extras
|
||||
?.getBoolean(ClipDescription.EXTRA_IS_SENSITIVE) == true,
|
||||
)
|
||||
}
|
||||
|
||||
@Config(sdk = [Build.VERSION_CODES.S_V2])
|
||||
@Test
|
||||
fun `doWork should use the legacy sensitive extra before Android 13`() {
|
||||
val overwrittenClip = captureEmptyClipboardOverwrite()
|
||||
|
||||
assertTrue(
|
||||
overwrittenClip
|
||||
?.description
|
||||
?.extras
|
||||
?.getBoolean("android.content.extra.IS_SENSITIVE") == true,
|
||||
)
|
||||
}
|
||||
|
||||
private fun captureEmptyClipboardOverwrite(): ClipData? {
|
||||
var overwrittenClip: ClipData? = null
|
||||
val listener = ClipboardManager.OnPrimaryClipChangedListener {
|
||||
val currentClip = clipboardManager.primaryClip
|
||||
if (
|
||||
overwrittenClip == null &&
|
||||
currentClip?.getItemAt(0)?.text.isNullOrEmpty()
|
||||
) {
|
||||
overwrittenClip = currentClip
|
||||
}
|
||||
}
|
||||
clipboardManager.setPrimaryClip(ClipData.newPlainText("", "password"))
|
||||
clipboardManager.addPrimaryClipChangedListener(listener)
|
||||
|
||||
worker.doWork()
|
||||
|
||||
clipboardManager.removePrimaryClipChangedListener(listener)
|
||||
return overwrittenClip
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user