Commit 831a525d authored by yangwu.jia's avatar yangwu.jia

Merge branch 'develop'

# Conflicts:
#	.gitignore
parents f8180293 1d3ace17
...@@ -9,5 +9,12 @@ build/ ...@@ -9,5 +9,12 @@ build/
pubspec.lock pubspec.lock
.flutter-plugins .flutter-plugins
flutter flutter
IDEWorkspaceChecks.plist
org.eclipse.buildship.core.prefs
Breakpoints_v2.xcbkptlist
flutter_boost2
example/android/app/.classpath
example/android/app/.project
example/android/.project
flutter_boost flutter_boost
example example
...@@ -23,6 +23,7 @@ import io.flutter.embedding.engine.renderer.FlutterRenderer; ...@@ -23,6 +23,7 @@ import io.flutter.embedding.engine.renderer.FlutterRenderer;
import io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener; import io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener;
import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.PluginRegistry; import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.editing.TextInputPlugin;
import io.flutter.plugin.platform.PlatformViewRegistry; import io.flutter.plugin.platform.PlatformViewRegistry;
import io.flutter.view.FlutterMain; import io.flutter.view.FlutterMain;
import io.flutter.view.FlutterView; import io.flutter.view.FlutterView;
......
...@@ -55,8 +55,6 @@ public class BoostFlutterView extends FrameLayout { ...@@ -55,8 +55,6 @@ public class BoostFlutterView extends FrameLayout {
private XFlutterView mFlutterView; private XFlutterView mFlutterView;
private PlatformPlugin mPlatformPlugin;
private Bundle mArguments; private Bundle mArguments;
private RenderingProgressCoverCreator mRenderingProgressCoverCreator; private RenderingProgressCoverCreator mRenderingProgressCoverCreator;
...@@ -116,8 +114,6 @@ public class BoostFlutterView extends FrameLayout { ...@@ -116,8 +114,6 @@ public class BoostFlutterView extends FrameLayout {
mArguments = new Bundle(); mArguments = new Bundle();
} }
mPlatformPlugin = new PlatformPlugin((Activity) getContext(), mFlutterEngine.getPlatformChannel());
mFlutterView = new XFlutterView(getContext(), getRenderMode(), getTransparencyMode()); mFlutterView = new XFlutterView(getContext(), getRenderMode(), getTransparencyMode());
addView(mFlutterView, new FrameLayout.LayoutParams( addView(mFlutterView, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
...@@ -213,7 +209,6 @@ public class BoostFlutterView extends FrameLayout { ...@@ -213,7 +209,6 @@ public class BoostFlutterView extends FrameLayout {
@Override @Override
protected void onAttachedToWindow() { protected void onAttachedToWindow() {
super.onAttachedToWindow(); super.onAttachedToWindow();
mPlatformPlugin.onPostResume();
ViewCompat.requestApplyInsets(this); ViewCompat.requestApplyInsets(this);
getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener); getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
} }
...@@ -297,10 +292,7 @@ public class BoostFlutterView extends FrameLayout { ...@@ -297,10 +292,7 @@ public class BoostFlutterView extends FrameLayout {
Debuger.log("BoostFlutterView onDestroy"); Debuger.log("BoostFlutterView onDestroy");
mFlutterView.removeOnFirstFrameRenderedListener(mOnFirstFrameRenderedListener); mFlutterView.removeOnFirstFrameRenderedListener(mOnFirstFrameRenderedListener);
AccessibilityBridge bridge=mFlutterView.getAccessibilityBridge(); mFlutterView.release();
if(bridge!=null){
bridge.release();
}
} }
//混合栈的返回和原来Flutter的返回逻辑不同 //混合栈的返回和原来Flutter的返回逻辑不同
......
...@@ -34,6 +34,7 @@ import android.util.DisplayMetrics; ...@@ -34,6 +34,7 @@ import android.util.DisplayMetrics;
import android.view.View; import android.view.View;
import android.view.Window; import android.view.Window;
import android.view.WindowManager; import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
...@@ -251,4 +252,41 @@ public class Utils { ...@@ -251,4 +252,41 @@ public class Utils {
} }
return line; return line;
} }
public static void fixInputMethodManagerLeak(Context destContext) {
if (destContext == null) {
return;
}
InputMethodManager imm = (InputMethodManager) destContext.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm == null) {
return;
}
String [] arr = new String[]{"mLastSrvView", "mServedView", "mNextServedView"};
Field f = null;
Object obj_get = null;
for (int i = 0;i < arr.length;i ++) {
String param = arr[i];
try{
f = imm.getClass().getDeclaredField(param);
if (f.isAccessible() == false) {
f.setAccessible(true);
} // author: sodino mail:sodino@qq.com
obj_get = f.get(imm);
if (obj_get != null && obj_get instanceof View) {
View v_get = (View) obj_get;
if (v_get.getContext() == destContext) { // 被InputMethodManager持有引用的context是想要目标销毁的
f.set(imm, null); // 置空,破坏掉path to gc节点
} else {
// 不是想要目标销毁的,即为又进了另一层界面了,不要处理,避免影响原逻辑,也就不用继续for循环了
break;
}
}
}catch(Throwable t){
t.printStackTrace();
}
}
}
} }
\ No newline at end of file
package com.idlefish.flutterboost;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import io.flutter.embedding.engine.systemchannels.KeyEventChannel;
import io.flutter.plugin.editing.TextInputPlugin;
public class XAndroidKeyProcessor {
@NonNull
private final KeyEventChannel keyEventChannel;
@NonNull
private final XTextInputPlugin textInputPlugin;
private int combiningCharacter;
public XAndroidKeyProcessor(@NonNull KeyEventChannel keyEventChannel, @NonNull XTextInputPlugin textInputPlugin) {
this.keyEventChannel = keyEventChannel;
this.textInputPlugin = textInputPlugin;
}
public void onKeyUp(@NonNull KeyEvent keyEvent) {
Character complexCharacter = applyCombiningCharacterToBaseCharacter(keyEvent.getUnicodeChar());
keyEventChannel.keyUp(
new KeyEventChannel.FlutterKeyEvent(keyEvent, complexCharacter)
);
}
public void onKeyDown(@NonNull KeyEvent keyEvent) {
if (textInputPlugin.getLastInputConnection() != null
&& textInputPlugin.getInputMethodManager().isAcceptingText()) {
textInputPlugin.getLastInputConnection().sendKeyEvent(keyEvent);
}
Character complexCharacter = applyCombiningCharacterToBaseCharacter(keyEvent.getUnicodeChar());
keyEventChannel.keyDown(
new KeyEventChannel.FlutterKeyEvent(keyEvent, complexCharacter)
);
}
/**
* Applies the given Unicode character in {@code newCharacterCodePoint} to a previously
* entered Unicode combining character and returns the combination of these characters
* if a combination exists.
* <p>
* This method mutates {@link #combiningCharacter} over time to combine characters.
* <p>
* One of the following things happens in this method:
* <ul>
* <li>If no previous {@link #combiningCharacter} exists and the {@code newCharacterCodePoint}
* is not a combining character, then {@code newCharacterCodePoint} is returned.</li>
* <li>If no previous {@link #combiningCharacter} exists and the {@code newCharacterCodePoint}
* is a combining character, then {@code newCharacterCodePoint} is saved as the
* {@link #combiningCharacter} and null is returned.</li>
* <li>If a previous {@link #combiningCharacter} exists and the {@code newCharacterCodePoint}
* is also a combining character, then the {@code newCharacterCodePoint} is combined with
* the existing {@link #combiningCharacter} and null is returned.</li>
* <li>If a previous {@link #combiningCharacter} exists and the {@code newCharacterCodePoint}
* is not a combining character, then the {@link #combiningCharacter} is applied to the
* regular {@code newCharacterCodePoint} and the resulting complex character is returned. The
* {@link #combiningCharacter} is cleared.</li>
* </ul>
* <p>
* The following reference explains the concept of a "combining character":
* https://en.wikipedia.org/wiki/Combining_character
*/
@Nullable
private Character applyCombiningCharacterToBaseCharacter(int newCharacterCodePoint) {
if (newCharacterCodePoint == 0) {
return null;
}
Character complexCharacter = (char) newCharacterCodePoint;
boolean isNewCodePointACombiningCharacter = (newCharacterCodePoint & KeyCharacterMap.COMBINING_ACCENT) != 0;
if (isNewCodePointACombiningCharacter) {
// If a combining character was entered before, combine this one with that one.
int plainCodePoint = newCharacterCodePoint & KeyCharacterMap.COMBINING_ACCENT_MASK;
if (combiningCharacter != 0) {
combiningCharacter = KeyCharacterMap.getDeadChar(combiningCharacter, plainCodePoint);
} else {
combiningCharacter = plainCodePoint;
}
} else {
// The new character is a regular character. Apply combiningCharacter to it, if it exists.
if (combiningCharacter != 0) {
int combinedChar = KeyCharacterMap.getDeadChar(combiningCharacter, newCharacterCodePoint);
if (combinedChar > 0) {
complexCharacter = (char) combinedChar;
}
combiningCharacter = 0;
}
}
return complexCharacter;
}
}
\ No newline at end of file
...@@ -59,9 +59,9 @@ public class XFlutterView extends FrameLayout { ...@@ -59,9 +59,9 @@ public class XFlutterView extends FrameLayout {
// These components essentially add some additional behavioral logic on top of // These components essentially add some additional behavioral logic on top of
// existing, stateless system channels, e.g., KeyEventChannel, TextInputChannel, etc. // existing, stateless system channels, e.g., KeyEventChannel, TextInputChannel, etc.
@Nullable @Nullable
private TextInputPlugin textInputPlugin; private XTextInputPlugin textInputPlugin;
@Nullable @Nullable
private AndroidKeyProcessor androidKeyProcessor; private XAndroidKeyProcessor androidKeyProcessor;
@Nullable @Nullable
private AndroidTouchProcessor androidTouchProcessor; private AndroidTouchProcessor androidTouchProcessor;
@Nullable @Nullable
...@@ -168,8 +168,14 @@ public class XFlutterView extends FrameLayout { ...@@ -168,8 +168,14 @@ public class XFlutterView extends FrameLayout {
@Override @Override
protected void onConfigurationChanged(Configuration newConfig) { protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig); super.onConfigurationChanged(newConfig);
sendLocalesToFlutter(newConfig); try {
sendUserSettingsToFlutter(); sendLocalesToFlutter(newConfig);
sendUserSettingsToFlutter();
}catch (Throwable e){
Log.e(TAG, "onConfigurationChanged error ");
}
} }
/** /**
...@@ -398,16 +404,7 @@ public class XFlutterView extends FrameLayout { ...@@ -398,16 +404,7 @@ public class XFlutterView extends FrameLayout {
} }
} }
public AccessibilityBridge getAccessibilityBridge() {
if (accessibilityBridge != null) {
return accessibilityBridge;
} else {
// TODO(goderbauer): when a11y is off this should return a one-off snapshot of
// the a11y
// tree.
return null;
}
}
// TODO(mattcarroll): Confer with Ian as to why we need this method. Delete if possible, otherwise add comments. // TODO(mattcarroll): Confer with Ian as to why we need this method. Delete if possible, otherwise add comments.
private void resetWillNotDraw(boolean isAccessibilityEnabled, boolean isTouchExplorationEnabled) { private void resetWillNotDraw(boolean isAccessibilityEnabled, boolean isTouchExplorationEnabled) {
if(flutterEngine==null) return; if(flutterEngine==null) return;
...@@ -453,34 +450,45 @@ public class XFlutterView extends FrameLayout { ...@@ -453,34 +450,45 @@ public class XFlutterView extends FrameLayout {
// Initialize various components that know how to process Android View I/O // Initialize various components that know how to process Android View I/O
// in a way that Flutter understands. // in a way that Flutter understands.
textInputPlugin = new TextInputPlugin( if(textInputPlugin==null){
this, textInputPlugin = new XTextInputPlugin(
this.flutterEngine.getDartExecutor() this,
); this.flutterEngine.getDartExecutor()
androidKeyProcessor = new AndroidKeyProcessor( );
this.flutterEngine.getKeyEventChannel(), androidKeyProcessor = new XAndroidKeyProcessor(
textInputPlugin this.flutterEngine.getKeyEventChannel(),
); textInputPlugin
);
}
androidTouchProcessor = new AndroidTouchProcessor(this.flutterEngine.getRenderer()); androidTouchProcessor = new AndroidTouchProcessor(this.flutterEngine.getRenderer());
accessibilityBridge = new AccessibilityBridge(
this, if(accessibilityBridge==null){
flutterEngine.getAccessibilityChannel(), accessibilityBridge = new AccessibilityBridge(
(AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE), this,
getContext().getContentResolver(), flutterEngine.getAccessibilityChannel(),
// TODO(mattcaroll): plumb the platform views controller to the accessibility bridge. (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE),
// https://github.com/flutter/flutter/issues/29618 getContext().getContentResolver(),
null // TODO(mattcaroll): plumb the platform views controller to the accessibility bridge.
); // https://github.com/flutter/flutter/issues/29618
accessibilityBridge.setOnAccessibilityChangeListener(onAccessibilityChangeListener); null
resetWillNotDraw( );
accessibilityBridge.isAccessibilityEnabled(), accessibilityBridge.setOnAccessibilityChangeListener(onAccessibilityChangeListener);
accessibilityBridge.isTouchExplorationEnabled() resetWillNotDraw(
); accessibilityBridge.isAccessibilityEnabled(),
accessibilityBridge.isTouchExplorationEnabled()
);
textInputPlugin.getInputMethodManager().restartInput(this);
}
// Inform the Android framework that it should retrieve a new InputConnection // Inform the Android framework that it should retrieve a new InputConnection
// now that an engine is attached. // now that an engine is attached.
// TODO(mattcarroll): once this is proven to work, move this line ot TextInputPlugin // TODO(mattcarroll): once this is proven to work, move this line ot TextInputPlugin
textInputPlugin.getInputMethodManager().restartInput(this);
// Push View and Context related information from Android to Flutter. // Push View and Context related information from Android to Flutter.
sendUserSettingsToFlutter(); sendUserSettingsToFlutter();
...@@ -488,6 +496,16 @@ public class XFlutterView extends FrameLayout { ...@@ -488,6 +496,16 @@ public class XFlutterView extends FrameLayout {
sendViewportMetricsToFlutter(); sendViewportMetricsToFlutter();
} }
public void release(){
if(accessibilityBridge!=null){
accessibilityBridge.release();
}
textInputPlugin.release();
}
/** /**
* Disconnects this {@code FlutterView} from a previously attached {@link FlutterEngine}. * Disconnects this {@code FlutterView} from a previously attached {@link FlutterEngine}.
* *
...@@ -511,7 +529,6 @@ public class XFlutterView extends FrameLayout { ...@@ -511,7 +529,6 @@ public class XFlutterView extends FrameLayout {
// signifies that this View does not process input (until a new engine is attached). // signifies that this View does not process input (until a new engine is attached).
// TODO(mattcarroll): once this is proven to work, move this line ot TextInputPlugin // TODO(mattcarroll): once this is proven to work, move this line ot TextInputPlugin
textInputPlugin.getInputMethodManager().restartInput(this); textInputPlugin.getInputMethodManager().restartInput(this);
// Instruct our FlutterRenderer that we are no longer interested in being its RenderSurface. // Instruct our FlutterRenderer that we are no longer interested in being its RenderSurface.
flutterEngine.getRenderer().detachFromRenderSurface(); flutterEngine.getRenderer().detachFromRenderSurface();
flutterEngine = null; flutterEngine = null;
...@@ -560,10 +577,13 @@ public class XFlutterView extends FrameLayout { ...@@ -560,10 +577,13 @@ public class XFlutterView extends FrameLayout {
* FlutterEngine must be non-null when this method is invoked. * FlutterEngine must be non-null when this method is invoked.
*/ */
private void sendUserSettingsToFlutter() { private void sendUserSettingsToFlutter() {
flutterEngine.getSettingsChannel().startMessage() if(flutterEngine!=null&&flutterEngine.getSettingsChannel()!=null){
.setTextScaleFactor(getResources().getConfiguration().fontScale) flutterEngine.getSettingsChannel().startMessage()
.setUse24HourFormat(DateFormat.is24HourFormat(getContext())) .setTextScaleFactor(getResources().getConfiguration().fontScale)
.send(); .setUse24HourFormat(DateFormat.is24HourFormat(getContext()))
.send();
}
} }
// TODO(mattcarroll): consider introducing a system channel for this communication instead of JNI // TODO(mattcarroll): consider introducing a system channel for this communication instead of JNI
......
package com.idlefish.flutterboost;
import android.content.Context;
import android.text.Editable;
import android.text.Selection;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import io.flutter.embedding.engine.systemchannels.TextInputChannel;
import io.flutter.plugin.common.ErrorLogResult;
import io.flutter.plugin.common.MethodChannel;
class XInputConnectionAdaptor extends BaseInputConnection {
private final View mFlutterView;
private final int mClient;
private final TextInputChannel textInputChannel;
private final Editable mEditable;
private int mBatchCount;
private InputMethodManager mImm;
private static final MethodChannel.Result logger =
new ErrorLogResult("FlutterTextInput");
public XInputConnectionAdaptor(
View view,
int client,
TextInputChannel textInputChannel,
Editable editable
) {
super(view, true);
mFlutterView = view;
mClient = client;
this.textInputChannel = textInputChannel;
mEditable = editable;
mBatchCount = 0;
mImm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
}
// Send the current state of the editable to Flutter.
private void updateEditingState() {
// If the IME is in the middle of a batch edit, then wait until it completes.
if (mBatchCount > 0)
return;
int selectionStart = Selection.getSelectionStart(mEditable);
int selectionEnd = Selection.getSelectionEnd(mEditable);
int composingStart = BaseInputConnection.getComposingSpanStart(mEditable);
int composingEnd = BaseInputConnection.getComposingSpanEnd(mEditable);
mImm.updateSelection(mFlutterView,
selectionStart, selectionEnd,
composingStart, composingEnd);
textInputChannel.updateEditingState(
mClient,
mEditable.toString(),
selectionStart,
selectionEnd,
composingStart,
composingEnd
);
}
@Override
public Editable getEditable() {
return mEditable;
}
@Override
public boolean beginBatchEdit() {
mBatchCount++;
return super.beginBatchEdit();
}
@Override
public boolean endBatchEdit() {
boolean result = super.endBatchEdit();
mBatchCount--;
updateEditingState();
return result;
}
@Override
public boolean commitText(CharSequence text, int newCursorPosition) {
boolean result = super.commitText(text, newCursorPosition);
updateEditingState();
return result;
}
@Override
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
if (Selection.getSelectionStart(mEditable) == -1)
return true;
boolean result = super.deleteSurroundingText(beforeLength, afterLength);
updateEditingState();
return result;
}
@Override
public boolean setComposingRegion(int start, int end) {
boolean result = super.setComposingRegion(start, end);
updateEditingState();
return result;
}
@Override
public boolean setComposingText(CharSequence text, int newCursorPosition) {
boolean result;
if (text.length() == 0) {
result = super.commitText(text, newCursorPosition);
} else {
result = super.setComposingText(text, newCursorPosition);
}
updateEditingState();
return result;
}
@Override
public boolean setSelection(int start, int end) {
boolean result = super.setSelection(start, end);
updateEditingState();
return result;
}
@Override
public boolean sendKeyEvent(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
int selStart = Selection.getSelectionStart(mEditable);
int selEnd = Selection.getSelectionEnd(mEditable);
if (selEnd > selStart) {
// Delete the selection.
Selection.setSelection(mEditable, selStart);
mEditable.delete(selStart, selEnd);
updateEditingState();
return true;
} else if (selStart > 0) {
// Delete to the left of the cursor.
int newSel = Math.max(selStart - 1, 0);
Selection.setSelection(mEditable, newSel);
mEditable.delete(newSel, selStart);
updateEditingState();
return true;
}
} else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) {
int selStart = Selection.getSelectionStart(mEditable);
int newSel = Math.max(selStart - 1, 0);
setSelection(newSel, newSel);
return true;
} else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT) {
int selStart = Selection.getSelectionStart(mEditable);
int newSel = Math.min(selStart + 1, mEditable.length());
setSelection(newSel, newSel);
return true;
} else {
// Enter a character.
int character = event.getUnicodeChar();
if (character != 0) {
int selStart = Math.max(0, Selection.getSelectionStart(mEditable));
int selEnd = Math.max(0, Selection.getSelectionEnd(mEditable));
if (selEnd != selStart)
mEditable.delete(selStart, selEnd);
mEditable.insert(selStart, String.valueOf((char) character));
setSelection(selStart + 1, selStart + 1);
updateEditingState();
}
return true;
}
}
return false;
}
@Override
public boolean performEditorAction(int actionCode) {
switch (actionCode) {
case EditorInfo.IME_ACTION_NONE:
textInputChannel.newline(mClient);
break;
case EditorInfo.IME_ACTION_UNSPECIFIED:
textInputChannel.unspecifiedAction(mClient);
break;
case EditorInfo.IME_ACTION_GO:
textInputChannel.go(mClient);
break;
case EditorInfo.IME_ACTION_SEARCH:
textInputChannel.search(mClient);
break;
case EditorInfo.IME_ACTION_SEND:
textInputChannel.send(mClient);
break;
case EditorInfo.IME_ACTION_NEXT:
textInputChannel.next(mClient);
break;
case EditorInfo.IME_ACTION_PREVIOUS:
textInputChannel.previous(mClient);
break;
default:
case EditorInfo.IME_ACTION_DONE:
textInputChannel.done(mClient);
break;
}
return true;
}
}
\ No newline at end of file
package com.idlefish.flutterboost;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.Editable;
import android.text.InputType;
import android.text.Selection;
import android.view.View;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.embedding.engine.systemchannels.TextInputChannel;
import io.flutter.view.FlutterView;
/**
* Android implementation of the text input plugin.
*/
public class XTextInputPlugin {
@NonNull
private View mView;
@NonNull
private final InputMethodManager mImm;
@NonNull
private final TextInputChannel textInputChannel;
private int mClient = 0;
@Nullable
private TextInputChannel.Configuration configuration;
@Nullable
private Editable mEditable;
private boolean mRestartInputPending;
@Nullable
private InputConnection lastInputConnection;
public XTextInputPlugin(View view, @NonNull DartExecutor dartExecutor) {
mView = view;
mImm = (InputMethodManager) view.getContext().getSystemService(
Context.INPUT_METHOD_SERVICE);
textInputChannel = new TextInputChannel(dartExecutor);
textInputChannel.setTextInputMethodHandler(new TextInputChannel.TextInputMethodHandler() {
@Override
public void show() {
showTextInput(mView);
}
@Override
public void hide() {
hideTextInput(mView);
}
@Override
public void setClient(int textInputClientId, TextInputChannel.Configuration configuration) {
setTextInputClient(textInputClientId, configuration);
}
@Override
public void setEditingState(TextInputChannel.TextEditState editingState) {
setTextInputEditingState(mView, editingState);
}
@Override
public void clearClient() {
clearTextInputClient();
}
});
}
public void release(){
mView=null;
}
@NonNull
public InputMethodManager getInputMethodManager() {
return mImm;
}
private static int inputTypeFromTextInputType(
TextInputChannel.InputType type,
boolean obscureText,
boolean autocorrect,
TextInputChannel.TextCapitalization textCapitalization
) {
if (type.type == TextInputChannel.TextInputType.DATETIME) {
return InputType.TYPE_CLASS_DATETIME;
} else if (type.type == TextInputChannel.TextInputType.NUMBER) {
int textType = InputType.TYPE_CLASS_NUMBER;
if (type.isSigned) {
textType |= InputType.TYPE_NUMBER_FLAG_SIGNED;
}
if (type.isDecimal) {
textType |= InputType.TYPE_NUMBER_FLAG_DECIMAL;
}
return textType;
} else if (type.type == TextInputChannel.TextInputType.PHONE) {
return InputType.TYPE_CLASS_PHONE;
}
int textType = InputType.TYPE_CLASS_TEXT;
if (type.type == TextInputChannel.TextInputType.MULTILINE) {
textType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE;
} else if (type.type == TextInputChannel.TextInputType.EMAIL_ADDRESS) {
textType |= InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
} else if (type.type == TextInputChannel.TextInputType.URL) {
textType |= InputType.TYPE_TEXT_VARIATION_URI;
}
if (obscureText) {
// Note: both required. Some devices ignore TYPE_TEXT_FLAG_NO_SUGGESTIONS.
textType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
textType |= InputType.TYPE_TEXT_VARIATION_PASSWORD;
} else {
if (autocorrect) textType |= InputType.TYPE_TEXT_FLAG_AUTO_CORRECT;
}
if (textCapitalization == TextInputChannel.TextCapitalization.CHARACTERS) {
textType |= InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS;
} else if (textCapitalization == TextInputChannel.TextCapitalization.WORDS) {
textType |= InputType.TYPE_TEXT_FLAG_CAP_WORDS;
} else if (textCapitalization == TextInputChannel.TextCapitalization.SENTENCES) {
textType |= InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
}
return textType;
}
public InputConnection createInputConnection(View view, EditorInfo outAttrs) {
if (mClient == 0) {
lastInputConnection = null;
return lastInputConnection;
}
outAttrs.inputType = inputTypeFromTextInputType(
configuration.inputType,
configuration.obscureText,
configuration.autocorrect,
configuration.textCapitalization
);
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
int enterAction;
if (configuration.inputAction == null) {
// If an explicit input action isn't set, then default to none for multi-line fields
// and done for single line fields.
enterAction = (InputType.TYPE_TEXT_FLAG_MULTI_LINE & outAttrs.inputType) != 0
? EditorInfo.IME_ACTION_NONE
: EditorInfo.IME_ACTION_DONE;
} else {
enterAction = configuration.inputAction;
}
if (configuration.actionLabel != null) {
outAttrs.actionLabel = configuration.actionLabel;
outAttrs.actionId = enterAction;
}
outAttrs.imeOptions |= enterAction;
XInputConnectionAdaptor connection = new XInputConnectionAdaptor(
view,
mClient,
textInputChannel,
mEditable
);
outAttrs.initialSelStart = Selection.getSelectionStart(mEditable);
outAttrs.initialSelEnd = Selection.getSelectionEnd(mEditable);
lastInputConnection = connection;
return lastInputConnection;
}
@Nullable
public InputConnection getLastInputConnection() {
return lastInputConnection;
}
private void showTextInput(View view) {
view.requestFocus();
mImm.showSoftInput(view, 0);
}
private void hideTextInput(View view) {
mImm.hideSoftInputFromWindow(view.getApplicationWindowToken(), 0);
}
private void setTextInputClient(int client, TextInputChannel.Configuration configuration) {
mClient = client;
this.configuration = configuration;
mEditable = Editable.Factory.getInstance().newEditable("");
// setTextInputClient will be followed by a call to setTextInputEditingState.
// Do a restartInput at that time.
mRestartInputPending = true;
}
private void applyStateToSelection(TextInputChannel.TextEditState state) {
int selStart = state.selectionStart;
int selEnd = state.selectionEnd;
if (selStart >= 0 && selStart <= mEditable.length() && selEnd >= 0
&& selEnd <= mEditable.length()) {
Selection.setSelection(mEditable, selStart, selEnd);
} else {
Selection.removeSelection(mEditable);
}
}
private void setTextInputEditingState(View view, TextInputChannel.TextEditState state) {
if (!mRestartInputPending && state.text.equals(mEditable.toString())) {
applyStateToSelection(state);
mImm.updateSelection(mView, Math.max(Selection.getSelectionStart(mEditable), 0),
Math.max(Selection.getSelectionEnd(mEditable), 0),
BaseInputConnection.getComposingSpanStart(mEditable),
BaseInputConnection.getComposingSpanEnd(mEditable));
} else {
mEditable.replace(0, mEditable.length(), state.text);
applyStateToSelection(state);
mImm.restartInput(view);
mRestartInputPending = false;
}
}
private void clearTextInputClient() {
mClient = 0;
}
}
\ No newline at end of file
...@@ -185,8 +185,10 @@ public abstract class BoostFlutterActivity extends Activity implements IFlutterV ...@@ -185,8 +185,10 @@ public abstract class BoostFlutterActivity extends Activity implements IFlutterV
@Override @Override
protected void onDestroy() { protected void onDestroy() {
Utils.fixInputMethodManagerLeak(this);
mSyncer.onDestroy(); mSyncer.onDestroy();
super.onDestroy(); super.onDestroy();
} }
@Override @Override
......
...@@ -52,6 +52,8 @@ ...@@ -52,6 +52,8 @@
_dummy = [[FLBFlutterViewContainer alloc] initWithEngine:_engine _dummy = [[FLBFlutterViewContainer alloc] initWithEngine:_engine
nibName:nil nibName:nil
bundle:nil]; bundle:nil];
_dummy.name = kIgnoreMessageWithName;
Class clazz = NSClassFromString(@"GeneratedPluginRegistrant"); Class clazz = NSClassFromString(@"GeneratedPluginRegistrant");
if (clazz) { if (clazz) {
if ([clazz respondsToSelector:NSSelectorFromString(@"registerWithRegistry:")]) { if ([clazz respondsToSelector:NSSelectorFromString(@"registerWithRegistry:")]) {
...@@ -121,6 +123,9 @@ ...@@ -121,6 +123,9 @@
- (void)prepareEngineIfNeeded - (void)prepareEngineIfNeeded
{ {
// if ([_dummy respondsToSelector:@selector(setEnableForRunnersBatch:)]) {
// [_dummy setEnableForRunnersBatch:YES];
// }
[self detach]; [self detach];
[_dummy surfaceUpdated:YES]; [_dummy surfaceUpdated:YES];
} }
......
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
#import <Flutter/Flutter.h> #import <Flutter/Flutter.h>
#import "FLBTypes.h" #import "FLBTypes.h"
#define kIgnoreMessageWithName @"__flutterboost_dummy__"
@interface BoostMessageChannel : NSObject @interface BoostMessageChannel : NSObject
+ (void)onNativePageResult:(void (^)(NSNumber *))result uniqueId:(NSString *)uniqueId key:(NSString *)key resultData:(NSDictionary *)resultData params:(NSDictionary *)params; + (void)onNativePageResult:(void (^)(NSNumber *))result uniqueId:(NSString *)uniqueId key:(NSString *)key resultData:(NSDictionary *)resultData params:(NSDictionary *)params;
......
...@@ -100,6 +100,10 @@ ...@@ -100,6 +100,10 @@
+ (void)didShowPageContainer:(void (^)(NSNumber *))result pageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId + (void)didShowPageContainer:(void (^)(NSNumber *))result pageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId
{ {
if ([pageName isEqualToString:kIgnoreMessageWithName]) {
return;
}
NSMutableDictionary *tmp = [NSMutableDictionary dictionary]; NSMutableDictionary *tmp = [NSMutableDictionary dictionary];
if(pageName) tmp[@"pageName"] = pageName; if(pageName) tmp[@"pageName"] = pageName;
if(params) tmp[@"params"] = params; if(params) tmp[@"params"] = params;
...@@ -113,6 +117,10 @@ ...@@ -113,6 +117,10 @@
+ (void)willShowPageContainer:(void (^)(NSNumber *))result pageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId + (void)willShowPageContainer:(void (^)(NSNumber *))result pageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId
{ {
if ([pageName isEqualToString:kIgnoreMessageWithName]) {
return;
}
NSMutableDictionary *tmp = [NSMutableDictionary dictionary]; NSMutableDictionary *tmp = [NSMutableDictionary dictionary];
if(pageName) tmp[@"pageName"] = pageName; if(pageName) tmp[@"pageName"] = pageName;
if(params) tmp[@"params"] = params; if(params) tmp[@"params"] = params;
...@@ -126,6 +134,10 @@ ...@@ -126,6 +134,10 @@
+ (void)willDisappearPageContainer:(void (^)(NSNumber *))result pageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId + (void)willDisappearPageContainer:(void (^)(NSNumber *))result pageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId
{ {
if ([pageName isEqualToString:kIgnoreMessageWithName]) {
return;
}
NSMutableDictionary *tmp = [NSMutableDictionary dictionary]; NSMutableDictionary *tmp = [NSMutableDictionary dictionary];
if(pageName) tmp[@"pageName"] = pageName; if(pageName) tmp[@"pageName"] = pageName;
if(params) tmp[@"params"] = params; if(params) tmp[@"params"] = params;
...@@ -139,6 +151,10 @@ ...@@ -139,6 +151,10 @@
+ (void)didDisappearPageContainer:(void (^)(NSNumber *))result pageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId + (void)didDisappearPageContainer:(void (^)(NSNumber *))result pageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId
{ {
if ([pageName isEqualToString:kIgnoreMessageWithName]) {
return;
}
NSMutableDictionary *tmp = [NSMutableDictionary dictionary]; NSMutableDictionary *tmp = [NSMutableDictionary dictionary];
if(pageName) tmp[@"pageName"] = pageName; if(pageName) tmp[@"pageName"] = pageName;
if(params) tmp[@"params"] = params; if(params) tmp[@"params"] = params;
...@@ -152,6 +168,10 @@ ...@@ -152,6 +168,10 @@
+ (void)didInitPageContainer:(void (^)(NSNumber *))result pageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId + (void)didInitPageContainer:(void (^)(NSNumber *))result pageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId
{ {
if ([pageName isEqualToString:kIgnoreMessageWithName]) {
return;
}
NSMutableDictionary *tmp = [NSMutableDictionary dictionary]; NSMutableDictionary *tmp = [NSMutableDictionary dictionary];
if(pageName) tmp[@"pageName"] = pageName; if(pageName) tmp[@"pageName"] = pageName;
if(params) tmp[@"params"] = params; if(params) tmp[@"params"] = params;
...@@ -169,6 +189,10 @@ ...@@ -169,6 +189,10 @@
+ (void)willDeallocPageContainer:(void (^)(NSNumber *))result pageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId + (void)willDeallocPageContainer:(void (^)(NSNumber *))result pageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId
{ {
if ([pageName isEqualToString:kIgnoreMessageWithName]) {
return;
}
NSMutableDictionary *tmp = [NSMutableDictionary dictionary]; NSMutableDictionary *tmp = [NSMutableDictionary dictionary];
if(pageName) tmp[@"pageName"] = pageName; if(pageName) tmp[@"pageName"] = pageName;
if(params) tmp[@"params"] = params; if(params) tmp[@"params"] = params;
......
...@@ -27,7 +27,9 @@ ...@@ -27,7 +27,9 @@
#import "FLBFlutterContainer.h" #import "FLBFlutterContainer.h"
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@interface FLBFlutterViewContainer : FlutterViewController<FLBFlutterContainer> @interface FLB2FlutterViewContainer : FlutterViewController<FLBFlutterContainer>
@property (nonatomic,copy,readwrite) NSString *name;
- (void)surfaceUpdated:(BOOL)appeared; - (void)surfaceUpdated:(BOOL)appeared;
- (void)setEnableForRunnersBatch:(BOOL)enable;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END
...@@ -32,8 +32,7 @@ ...@@ -32,8 +32,7 @@
#define FLUTTER_VIEW FLUTTER_APP.flutterViewController.view #define FLUTTER_VIEW FLUTTER_APP.flutterViewController.view
#define FLUTTER_VC FLUTTER_APP.flutterViewController #define FLUTTER_VC FLUTTER_APP.flutterViewController
@interface FLBFlutterViewContainer () @interface FLB2FlutterViewContainer ()
@property (nonatomic,copy,readwrite) NSString *name;
@property (nonatomic,strong,readwrite) NSDictionary *params; @property (nonatomic,strong,readwrite) NSDictionary *params;
@property (nonatomic,assign) long long identifier; @property (nonatomic,assign) long long identifier;
@end @end
...@@ -145,6 +144,10 @@ static NSUInteger kInstanceCounter = 0; ...@@ -145,6 +144,10 @@ static NSUInteger kInstanceCounter = 0;
[FLUTTER_APP.flutterProvider detach]; [FLUTTER_APP.flutterProvider detach];
} }
- (void)setEnableForRunnersBatch:(BOOL)enable{
//dummy function
NSLog(@"[DEBUG]- I did nothing, I am innocent");
}
#pragma mark - Life circle methods #pragma mark - Life circle methods
...@@ -157,9 +160,11 @@ static NSUInteger kInstanceCounter = 0; ...@@ -157,9 +160,11 @@ static NSUInteger kInstanceCounter = 0;
- (void)viewWillAppear:(BOOL)animated - (void)viewWillAppear:(BOOL)animated
{ {
if([FLUTTER_APP contains:self]){ if([FLUTTER_APP contains:self]){
[self surfaceUpdated:NO];
[self detatchFlutterEngine]; [self detatchFlutterEngine];
}else{ }else{
[self attatchFlutterEngine]; [self attatchFlutterEngine];
[self surfaceUpdated:YES];
} }
[FLUTTER_APP resume]; [FLUTTER_APP resume];
...@@ -210,14 +215,14 @@ static NSUInteger kInstanceCounter = 0; ...@@ -210,14 +215,14 @@ static NSUInteger kInstanceCounter = 0;
- (void)viewDidDisappear:(BOOL)animated - (void)viewDidDisappear:(BOOL)animated
{ {
[super viewDidDisappear:animated];
[FLUTTER_APP resume]; [FLUTTER_APP resume];
[BoostMessageChannel didDisappearPageContainer:^(NSNumber *result) {} [BoostMessageChannel didDisappearPageContainer:^(NSNumber *result) {}
pageName:_name pageName:_name
params:_params params:_params
uniqueId:self.uniqueIDString]; uniqueId:self.uniqueIDString];
[super viewDidDisappear:animated];
[FLUTTER_APP resume];
} }
- (void)installSplashScreenViewIfNecessary { - (void)installSplashScreenViewIfNecessary {
......
...@@ -30,6 +30,7 @@ import '../support/logger.dart'; ...@@ -30,6 +30,7 @@ import '../support/logger.dart';
enum ContainerLifeCycle { enum ContainerLifeCycle {
Init, Init,
Appear, Appear,
WillDisappear,
Disappear, Disappear,
Destroy, Destroy,
Background, Background,
......
...@@ -236,6 +236,8 @@ class ContainerCoordinator { ...@@ -236,6 +236,8 @@ class ContainerCoordinator {
} }
bool _nativeContainerWillDisappear(String name, Map params, String pageId) { bool _nativeContainerWillDisappear(String name, Map params, String pageId) {
performContainerLifeCycle(_createContainerSettings(name, params, pageId),
ContainerLifeCycle.WillDisappear);
return true; return true;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment