aboutsummaryrefslogtreecommitdiffstats
path: root/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'app/src')
-rw-r--r--app/src/main/java/app/passwordstore/ui/autofill/AutofillPublisherChangedActivity.kt115
-rw-r--r--app/src/main/java/app/passwordstore/ui/autofill/PublisherChangedScreen.kt121
-rw-r--r--app/src/main/res/layout/activity_oreo_autofill_publisher_changed.xml174
-rw-r--r--app/src/main/res/values/dimens.xml1
4 files changed, 175 insertions, 236 deletions
diff --git a/app/src/main/java/app/passwordstore/ui/autofill/AutofillPublisherChangedActivity.kt b/app/src/main/java/app/passwordstore/ui/autofill/AutofillPublisherChangedActivity.kt
index 5b55143a..803896a5 100644
--- a/app/src/main/java/app/passwordstore/ui/autofill/AutofillPublisherChangedActivity.kt
+++ b/app/src/main/java/app/passwordstore/ui/autofill/AutofillPublisherChangedActivity.kt
@@ -13,21 +13,20 @@ import android.os.Build
import android.os.Bundle
import android.service.autofill.FillResponse
import android.text.format.DateUtils
-import android.view.View
import android.view.autofill.AutofillManager
+import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.IntentCompat
import app.passwordstore.R
-import app.passwordstore.databinding.ActivityOreoAutofillPublisherChangedBinding
+import app.passwordstore.ui.compose.theme.APSTheme
import app.passwordstore.util.autofill.AutofillMatcher
import app.passwordstore.util.autofill.AutofillPublisherChangedException
import app.passwordstore.util.extensions.asLog
import app.passwordstore.util.extensions.getApplicationInfoCompat
import app.passwordstore.util.extensions.getPackageInfoCompat
-import app.passwordstore.util.extensions.viewBinding
import com.github.androidpasswordstore.autofillparser.FormOrigin
import com.github.androidpasswordstore.autofillparser.computeCertificatesHash
-import com.github.michaelbull.result.onFailure
+import com.github.michaelbull.result.getOrElse
import com.github.michaelbull.result.runCatching
import logcat.LogPriority.ERROR
import logcat.logcat
@@ -65,15 +64,15 @@ class AutofillPublisherChangedActivity : AppCompatActivity() {
}
}
- private lateinit var appPackage: String
- private val binding by viewBinding(ActivityOreoAutofillPublisherChangedBinding::inflate)
-
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setContentView(binding.root)
setFinishOnTouchOutside(true)
+ // Tapjacking guard: a touch that reaches this window through another app's window (an overlay
+ // drawn to trick the user into re-enabling) is dropped. ViewGroup.dispatchTouchEvent checks the
+ // decor view's own flag before dispatching to any child, so the ComposeView is covered.
+ window.decorView.filterTouchesWhenObscured = true
- appPackage =
+ val appPackage =
intent.getStringExtra(EXTRA_APP_PACKAGE)
?: run {
logcat(ERROR) { "AutofillPublisherChangedActivity started without EXTRA_PACKAGE_NAME" }
@@ -81,63 +80,57 @@ class AutofillPublisherChangedActivity : AppCompatActivity() {
return
}
supportActionBar?.hide()
- showPackageInfo()
- with(binding) {
- okButton.setOnClickListener { finish() }
- advancedButton.setOnClickListener {
- advancedButton.visibility = View.GONE
- warningAppAdvancedInfo.visibility = View.VISIBLE
- resetButton.visibility = View.VISIBLE
- }
- resetButton.setOnClickListener {
- AutofillMatcher.clearMatchesFor(
- this@AutofillPublisherChangedActivity,
- FormOrigin.App(appPackage),
- )
- val fillResponse =
- IntentCompat.getParcelableExtra(
- intent,
- EXTRA_FILL_RESPONSE_AFTER_RESET,
- FillResponse::class.java,
+ val (packageInfo, appInfo, hash) =
+ runCatching {
+ Triple(
+ packageManager.getPackageInfoCompat(appPackage, PackageManager.GET_META_DATA),
+ packageManager.getApplicationInfoCompat(appPackage, PackageManager.GET_META_DATA),
+ computeCertificatesHash(this, appPackage),
)
- setResult(
- RESULT_OK,
- Intent().apply { putExtra(AutofillManager.EXTRA_AUTHENTICATION_RESULT, fillResponse) },
+ }
+ .getOrElse { e ->
+ logcat(ERROR) { e.asLog("Failed to retrieve package info for $appPackage") }
+ finish()
+ return
+ }
+ val appName =
+ getString(
+ R.string.oreo_autofill_warning_publisher_app_name,
+ packageManager.getApplicationLabel(appInfo),
+ )
+ val installed =
+ getString(
+ R.string.oreo_autofill_warning_publisher_install_time,
+ DateUtils.getRelativeTimeSpanString(packageInfo.firstInstallTime),
+ )
+ val details =
+ getString(R.string.oreo_autofill_warning_publisher_advanced_info_template, appPackage, hash)
+ setContent {
+ APSTheme {
+ PublisherChangedScreen(
+ appName = appName,
+ installed = installed,
+ details = details,
+ onKeepDisabled = { finish() },
+ onReset = { reset(appPackage) },
)
- finish()
}
}
}
- private fun showPackageInfo() {
- runCatching {
- with(binding) {
- val packageInfo =
- packageManager.getPackageInfoCompat(appPackage, PackageManager.GET_META_DATA)
- val installTime = DateUtils.getRelativeTimeSpanString(packageInfo.firstInstallTime)
- warningAppInstallDate.text =
- getString(R.string.oreo_autofill_warning_publisher_install_time, installTime)
- val appInfo =
- packageManager.getApplicationInfoCompat(appPackage, PackageManager.GET_META_DATA)
- warningAppName.text =
- getString(
- R.string.oreo_autofill_warning_publisher_app_name,
- packageManager.getApplicationLabel(appInfo),
- )
-
- val currentHash =
- computeCertificatesHash(this@AutofillPublisherChangedActivity, appPackage)
- warningAppAdvancedInfo.text =
- getString(
- R.string.oreo_autofill_warning_publisher_advanced_info_template,
- appPackage,
- currentHash,
- )
- }
- }
- .onFailure { e ->
- logcat(ERROR) { e.asLog("Failed to retrieve package info for $appPackage") }
- finish()
- }
+ /** Forgets the app's matches and fills with the response the service prepared for this case. */
+ private fun reset(appPackage: String) {
+ AutofillMatcher.clearMatchesFor(this, FormOrigin.App(appPackage))
+ val fillResponse =
+ IntentCompat.getParcelableExtra(
+ intent,
+ EXTRA_FILL_RESPONSE_AFTER_RESET,
+ FillResponse::class.java,
+ )
+ setResult(
+ RESULT_OK,
+ Intent().apply { putExtra(AutofillManager.EXTRA_AUTHENTICATION_RESULT, fillResponse) },
+ )
+ finish()
}
}
diff --git a/app/src/main/java/app/passwordstore/ui/autofill/PublisherChangedScreen.kt b/app/src/main/java/app/passwordstore/ui/autofill/PublisherChangedScreen.kt
new file mode 100644
index 00000000..8c210b79
--- /dev/null
+++ b/app/src/main/java/app/passwordstore/ui/autofill/PublisherChangedScreen.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package app.passwordstore.ui.autofill
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.text.selection.SelectionContainer
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.Button
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedButton
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+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.text.font.FontFamily
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import app.passwordstore.R
+import app.passwordstore.ui.compose.theme.SpacingLarge
+import app.passwordstore.ui.compose.theme.SpacingLargest
+
+/**
+ * Warns that the app being filled ([appName], installed [installed]) is signed by another publisher
+ * than when an entry was first matched to it. [onKeepDisabled] fills nothing. [onReset] forgets the
+ * old matches and fills; it is offered only after "Advanced information", which shows [details]
+ * (package and certificate hash), so re-enabling always takes two deliberate taps.
+ */
+@Composable
+fun PublisherChangedScreen(
+ appName: String,
+ installed: String,
+ details: String,
+ onKeepDisabled: () -> Unit,
+ onReset: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ var advanced by rememberSaveable { mutableStateOf(false) }
+ val center = Modifier.fillMaxWidth()
+ Surface(modifier) {
+ Column(
+ Modifier.verticalScroll(rememberScrollState()).padding(SpacingLargest),
+ verticalArrangement = Arrangement.spacedBy(SpacingLarge),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Icon(
+ painterResource(R.drawable.ic_warning_red_24dp),
+ contentDescription =
+ stringResource(R.string.oreo_autofill_warning_publisher_warning_sign_description),
+ modifier = Modifier.size(40.dp),
+ tint = MaterialTheme.colorScheme.error,
+ )
+ // ponytail: stringResource drops the footer's <b> in every locale but Italian (whose footer
+ // has none); the red title carries the emphasis. Keep spans with getText if it is missed.
+ Text(
+ stringResource(R.string.oreo_autofill_warning_publisher_dataset_title),
+ modifier = center,
+ color = MaterialTheme.colorScheme.error,
+ textAlign = TextAlign.Center,
+ style = MaterialTheme.typography.titleLarge,
+ )
+ Text(
+ stringResource(R.string.oreo_autofill_warning_publisher_header),
+ style = MaterialTheme.typography.bodyMedium,
+ )
+ Text(
+ appName,
+ modifier = center,
+ textAlign = TextAlign.Center,
+ style = MaterialTheme.typography.titleMedium,
+ )
+ Text(
+ installed,
+ modifier = center,
+ textAlign = TextAlign.Center,
+ style = MaterialTheme.typography.bodySmall,
+ )
+ Text(
+ stringResource(R.string.oreo_autofill_warning_publisher_footer),
+ style = MaterialTheme.typography.bodyMedium,
+ )
+ Button(onClick = onKeepDisabled, modifier = center) {
+ Text(
+ stringResource(R.string.oreo_autofill_warning_publisher_changed_disable_autofill_button)
+ )
+ }
+ if (!advanced) {
+ OutlinedButton(onClick = { advanced = true }, modifier = center) {
+ Text(stringResource(R.string.oreo_autofill_warning_publisher_advanced_info_button))
+ }
+ } else {
+ OutlinedButton(onClick = onReset, modifier = center) {
+ Text(stringResource(R.string.oreo_autofill_warning_publisher_reenable_button))
+ }
+ SelectionContainer {
+ Text(
+ details,
+ modifier = center,
+ textAlign = TextAlign.Center,
+ style = MaterialTheme.typography.bodySmall.copy(fontFamily = FontFamily.Monospace),
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/res/layout/activity_oreo_autofill_publisher_changed.xml b/app/src/main/res/layout/activity_oreo_autofill_publisher_changed.xml
deleted file mode 100644
index 89d64fe4..00000000
--- a/app/src/main/res/layout/activity_oreo_autofill_publisher_changed.xml
+++ /dev/null
@@ -1,174 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- ~ SPDX-License-Identifier: GPL-3.0-only
- -->
-
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:animateLayoutChanges="true"
- android:elevation="2dp"
- android:paddingBottom="16dp"
- android:scrollbars="vertical"
- tools:context="app.passwordstore.ui.autofill.AutofillPublisherChangedActivity">
-
- <ImageView
- android:id="@+id/cover"
- android:layout_width="60dp"
- android:layout_height="60dp"
- android:background="@android:color/transparent"
- android:contentDescription="@string/app_name"
- android:src="@drawable/ic_launcher_foreground"
- app:layout_constraintBottom_toTopOf="@id/warningSign"
- app:layout_constraintEnd_toStartOf="@id/app_name"
- app:layout_constraintHorizontal_chainStyle="packed"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
-
- <TextView
- android:id="@+id/app_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/app_name"
- android:textSize="18sp"
- android:textStyle="bold"
- app:layout_constraintBottom_toBottomOf="@id/cover"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toEndOf="@id/cover"
- app:layout_constraintTop_toTopOf="@id/cover" />
-
- <ImageView
- android:id="@+id/warningSign"
- android:layout_width="40dp"
- android:layout_height="40dp"
- android:layout_marginStart="@dimen/activity_horizontal_margin"
- android:contentDescription="@string/oreo_autofill_warning_publisher_warning_sign_description"
- android:src="@drawable/ic_warning_red_24dp"
- app:layout_constraintBottom_toBottomOf="@id/warningHeader"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="@id/warningHeader" />
-
- <TextView
- android:id="@+id/warningHeader"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="8dp"
- android:layout_marginTop="@dimen/activity_vertical_margin"
- android:layout_marginEnd="@dimen/activity_horizontal_margin"
- android:text="@string/oreo_autofill_warning_publisher_header"
- android:textSize="12sp"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toEndOf="@id/warningSign"
- app:layout_constraintTop_toBottomOf="@id/cover" />
-
- <TextView
- android:id="@+id/warningAppName"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/activity_horizontal_margin"
- android:layout_marginTop="@dimen/activity_vertical_margin"
- android:layout_marginEnd="@dimen/activity_horizontal_margin"
- android:gravity="center_horizontal"
- android:textSize="12sp"
- app:layout_constraintBottom_toTopOf="@id/warningAppInstallDate"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/warningHeader"
- tools:text="Example Banking" />
-
- <TextView
- android:id="@+id/warningAppInstallDate"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/activity_horizontal_margin"
- android:layout_marginEnd="@dimen/activity_horizontal_margin"
- android:gravity="center_horizontal"
- android:textSize="12sp"
- app:layout_constraintBottom_toTopOf="@id/warningAppFooter"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/warningAppName"
- tools:text="Installed: 5 days ago" />
-
- <TextView
- android:id="@+id/warningAppFooter"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/activity_horizontal_margin"
- android:layout_marginTop="@dimen/activity_vertical_margin"
- android:layout_marginEnd="@dimen/activity_horizontal_margin"
- android:text="@string/oreo_autofill_warning_publisher_footer"
- android:textSize="12sp"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/warningAppInstallDate" />
-
- <com.google.android.material.button.MaterialButton
- android:id="@+id/okButton"
- style="?attr/materialButtonOutlinedStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/activity_horizontal_margin"
- android:layout_marginTop="@dimen/activity_vertical_margin"
- android:layout_marginEnd="@dimen/activity_horizontal_margin"
- android:minWidth="240dp"
- android:text="@string/oreo_autofill_warning_publisher_changed_disable_autofill_button"
- android:textSize="12sp"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/warningAppFooter" />
-
- <com.google.android.material.button.MaterialButton
- android:id="@+id/advancedButton"
- style="?attr/materialButtonOutlinedStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/activity_horizontal_margin"
- android:layout_marginEnd="@dimen/activity_horizontal_margin"
- android:minWidth="240dp"
- android:text="@string/oreo_autofill_warning_publisher_advanced_info_button"
- android:textSize="12sp"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/okButton"
- tools:visibility="visible" />
-
- <com.google.android.material.button.MaterialButton
- android:id="@+id/resetButton"
- style="?attr/materialButtonOutlinedStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/activity_horizontal_margin"
- android:layout_marginEnd="@dimen/activity_horizontal_margin"
- android:minWidth="240dp"
- android:text="@string/oreo_autofill_warning_publisher_reenable_button"
- android:textSize="12sp"
- android:visibility="gone"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/advancedButton"
- tools:visibility="visible" />
-
- <TextView
- android:id="@+id/warningAppAdvancedInfo"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/activity_horizontal_margin"
- android:layout_marginTop="16dp"
- android:layout_marginEnd="@dimen/activity_horizontal_margin"
- android:fontFamily="@font/jetbrainsmono_nl_regular"
- android:gravity="center_horizontal"
- android:textIsSelectable="true"
- android:textSize="10sp"
- android:visibility="gone"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/resetButton"
- tools:ignore="SmallSp"
- tools:text="Package: com.example.banking\n\nHash:\n8P1sW0EPJcslw7UzRsiXL64w+O50Ed+RBICtay1g24M="
- tools:visibility="visible" />
-
-</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 44d4f581..de2ac854 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -6,5 +6,4 @@
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
- <dimen name="activity_vertical_margin">16dp</dimen>
</resources>