Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Platform channel for predictive back in route transitions on android #49093

Merged
merged 6 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -40968,6 +40968,7 @@ ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/rend
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterUiDisplayListener.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/RenderSurface.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/SurfaceTextureWrapper.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/BackGestureChannel.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/DeferredComponentChannel.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyEventChannel.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyboardChannel.java + ../../../flutter/LICENSE
Expand Down Expand Up @@ -43854,6 +43855,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/render
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/SurfaceTextureSurfaceProducer.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/SurfaceTextureWrapper.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/BackGestureChannel.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/DeferredComponentChannel.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyEventChannel.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyboardChannel.java
Expand Down
1 change: 1 addition & 0 deletions shell/platform/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ android_java_sources = [
"io/flutter/embedding/engine/renderer/SurfaceTextureSurfaceProducer.java",
"io/flutter/embedding/engine/renderer/SurfaceTextureWrapper.java",
"io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java",
"io/flutter/embedding/engine/systemchannels/BackGestureChannel.java",
"io/flutter/embedding/engine/systemchannels/DeferredComponentChannel.java",
"io/flutter/embedding/engine/systemchannels/KeyEventChannel.java",
"io/flutter/embedding/engine/systemchannels/KeyboardChannel.java",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.INITIAL_ROUTE_META_DATA_KEY;
import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.NORMAL_THEME_META_DATA_KEY;

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
Expand All @@ -35,10 +36,13 @@
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.window.BackEvent;
import android.window.OnBackAnimationCallback;
import android.window.OnBackInvokedCallback;
import android.window.OnBackInvokedDispatcher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
Expand Down Expand Up @@ -678,18 +682,43 @@ public void unregisterOnBackInvokedCallback() {
}

private final OnBackInvokedCallback onBackInvokedCallback =
Build.VERSION.SDK_INT >= API_LEVELS.API_33
? new OnBackInvokedCallback() {
// TODO(garyq): Remove SuppressWarnings annotation. This was added to workaround
// a google3 bug where the linter is not properly running against API 33, causing
// a failure here. See b/243609613 and https://github.com/flutter/flutter/issues/111295
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've edited the PR description to say "Fixes flutter/flutter#111295". It looks like that Google bug is closed now, so I think it should be fine to remove the annotation like you did.

@SuppressWarnings("Override")
@Override
public void onBackInvoked() {
onBackPressed();
}
}
: null;
Build.VERSION.SDK_INT < API_LEVELS.API_33 ? null : createOnBackInvokedCallback();

@VisibleForTesting
protected OnBackInvokedCallback getOnBackInvokedCallback() {
return onBackInvokedCallback;
}

@NonNull
@TargetApi(API_LEVELS.API_33)
@RequiresApi(API_LEVELS.API_33)
private OnBackInvokedCallback createOnBackInvokedCallback() {
if (Build.VERSION.SDK_INT >= API_LEVELS.API_34) {
return new OnBackAnimationCallback() {
@Override
public void onBackInvoked() {
commitBackGesture();
}

@Override
public void onBackCancelled() {
cancelBackGesture();
}

@Override
public void onBackProgressed(@NonNull BackEvent backEvent) {
updateBackGestureProgress(backEvent);
}

@Override
public void onBackStarted(@NonNull BackEvent backEvent) {
startBackGesture(backEvent);
}
};
}

return this::onBackPressed;
}

@Override
public void setFrameworkHandlesBack(boolean frameworkHandlesBack) {
Expand Down Expand Up @@ -899,6 +928,38 @@ public void onBackPressed() {
}
}

@TargetApi(API_LEVELS.API_34)
@RequiresApi(API_LEVELS.API_34)
public void startBackGesture(@NonNull BackEvent backEvent) {
if (stillAttachedForEvent("startBackGesture")) {
delegate.startBackGesture(backEvent);
}
}

@TargetApi(API_LEVELS.API_34)
@RequiresApi(API_LEVELS.API_34)
public void updateBackGestureProgress(@NonNull BackEvent backEvent) {
if (stillAttachedForEvent("updateBackGestureProgress")) {
delegate.updateBackGestureProgress(backEvent);
}
}

@TargetApi(API_LEVELS.API_34)
@RequiresApi(API_LEVELS.API_34)
public void commitBackGesture() {
if (stillAttachedForEvent("commitBackGesture")) {
delegate.commitBackGesture();
}
}

@TargetApi(API_LEVELS.API_34)
@RequiresApi(API_LEVELS.API_34)
public void cancelBackGesture() {
if (stillAttachedForEvent("cancelBackGesture")) {
delegate.cancelBackGesture();
}
}

@Override
public void onRequestPermissionsResult(
int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.DEFAULT_INITIAL_ROUTE;

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
Expand All @@ -16,10 +17,14 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnPreDrawListener;
import android.window.BackEvent;
import android.window.OnBackAnimationCallback;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.Lifecycle;
import io.flutter.Build.API_LEVELS;
import io.flutter.FlutterInjector;
import io.flutter.Log;
import io.flutter.embedding.engine.FlutterEngine;
Expand Down Expand Up @@ -779,6 +784,100 @@ void onBackPressed() {
}
}

/**
* Invoke this from {@link OnBackAnimationCallback#onBackStarted(BackEvent)}.
*
* <p>This method should be called when the back gesture is initiated. It should be invoked as
* part of the implementation of {@link OnBackAnimationCallback}.
*
* <p>This method delegates the handling of the start of a back gesture to the Flutter framework,
* which is responsible for the appropriate response, such as initiating animations or preparing
* the UI for the back navigation process.
*
* @param backEvent The BackEvent object containing information about the touch.
*/
@TargetApi(API_LEVELS.API_34)
@RequiresApi(API_LEVELS.API_34)
void startBackGesture(@NonNull BackEvent backEvent) {
ensureAlive();
if (flutterEngine != null) {
Log.v(TAG, "Forwarding startBackGesture() to FlutterEngine.");
flutterEngine.getBackGestureChannel().startBackGesture(backEvent);
} else {
Log.w(TAG, "Invoked startBackGesture() before FlutterFragment was attached to an Activity.");
}
}

/**
* Invoke this from {@link OnBackAnimationCallback#onBackProgressed(BackEvent)}.
*
* <p>This method should be called in response to progress in a back gesture, as part of the
* implementation of {@link OnBackAnimationCallback}.
*
* <p>This method delegates to the Flutter framework to update UI elements or animations based on
* the progression of the back gesture.
*
* @param backEvent An BackEvent object describing the progress event.
*/
@TargetApi(API_LEVELS.API_34)
@RequiresApi(API_LEVELS.API_34)
void updateBackGestureProgress(@NonNull BackEvent backEvent) {
ensureAlive();
if (flutterEngine != null) {
Log.v(TAG, "Forwarding updateBackGestureProgress() to FlutterEngine.");
flutterEngine.getBackGestureChannel().updateBackGestureProgress(backEvent);
} else {
Log.w(
TAG,
"Invoked updateBackGestureProgress() before FlutterFragment was attached to an Activity.");
}
}

/**
* Invoke this from {@link OnBackAnimationCallback#onBackInvoked()}.
*
* <p>This method is called to signify the completion of a back gesture and commits the navigation
* action initiated by the gesture. It should be invoked as the final step in handling a back
* gesture.
*
* <p>This method indicates to the Flutter framework that it should proceed with the back
* navigation, including finalizing animations and updating the UI to reflect the navigation
* outcome.
*/
@TargetApi(API_LEVELS.API_34)
@RequiresApi(API_LEVELS.API_34)
void commitBackGesture() {
ensureAlive();
if (flutterEngine != null) {
Log.v(TAG, "Forwarding commitBackGesture() to FlutterEngine.");
flutterEngine.getBackGestureChannel().commitBackGesture();
} else {
Log.w(TAG, "Invoked commitBackGesture() before FlutterFragment was attached to an Activity.");
}
}

/**
* Invoke this from {@link OnBackAnimationCallback#onBackCancelled()}.
*
* <p>This method should be called when a back gesture is cancelled or the back button is pressed.
* It informs the Flutter framework about the cancellation.
*
* <p>This method enables the Flutter framework to rollback any UI changes or animations initiated
* in response to the back gesture. This includes resetting UI elements to their state prior to
* the gesture's start.
*/
@TargetApi(API_LEVELS.API_34)
@RequiresApi(API_LEVELS.API_34)
void cancelBackGesture() {
ensureAlive();
if (flutterEngine != null) {
Log.v(TAG, "Forwarding cancelBackGesture() to FlutterEngine.");
flutterEngine.getBackGestureChannel().cancelBackGesture();
} else {
Log.w(TAG, "Invoked cancelBackGesture() before FlutterFragment was attached to an Activity.");
}
}

/**
* Invoke this from {@link android.app.Activity#onRequestPermissionsResult(int, String[], int[])}
* or {@code Fragment#onRequestPermissionsResult(int, String[], int[])}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import io.flutter.embedding.engine.renderer.FlutterRenderer;
import io.flutter.embedding.engine.renderer.RenderSurface;
import io.flutter.embedding.engine.systemchannels.AccessibilityChannel;
import io.flutter.embedding.engine.systemchannels.BackGestureChannel;
import io.flutter.embedding.engine.systemchannels.DeferredComponentChannel;
import io.flutter.embedding.engine.systemchannels.LifecycleChannel;
import io.flutter.embedding.engine.systemchannels.LocalizationChannel;
Expand Down Expand Up @@ -95,6 +96,7 @@ public class FlutterEngine implements ViewUtils.DisplayUpdater {
@NonNull private final LocalizationChannel localizationChannel;
@NonNull private final MouseCursorChannel mouseCursorChannel;
@NonNull private final NavigationChannel navigationChannel;
@NonNull private final BackGestureChannel backGestureChannel;
@NonNull private final RestorationChannel restorationChannel;
@NonNull private final PlatformChannel platformChannel;
@NonNull private final ProcessTextChannel processTextChannel;
Expand Down Expand Up @@ -331,6 +333,7 @@ public FlutterEngine(
localizationChannel = new LocalizationChannel(dartExecutor);
mouseCursorChannel = new MouseCursorChannel(dartExecutor);
navigationChannel = new NavigationChannel(dartExecutor);
backGestureChannel = new BackGestureChannel(dartExecutor);
platformChannel = new PlatformChannel(dartExecutor);
processTextChannel = new ProcessTextChannel(dartExecutor, context.getPackageManager());
restorationChannel = new RestorationChannel(dartExecutor, waitForRestorationData);
Expand Down Expand Up @@ -541,6 +544,12 @@ public NavigationChannel getNavigationChannel() {
return navigationChannel;
}

/** System channel that sends back gesture commands from Android to Flutter. */
@NonNull
public BackGestureChannel getBackGestureChannel() {
return backGestureChannel;
}

/**
* System channel that sends platform-oriented requests and information to Flutter, e.g., requests
* to play sounds, requests for haptics, system chrome settings, etc.
Expand Down
Loading