From 8d79765778d7e19217d2f5c7a59d94fe767cb9d0 Mon Sep 17 00:00:00 2001 From: "yangwu.jia" <yangwu.jia@taobao.com> Date: Wed, 11 Sep 2019 17:46:30 +0800 Subject: [PATCH] fix the leak --- android/build.gradle | 2 +- .../flutterboost/BoostFlutterView.java | 5 +- .../flutterboost/XAndroidKeyProcessor.java | 2 + .../flutterboost/XPlatformPlugin.java | 257 ++++++++++++++++++ 4 files changed, 263 insertions(+), 3 deletions(-) create mode 100644 android/src/main/java/com/idlefish/flutterboost/XPlatformPlugin.java diff --git a/android/build.gradle b/android/build.gradle index 02dca0a..3eb52e8 100755 --- a/android/build.gradle +++ b/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 26 + compileSdkVersion 28 buildToolsVersion '27.0.3' defaultConfig { minSdkVersion 16 diff --git a/android/src/main/java/com/idlefish/flutterboost/BoostFlutterView.java b/android/src/main/java/com/idlefish/flutterboost/BoostFlutterView.java index c5942af..45636bd 100644 --- a/android/src/main/java/com/idlefish/flutterboost/BoostFlutterView.java +++ b/android/src/main/java/com/idlefish/flutterboost/BoostFlutterView.java @@ -54,7 +54,7 @@ public class BoostFlutterView extends FrameLayout { private BoostFlutterEngine mFlutterEngine; private XFlutterView mFlutterView; - private PlatformPlugin mPlatformPlugin; + private XPlatformPlugin mPlatformPlugin; private Bundle mArguments; @@ -114,7 +114,7 @@ public class BoostFlutterView extends FrameLayout { if (mArguments == null) { mArguments = new Bundle(); } - mPlatformPlugin = new PlatformPlugin((Activity) getContext(), mFlutterEngine.getPlatformChannel()); + mPlatformPlugin = new XPlatformPlugin((Activity) getContext(), mFlutterEngine.getPlatformChannel()); mFlutterView = new XFlutterView(getContext(), getRenderMode(), getTransparencyMode()); addView(mFlutterView, new FrameLayout.LayoutParams( @@ -295,6 +295,7 @@ public class BoostFlutterView extends FrameLayout { mFlutterView.removeOnFirstFrameRenderedListener(mOnFirstFrameRenderedListener); mFlutterView.release(); + mPlatformPlugin.release(); } //æ··åˆæ ˆçš„返回和原æ¥Flutter的返回逻辑ä¸åŒ diff --git a/android/src/main/java/com/idlefish/flutterboost/XAndroidKeyProcessor.java b/android/src/main/java/com/idlefish/flutterboost/XAndroidKeyProcessor.java index e3afb39..78df9eb 100644 --- a/android/src/main/java/com/idlefish/flutterboost/XAndroidKeyProcessor.java +++ b/android/src/main/java/com/idlefish/flutterboost/XAndroidKeyProcessor.java @@ -13,6 +13,7 @@ public class XAndroidKeyProcessor { private final KeyEventChannel keyEventChannel; @NonNull private final XTextInputPlugin textInputPlugin; + private int combiningCharacter; public XAndroidKeyProcessor(@NonNull KeyEventChannel keyEventChannel, @NonNull XTextInputPlugin textInputPlugin) { @@ -20,6 +21,7 @@ public class XAndroidKeyProcessor { this.textInputPlugin = textInputPlugin; } + public void onKeyUp(@NonNull KeyEvent keyEvent) { Character complexCharacter = applyCombiningCharacterToBaseCharacter(keyEvent.getUnicodeChar()); keyEventChannel.keyUp( 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..dbf4ee6 --- /dev/null +++ b/android/src/main/java/com/idlefish/flutterboost/XPlatformPlugin.java @@ -0,0 +1,257 @@ +package com.idlefish.flutterboost; +import android.app.Activity; +import android.app.ActivityManager.TaskDescription; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.os.Build; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.HapticFeedbackConstants; +import android.view.SoundEffectConstants; +import android.view.View; +import android.view.Window; + +import java.util.List; + +import io.flutter.embedding.engine.systemchannels.PlatformChannel; +import io.flutter.plugin.common.ActivityLifecycleListener; + +public class XPlatformPlugin implements ActivityLifecycleListener { + public static final int DEFAULT_SYSTEM_UI = View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + + private Activity activity; + private final PlatformChannel platformChannel; + private PlatformChannel.SystemChromeStyle currentTheme; + private int mEnabledOverlays; + + private final 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); + } + }; + + public XPlatformPlugin(Activity activity, PlatformChannel platformChannel) { + this.activity = activity; + this.platformChannel = platformChannel; + this.platformChannel.setPlatformMessageHandler(mPlatformMessageHandler); + + mEnabledOverlays = DEFAULT_SYSTEM_UI; + } + + 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); + } + + 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 TaskDescription(description.label)); + } + if (Build.VERSION.SDK_INT >= 28) { + TaskDescription taskDescription = new 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(); + } + + private 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 availible 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); + } + + @Override + public void onPostResume() { + updateSystemUiOverlays(); + } + public void release(){ + this.activity=null; + } +} \ No newline at end of file -- 2.26.2