Skip to content

Commit

Permalink
Android Embedding PR 14: Almost done with FlutterFragment. (#8000)
Browse files Browse the repository at this point in the history
  • Loading branch information
matthew-carroll committed Mar 5, 2019
1 parent fb3e35d commit 8f1fdcd
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 1 deletion.
Expand Up @@ -19,6 +19,7 @@
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.FlutterShellArgs;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.plugin.platform.PlatformPlugin;
import io.flutter.view.FlutterMain;

import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
Expand Down Expand Up @@ -160,6 +161,8 @@ protected static Bundle createArgsBundle(@Nullable String dartEntrypoint,
private FlutterEngine flutterEngine;
@Nullable
private FlutterView flutterView;
@Nullable
private PlatformPlugin platformPlugin;

public FlutterFragment() {
// Ensure that we at least have an empty Bundle of arguments so that we don't
Expand All @@ -181,11 +184,30 @@ public FlutterEngine getFlutterEngine() {
public void onAttach(Context context) {
super.onAttach(context);

initializeFlutter(getContextCompat());

// When "retain instance" is true, the FlutterEngine will survive configuration
// changes. Therefore, we create a new one only if one does not already exist.
if (flutterEngine == null) {
createFlutterEngine();
}

// Regardless of whether or not a FlutterEngine already existed, the PlatformPlugin
// is bound to a specific Activity. Therefore, it needs to be created and configured
// every time this Fragment attaches to a new Activity.
// TODO(mattcarroll): the PlatformPlugin needs to be reimagined because it implicitly takes
// control of the entire window. This is unacceptable for non-fullscreen
// use-cases.
platformPlugin = new PlatformPlugin(getActivity(), flutterEngine.getPlatformChannel());
}

private void initializeFlutter(@NonNull Context context) {
String[] flutterShellArgsArray = getArguments().getStringArray(ARG_FLUTTER_INITIALIZATION_ARGS);
FlutterShellArgs flutterShellArgs = new FlutterShellArgs(
flutterShellArgsArray != null ? flutterShellArgsArray : new String[] {}
);

FlutterMain.ensureInitializationComplete(context.getApplicationContext(), flutterShellArgs.toArray());
}

/**
Expand Down Expand Up @@ -307,7 +329,72 @@ protected String getDartEntrypointFunctionName() {
// TODO(mattcarroll): determine why this can't be in onResume(). Comment reason, or move if possible.
public void onPostResume() {
Log.d(TAG, "onPostResume()");
flutterEngine.getLifecycleChannel().appIsResumed();
if (flutterEngine != null) {
flutterEngine.getLifecycleChannel().appIsResumed();

// TODO(mattcarroll): find a better way to handle the update of UI overlays than calling through
// to platformPlugin. We're implicitly entangling the Window, Activity, Fragment,
// and engine all with this one call.
platformPlugin.onPostResume();

// TODO(mattcarroll): consider a more abstract way to invoke this behavior. It is very strange for
// a Fragment to have a seemingly random View responsibility, but this is what
// existed in the original embedding and I don't have a good alternative yet.
flutterView.updateAccessibilityFeatures();
} else {
Log.w(TAG, "onPostResume() invoked before FlutterFragment was attached to an Activity.");
}
}

@Override
public void onPause() {
super.onPause();
Log.d(TAG, "onPause()");
flutterEngine.getLifecycleChannel().appIsInactive();
}

@Override
public void onStop() {
super.onStop();
Log.d(TAG, "onStop()");
flutterEngine.getLifecycleChannel().appIsPaused();
}

@Override
public void onDestroyView() {
super.onDestroyView();
Log.d(TAG, "onDestroyView()");
flutterView.detachFromFlutterEngine();
}

@Override
public void onDetach() {
super.onDetach();
Log.d(TAG, "onDetach()");

// Null out the platformPlugin to avoid a possible retain cycle between the plugin, this Fragment,
// and this Fragment's Activity.
platformPlugin = null;

// Destroy our FlutterEngine if we're not set to retain it.
if (!retainFlutterIsolateAfterFragmentDestruction()) {
flutterEngine.destroy();
flutterEngine = null;
}
}

/**
* Returns true if the {@link FlutterEngine} within this {@code FlutterFragment} should outlive
* the {@code FlutterFragment}, itself.
*
* Defaults to false. This method can be overridden in subclasses to retain the
* {@link FlutterEngine}.
*/
// TODO(mattcarroll): consider a dynamic determination of this preference based on whether the
// engine was created automatically, or if the engine was provided manually.
// Manually provided engines should probably not be destroyed.
protected boolean retainFlutterIsolateAfterFragmentDestruction() {
return false;
}

/**
Expand Down
Expand Up @@ -9,6 +9,7 @@
import android.graphics.Rect;
import android.os.Build;
import android.os.LocaleList;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.format.DateFormat;
Expand Down Expand Up @@ -334,6 +335,15 @@ public boolean onHoverEvent(MotionEvent event) {
}
//-------- End: Process UI I/O that Flutter cares about. ---------

//-------- Start: Accessibility -------
/**
* No-op. Placeholder so that the containing Fragment can call through, but not yet implemented.
*/
public void updateAccessibilityFeatures() {
// TODO(mattcarroll): bring in accessibility code from old FlutterView.
}
//-------- End: Accessibility ---------

/**
* Connects this {@code FlutterView} to the given {@link FlutterEngine}.
*
Expand Down

0 comments on commit 8f1fdcd

Please sign in to comment.