Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
F
flutter_boost_1.22.4
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
李增强
flutter_boost_1.22.4
Commits
67c34f5b
Commit
67c34f5b
authored
Oct 15, 2019
by
yangwu.jia
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
resolve Memory Leaks
parent
dd87a8bf
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
37 additions
and
930 deletions
+37
-930
android/src/main/java/com/idlefish/flutterboost/Utils.java
android/src/main/java/com/idlefish/flutterboost/Utils.java
+3
-4
android/src/main/java/com/idlefish/flutterboost/XAndroidKeyProcessor.java
.../java/com/idlefish/flutterboost/XAndroidKeyProcessor.java
+0
-99
android/src/main/java/com/idlefish/flutterboost/XFlutterView.java
...src/main/java/com/idlefish/flutterboost/XFlutterView.java
+31
-5
android/src/main/java/com/idlefish/flutterboost/XInputConnectionAdaptor.java
...va/com/idlefish/flutterboost/XInputConnectionAdaptor.java
+0
-208
android/src/main/java/com/idlefish/flutterboost/XPlatformPlugin.java
.../main/java/com/idlefish/flutterboost/XPlatformPlugin.java
+0
-257
android/src/main/java/com/idlefish/flutterboost/XTextInputPlugin.java
...main/java/com/idlefish/flutterboost/XTextInputPlugin.java
+0
-357
android/src/main/java/com/idlefish/flutterboost/containers/FlutterActivityAndFragmentDelegate.java
...rboost/containers/FlutterActivityAndFragmentDelegate.java
+3
-0
No files found.
android/src/main/java/com/idlefish/flutterboost/Utils.java
View file @
67c34f5b
...
...
@@ -272,14 +272,13 @@ public class Utils {
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节点
if
(
v_get
.
getContext
()
==
destContext
)
{
f
.
set
(
imm
,
null
);
}
else
{
// 不是想要目标销毁的,即为又进了另一层界面了,不要处理,避免影响原逻辑,也就不用继续for循环了
break
;
}
}
...
...
android/src/main/java/com/idlefish/flutterboost/XAndroidKeyProcessor.java
deleted
100644 → 0
View file @
dd87a8bf
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
android/src/main/java/com/idlefish/flutterboost/XFlutterView.java
View file @
67c34f5b
...
...
@@ -25,6 +25,7 @@ import android.view.inputmethod.EditorInfo;
import
android.view.inputmethod.InputConnection
;
import
android.widget.FrameLayout
;
import
java.lang.reflect.Field
;
import
java.util.ArrayList
;
import
java.util.HashSet
;
import
java.util.List
;
...
...
@@ -36,6 +37,7 @@ import io.flutter.embedding.android.*;
import
io.flutter.embedding.engine.FlutterEngine
;
import
io.flutter.embedding.engine.renderer.FlutterRenderer
;
import
io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener
;
import
io.flutter.embedding.engine.systemchannels.TextInputChannel
;
import
io.flutter.plugin.editing.TextInputPlugin
;
import
io.flutter.plugin.platform.PlatformViewsController
;
import
io.flutter.view.AccessibilityBridge
;
...
...
@@ -586,12 +588,12 @@ public class XFlutterView extends FrameLayout {
// in a way that Flutter understands.
if
(
this
.
textInputPlugin
!=
null
){
this
.
textInputPlugin
.
destroy
();
resolveMemoryLeaks
();
}
this
.
textInputPlugin
=
new
TextInputPlugin
(
this
,
this
.
flutterEngine
.
getDartExecutor
(),
this
.
flutterEngine
.
getPlatformViewsController
());
this
.
textInputPlugin
.
getInputMethodManager
().
restartInput
(
this
);
this
.
androidKeyProcessor
=
new
AndroidKeyProcessor
(
this
.
flutterEngine
.
getKeyEventChannel
(),
...
...
@@ -619,7 +621,7 @@ public class XFlutterView extends FrameLayout {
// Inform the Android framework that it should retrieve a new InputConnection
// now that an engine is attached.
// TODO(mattcarroll): once this is proven to work, move this line ot TextInputPlugin
//
textInputPlugin.getInputMethodManager().restartInput(this);
textInputPlugin
.
getInputMethodManager
().
restartInput
(
this
);
// Push View and Context related information from Android to Flutter.
sendUserSettingsToFlutter
();
...
...
@@ -673,8 +675,8 @@ public class XFlutterView extends FrameLayout {
// 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
textInputPlugin
.
getInputMethodManager
().
restartInput
(
this
);
//
textInputPlugin.destroy();
textInputPlugin
.
destroy
();
resolveMemoryLeaks
();
// Instruct our FlutterRenderer that we are no longer interested in being its RenderSurface.
FlutterRenderer
flutterRenderer
=
flutterEngine
.
getRenderer
();
didRenderFirstFrame
=
false
;
...
...
@@ -683,6 +685,30 @@ public class XFlutterView extends FrameLayout {
flutterEngine
=
null
;
}
public
void
resolveMemoryLeaks
(){
try
{
Class
clazz
=
TextInputPlugin
.
class
;
for
(
Field
f
:
clazz
.
getDeclaredFields
())
{
System
.
out
.
println
(
f
.
isAccessible
());
f
.
setAccessible
(
true
);
if
(
f
.
get
(
this
.
textInputPlugin
)
instanceof
TextInputChannel
){
System
.
out
.
println
(
"xxxxxx:"
+
f
.
getName
());
TextInputChannel
channel
=(
TextInputChannel
)
f
.
get
(
this
.
textInputPlugin
);
channel
.
setTextInputMethodHandler
(
null
);
}
}
}
catch
(
Throwable
e
)
{
e
.
printStackTrace
();
}
}
/**
* Returns true if this {@code FlutterView} is currently attached to a {@link FlutterEngine}.
*/
...
...
android/src/main/java/com/idlefish/flutterboost/XInputConnectionAdaptor.java
deleted
100644 → 0
View file @
dd87a8bf
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
android/src/main/java/com/idlefish/flutterboost/XPlatformPlugin.java
deleted
100644 → 0
View file @
dd87a8bf
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
<
28
&&
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
,
null
,
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
android/src/main/java/com/idlefish/flutterboost/XTextInputPlugin.java
deleted
100644 → 0
View file @
dd87a8bf
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.plugin.editing.TextInputPlugin
;
import
io.flutter.plugin.platform.PlatformViewsController
;
/**
* Android implementation of the text input plugin.
*/
public
class
XTextInputPlugin
{
@NonNull
private
View
mView
;
@NonNull
private
final
InputMethodManager
mImm
;
@NonNull
private
final
TextInputChannel
textInputChannel
;
@NonNull
private
InputTarget
inputTarget
=
new
InputTarget
(
InputTarget
.
Type
.
NO_TARGET
,
0
);
@Nullable
private
TextInputChannel
.
Configuration
configuration
;
@Nullable
private
Editable
mEditable
;
private
boolean
mRestartInputPending
;
@Nullable
private
InputConnection
lastInputConnection
;
@NonNull
private
PlatformViewsController
platformViewsController
;
// When true following calls to createInputConnection will return the cached lastInputConnection if the input
// target is a platform view. See the comments on lockPlatformViewInputConnection for more details.
private
boolean
isInputConnectionLocked
;
public
XTextInputPlugin
(
View
view
,
TextInputChannel
textInputChannel
,
PlatformViewsController
platformViewsController
)
{
mView
=
view
;
mImm
=
(
InputMethodManager
)
view
.
getContext
().
getSystemService
(
Context
.
INPUT_METHOD_SERVICE
);
this
.
textInputChannel
=
textInputChannel
;
this
.
platformViewsController
=
platformViewsController
;
// this.platformViewsController.attachTextInputPlugin(this);
}
public
void
release
(){
mView
=
null
;
}
public
void
setTextInputMethodHandler
(){
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
setPlatformViewClient
(
int
platformViewId
)
{
setPlatformViewTextInputClient
(
platformViewId
);
}
@Override
public
void
setEditingState
(
TextInputChannel
.
TextEditState
editingState
)
{
setTextInputEditingState
(
mView
,
editingState
);
}
@Override
public
void
clearClient
()
{
clearTextInputClient
();
}
});
}
@NonNull
public
InputMethodManager
getInputMethodManager
()
{
return
mImm
;
}
/***
* Use the current platform view input connection until unlockPlatformViewInputConnection is called.
*
* The current input connection instance is cached and any following call to @{link createInputConnection} returns
* the cached connection until unlockPlatformViewInputConnection is called.
*
* This is a no-op if the current input target isn't a platform view.
*
* This is used to preserve an input connection when moving a platform view from one virtual display to another.
*/
public
void
lockPlatformViewInputConnection
()
{
if
(
inputTarget
.
type
==
InputTarget
.
Type
.
PLATFORM_VIEW
)
{
isInputConnectionLocked
=
true
;
}
}
/**
* Unlocks the input connection.
*
* See also: @{link lockPlatformViewInputConnection}.
*/
public
void
unlockPlatformViewInputConnection
()
{
isInputConnectionLocked
=
false
;
}
/**
* Detaches the text input plugin from the platform views controller.
*
* The TextInputPlugin instance should not be used after calling this.
*/
public
void
destroy
()
{
platformViewsController
.
detachTextInputPlugin
();
}
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
;
}
else
if
(
type
.
type
==
TextInputChannel
.
TextInputType
.
VISIBLE_PASSWORD
)
{
textType
|=
InputType
.
TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
;
}
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
(
inputTarget
.
type
==
InputTarget
.
Type
.
NO_TARGET
)
{
lastInputConnection
=
null
;
return
null
;
}
if
(
inputTarget
.
type
==
InputTarget
.
Type
.
PLATFORM_VIEW
)
{
if
(
isInputConnectionLocked
)
{
return
lastInputConnection
;
}
lastInputConnection
=
platformViewsController
.
getPlatformViewById
(
inputTarget
.
id
).
onCreateInputConnection
(
outAttrs
);
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
,
inputTarget
.
id
,
textInputChannel
,
mEditable
);
outAttrs
.
initialSelStart
=
Selection
.
getSelectionStart
(
mEditable
);
outAttrs
.
initialSelEnd
=
Selection
.
getSelectionEnd
(
mEditable
);
lastInputConnection
=
connection
;
return
lastInputConnection
;
}
@Nullable
public
InputConnection
getLastInputConnection
()
{
return
lastInputConnection
;
}
/**
* Clears a platform view text input client if it is the current input target.
*
* This is called when a platform view is disposed to make sure we're not hanging to a stale input
* connection.
*/
public
void
clearPlatformViewClient
(
int
platformViewId
)
{
if
(
inputTarget
.
type
==
InputTarget
.
Type
.
PLATFORM_VIEW
&&
inputTarget
.
id
==
platformViewId
)
{
inputTarget
=
new
InputTarget
(
InputTarget
.
Type
.
NO_TARGET
,
0
);
hideTextInput
(
mView
);
mImm
.
restartInput
(
mView
);
mRestartInputPending
=
false
;
}
}
private
void
showTextInput
(
View
view
)
{
view
.
requestFocus
();
mImm
.
showSoftInput
(
view
,
0
);
}
private
void
hideTextInput
(
View
view
)
{
// Note: a race condition may lead to us hiding the keyboard here just after a platform view has shown it.
// This can only potentially happen when switching focus from a Flutter text field to a platform view's text
// field(by text field here I mean anything that keeps the keyboard open).
// See: https://github.com/flutter/flutter/issues/34169
mImm
.
hideSoftInputFromWindow
(
view
.
getApplicationWindowToken
(),
0
);
}
private
void
setTextInputClient
(
int
client
,
TextInputChannel
.
Configuration
configuration
)
{
inputTarget
=
new
InputTarget
(
InputTarget
.
Type
.
FRAMEWORK_CLIENT
,
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
;
unlockPlatformViewInputConnection
();
}
private
void
setPlatformViewTextInputClient
(
int
platformViewId
)
{
// We need to make sure that the Flutter view is focused so that no imm operations get short circuited.
// Not asking for focus here specifically manifested in a but on API 28 devices where the platform view's
// request to show a keyboard was ignored.
mView
.
requestFocus
();
inputTarget
=
new
InputTarget
(
InputTarget
.
Type
.
PLATFORM_VIEW
,
platformViewId
);
mImm
.
restartInput
(
mView
);
mRestartInputPending
=
false
;
}
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
()
{
if
(
inputTarget
.
type
==
InputTarget
.
Type
.
PLATFORM_VIEW
)
{
// Focus changes in the framework tree have no guarantees on the order focus nodes are notified. A node
// that lost focus may be notified before or after a node that gained focus.
// When moving the focus from a Flutter text field to an AndroidView, it is possible that the Flutter text
// field's focus node will be notified that it lost focus after the AndroidView was notified that it gained
// focus. When this happens the text field will send a clearTextInput command which we ignore.
// By doing this we prevent the framework from clearing a platform view input client(the only way to do so
// is to set a new framework text client). I don't see an obvious use case for "clearing" a platform views
// text input client, and it may be error prone as we don't know how the platform view manages the input
// connection and we probably shouldn't interfere.
// If we ever want to allow the framework to clear a platform view text client we should probably consider
// changing the focus manager such that focus nodes that lost focus are notified before focus nodes that
// gained focus as part of the same focus event.
return
;
}
inputTarget
=
new
InputTarget
(
InputTarget
.
Type
.
NO_TARGET
,
0
);
unlockPlatformViewInputConnection
();
}
static
private
class
InputTarget
{
enum
Type
{
NO_TARGET
,
// InputConnection is managed by the TextInputPlugin, and events are forwarded to the Flutter framework.
FRAMEWORK_CLIENT
,
// InputConnection is managed by an embedded platform view.
PLATFORM_VIEW
}
public
InputTarget
(
@NonNull
Type
type
,
int
id
)
{
this
.
type
=
type
;
this
.
id
=
id
;
}
@NonNull
Type
type
;
// The ID of the input target.
//
// For framework clients this is the framework input connection client ID.
// For platform views this is the platform view's ID.
int
id
;
}
}
\ No newline at end of file
android/src/main/java/com/idlefish/flutterboost/containers/FlutterActivityAndFragmentDelegate.java
View file @
67c34f5b
...
...
@@ -19,6 +19,7 @@ import java.util.Map;
import
com.idlefish.flutterboost.NewFlutterBoost
;
import
com.idlefish.flutterboost.Utils
;
import
com.idlefish.flutterboost.XFlutterView
;
import
com.idlefish.flutterboost.interfaces.IFlutterViewContainer
;
import
com.idlefish.flutterboost.interfaces.IOperateSyncer
;
...
...
@@ -271,6 +272,8 @@ public class FlutterActivityAndFragmentDelegate implements IFlutterViewContaine
platformPlugin
=
null
;
}
Utils
.
fixInputMethodManagerLeak
(
host
.
getActivity
());
// Destroy our FlutterEngine if we're not set to retain it.
// if (host.shouldDestroyEngineWithHost()) {
// flutterEngine.destroy();
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment