From 698e6a737bed44fe181357b0f0f84f4fad0d5815 Mon Sep 17 00:00:00 2001
From: justin <noborder@qq.com>
Date: Wed, 22 Apr 2020 17:18:09 +0800
Subject: [PATCH] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E6=B3=84=E6=BC=8F=E9=97=AE?=
 =?UTF-8?q?=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 android/build.gradle                          |   2 +-
 .../flutterboost/XPlatformPlugin.java         | 311 ++++++++++++++++++
 .../containers/BoostFlutterActivity.java      |   9 +-
 .../FlutterActivityAndFragmentDelegate.java   |  41 ++-
 .../containers/FlutterFragment.java           |  10 +-
 5 files changed, 342 insertions(+), 31 deletions(-)
 create mode 100644 android/src/main/java/com/idlefish/flutterboost/XPlatformPlugin.java

diff --git a/android/build.gradle b/android/build.gradle
index bd2e6c7..1821bbe 100755
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -22,7 +22,7 @@ rootProject.allprojects {
 apply plugin: 'com.android.library'
 
 android {
-    compileSdkVersion 28
+    compileSdkVersion 29
     buildToolsVersion '28.0.3'
     defaultConfig {
         minSdkVersion 16
diff --git a/android/src/main/java/com/idlefish/flutterboost/XPlatformPlugin.java b/android/src/main/java/com/idlefish/flutterboost/XPlatformPlugin.java
new file mode 100644
index 0000000..d4702a8
--- /dev/null
+++ b/android/src/main/java/com/idlefish/flutterboost/XPlatformPlugin.java
@@ -0,0 +1,311 @@
+package com.idlefish.flutterboost;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Build;
+
+
+import android.view.HapticFeedbackConstants;
+import android.view.SoundEffectConstants;
+import android.view.View;
+import android.view.Window;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import io.flutter.embedding.engine.systemchannels.PlatformChannel;
+
+public class XPlatformPlugin {
+
+
+    public static final int DEFAULT_SYSTEM_UI = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+
+    private  Activity activity;
+    private  PlatformChannel platformChannel;
+    private PlatformChannel.SystemChromeStyle currentTheme;
+    private int mEnabledOverlays;
+
+    private  PlatformChannel.PlatformMessageHandler mPlatformMessageHandler = new PlatformChannel.PlatformMessageHandler() {
+        @Override
+        public void playSystemSound(@NonNull PlatformChannel.SoundType soundType) {
+            XPlatformPlugin.this.playSystemSound(soundType);
+        }
+
+        @Override
+        public void vibrateHapticFeedback(@NonNull PlatformChannel.HapticFeedbackType feedbackType) {
+            XPlatformPlugin.this.vibrateHapticFeedback(feedbackType);
+        }
+
+        @Override
+        public void setPreferredOrientations(int androidOrientation) {
+            setSystemChromePreferredOrientations(androidOrientation);
+        }
+
+        @Override
+        public void setApplicationSwitcherDescription(@NonNull PlatformChannel.AppSwitcherDescription description) {
+            setSystemChromeApplicationSwitcherDescription(description);
+        }
+
+        @Override
+        public void showSystemOverlays(@NonNull List<PlatformChannel.SystemUiOverlay> overlays) {
+            setSystemChromeEnabledSystemUIOverlays(overlays);
+        }
+
+        @Override
+        public void restoreSystemUiOverlays() {
+            restoreSystemChromeSystemUIOverlays();
+        }
+
+        @Override
+        public void setSystemUiOverlayStyle(@NonNull PlatformChannel.SystemChromeStyle systemUiOverlayStyle) {
+            setSystemChromeSystemUIOverlayStyle(systemUiOverlayStyle);
+        }
+
+        @Override
+        public void popSystemNavigator() {
+            XPlatformPlugin.this.popSystemNavigator();
+        }
+
+        @Override
+        public CharSequence getClipboardData(@Nullable PlatformChannel.ClipboardContentFormat format) {
+            return XPlatformPlugin.this.getClipboardData(format);
+        }
+
+        @Override
+        public void setClipboardData(@NonNull String text) {
+            XPlatformPlugin.this.setClipboardData(text);
+        }
+
+        @Override
+        public List<Rect> getSystemGestureExclusionRects() {
+            return XPlatformPlugin.this.getSystemGestureExclusionRects();
+        }
+
+        @Override
+        public void setSystemGestureExclusionRects(@NonNull ArrayList<Rect> rects) {
+            XPlatformPlugin.this.setSystemGestureExclusionRects(rects);
+        }
+    };
+
+    public XPlatformPlugin( PlatformChannel platformChannel) {
+
+        this.platformChannel = platformChannel;
+
+        mEnabledOverlays = DEFAULT_SYSTEM_UI;
+    }
+
+    public void attachToActivity(Activity activity ){
+        this.activity = activity;
+        this.platformChannel.setPlatformMessageHandler(mPlatformMessageHandler);
+
+    }
+    /**
+     * Releases all resources held by this {@code PlatformPlugin}.
+     * <p>
+     * Do not invoke any methods on a {@code PlatformPlugin} after invoking this method.
+     */
+    public void detachActivity() {
+       this.activity=null;
+       this.mPlatformMessageHandler=null;
+    }
+
+    private void playSystemSound(PlatformChannel.SoundType soundType) {
+        if (soundType == PlatformChannel.SoundType.CLICK) {
+            View view = activity.getWindow().getDecorView();
+            view.playSoundEffect(SoundEffectConstants.CLICK);
+        }
+    }
+
+    private void vibrateHapticFeedback(PlatformChannel.HapticFeedbackType feedbackType) {
+        View view = activity.getWindow().getDecorView();
+        switch (feedbackType) {
+            case STANDARD:
+                view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+                break;
+            case LIGHT_IMPACT:
+                view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
+                break;
+            case MEDIUM_IMPACT:
+                view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
+                break;
+            case HEAVY_IMPACT:
+                // HapticFeedbackConstants.CONTEXT_CLICK from API level 23.
+                view.performHapticFeedback(6);
+                break;
+            case SELECTION_CLICK:
+                view.performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK);
+                break;
+        }
+    }
+
+    private void setSystemChromePreferredOrientations(int androidOrientation) {
+        activity.setRequestedOrientation(androidOrientation);
+    }
+
+    @SuppressWarnings("deprecation")
+    private void setSystemChromeApplicationSwitcherDescription(PlatformChannel.AppSwitcherDescription description) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+            return;
+        }
+
+        // Linter refuses to believe we're only executing this code in API 28 unless we use distinct if blocks and
+        // hardcode the API 28 constant.
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P && Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
+            activity.setTaskDescription(new ActivityManager.TaskDescription(description.label, /*icon=*/ null, description.color));
+        }
+        if (Build.VERSION.SDK_INT >= 28) {
+            ActivityManager.TaskDescription taskDescription = new ActivityManager.TaskDescription(description.label, 0, description.color);
+            activity.setTaskDescription(taskDescription);
+        }
+    }
+
+    private void setSystemChromeEnabledSystemUIOverlays(List<PlatformChannel.SystemUiOverlay> overlaysToShow) {
+        // Start by assuming we want to hide all system overlays (like an immersive game).
+        int enabledOverlays = DEFAULT_SYSTEM_UI
+                | View.SYSTEM_UI_FLAG_FULLSCREEN
+                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+
+        if (overlaysToShow.size() == 0) {
+            enabledOverlays |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+        }
+
+        // Re-add any desired system overlays.
+        for (int i = 0; i < overlaysToShow.size(); ++i) {
+            PlatformChannel.SystemUiOverlay overlayToShow = overlaysToShow.get(i);
+            switch (overlayToShow) {
+                case TOP_OVERLAYS:
+                    enabledOverlays &= ~View.SYSTEM_UI_FLAG_FULLSCREEN;
+                    break;
+                case BOTTOM_OVERLAYS:
+                    enabledOverlays &= ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
+                    enabledOverlays &= ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+                    break;
+            }
+        }
+
+        mEnabledOverlays = enabledOverlays;
+        updateSystemUiOverlays();
+    }
+
+    /**
+     * Refreshes Android's window system UI (AKA system chrome) to match Flutter's desired
+     * {@link PlatformChannel.SystemChromeStyle}.
+     * <p>
+     * Updating the system UI Overlays is accomplished by altering the decor view of the
+     * {@link Window} associated with the {@link Activity} that was provided to this
+     * {@code PlatformPlugin}.
+     */
+    public void updateSystemUiOverlays(){
+        activity.getWindow().getDecorView().setSystemUiVisibility(mEnabledOverlays);
+        if (currentTheme != null) {
+            setSystemChromeSystemUIOverlayStyle(currentTheme);
+        }
+    }
+
+    private void restoreSystemChromeSystemUIOverlays() {
+        updateSystemUiOverlays();
+    }
+
+    private void setSystemChromeSystemUIOverlayStyle(PlatformChannel.SystemChromeStyle systemChromeStyle) {
+        Window window = activity.getWindow();
+        View view = window.getDecorView();
+        int flags = view.getSystemUiVisibility();
+        // You can change the navigation bar color (including translucent colors)
+        // in Android, but you can't change the color of the navigation buttons until Android O.
+        // LIGHT vs DARK effectively isn't supported until then.
+        // Build.VERSION_CODES.O
+        if (Build.VERSION.SDK_INT >= 26) {
+            if (systemChromeStyle.systemNavigationBarIconBrightness != null) {
+                switch (systemChromeStyle.systemNavigationBarIconBrightness) {
+                    case DARK:
+                        //View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
+                        flags |= 0x10;
+                        break;
+                    case LIGHT:
+                        flags &= ~0x10;
+                        break;
+                }
+            }
+            if (systemChromeStyle.systemNavigationBarColor != null) {
+                window.setNavigationBarColor(systemChromeStyle.systemNavigationBarColor);
+            }
+        }
+        // Build.VERSION_CODES.M
+        if (Build.VERSION.SDK_INT >= 23) {
+            if (systemChromeStyle.statusBarIconBrightness != null) {
+                switch (systemChromeStyle.statusBarIconBrightness) {
+                    case DARK:
+                        // View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
+                        flags |= 0x2000;
+                        break;
+                    case LIGHT:
+                        flags &= ~0x2000;
+                        break;
+                }
+            }
+            if (systemChromeStyle.statusBarColor != null) {
+                window.setStatusBarColor(systemChromeStyle.statusBarColor);
+            }
+        }
+        if (systemChromeStyle.systemNavigationBarDividerColor != null) {
+            // Not available until Android P.
+            // window.setNavigationBarDividerColor(systemNavigationBarDividerColor);
+        }
+        view.setSystemUiVisibility(flags);
+        currentTheme = systemChromeStyle;
+    }
+
+    private void popSystemNavigator() {
+        activity.finish();
+    }
+
+    private CharSequence getClipboardData(PlatformChannel.ClipboardContentFormat format) {
+        ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
+        ClipData clip = clipboard.getPrimaryClip();
+        if (clip == null)
+            return null;
+
+        if (format == null || format == PlatformChannel.ClipboardContentFormat.PLAIN_TEXT) {
+            return clip.getItemAt(0).coerceToText(activity);
+        }
+
+        return null;
+    }
+
+    private void setClipboardData(String text) {
+        ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
+        ClipData clip = ClipData.newPlainText("text label?", text);
+        clipboard.setPrimaryClip(clip);
+    }
+
+
+    private List<Rect> getSystemGestureExclusionRects() {
+        if (Build.VERSION.SDK_INT >= 29) {
+            Window window = activity.getWindow();
+            View view = window.getDecorView();
+            return view.getSystemGestureExclusionRects();
+        }
+
+        return null;
+    }
+
+    private void setSystemGestureExclusionRects(ArrayList<Rect> rects) {
+        if (Build.VERSION.SDK_INT < 29) {
+            return;
+        }
+
+        Window window = activity.getWindow();
+        View view = window.getDecorView();
+        view.setSystemGestureExclusionRects(rects);
+    }
+
+}
diff --git a/android/src/main/java/com/idlefish/flutterboost/containers/BoostFlutterActivity.java b/android/src/main/java/com/idlefish/flutterboost/containers/BoostFlutterActivity.java
index 14e85fb..91c2773 100644
--- a/android/src/main/java/com/idlefish/flutterboost/containers/BoostFlutterActivity.java
+++ b/android/src/main/java/com/idlefish/flutterboost/containers/BoostFlutterActivity.java
@@ -21,6 +21,7 @@ import android.view.*;
 import android.widget.*;
 import com.idlefish.flutterboost.FlutterBoost;
 import com.idlefish.flutterboost.XFlutterView;
+import com.idlefish.flutterboost.XPlatformPlugin;
 import io.flutter.Log;
 import io.flutter.embedding.android.DrawableSplashScreen;
 import io.flutter.embedding.android.FlutterView;
@@ -439,12 +440,8 @@ public class BoostFlutterActivity extends Activity
 
     @Nullable
     @Override
-    public PlatformPlugin providePlatformPlugin(@Nullable Activity activity, @NonNull FlutterEngine flutterEngine) {
-        if (activity != null) {
-            return new PlatformPlugin(getActivity(), flutterEngine.getPlatformChannel());
-        } else {
-            return null;
-        }
+    public XPlatformPlugin providePlatformPlugin( @NonNull FlutterEngine flutterEngine) {
+        return new XPlatformPlugin( flutterEngine.getPlatformChannel());
     }
 
     /**
diff --git a/android/src/main/java/com/idlefish/flutterboost/containers/FlutterActivityAndFragmentDelegate.java b/android/src/main/java/com/idlefish/flutterboost/containers/FlutterActivityAndFragmentDelegate.java
index 760892e..8b919c8 100644
--- a/android/src/main/java/com/idlefish/flutterboost/containers/FlutterActivityAndFragmentDelegate.java
+++ b/android/src/main/java/com/idlefish/flutterboost/containers/FlutterActivityAndFragmentDelegate.java
@@ -23,6 +23,7 @@ import java.util.Map;
 import com.idlefish.flutterboost.FlutterBoost;
 import com.idlefish.flutterboost.Utils;
 import com.idlefish.flutterboost.XFlutterView;
+import com.idlefish.flutterboost.XPlatformPlugin;
 import com.idlefish.flutterboost.interfaces.IFlutterViewContainer;
 import com.idlefish.flutterboost.interfaces.IOperateSyncer;
 import io.flutter.Log;
@@ -30,6 +31,7 @@ import io.flutter.app.FlutterActivity;
 import io.flutter.embedding.android.*;
 import io.flutter.embedding.engine.FlutterEngine;
 import io.flutter.embedding.engine.FlutterShellArgs;
+import io.flutter.embedding.engine.plugins.activity.ActivityControlSurface;
 import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
 import io.flutter.plugin.platform.PlatformPlugin;
 
@@ -39,7 +41,7 @@ public class FlutterActivityAndFragmentDelegate implements IFlutterViewContainer
 
 
     private static final String TAG = "FlutterActivityAndFragmentDelegate";
-
+    private  static int ACTIVITY_CONTROL_SURFACE_ATTACH_TO_ACTVITY_HASH_CODE=0;
     @NonNull
     private Host host;
     @Nullable
@@ -49,7 +51,7 @@ public class FlutterActivityAndFragmentDelegate implements IFlutterViewContainer
     @Nullable
     private XFlutterView flutterView;
     @Nullable
-    private PlatformPlugin platformPlugin;
+    private XPlatformPlugin platformPlugin;
 
     private boolean isFlutterEngineFromHost;
 
@@ -95,7 +97,7 @@ public class FlutterActivityAndFragmentDelegate implements IFlutterViewContainer
         // 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 = host.providePlatformPlugin(host.getActivity(), flutterEngine);
+        platformPlugin = host.providePlatformPlugin(flutterEngine);
 
 
         host.configureFlutterEngine(flutterEngine);
@@ -129,13 +131,6 @@ public class FlutterActivityAndFragmentDelegate implements IFlutterViewContainer
         Log.v(TAG, "Creating FlutterView.");
 
 
-        flutterEngine.getActivityControlSurface().attachToActivity(
-                host.getActivity(),
-                host.getLifecycle()
-        );
-
-
-
         mSyncer = FlutterBoost.instance().containerManager().generateSyncer(this);
 
         ensureAlive();
@@ -176,13 +171,19 @@ public class FlutterActivityAndFragmentDelegate implements IFlutterViewContainer
         Log.v(TAG, "onResume()");
         ensureAlive();
         flutterEngine.getLifecycleChannel().appIsResumed();
-
-        flutterEngine.getActivityControlSurface().attachToActivity(
-                host.getActivity(),
-                host.getLifecycle()
-        );
+        if(ACTIVITY_CONTROL_SURFACE_ATTACH_TO_ACTVITY_HASH_CODE==0||
+                ACTIVITY_CONTROL_SURFACE_ATTACH_TO_ACTVITY_HASH_CODE!=this.host.getActivity().hashCode()){
+            flutterEngine.getActivityControlSurface().detachFromActivityForConfigChanges();
+            flutterEngine.getActivityControlSurface().attachToActivity(
+                    host.getActivity(),
+                    host.getLifecycle()
+            );
+            ACTIVITY_CONTROL_SURFACE_ATTACH_TO_ACTVITY_HASH_CODE=this.host.getActivity().hashCode();
+        }
 
 
+        if(platformPlugin!=null)
+            platformPlugin.attachToActivity( host.getActivity());
 
 
     }
@@ -191,7 +192,6 @@ public class FlutterActivityAndFragmentDelegate implements IFlutterViewContainer
     public void onPostResume() {
         Log.v(TAG, "onPostResume()");
         ensureAlive();
-//        Utils.setStatusBarLightMode(host.getActivity(), true);
 
     }
 
@@ -230,10 +230,15 @@ public class FlutterActivityAndFragmentDelegate implements IFlutterViewContainer
         // Null out the platformPlugin to avoid a possible retain cycle between the plugin, this Fragment,
         // and this Fragment's Activity.
         if (platformPlugin != null) {
-//            platformPlugin.destroy();
+            platformPlugin.detachActivity();
             platformPlugin = null;
         }
 
+        if(ACTIVITY_CONTROL_SURFACE_ATTACH_TO_ACTVITY_HASH_CODE!=0||
+                ACTIVITY_CONTROL_SURFACE_ATTACH_TO_ACTVITY_HASH_CODE==this.host.getActivity().hashCode()){
+            flutterEngine.getActivityControlSurface().detachFromActivityForConfigChanges();
+        }
+
         Utils.fixInputMethodManagerLeak(host.getActivity());
 
     }
@@ -461,7 +466,7 @@ public class FlutterActivityAndFragmentDelegate implements IFlutterViewContainer
          * Flutter experience should control system chrome.
          */
         @Nullable
-        PlatformPlugin providePlatformPlugin(@Nullable Activity activity, @NonNull FlutterEngine flutterEngine);
+        XPlatformPlugin providePlatformPlugin( @NonNull FlutterEngine flutterEngine);
 
         /**
          * Hook for the host to configure the {@link FlutterEngine} as desired.
diff --git a/android/src/main/java/com/idlefish/flutterboost/containers/FlutterFragment.java b/android/src/main/java/com/idlefish/flutterboost/containers/FlutterFragment.java
index 35c38f2..dbe0799 100644
--- a/android/src/main/java/com/idlefish/flutterboost/containers/FlutterFragment.java
+++ b/android/src/main/java/com/idlefish/flutterboost/containers/FlutterFragment.java
@@ -16,6 +16,7 @@ import androidx.fragment.app.FragmentActivity;
 import com.idlefish.flutterboost.FlutterBoost;
 import com.idlefish.flutterboost.Utils;
 import com.idlefish.flutterboost.XFlutterView;
+import com.idlefish.flutterboost.XPlatformPlugin;
 import io.flutter.embedding.android.*;
 import io.flutter.embedding.engine.FlutterEngine;
 import io.flutter.embedding.engine.FlutterShellArgs;
@@ -467,12 +468,9 @@ public class FlutterFragment extends Fragment implements FlutterActivityAndFragm
 
     @Nullable
     @Override
-    public PlatformPlugin providePlatformPlugin(@Nullable Activity activity, @NonNull FlutterEngine flutterEngine) {
-        if (activity != null) {
-            return new PlatformPlugin(getActivity(), flutterEngine.getPlatformChannel());
-        } else {
-            return null;
-        }
+    public XPlatformPlugin providePlatformPlugin( @NonNull FlutterEngine flutterEngine) {
+        return new XPlatformPlugin(flutterEngine.getPlatformChannel());
+
     }
 
     /**
-- 
2.26.2