Fixed
Status Update
Comments
le...@gmail.com <le...@gmail.com> #2
Project: platform/frameworks/support
Branch: androidx-master-dev
commit c60f33e229e31ab328ef6b59dab63b264954831c
Author: Alexandre Elias <aelias@google.com>
Date: Fri Jul 10 16:23:09 2020
Semantics no-op cleanups
Partly in response to lmr's broad code review, I did a pass of
superficial API/implementation cleanup. The main changes are:
- I changed each Boolean SemanticsProperty where false is equivalent to
not being present to take "Unit" instead. This is conceptually
clearer: it avoids questions like "can I cancel out a semantics from a
merged child by setting it to false?" Because "property = Unit" looks
weird, I also changed the style of these to "property()".
- I moved the Semantics id generator closer to where it's used, in
SemanticsModifierCore. I made it internal and an AtomicInt.
(Note that integer ids are heavily used in the Android
AccessibilityNodeInfo APIs so I can't simply remove them entirely.)
- I deleted dead code. Some examples include SemanticsHintOverrides,
a public API not connected to anything, and SemanticsPropertyKey
merge() open method which is never called. (In both cases I have
a different plan in mind for accessibility.)
Fixes: 145951226
Fixes: 145955412
Test: existing tests
Relnote: "Single-value semantics properties now use a calling style.
For example, 'semantics { hidden = true }' is now written as:
'semantics { hidden() }'."
Change-Id: Ic1afd12ea22c926babc9662f1804d80b33aa0cfc
M ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/LayoutNodeModifierBenchmark.kt
M ui/ui-core/api/0.1.0-dev15.txt
M ui/ui-core/api/current.txt
M ui/ui-core/api/public_plus_experimental_0.1.0-dev15.txt
M ui/ui-core/api/public_plus_experimental_current.txt
M ui/ui-core/api/restricted_0.1.0-dev15.txt
M ui/ui-core/api/restricted_current.txt
M ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/graphics/vector/VectorTest.kt
M ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/semantics/SemanticsTests.kt
M ui/ui-core/src/androidMain/kotlin/androidx/ui/core/AndroidActuals.kt
M ui/ui-core/src/androidMain/kotlin/androidx/ui/core/AndroidComposeView.kt
M ui/ui-core/src/androidMain/kotlin/androidx/ui/core/AndroidComposeViewAccessibilityDelegateCompat.kt
M ui/ui-core/src/androidMain/kotlin/androidx/ui/core/AndroidPopup.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/core/Expect.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/core/semantics/SemanticsConfiguration.kt
D ui/ui-core/src/commonMain/kotlin/androidx/ui/core/semantics/SemanticsHintOverrides.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/core/semantics/SemanticsModifier.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/core/semantics/SemanticsNode.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/core/semantics/SemanticsOwner.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/core/semantics/SemanticsWrapper.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/semantics/SemanticsProperties.kt
M ui/ui-foundation/api/0.1.0-dev15.txt
M ui/ui-foundation/api/current.txt
M ui/ui-foundation/api/public_plus_experimental_0.1.0-dev15.txt
M ui/ui-foundation/api/public_plus_experimental_current.txt
M ui/ui-foundation/api/restricted_0.1.0-dev15.txt
M ui/ui-foundation/api/restricted_current.txt
M ui/ui-foundation/src/main/java/androidx/ui/foundation/Clickable.kt
M ui/ui-foundation/src/main/java/androidx/ui/foundation/Dialog.kt
M ui/ui-foundation/src/main/java/androidx/ui/foundation/Scroller.kt
M ui/ui-foundation/src/main/java/androidx/ui/foundation/selection/Selectable.kt
M ui/ui-foundation/src/main/java/androidx/ui/foundation/selection/Toggleable.kt
M ui/ui-foundation/src/main/java/androidx/ui/foundation/semantics/FoundationSemanticsProperties.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/ButtonTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/CardTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/CheckboxScreenshotTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/RadioButtonScreenshotTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/ScaffoldTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/SnackbarTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/SurfaceTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/ripple/RippleIndicationTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/textfield/TextFieldScreenshotTest.kt
M ui/ui-material/src/main/java/androidx/ui/material/AppBar.kt
M ui/ui-material/src/main/java/androidx/ui/material/TextFieldImpl.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/AssertsTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/CallSemanticsActionTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/ErrorMessagesTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/FindersTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/PrintToStringTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/ScrollToTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/TextActionsTest.kt
M ui/ui-test/src/main/java/androidx/ui/test/Actions.kt
M ui/ui-test/src/main/java/androidx/ui/test/Filters.kt
M ui/ui-text/api/0.1.0-dev15.txt
M ui/ui-text/api/current.txt
M ui/ui-text/api/public_plus_experimental_0.1.0-dev15.txt
M ui/ui-text/api/public_plus_experimental_current.txt
M ui/ui-text/api/restricted_0.1.0-dev15.txt
M ui/ui-text/api/restricted_current.txt
M ui/ui-text/src/commonMain/kotlin/androidx/ui/text/CoreTextField.kt
M ui/ui-text/src/commonMain/kotlin/androidx/ui/text/TextSemanticsProperties.kt
https://android-review.googlesource.com/1360099
Branch: androidx-master-dev
commit c60f33e229e31ab328ef6b59dab63b264954831c
Author: Alexandre Elias <aelias@google.com>
Date: Fri Jul 10 16:23:09 2020
Semantics no-op cleanups
Partly in response to lmr's broad code review, I did a pass of
superficial API/implementation cleanup. The main changes are:
- I changed each Boolean SemanticsProperty where false is equivalent to
not being present to take "Unit" instead. This is conceptually
clearer: it avoids questions like "can I cancel out a semantics from a
merged child by setting it to false?" Because "property = Unit" looks
weird, I also changed the style of these to "property()".
- I moved the Semantics id generator closer to where it's used, in
SemanticsModifierCore. I made it internal and an AtomicInt.
(Note that integer ids are heavily used in the Android
AccessibilityNodeInfo APIs so I can't simply remove them entirely.)
- I deleted dead code. Some examples include SemanticsHintOverrides,
a public API not connected to anything, and SemanticsPropertyKey
merge() open method which is never called. (In both cases I have
a different plan in mind for accessibility.)
Fixes: 145951226
Fixes: 145955412
Test: existing tests
Relnote: "Single-value semantics properties now use a calling style.
For example, 'semantics { hidden = true }' is now written as:
'semantics { hidden() }'."
Change-Id: Ic1afd12ea22c926babc9662f1804d80b33aa0cfc
M ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/LayoutNodeModifierBenchmark.kt
M ui/ui-core/api/0.1.0-dev15.txt
M ui/ui-core/api/current.txt
M ui/ui-core/api/public_plus_experimental_0.1.0-dev15.txt
M ui/ui-core/api/public_plus_experimental_current.txt
M ui/ui-core/api/restricted_0.1.0-dev15.txt
M ui/ui-core/api/restricted_current.txt
M ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/graphics/vector/VectorTest.kt
M ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/semantics/SemanticsTests.kt
M ui/ui-core/src/androidMain/kotlin/androidx/ui/core/AndroidActuals.kt
M ui/ui-core/src/androidMain/kotlin/androidx/ui/core/AndroidComposeView.kt
M ui/ui-core/src/androidMain/kotlin/androidx/ui/core/AndroidComposeViewAccessibilityDelegateCompat.kt
M ui/ui-core/src/androidMain/kotlin/androidx/ui/core/AndroidPopup.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/core/Expect.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/core/semantics/SemanticsConfiguration.kt
D ui/ui-core/src/commonMain/kotlin/androidx/ui/core/semantics/SemanticsHintOverrides.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/core/semantics/SemanticsModifier.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/core/semantics/SemanticsNode.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/core/semantics/SemanticsOwner.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/core/semantics/SemanticsWrapper.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/semantics/SemanticsProperties.kt
M ui/ui-foundation/api/0.1.0-dev15.txt
M ui/ui-foundation/api/current.txt
M ui/ui-foundation/api/public_plus_experimental_0.1.0-dev15.txt
M ui/ui-foundation/api/public_plus_experimental_current.txt
M ui/ui-foundation/api/restricted_0.1.0-dev15.txt
M ui/ui-foundation/api/restricted_current.txt
M ui/ui-foundation/src/main/java/androidx/ui/foundation/Clickable.kt
M ui/ui-foundation/src/main/java/androidx/ui/foundation/Dialog.kt
M ui/ui-foundation/src/main/java/androidx/ui/foundation/Scroller.kt
M ui/ui-foundation/src/main/java/androidx/ui/foundation/selection/Selectable.kt
M ui/ui-foundation/src/main/java/androidx/ui/foundation/selection/Toggleable.kt
M ui/ui-foundation/src/main/java/androidx/ui/foundation/semantics/FoundationSemanticsProperties.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/ButtonTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/CardTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/CheckboxScreenshotTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/RadioButtonScreenshotTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/ScaffoldTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/SnackbarTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/SurfaceTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/ripple/RippleIndicationTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/textfield/TextFieldScreenshotTest.kt
M ui/ui-material/src/main/java/androidx/ui/material/AppBar.kt
M ui/ui-material/src/main/java/androidx/ui/material/TextFieldImpl.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/AssertsTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/CallSemanticsActionTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/ErrorMessagesTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/FindersTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/PrintToStringTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/ScrollToTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/TextActionsTest.kt
M ui/ui-test/src/main/java/androidx/ui/test/Actions.kt
M ui/ui-test/src/main/java/androidx/ui/test/Filters.kt
M ui/ui-text/api/0.1.0-dev15.txt
M ui/ui-text/api/current.txt
M ui/ui-text/api/public_plus_experimental_0.1.0-dev15.txt
M ui/ui-text/api/public_plus_experimental_current.txt
M ui/ui-text/api/restricted_0.1.0-dev15.txt
M ui/ui-text/api/restricted_current.txt
M ui/ui-text/src/commonMain/kotlin/androidx/ui/text/CoreTextField.kt
M ui/ui-text/src/commonMain/kotlin/androidx/ui/text/TextSemanticsProperties.kt
ve...@google.com <ve...@google.com>
ve...@google.com <ve...@google.com> #3
ma...@zentity.com <ma...@zentity.com> #4
Hi I guys, same problem in version 1.0.0-alpha03, but in my case the stacktrace is:
java.lang.NullPointerException: Attempt to invoke virtual method 'androidx.fragment.app.FragmentTransaction androidx.fragment.app.FragmentManager.beginTransaction()' on a null object reference
at androidx.fragment.app.DialogFragment.dismissInternal(DialogFragment.java:219)
at androidx.fragment.app.DialogFragment.dismiss(DialogFragment.java:191)
at androidx.biometric.FingerprintDialogFragment.handleDismissDialog(FingerprintDialogFragment.java:334)
at androidx.biometric.FingerprintDialogFragment$H.handleMessage(FingerprintDialogFragment.java:99)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:156)
at android.app.ActivityThread.main(ActivityThread.java:6623)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:942)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:832)
This happens after screen rotation when I touch with finger to sensor. No special rotation handling in my case, so the view is always recreated.
There is part of code which I use to show the BiometricPrompt dialog:
class SetFingerprintFragment : BaseFragment() {
private val viewModel by viewModel<SetFingerprintViewModel>()
...
private fun scanFingerprint(fingerprintCipher: Cipher) {
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Title TODO")
.setDescription("Desc TODO")
.setNegativeButtonText(getLocalizedString(R.string.general_cancel))
.build()
val biometricPrompt = BiometricPrompt(
requireActivity(),
MainThreadExecutor,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
viewModel.fingerprintConfirmed(requireNotNull(result.cryptoObject?.cipher))
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
if (errorCode == BiometricPrompt.ERROR_NEGATIVE_BUTTON) {
return
}
Toast.makeText(context, errString, Toast.LENGTH_LONG).show()
}
}
)
// authenticate fingerprint and open cipher key for encryption
biometricPrompt.authenticate(promptInfo, BiometricPrompt.CryptoObject(fingerprintCipher))
}
}
java.lang.NullPointerException: Attempt to invoke virtual method 'androidx.fragment.app.FragmentTransaction androidx.fragment.app.FragmentManager.beginTransaction()' on a null object reference
at androidx.fragment.app.DialogFragment.dismissInternal(DialogFragment.java:219)
at androidx.fragment.app.DialogFragment.dismiss(DialogFragment.java:191)
at androidx.biometric.FingerprintDialogFragment.handleDismissDialog(FingerprintDialogFragment.java:334)
at androidx.biometric.FingerprintDialogFragment$H.handleMessage(FingerprintDialogFragment.java:99)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:156)
at android.app.ActivityThread.main(ActivityThread.java:6623)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:942)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:832)
This happens after screen rotation when I touch with finger to sensor. No special rotation handling in my case, so the view is always recreated.
There is part of code which I use to show the BiometricPrompt dialog:
class SetFingerprintFragment : BaseFragment() {
private val viewModel by viewModel<SetFingerprintViewModel>()
...
private fun scanFingerprint(fingerprintCipher: Cipher) {
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Title TODO")
.setDescription("Desc TODO")
.setNegativeButtonText(getLocalizedString(R.string.general_cancel))
.build()
val biometricPrompt = BiometricPrompt(
requireActivity(),
MainThreadExecutor,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
viewModel.fingerprintConfirmed(requireNotNull(result.cryptoObject?.cipher))
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
if (errorCode == BiometricPrompt.ERROR_NEGATIVE_BUTTON) {
return
}
Toast.makeText(context, errString, Toast.LENGTH_LONG).show()
}
}
)
// authenticate fingerprint and open cipher key for encryption
biometricPrompt.authenticate(promptInfo, BiometricPrompt.CryptoObject(fingerprintCipher))
}
}
ma...@zentity.com <ma...@zentity.com> #5
Hi,
Figure out, where is problem. The key is the Biometric Prompt depends on lifecycle observer, which is attached to activity while creating BiometricPrompt instance. Thus it's crucial to create BiometricPrompt instance on every view recreation. In my case the correct implementation is:
class SetFingerprintFragment : BaseFragment() {
private val viewModel by viewModel<SetFingerprintViewModel>()
private lateinit var biometricPrompt: BiometricPrompt
...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
biometricPrompt = BiometricPrompt(
requireActivity(),
MainThreadExecutor,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
viewModel.fingerprintConfirmed(requireNotNull(result.cryptoObject?.cipher))
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
if (errorCode == BiometricPrompt.ERROR_NEGATIVE_BUTTON) {
return
}
Toast.makeText(context, errString, Toast.LENGTH_LONG).show()
}
}
)
}
private fun scanFingerprint(fingerprintCipher: Cipher) {
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Title TODO")
.setDescription("Desc TODO")
.setNegativeButtonText(getLocalizedString(R.string.general_cancel))
.build()
// authenticate fingerprint and open cipher key for encryption
biometricPrompt.authenticate(promptInfo, BiometricPrompt.CryptoObject(fingerprintCipher))
}
}
Figure out, where is problem. The key is the Biometric Prompt depends on lifecycle observer, which is attached to activity while creating BiometricPrompt instance. Thus it's crucial to create BiometricPrompt instance on every view recreation. In my case the correct implementation is:
class SetFingerprintFragment : BaseFragment() {
private val viewModel by viewModel<SetFingerprintViewModel>()
private lateinit var biometricPrompt: BiometricPrompt
...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
biometricPrompt = BiometricPrompt(
requireActivity(),
MainThreadExecutor,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
viewModel.fingerprintConfirmed(requireNotNull(result.cryptoObject?.cipher))
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
if (errorCode == BiometricPrompt.ERROR_NEGATIVE_BUTTON) {
return
}
Toast.makeText(context, errString, Toast.LENGTH_LONG).show()
}
}
)
}
private fun scanFingerprint(fingerprintCipher: Cipher) {
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Title TODO")
.setDescription("Desc TODO")
.setNegativeButtonText(getLocalizedString(R.string.general_cancel))
.build()
// authenticate fingerprint and open cipher key for encryption
biometricPrompt.authenticate(promptInfo, BiometricPrompt.CryptoObject(fingerprintCipher))
}
}
le...@gmail.com <le...@gmail.com> #6
Although recreating the prompt seems to prevent this issue, there is also another problem that occurs when you do so that I reported (https://issuetracker.google.com/issues/123811924 ).
So it seems that whatever approach you take, crashes might occur no matter what.
So it seems that whatever approach you take, crashes might occur no matter what.
kc...@google.com <kc...@google.com> #7
Over to Josh to take a look when he has time
jo...@google.com <jo...@google.com> #8
This bug should be fixed with the above cl and following the steps to recreate the prompt as in #5
Description
- Reproduced on a device with Android O
I'm providing the source code and an apk that reproduce the issue.
Steps to reproduce:
- Start my apk
- Rotate the screen to landscape when biometric dialog is visible (you can rotate an arbitrary number of times, it doesn't matter)
- Place the fingerprint
- Crash
Sourcecode:
Stacktrace:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.lithium.leona.openstud, PID: 6782
java.lang.IllegalStateException: Fragment FingerprintDialogFragment{fcd4576 (b3672114-cb16-4247-b15b-c5517c72cc9d)} not associated with a fragment manager.
at androidx.fragment.app.Fragment.requireFragmentManager(Fragment.java:824)
at androidx.fragment.app.DialogFragment.dismissInternal(DialogFragment.java:219)
at androidx.fragment.app.DialogFragment.dismiss(DialogFragment.java:191)
at androidx.biometric.FingerprintDialogFragment.handleDismissDialog(FingerprintDialogFragment.java:334)
at androidx.biometric.FingerprintDialogFragment$H.handleMessage(FingerprintDialogFragment.java:99)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)