Upgrade to Pro — share decks privately, control downloads, hide ads and more …

IO Extended 23 - What's up with Android Back

IO Extended 23 - What's up with Android Back

GDG Montreal

June 22, 2023
Tweet

More Decks by GDG Montreal

Other Decks in Programming

Transcript

  1. override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { return if

    (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() === 0) { // do something on back. true } else super.onKeyDown(keyCode, event) } https://1000logos.net/android-logo/
  2. NativePreInputStage @Override protected int onProcess(QueuedInputEvent q) { if (q.mEvent instanceof

    KeyEvent) { fi nal KeyEvent event = (KeyEvent) q.mEvent; // If the new back dispatch is enabled, intercept KEYCODE_BACK before it reaches the // view tree or IME, and invoke the appropriate {@link OnBackInvokedCallback}. if (isBack(event) && mContext != null && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) { OnBackInvokedCallback topCallback = getOnBackInvokedDispatcher().getTopCallback(); if (event.getAction() == KeyEvent.ACTION_UP) { if (topCallback != null) { topCallback.onBackInvoked(); return FINISH_HANDLED; } } // .. } } } https://shorturl.at/jCGUV
  3. onBackPressed() public void onBackPressed() { mOnBackPressedDispatcher.onBackPressed(); } private fi nal

    OnBackPressedDispatcher mOnBackPressedDispatcher = new OnBackPressedDispatcher(new Runnable() { @Override public void run() { try { ComponentActivity.super.onBackPressed(); } catch (IllegalStateException e) { } } }); https://developer.android.com/jetpack/androidx/releases/activity#1.6.0-alpha05
  4. onBackPressedDispatcher public void onBackPressed() { Iterator<OnBackPressedCallback> iterator = mOnBackPressedCallbacks.descendingIterator(); while

    (iterator.hasNext()) { OnBackPressedCallback callback = iterator.next(); if (callback.isEnabled()) { callback.handleOnBackPressed(); return; } } if (mFallbackOnBackPressed != null) { mFallbackOnBackPressed.run(); } }
  5. OnBackPressedCallback - Activity onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) { override fun

    handleOnBackPressed() { isEnabled = false } }) if (Build.VERSION.SDK_INT >= 33) { updateBackInvokedCallbackState() onBackPressedCallback.enabledChangedCallback = enabledChangedCallback } LifeCycleOwner OnBackPressed Callback
  6. onBackPressedCallback - Dialog MaterialAlertDialogBuilder(this) .setTitle(getString(R.string.alert_dialog_title)) .setPositiveButton( getString(R.string.ok) ) { dialog,

    _ -> dialog.dismiss() } .create() .apply { onBackPressedDispatcher.addCallback(this) { dismiss() // remove this callback remove() } }.show()
  7. onBackPressedCallback - Fragment val backPressedCallback = object : OnBackPressedCallback(enabled =

    true) { override fun handleOnBackPressed() { //... back logic } } requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, backPressedCallback)
  8. onBackPressedCallback // De fi ne a callback val callback =

    onBackPressedDispatcher.addCallback(enabled = true) { // Do your custom stuff here.. }
  9. onBackPressedCallback // De fi ne a callback val callback =

    onBackPressedDispatcher.addCallback(enabled = true) { // Do your custom stuff here.. } // remove the callback from onBackPressedDispatcher callback.remove()
  10. onBackPressedCallback // De fi ne a callback val callback =

    onBackPressedDispatcher.addCallback(enabled = true) { // Do your custom stuff here.. } // remove the callback from onBackPressedDispatcher callback.remove() // update the enable state callback.isEnabled = true
  11. onBackPressedCallback // De fi ne a callback val callback =

    onBackPressedDispatcher.addCallback(enabled = true) { // Do your custom stuff here.. } // remove the callback from onBackPressedDispatcher callback.remove() // update the enable state callback.isEnabled = true // check if any enabled callbacks exists onBackPressedDispatcher.hasEnabledCallbacks()
  12. Option 1, if (aheadOfTimeBack) { // Add onBackPressed as default

    back behavior. mDefaultBackCallback = this::navigateBack; getOnBackInvokedDispatcher().registerSystemOnBackInvokedCallback(mDefaultBackCallback); } <application // .. android:enableOnBackInvokedCallback=“true" // .. >
  13. Platform API if (BuildCompat.isAtLeastT()) { // Step 1. val callback

    = OnBackInvokedCallback { lifecycleScope.launch { mainActivityViewModel.updateFlag(shouldUnregister = true) } } }
  14. Platform API if (BuildCompat.isAtLeastT()) { // Step 1. val callback

    = OnBackInvokedCallback { lifecycleScope.launch { mainActivityViewModel.updateFlag(shouldUnregister = true) } } // Step 2. onBackInvokedDispatcher.registerOnBackInvokedCallback( OnBackInvokedDispatcher.PRIORITY_DEFAULT, callback ) } DEFAULT or OVERLAY
  15. Platform API if (BuildCompat.isAtLeastT()) { // Step 1. val callback

    = OnBackInvokedCallback { lifecycleScope.launch { mainActivityViewModel.updateFlag(shouldUnregister = true) } } // Step 2. onBackInvokedDispatcher.registerOnBackInvokedCallback( OnBackInvokedDispatcher.PRIORITY_DEFAULT, callback ) // Step 3. lifecycleScope.launch { mainActivityViewModel.backCallback.collectLatest { backFlag -> if (backFlag) { onBackInvokedDispatcher.unregisterOnBackInvokedCallback(callback) } } } } DEFAULT or OVERLAY
  16. Enable gesture at activity level <application android:enableOnBackInvokedCallback=“false" > <activity android:name=".MainActivity"

    android:enableOnBackInvokedCallback="false" android:exported=“true"/> <activity android:name=".SecondActivity" android:enableOnBackInvokedCallback="true" android:exported="false" /> </application>
  17. Cross activity custom transitions overrideActivityTransition( OVERRIDE_TRANSITION_OPEN, R.anim.slide_in_left, R.anim.slide_out_left ) overrideActivityTransition(

    OVERRIDE_TRANSITION_CLOSE, R.anim.slide_in_left, R.anim.slide_out_left ) https://developer.android.com/jetpack/androidx/releases/activity#1.8.0-alpha01
  18. Back progress OnBackPressedCallback(enabled = true) { override fun handleOnBackProgressed(backEvent: BackEvent)

    { when (backEvent.swipeEdge) { BackEvent.EDGE_LEFT -> composeHolder.translationX = backEvent.progress * maxXShift BackEvent.EDGE_RIGHT -> composeHolder.translationX = -(backEvent.progress * maxXShift) } } override fun handleOnBackStarted() { } override fun handleOnBackCancelled() { } } https://developer.android.com/jetpack/androidx/releases/activity#1.8.0-alpha01
  19. AndroidX Transition 🤝 back Gesture https://developer.android.com/jetpack/androidx/releases/transition#1.5.0-alpha01 val callbackWithTransitionsAPI = object

    : OnBackPressedCallback(enabled = true) { var controller: TransitionSeekController? = null override fun handleOnBackStarted(backEvent: BackEvent) { controller = TransitionManager.controlDelayedTransition( binding.root, // rootView that contains "tv1" and “tv2" Fade() // Slide, ChangeBound, Explode, etc (Only Transitions work that supports seeking) ) binding.tv1.isVisible = true binding.tv2.isVisible = false } override fun handleOnBackProgressed(backEvent: BackEvent) { if (controller?.isReady == true) { controller?.currentPlayTimeMillis = (backEvent.progress * controller?.durationMillis!!).toLong() } } override fun handleOnBackPressed() { controller?.animateToEnd() this.isEnabled = false } override fun handleOnBackCancelled() { // If the user cancels the back gesture, reset the state TransitionManager.beginDelayedTransition(binding.root) binding.tv1.isVisible = true binding.tv2.isVisible = true } }
  20. References • https://android-developers.googleblog.com/2009/12/back-and-other-hard-keys-three-stories.html • https://developer.android.com/guide/navigation/predictive-back-gesture?authuser=1#migrate-existing • https://www.youtube.com/watch?v=Elpqr5xpLxQ&t=514s • https://developer.android.com/reference/kotlin/androidx/activity/compose/package-summary#backhandler •

    https://cs.android.com/android/platform/superproject/+/refs/heads/master:frameworks/base/core/java/android/view/ ViewRootImpl.java;drc=7346c436e5a11ce08f6a80dcfeb8ef941ca30176;l=11055?q=NativePreImeInputStage%20 • https://developer.android.com/design/ui/mobile/guides/patterns/predictive-back https://logos.fandom.com/wiki/Android/Versions