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
20a64699
Commit
20a64699
authored
Oct 31, 2019
by
nightfallsad
Committed by
GitHub
Oct 31, 2019
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #4 from alibaba/feature/flutter_1.9_androidx_upgrade
Feature/flutter 1.9 androidx upgrade
parents
484f9d68
35b9977f
Changes
21
Hide whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
1377 additions
and
312 deletions
+1377
-312
android/src/main/AndroidManifest.xml
android/src/main/AndroidManifest.xml
+1
-3
android/src/main/java/com/idlefish/flutterboost/BoostPluginRegistry.java
...n/java/com/idlefish/flutterboost/BoostPluginRegistry.java
+97
-99
android/src/main/java/com/idlefish/flutterboost/BoostRegistrar.java
...c/main/java/com/idlefish/flutterboost/BoostRegistrar.java
+203
-0
android/src/main/java/com/idlefish/flutterboost/ContainerRecord.java
.../main/java/com/idlefish/flutterboost/ContainerRecord.java
+1
-35
android/src/main/java/com/idlefish/flutterboost/FlutterViewContainerManager.java
...om/idlefish/flutterboost/FlutterViewContainerManager.java
+7
-0
android/src/main/java/com/idlefish/flutterboost/NewFlutterBoost.java
.../main/java/com/idlefish/flutterboost/NewFlutterBoost.java
+113
-29
android/src/main/java/com/idlefish/flutterboost/Platform.java
...oid/src/main/java/com/idlefish/flutterboost/Platform.java
+9
-33
android/src/main/java/com/idlefish/flutterboost/Utils.java
android/src/main/java/com/idlefish/flutterboost/Utils.java
+2
-2
android/src/main/java/com/idlefish/flutterboost/XAndroidKeyProcessor.java
.../java/com/idlefish/flutterboost/XAndroidKeyProcessor.java
+99
-0
android/src/main/java/com/idlefish/flutterboost/XFlutterView.java
...src/main/java/com/idlefish/flutterboost/XFlutterView.java
+28
-16
android/src/main/java/com/idlefish/flutterboost/XInputConnectionAdaptor.java
...va/com/idlefish/flutterboost/XInputConnectionAdaptor.java
+263
-0
android/src/main/java/com/idlefish/flutterboost/XTextInputPlugin.java
...main/java/com/idlefish/flutterboost/XTextInputPlugin.java
+356
-0
android/src/main/java/com/idlefish/flutterboost/containers/FlutterActivityAndFragmentDelegate.java
...rboost/containers/FlutterActivityAndFragmentDelegate.java
+55
-74
android/src/main/java/com/idlefish/flutterboost/containers/FlutterSplashView.java
...m/idlefish/flutterboost/containers/FlutterSplashView.java
+4
-2
android/src/main/java/com/idlefish/flutterboost/containers/NewBoostFlutterActivity.java
...fish/flutterboost/containers/NewBoostFlutterActivity.java
+6
-7
android/src/main/java/com/idlefish/flutterboost/containers/NewFlutterFragment.java
.../idlefish/flutterboost/containers/NewFlutterFragment.java
+6
-6
example/android/app/src/main/java/com/taobao/idlefish/flutterboostexample/MyApplication.java
...om/taobao/idlefish/flutterboostexample/MyApplication.java
+30
-4
example/android/app/src/main/java/com/taobao/idlefish/flutterboostexample/TextPlatformViewPlugin.java
.../idlefish/flutterboostexample/TextPlatformViewPlugin.java
+3
-2
example/lib/main.dart
example/lib/main.dart
+1
-0
example/lib/platform_view.dart
example/lib/platform_view.dart
+51
-0
example/lib/simple_page_widgets.dart
example/lib/simple_page_widgets.dart
+42
-0
No files found.
android/src/main/AndroidManifest.xml
View file @
20a64699
<manifest
xmlns:android=
"http://schemas.android.com/apk/res/android"
package=
"com.idlefish.flutterboost"
>
<application>
<activity
android:name=
"com.idlefish.flutterboost.containers.BoostFlutterDefaultActivity"
/>
</application>
</manifest>
android/src/main/java/com/idlefish/flutterboost/BoostPluginRegistry.java
View file @
20a64699
package
com.idlefish.flutterboost
;
import
android.app.Activity
;
import
android.content.Context
;
import
androidx.annotation.Nullable
;
import
com.idlefish.flutterboost.interfaces.IContainerRecord
;
import
io.flutter.app.FlutterPluginRegistry
;
import
io.flutter.Log
;
import
io.flutter.embedding.engine.FlutterEngine
;
import
io.flutter.embedding.engine.plugins.shim.ShimPluginRegistry
;
import
io.flutter.plugin.common.BinaryMessenger
;
import
io.flutter.embedding.engine.plugins.FlutterPlugin
;
import
io.flutter.embedding.engine.plugins.activity.ActivityAware
;
import
io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
;
import
io.flutter.plugin.common.PluginRegistry
;
import
io.flutter.plugin.platform.PlatformViewRegistry
;
import
io.flutter.view.FlutterView
;
import
io.flutter.view.TextureRegistry
;
import
java.
lang.ref.WeakReference
;
import
java.
util.*
;
public
class
BoostPluginRegistry
extends
ShimPluginRegistry
{
protected
WeakReference
<
Activity
>
mCurrentActivityRef
;
private
FlutterEngine
mEngine
;
private
Context
mContext
;
public
BoostPluginRegistry
(
FlutterEngine
engine
,
Context
context
)
{
super
(
engine
);
mEngine
=
engine
;
mContext
=
context
;
}
public
class
BoostPluginRegistry
implements
PluginRegistry
{
private
static
final
String
TAG
=
"ShimPluginRegistry"
;
private
final
FlutterEngine
flutterEngine
;
private
final
Map
<
String
,
Object
>
pluginMap
=
new
HashMap
();
private
final
BoostRegistrarAggregate
shimRegistrarAggregate
;
public
BoostRegistrarAggregate
getRegistrarAggregate
()
{
return
shimRegistrarAggregate
;
}
public
BoostPluginRegistry
(
FlutterEngine
flutterEngine
)
{
this
.
flutterEngine
=
flutterEngine
;
this
.
shimRegistrarAggregate
=
new
BoostRegistrarAggregate
();
this
.
flutterEngine
.
getPlugins
().
add
(
this
.
shimRegistrarAggregate
);
}
public
PluginRegistry
.
Registrar
registrarFor
(
String
pluginKey
)
{
return
new
BoostRegistrar
(
mEngine
,
super
.
registrarFor
(
pluginKey
));
public
Registrar
registrarFor
(
String
pluginKey
)
{
Log
.
v
(
"ShimPluginRegistry"
,
"Creating plugin Registrar for '"
+
pluginKey
+
"'"
);
if
(
this
.
pluginMap
.
containsKey
(
pluginKey
))
{
throw
new
IllegalStateException
(
"Plugin key "
+
pluginKey
+
" is already in use"
);
}
else
{
this
.
pluginMap
.
put
(
pluginKey
,
(
Object
)
null
);
BoostRegistrar
registrar
=
new
BoostRegistrar
(
pluginKey
,
this
.
pluginMap
);
this
.
shimRegistrarAggregate
.
addPlugin
(
registrar
);
return
registrar
;
}
}
public
void
currentActivity
(
@Nullable
Activity
activit
y
)
{
mCurrentActivityRef
=
new
WeakReference
<>(
activit
y
);
public
boolean
hasPlugin
(
String
pluginKe
y
)
{
return
this
.
pluginMap
.
containsKey
(
pluginKe
y
);
}
public
class
BoostRegistrar
implements
PluginRegistry
.
Registrar
{
public
Object
valuePublishedByPlugin
(
String
pluginKey
)
{
return
this
.
pluginMap
.
get
(
pluginKey
);
}
private
final
PluginRegistry
.
Registrar
mRegistrar
;
private
final
FlutterEngine
mEngine
;
public
static
class
BoostRegistrarAggregate
implements
FlutterPlugin
,
ActivityAware
{
private
final
Set
<
BoostRegistrar
>
shimRegistrars
;
private
FlutterPluginBinding
flutterPluginBinding
;
private
ActivityPluginBinding
activityPluginBinding
;
BoostRegistrar
(
FlutterEngine
engine
,
PluginRegistry
.
Registrar
registrar
)
{
mRegistrar
=
registrar
;
mEngine
=
engine
;
public
ActivityPluginBinding
getActivityPluginBinding
()
{
return
activityPluginBinding
;
}
@Override
public
Activity
activity
()
{
Activity
activity
;
IContainerRecord
record
;
private
BoostRegistrarAggregate
()
{
this
.
shimRegistrars
=
new
HashSet
();
}
record
=
NewFlutterBoost
.
instance
().
containerManager
().
getCurrentTopRecord
();
if
(
record
==
null
)
{
record
=
NewFlutterBoost
.
instance
().
containerManager
().
getLastGenerateRecord
();
public
void
addPlugin
(
BoostRegistrar
shimRegistrar
)
{
this
.
shimRegistrars
.
add
(
shimRegistrar
);
if
(
this
.
flutterPluginBinding
!=
null
)
{
shimRegistrar
.
onAttachedToEngine
(
this
.
flutterPluginBinding
);
}
if
(
record
==
null
)
{
activity
=
NewFlutterBoost
.
instance
().
currentActivity
();
}
else
{
activity
=
record
.
getContainer
().
getContextActivity
();
if
(
this
.
activityPluginBinding
!=
null
)
{
shimRegistrar
.
onAttachedToActivity
(
this
.
activityPluginBinding
);
}
if
(
activity
==
null
&&
mCurrentActivityRef
!=
null
)
{
activity
=
mCurrentActivityRef
.
get
();
}
}
public
void
onAttachedToEngine
(
FlutterPluginBinding
binding
)
{
this
.
flutterPluginBinding
=
binding
;
Iterator
var2
=
this
.
shimRegistrars
.
iterator
();
if
(
activity
==
null
)
{
throw
new
RuntimeException
(
"current has no valid Activity yet"
);
while
(
var2
.
hasNext
())
{
BoostRegistrar
shimRegistrar
=
(
BoostRegistrar
)
var2
.
next
();
shimRegistrar
.
onAttachedToEngine
(
binding
);
}
return
activity
;
}
@Override
public
Context
context
()
{
return
BoostPluginRegistry
.
this
.
mContext
;
}
public
void
onDetachedFromEngine
(
FlutterPluginBinding
binding
)
{
Iterator
var2
=
this
.
shimRegistrars
.
iterator
();
@Override
public
Context
activeContext
()
{
return
BoostPluginRegistry
.
this
.
mContext
;
}
while
(
var2
.
hasNext
())
{
BoostRegistrar
shimRegistrar
=
(
BoostRegistrar
)
var2
.
next
();
shimRegistrar
.
onDetachedFromEngine
(
binding
)
;
}
@Override
public
BinaryMessenger
messenger
()
{
return
mEngine
.
getDartExecutor
();
this
.
flutterPluginBinding
=
null
;
}
@Override
public
TextureRegistry
textures
()
{
return
mEngine
.
getRenderer
();
}
public
void
onAttachedToActivity
(
ActivityPluginBinding
binding
)
{
this
.
activityPluginBinding
=
binding
;
Iterator
var2
=
this
.
shimRegistrars
.
iterator
();
@Override
public
PlatformViewRegistry
platformViewRegistry
()
{
return
mEngine
.
getPlatformViewsController
().
getRegistry
(
);
}
while
(
var2
.
hasNext
())
{
BoostRegistrar
shimRegistrar
=
(
BoostRegistrar
)
var2
.
next
();
shimRegistrar
.
onAttachedToActivity
(
binding
);
}
@Override
public
FlutterView
view
()
{
throw
new
RuntimeException
(
"should not use!!!"
);
}
@Override
public
String
lookupKeyForAsset
(
String
s
)
{
return
mRegistrar
.
lookupKeyForAsset
(
s
);
}
public
void
onDetachedFromActivityForConfigChanges
()
{
Iterator
var1
=
this
.
shimRegistrars
.
iterator
();
@Override
public
String
lookupKeyForAsset
(
String
s
,
String
s1
)
{
return
mRegistrar
.
lookupKeyForAsset
(
s
,
s1
);
}
while
(
var1
.
hasNext
())
{
BoostRegistrar
shimRegistrar
=
(
BoostRegistrar
)
var1
.
next
();
shimRegistrar
.
onDetachedFromActivity
(
);
}
@Override
public
PluginRegistry
.
Registrar
publish
(
Object
o
)
{
return
mRegistrar
.
publish
(
o
);
this
.
activityPluginBinding
=
null
;
}
@Override
public
PluginRegistry
.
Registrar
addRequestPermissionsResultListener
(
PluginRegistry
.
RequestPermissionsResultListener
requestPermissionsResultListener
)
{
return
mRegistrar
.
addRequestPermissionsResultListener
(
requestPermissionsResultListener
);
}
public
void
onReattachedToActivityForConfigChanges
(
ActivityPluginBinding
binding
)
{
Iterator
var2
=
this
.
shimRegistrars
.
iterator
();
@Override
public
PluginRegistry
.
Registrar
addActivityResultListener
(
PluginRegistry
.
ActivityResultListener
activityResultListener
)
{
return
mRegistrar
.
addActivityResultListener
(
activityResultListener
);
}
while
(
var2
.
hasNext
())
{
BoostRegistrar
shimRegistrar
=
(
BoostRegistrar
)
var2
.
next
();
shimRegistrar
.
onReattachedToActivityForConfigChanges
(
binding
);
}
@Override
public
PluginRegistry
.
Registrar
addNewIntentListener
(
PluginRegistry
.
NewIntentListener
newIntentListener
)
{
return
mRegistrar
.
addNewIntentListener
(
newIntentListener
);
}
@Override
public
PluginRegistry
.
Registrar
addUserLeaveHintListener
(
PluginRegistry
.
UserLeaveHintListener
userLeaveHintListener
)
{
return
mRegistrar
.
addUserLeaveHintListener
(
userLeaveHintListener
);
}
public
void
onDetachedFromActivity
()
{
Iterator
var1
=
this
.
shimRegistrars
.
iterator
();
while
(
var1
.
hasNext
())
{
BoostRegistrar
shimRegistrar
=
(
BoostRegistrar
)
var1
.
next
();
shimRegistrar
.
onDetachedFromActivity
();
}
@Override
public
PluginRegistry
.
Registrar
addViewDestroyListener
(
PluginRegistry
.
ViewDestroyListener
viewDestroyListener
)
{
return
mRegistrar
.
addViewDestroyListener
(
viewDestroyListener
);
}
}
}
}
android/src/main/java/com/idlefish/flutterboost/BoostRegistrar.java
0 → 100644
View file @
20a64699
package
com.idlefish.flutterboost
;
import
android.app.Activity
;
import
android.content.Context
;
import
android.support.annotation.NonNull
;
import
io.flutter.Log
;
import
io.flutter.embedding.engine.plugins.FlutterPlugin
;
import
io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding
;
import
io.flutter.embedding.engine.plugins.activity.ActivityAware
;
import
io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
;
import
io.flutter.plugin.common.BinaryMessenger
;
import
io.flutter.plugin.common.PluginRegistry.ActivityResultListener
;
import
io.flutter.plugin.common.PluginRegistry.NewIntentListener
;
import
io.flutter.plugin.common.PluginRegistry.Registrar
;
import
io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener
;
import
io.flutter.plugin.common.PluginRegistry.UserLeaveHintListener
;
import
io.flutter.plugin.common.PluginRegistry.ViewDestroyListener
;
import
io.flutter.plugin.platform.PlatformViewRegistry
;
import
io.flutter.view.FlutterMain
;
import
io.flutter.view.FlutterNativeView
;
import
io.flutter.view.FlutterView
;
import
io.flutter.view.TextureRegistry
;
import
java.util.HashSet
;
import
java.util.Iterator
;
import
java.util.Map
;
import
java.util.Set
;
class
BoostRegistrar
implements
Registrar
,
FlutterPlugin
,
ActivityAware
{
private
static
final
String
TAG
=
"ShimRegistrar"
;
private
final
Map
<
String
,
Object
>
globalRegistrarMap
;
private
final
String
pluginId
;
private
final
Set
<
ViewDestroyListener
>
viewDestroyListeners
=
new
HashSet
();
private
final
Set
<
RequestPermissionsResultListener
>
requestPermissionsResultListeners
=
new
HashSet
();
private
final
Set
<
ActivityResultListener
>
activityResultListeners
=
new
HashSet
();
private
final
Set
<
NewIntentListener
>
newIntentListeners
=
new
HashSet
();
private
final
Set
<
UserLeaveHintListener
>
userLeaveHintListeners
=
new
HashSet
();
private
FlutterPluginBinding
pluginBinding
;
private
ActivityPluginBinding
activityPluginBinding
;
public
BoostRegistrar
(
@NonNull
String
pluginId
,
@NonNull
Map
<
String
,
Object
>
globalRegistrarMap
)
{
this
.
pluginId
=
pluginId
;
this
.
globalRegistrarMap
=
globalRegistrarMap
;
}
public
Activity
activity
()
{
if
(
this
.
activityPluginBinding
!=
null
){
return
this
.
activityPluginBinding
.
getActivity
();
}
if
(
NewFlutterBoost
.
instance
().
currentActivity
()!=
null
){
return
NewFlutterBoost
.
instance
().
currentActivity
();
}
return
null
;
}
public
Context
context
()
{
return
this
.
pluginBinding
!=
null
?
this
.
pluginBinding
.
getApplicationContext
()
:
null
;
}
public
Context
activeContext
()
{
return
(
Context
)(
this
.
activityPluginBinding
==
null
?
this
.
context
()
:
this
.
activity
());
}
public
BinaryMessenger
messenger
()
{
return
this
.
pluginBinding
!=
null
?
this
.
pluginBinding
.
getFlutterEngine
().
getDartExecutor
()
:
null
;
}
public
TextureRegistry
textures
()
{
return
this
.
pluginBinding
!=
null
?
this
.
pluginBinding
.
getFlutterEngine
().
getRenderer
()
:
null
;
}
public
PlatformViewRegistry
platformViewRegistry
()
{
return
this
.
pluginBinding
!=
null
?
this
.
pluginBinding
.
getFlutterEngine
().
getPlatformViewsController
().
getRegistry
()
:
null
;
}
public
FlutterView
view
()
{
throw
new
UnsupportedOperationException
(
"The new embedding does not support the old FlutterView."
);
}
public
String
lookupKeyForAsset
(
String
asset
)
{
return
FlutterMain
.
getLookupKeyForAsset
(
asset
);
}
public
String
lookupKeyForAsset
(
String
asset
,
String
packageName
)
{
return
FlutterMain
.
getLookupKeyForAsset
(
asset
,
packageName
);
}
public
Registrar
publish
(
Object
value
)
{
this
.
globalRegistrarMap
.
put
(
this
.
pluginId
,
value
);
return
this
;
}
public
Registrar
addRequestPermissionsResultListener
(
RequestPermissionsResultListener
listener
)
{
this
.
requestPermissionsResultListeners
.
add
(
listener
);
if
(
this
.
activityPluginBinding
!=
null
)
{
this
.
activityPluginBinding
.
addRequestPermissionsResultListener
(
listener
);
}
return
this
;
}
public
Registrar
addActivityResultListener
(
ActivityResultListener
listener
)
{
this
.
activityResultListeners
.
add
(
listener
);
if
(
this
.
activityPluginBinding
!=
null
)
{
this
.
activityPluginBinding
.
addActivityResultListener
(
listener
);
}
return
this
;
}
public
Registrar
addNewIntentListener
(
NewIntentListener
listener
)
{
this
.
newIntentListeners
.
add
(
listener
);
if
(
this
.
activityPluginBinding
!=
null
)
{
this
.
activityPluginBinding
.
addOnNewIntentListener
(
listener
);
}
return
this
;
}
public
Registrar
addUserLeaveHintListener
(
UserLeaveHintListener
listener
)
{
this
.
userLeaveHintListeners
.
add
(
listener
);
if
(
this
.
activityPluginBinding
!=
null
)
{
this
.
activityPluginBinding
.
addOnUserLeaveHintListener
(
listener
);
}
return
this
;
}
@NonNull
public
Registrar
addViewDestroyListener
(
@NonNull
ViewDestroyListener
listener
)
{
this
.
viewDestroyListeners
.
add
(
listener
);
return
this
;
}
public
void
onAttachedToEngine
(
@NonNull
FlutterPluginBinding
binding
)
{
Log
.
v
(
"ShimRegistrar"
,
"Attached to FlutterEngine."
);
this
.
pluginBinding
=
binding
;
}
public
void
onDetachedFromEngine
(
@NonNull
FlutterPluginBinding
binding
)
{
Log
.
v
(
"ShimRegistrar"
,
"Detached from FlutterEngine."
);
Iterator
var2
=
this
.
viewDestroyListeners
.
iterator
();
while
(
var2
.
hasNext
())
{
ViewDestroyListener
listener
=
(
ViewDestroyListener
)
var2
.
next
();
listener
.
onViewDestroy
((
FlutterNativeView
)
null
);
}
this
.
pluginBinding
=
null
;
}
public
void
onAttachedToActivity
(
@NonNull
ActivityPluginBinding
binding
)
{
Log
.
v
(
"ShimRegistrar"
,
"Attached to an Activity."
);
this
.
activityPluginBinding
=
binding
;
this
.
addExistingListenersToActivityPluginBinding
();
}
public
void
onDetachedFromActivityForConfigChanges
()
{
Log
.
v
(
"ShimRegistrar"
,
"Detached from an Activity for config changes."
);
this
.
activityPluginBinding
=
null
;
}
public
void
onReattachedToActivityForConfigChanges
(
@NonNull
ActivityPluginBinding
binding
)
{
Log
.
v
(
"ShimRegistrar"
,
"Reconnected to an Activity after config changes."
);
this
.
activityPluginBinding
=
binding
;
this
.
addExistingListenersToActivityPluginBinding
();
}
public
void
onDetachedFromActivity
()
{
Log
.
v
(
"ShimRegistrar"
,
"Detached from an Activity."
);
this
.
activityPluginBinding
=
null
;
}
private
void
addExistingListenersToActivityPluginBinding
()
{
Iterator
var1
=
this
.
requestPermissionsResultListeners
.
iterator
();
while
(
var1
.
hasNext
())
{
RequestPermissionsResultListener
listener
=
(
RequestPermissionsResultListener
)
var1
.
next
();
this
.
activityPluginBinding
.
addRequestPermissionsResultListener
(
listener
);
}
var1
=
this
.
activityResultListeners
.
iterator
();
while
(
var1
.
hasNext
())
{
ActivityResultListener
listener
=
(
ActivityResultListener
)
var1
.
next
();
this
.
activityPluginBinding
.
addActivityResultListener
(
listener
);
}
var1
=
this
.
newIntentListeners
.
iterator
();
while
(
var1
.
hasNext
())
{
NewIntentListener
listener
=
(
NewIntentListener
)
var1
.
next
();
this
.
activityPluginBinding
.
addOnNewIntentListener
(
listener
);
}
var1
=
this
.
userLeaveHintListeners
.
iterator
();
while
(
var1
.
hasNext
())
{
UserLeaveHintListener
listener
=
(
UserLeaveHintListener
)
var1
.
next
();
this
.
activityPluginBinding
.
addOnUserLeaveHintListener
(
listener
);
}
}
}
android/src/main/java/com/idlefish/flutterboost/ContainerRecord.java
View file @
20a64699
...
...
@@ -175,6 +175,7 @@ public class ContainerRecord implements IContainerRecord {
@Override
public
void
onContainerResult
(
int
requestCode
,
int
resultCode
,
Map
<
String
,
Object
>
result
)
{
mManager
.
setContainerResult
(
this
,
requestCode
,
resultCode
,
result
);
}
...
...
@@ -192,41 +193,6 @@ public class ContainerRecord implements IContainerRecord {
public
void
onLowMemory
()
{
}
//
// @Override
// public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
// mContainer.getBoostFlutterView().onRequestPermissionsResult(requestCode, permissions, grantResults);
// }
//
// @Override
// public void onNewIntent(Intent intent) {
// mContainer.getBoostFlutterView().onNewIntent(intent);
// }
//
// @Override
// public void onActivityResult(int requestCode, int resultCode, Intent data) {
// mContainer.getBoostFlutterView().onActivityResult(requestCode, resultCode, data);
// }
//
// @Override
// public void onContainerResult(int requestCode, int resultCode, Map<String, Object> result) {
// mManager.setContainerResult(this, requestCode,resultCode, result);
// }
//
// @Override
// public void onUserLeaveHint() {
// mContainer.getBoostFlutterView().onUserLeaveHint();
// }
//
// @Override
// public void onTrimMemory(int level) {
// mContainer.getBoostFlutterView().onTrimMemory(level);
// }
//
// @Override
// public void onLowMemory() {
// mContainer.getBoostFlutterView().onLowMemory();
// }
private
class
MethodChannelProxy
{
...
...
android/src/main/java/com/idlefish/flutterboost/FlutterViewContainerManager.java
View file @
20a64699
...
...
@@ -84,6 +84,13 @@ public class FlutterViewContainerManager implements IContainerManager {
void
removeRecord
(
IContainerRecord
record
)
{
mRecordStack
.
remove
(
record
);
mRecordMap
.
remove
(
record
.
getContainer
());
if
(
mRecordStack
.
empty
()){
if
(
NewFlutterBoost
.
instance
().
platform
().
whenEngineDestroy
()==
NewFlutterBoost
.
ConfigBuilder
.
All_FLUTTER_ACTIVITY_DESTROY
){
NewFlutterBoost
.
instance
().
boostDestroy
();
}
}
}
void
setContainerResult
(
IContainerRecord
record
,
int
requestCode
,
int
resultCode
,
Map
<
String
,
Object
>
result
)
{
...
...
android/src/main/java/com/idlefish/flutterboost/NewFlutterBoost.java
View file @
20a64699
...
...
@@ -10,9 +10,14 @@ import com.idlefish.flutterboost.interfaces.*;
import
io.flutter.Log
;
import
io.flutter.embedding.android.FlutterView
;
import
io.flutter.embedding.engine.FlutterEngine
;
import
io.flutter.embedding.engine.FlutterShellArgs
;
import
io.flutter.embedding.engine.dart.DartExecutor
;
import
io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
;
import
io.flutter.embedding.engine.plugins.shim.ShimPluginRegistry
;
import
io.flutter.plugin.common.PluginRegistry
;
import
io.flutter.view.FlutterMain
;
import
java.lang.reflect.Method
;
import
java.util.HashMap
;
import
java.util.Map
;
...
...
@@ -21,12 +26,11 @@ public class NewFlutterBoost {
private
Platform
mPlatform
;
private
FlutterViewContainerManager
mManager
;
private
FlutterEngine
mEngine
;
private
Activity
mCurrentActiveActivity
;
private
Boost
PluginRegistry
mRegistry
;
private
PluginRegistry
mRegistry
;
static
NewFlutterBoost
sInstance
=
null
;
public
static
NewFlutterBoost
instance
()
{
if
(
sInstance
==
null
)
{
sInstance
=
new
NewFlutterBoost
();
...
...
@@ -41,19 +45,18 @@ public class NewFlutterBoost {
mManager
=
new
FlutterViewContainerManager
();
mRegistry
=
new
BoostPluginRegistry
(
this
.
engineProvider
(),
mPlatform
.
getApplication
());
platform
.
getApplication
().
registerActivityLifecycleCallbacks
(
new
Application
.
ActivityLifecycleCallbacks
()
{
@Override
public
void
onActivityCreated
(
Activity
activity
,
Bundle
savedInstanceState
)
{
Log
.
e
(
"bbbb1"
,
"xxxxx"
);
mCurrentActiveActivity
=
activity
;
if
(
mPlatform
.
whenEngineStart
()
==
ConfigBuilder
.
ANY_ACTIVITY_CREATED
)
{
Log
.
e
(
"bbbb2"
,
"xxxxx"
);
mRegistry
.
currentActivity
(
activity
);
doInitialFlutterViewRun
(
mPlatform
,
mRegistry
);
doInitialFlutter
();
}
}
...
...
@@ -62,7 +65,7 @@ public class NewFlutterBoost {
if
(
mCurrentActiveActivity
==
null
)
{
Debuger
.
log
(
"Application entry foreground"
);
if
(
NewFlutterBoost
.
instance
().
engineProvider
()
!=
null
)
{
if
(
createEngine
()
!=
null
)
{
HashMap
<
String
,
String
>
map
=
new
HashMap
<>();
map
.
put
(
"type"
,
"foreground"
);
channel
().
sendEvent
(
"lifecycle"
,
map
);
...
...
@@ -86,7 +89,7 @@ public class NewFlutterBoost {
if
(
mCurrentActiveActivity
==
activity
)
{
Debuger
.
log
(
"Application entry background"
);
if
(
mPlatform
.
engineProvider
()
!=
null
)
{
if
(
createEngine
()
!=
null
)
{
HashMap
<
String
,
String
>
map
=
new
HashMap
<>();
map
.
put
(
"type"
,
"background"
);
channel
().
sendEvent
(
"lifecycle"
,
map
);
...
...
@@ -105,7 +108,7 @@ public class NewFlutterBoost {
if
(
mCurrentActiveActivity
==
activity
)
{
Debuger
.
log
(
"Application entry background"
);
if
(
mPlatform
.
engineProvider
()
!=
null
)
{
if
(
createEngine
()
!=
null
)
{
HashMap
<
String
,
String
>
map
=
new
HashMap
<>();
map
.
put
(
"type"
,
"background"
);
channel
().
sendEvent
(
"lifecycle"
,
map
);
...
...
@@ -117,39 +120,46 @@ public class NewFlutterBoost {
if
(
mPlatform
.
whenEngineStart
()
==
ConfigBuilder
.
IMMEDIATELY
)
{
doInitialFlutter
ViewRun
(
mPlatform
,
mRegistry
);
doInitialFlutter
(
);
}
}
private
void
doInitialFlutterViewRun
(
Platform
platform
,
BoostPluginRegistry
registry
)
{
// Don't attempt to start a FlutterEngine if we're using a cached FlutterEngine.
// if (host.getCachedEngineId() != null) {
// return;
// }
FlutterEngine
flutterEngine
=
platform
.
engineProvider
();
public
void
doInitialFlutter
()
{
if
(
mEngine
!=
null
)
return
;
FlutterEngine
flutterEngine
=
createEngine
();
if
(
mPlatform
.
lifecycleListener
!=
null
){
mPlatform
.
lifecycleListener
.
onEngineCreated
();
}
if
(
flutterEngine
.
getDartExecutor
().
isExecutingDart
())
{
// No warning is logged because this situation will happen on every config
// change if the developer does not choose to retain the Fragment instance.
// So this is expected behavior in many cases.
return
;
}
platform
.
registerPlugins
(
registry
);
// The engine needs to receive the Flutter app's initial route before executing any
// Dart code to ensure that the initial route arrives in time to be applied.
if
(
p
latform
.
initialRoute
()
!=
null
)
{
flutterEngine
.
getNavigationChannel
().
setInitialRoute
(
p
latform
.
initialRoute
());
if
(
mP
latform
.
initialRoute
()
!=
null
)
{
flutterEngine
.
getNavigationChannel
().
setInitialRoute
(
mP
latform
.
initialRoute
());
}
// Configure the Dart entrypoint and execute it.
DartExecutor
.
DartEntrypoint
entrypoint
=
new
DartExecutor
.
DartEntrypoint
(
FlutterMain
.
findAppBundlePath
(),
"main"
);
flutterEngine
.
getDartExecutor
().
executeDartEntrypoint
(
entrypoint
);
mRegistry
=
new
BoostPluginRegistry
(
createEngine
());
registerPlugins
();
}
...
...
@@ -161,20 +171,28 @@ public class NewFlutterBoost {
public
static
int
ANY_ACTIVITY_CREATED
=
1
;
//当有任何Activity创建时,启动引擎
public
static
int
FLUTTER_ACTIVITY_CREATED
=
2
;
//当有flutterActivity创建时,启动引擎
public
static
int
APP_EXit
=
0
;
//所有flutter Activity destory 时,销毁engine
public
static
int
All_FLUTTER_ACTIVITY_DESTROY
=
1
;
//所有flutter Activity destory 时,销毁engine
private
String
dartEntrypoint
=
DEFAULT_DART_ENTRYPOINT
;
private
String
initialRoute
=
DEFAULT_INITIAL_ROUTE
;
private
int
whenEngineStart
=
ANY_ACTIVITY_CREATED
;
private
int
whenEngineDestory
=
APP_EXit
;
private
boolean
isDebug
=
false
;
private
FlutterView
.
RenderMode
renderMode
=
FlutterView
.
RenderMode
.
surfac
e
;
private
FlutterView
.
RenderMode
renderMode
=
FlutterView
.
RenderMode
.
textur
e
;
private
Application
mApp
;
private
INativeRouter
router
=
null
;
private
BoostLifecycleListener
lifecycleListener
;
public
ConfigBuilder
(
Application
app
,
INativeRouter
router
)
{
this
.
router
=
router
;
this
.
mApp
=
app
;
...
...
@@ -200,11 +218,19 @@ public class NewFlutterBoost {
return
this
;
}
public
ConfigBuilder
whenEngineStart
(
@NonNull
int
whenEngineStart
)
{
public
ConfigBuilder
whenEngineStart
(
int
whenEngineStart
)
{
this
.
whenEngineStart
=
whenEngineStart
;
return
this
;
}
public
ConfigBuilder
whenEngineDestory
(
int
whenEngineDestory
)
{
this
.
whenEngineDestory
=
whenEngineDestory
;
return
this
;
}
public
ConfigBuilder
lifecycleListener
(
BoostLifecycleListener
lifecycleListener
)
{
this
.
lifecycleListener
=
lifecycleListener
;
return
this
;
}
public
Platform
build
()
{
Platform
platform
=
new
Platform
()
{
...
...
@@ -232,22 +258,24 @@ public class NewFlutterBoost {
return
ConfigBuilder
.
this
.
whenEngineStart
;
}
@Override
public
int
whenEngineDestroy
()
{
return
ConfigBuilder
.
this
.
whenEngineDestory
;
}
public
FlutterView
.
RenderMode
renderMode
()
{
return
ConfigBuilder
.
this
.
renderMode
;
}
};
platform
.
lifecycleListener
=
this
.
lifecycleListener
;
return
platform
;
}
}
public
FlutterEngine
engineProvider
()
{
return
sInstance
.
mPlatform
.
engineProvider
();
}
public
IContainerManager
containerManager
()
{
return
sInstance
.
mManager
;
}
...
...
@@ -268,7 +296,63 @@ public class NewFlutterBoost {
return
mManager
.
findContainerById
(
id
);
}
public
Boost
PluginRegistry
getPluginRegistry
(){
public
PluginRegistry
getPluginRegistry
(){
return
mRegistry
;
}
private
FlutterEngine
createEngine
(){
if
(
mEngine
==
null
)
{
FlutterMain
.
startInitialization
(
mPlatform
.
getApplication
());
FlutterShellArgs
flutterShellArgs
=
new
FlutterShellArgs
(
new
String
[
0
]);
FlutterMain
.
ensureInitializationComplete
(
mPlatform
.
getApplication
().
getApplicationContext
(),
flutterShellArgs
.
toArray
());
mEngine
=
new
FlutterEngine
(
mPlatform
.
getApplication
().
getApplicationContext
());
}
return
mEngine
;
}
private
void
registerPlugins
()
{
try
{
Class
clz
=
Class
.
forName
(
"io.flutter.plugins.GeneratedPluginRegistrant"
);
Method
method
=
clz
.
getDeclaredMethod
(
"registerWith"
,
PluginRegistry
.
class
);
method
.
invoke
(
null
,
mRegistry
);
}
catch
(
Throwable
t
)
{
throw
new
RuntimeException
(
t
);
}
if
(
mPlatform
.
lifecycleListener
!=
null
){
mPlatform
.
lifecycleListener
.
onPluginsRegistered
();
}
}
public
FlutterEngine
engineProvider
()
{
return
mEngine
;
}
public
void
boostDestroy
(){
if
(
mEngine
!=
null
){
mEngine
.
destroy
();
}
if
(
mPlatform
.
lifecycleListener
!=
null
){
mPlatform
.
lifecycleListener
.
onEngineDestroy
();
}
mEngine
=
null
;
mRegistry
=
null
;
mCurrentActiveActivity
=
null
;
}
public
interface
BoostLifecycleListener
{
void
onEngineCreated
();
void
onPluginsRegistered
();
void
onEngineDestroy
();
}
}
android/src/main/java/com/idlefish/flutterboost/Platform.java
View file @
20a64699
...
...
@@ -8,57 +8,33 @@ import java.lang.reflect.Method;
import
java.util.Map
;
import
io.flutter.embedding.android.FlutterView
;
import
io.flutter.embedding.engine.FlutterEngine
;
import
io.flutter.embedding.engine.FlutterShellArgs
;
import
io.flutter.plugin.common.PluginRegistry
;
import
io.flutter.view.FlutterMain
;
public
abstract
class
Platform
{
public
abstract
Application
getApplication
();
public
FlutterEngine
mEngine
;
public
abstract
void
openContainer
(
Context
context
,
String
url
,
Map
<
String
,
Object
>
urlParams
,
int
requestCode
,
Map
<
String
,
Object
>
exts
)
;
public
abstract
Application
getApplication
();
public
abstract
int
whenEngineStart
();
public
abstract
int
whenEngineDestroy
();
public
abstract
void
openContainer
(
Context
context
,
String
url
,
Map
<
String
,
Object
>
urlParams
,
int
requestCode
,
Map
<
String
,
Object
>
exts
);
public
abstract
FlutterView
.
RenderMode
renderMode
(
);
public
abstract
int
whenEngineStart
()
;
public
abstract
boolean
isDebug
()
;
public
abstract
FlutterView
.
RenderMode
renderMode
();
public
abstract
boolean
isDebug
()
;
public
abstract
String
initialRoute
();
public
abstract
String
initialRoute
();
public
NewFlutterBoost
.
BoostLifecycleListener
lifecycleListener
;
public
void
closeContainer
(
IContainerRecord
record
,
Map
<
String
,
Object
>
result
,
Map
<
String
,
Object
>
exts
)
{
if
(
record
==
null
)
return
;
if
(
record
==
null
)
return
;
record
.
getContainer
().
finishContainer
(
result
);
}
public
FlutterEngine
engineProvider
()
{
if
(
mEngine
==
null
)
{
FlutterShellArgs
flutterShellArgs
=
new
FlutterShellArgs
(
new
String
[
0
]);
FlutterMain
.
ensureInitializationComplete
(
getApplication
().
getApplicationContext
(),
flutterShellArgs
.
toArray
());
mEngine
=
new
FlutterEngine
(
getApplication
().
getApplicationContext
());
}
return
mEngine
;
}
public
void
registerPlugins
(
PluginRegistry
registry
)
{
try
{
Class
clz
=
Class
.
forName
(
"io.flutter.plugins.GeneratedPluginRegistrant"
);
Method
method
=
clz
.
getDeclaredMethod
(
"registerWith"
,
PluginRegistry
.
class
);
method
.
invoke
(
null
,
registry
);
}
catch
(
Throwable
t
){
throw
new
RuntimeException
(
t
);
}
}
}
android/src/main/java/com/idlefish/flutterboost/Utils.java
View file @
20a64699
...
...
@@ -263,7 +263,7 @@ public class Utils {
return
;
}
String
[]
arr
=
new
String
[]{
"mServedView"
,
"mNextServedView"
};
String
[]
arr
=
new
String
[]{
"m
LastSrvView"
,
"m
ServedView"
,
"mNextServedView"
};
Field
f
=
null
;
Object
obj_get
=
null
;
for
(
int
i
=
0
;
i
<
arr
.
length
;
i
++)
{
...
...
@@ -283,7 +283,7 @@ public class Utils {
}
}
}
catch
(
Throwable
t
){
t
.
printStackTrace
();
//
t.printStackTrace();
}
}
}
...
...
android/src/main/java/com/idlefish/flutterboost/XAndroidKeyProcessor.java
0 → 100644
View file @
20a64699
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 @
20a64699
...
...
@@ -90,14 +90,16 @@ public class XFlutterView extends FrameLayout {
// These components essentially add some additional behavioral logic on top of
// existing, stateless system channels, e.g., KeyEventChannel, TextInputChannel, etc.
@Nullable
private
TextInputPlugin
textInputPlugin
;
private
X
TextInputPlugin
textInputPlugin
;
@Nullable
private
AndroidKeyProcessor
androidKeyProcessor
;
private
X
AndroidKeyProcessor
androidKeyProcessor
;
@Nullable
private
AndroidTouchProcessor
androidTouchProcessor
;
@Nullable
private
AccessibilityBridge
accessibilityBridge
;
private
boolean
hasAddFirstFrameRenderedListener
=
false
;
// Directly implemented View behavior that communicates with Flutter.
private
final
FlutterRenderer
.
ViewportMetrics
viewportMetrics
=
new
FlutterRenderer
.
ViewportMetrics
();
...
...
@@ -566,6 +568,7 @@ public class XFlutterView extends FrameLayout {
* See {@link #detachFromFlutterEngine()} for information on how to detach from a
* {@link FlutterEngine}.
*/
public
void
attachToFlutterEngine
(
@NonNull
FlutterEngine
flutterEngine
)
{
...
...
@@ -582,27 +585,35 @@ public class XFlutterView extends FrameLayout {
+
" to new engine."
);
detachFromFlutterEngine
();
}
this
.
requestFocus
();
this
.
flutterEngine
=
flutterEngine
;
// Instruct our FlutterRenderer that we are now its designated RenderSurface.
FlutterRenderer
flutterRenderer
=
this
.
flutterEngine
.
getRenderer
();
didRenderFirstFrame
=
flutterRenderer
.
hasRenderedFirstFrame
();
if
(!
hasAddFirstFrameRenderedListener
){
flutterRenderer
.
addOnFirstFrameRenderedListener
(
onFirstFrameRenderedListener
);
hasAddFirstFrameRenderedListener
=
true
;
}
flutterRenderer
.
attachToRenderSurface
(
renderSurface
);
flutterRenderer
.
addOnFirstFrameRenderedListener
(
onFirstFrameRenderedListener
);
// Initialize various components that know how to process Android View I/O
// in a way that Flutter understands.
if
(
this
.
textInputPlugin
!=
null
){
this
.
textInputPlugin
.
destroy
();
resolveMemoryLeaks
();
if
(
textInputPlugin
==
null
){
textInputPlugin
=
new
XTextInputPlugin
(
this
,
flutterEngine
.
getTextInputChannel
(),
this
.
flutterEngine
.
getPlatformViewsController
()
);
}
this
.
textInputPlugin
=
new
TextInputPlugin
(
this
,
this
.
flutterEngine
.
getDartExecutor
(),
this
.
flutterEngine
.
getPlatformViewsController
());
textInputPlugin
.
setTextInputMethodHandler
();
textInputPlugin
.
getInputMethodManager
().
restartInput
(
this
);
this
.
androidKeyProcessor
=
new
AndroidKeyProcessor
(
this
.
androidKeyProcessor
=
new
X
AndroidKeyProcessor
(
this
.
flutterEngine
.
getKeyEventChannel
(),
textInputPlugin
);
...
...
@@ -643,9 +654,9 @@ public class XFlutterView extends FrameLayout {
// If the first frame has already been rendered, notify all first frame listeners.
// Do this after all other initialization so that listeners don't inadvertently interact
// with a FlutterView that is only partially attached to a FlutterEngine.
if
(
didRenderFirstFrame
)
{
onFirstFrameRenderedListener
.
onFirstFrameRendered
();
}
//
if (didRenderFirstFrame) {
//
onFirstFrameRenderedListener.onFirstFrameRendered();
//
}
}
/**
...
...
@@ -681,9 +692,8 @@ public class XFlutterView extends FrameLayout {
// now that the engine is detached. The new InputConnection will be null, which
// 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
();
resolveMemoryLeaks
();
// resolveMemoryLeaks();
// Instruct our FlutterRenderer that we are no longer interested in being its RenderSurface.
FlutterRenderer
flutterRenderer
=
flutterEngine
.
getRenderer
();
// didRenderFirstFrame = false;
...
...
@@ -691,7 +701,9 @@ public class XFlutterView extends FrameLayout {
flutterRenderer
.
detachFromRenderSurface
();
flutterEngine
=
null
;
}
public
void
release
(){
textInputPlugin
.
release
();
}
public
void
resolveMemoryLeaks
(){
try
{
...
...
android/src/main/java/com/idlefish/flutterboost/XInputConnectionAdaptor.java
0 → 100644
View file @
20a64699
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
;
import
android.content.Context
;
import
android.text.DynamicLayout
;
import
android.text.Editable
;
import
android.text.Layout
;
import
android.text.Layout.Directions
;
import
android.text.Selection
;
import
android.text.TextPaint
;
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.Log
;
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
final
Layout
mLayout
;
@SuppressWarnings
(
"deprecation"
)
public
XInputConnectionAdaptor
(
View
view
,
int
client
,
TextInputChannel
textInputChannel
,
Editable
editable
)
{
super
(
view
,
true
);
mFlutterView
=
view
;
mClient
=
client
;
this
.
textInputChannel
=
textInputChannel
;
mEditable
=
editable
;
mBatchCount
=
0
;
// We create a dummy Layout with max width so that the selection
// shifting acts as if all text were in one line.
mLayout
=
new
DynamicLayout
(
mEditable
,
new
TextPaint
(),
Integer
.
MAX_VALUE
,
Layout
.
Alignment
.
ALIGN_NORMAL
,
1.0f
,
0.0f
,
false
);
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
;
}
// Sanitizes the index to ensure the index is within the range of the
// contents of editable.
private
static
int
clampIndexToEditable
(
int
index
,
Editable
editable
)
{
int
clamped
=
Math
.
max
(
0
,
Math
.
min
(
editable
.
length
(),
index
));
if
(
clamped
!=
index
)
{
Log
.
d
(
"flutter"
,
"Text selection index was clamped ("
+
index
+
"->"
+
clamped
+
") to remain in bounds. This may not be your fault, as some keyboards may select outside of bounds."
);
}
return
clamped
;
}
@Override
public
boolean
sendKeyEvent
(
KeyEvent
event
)
{
if
(
event
.
getAction
()
==
KeyEvent
.
ACTION_DOWN
)
{
if
(
event
.
getKeyCode
()
==
KeyEvent
.
KEYCODE_DEL
)
{
int
selStart
=
clampIndexToEditable
(
Selection
.
getSelectionStart
(
mEditable
),
mEditable
);
int
selEnd
=
clampIndexToEditable
(
Selection
.
getSelectionEnd
(
mEditable
),
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/right of the cursor depending on direction of text.
// TODO(garyq): Explore how to obtain per-character direction. The
// isRTLCharAt() call below is returning blanket direction assumption
// based on the first character in the line.
boolean
isRtl
=
mLayout
.
isRtlCharAt
(
mLayout
.
getLineForOffset
(
selStart
));
try
{
if
(
isRtl
)
{
Selection
.
extendRight
(
mEditable
,
mLayout
);
}
else
{
Selection
.
extendLeft
(
mEditable
,
mLayout
);
}
}
catch
(
IndexOutOfBoundsException
e
)
{
// On some Chinese devices (primarily Huawei, some Xiaomi),
// on initial app startup before focus is lost, the
// Selection.extendLeft and extendRight calls always extend
// from the index of the initial contents of mEditable. This
// try-catch will prevent crashing on Huawei devices by falling
// back to a simple way of deletion, although this a hack and
// will not handle emojis.
Selection
.
setSelection
(
mEditable
,
selStart
,
selStart
-
1
);
}
int
newStart
=
clampIndexToEditable
(
Selection
.
getSelectionStart
(
mEditable
),
mEditable
);
int
newEnd
=
clampIndexToEditable
(
Selection
.
getSelectionEnd
(
mEditable
),
mEditable
);
Selection
.
setSelection
(
mEditable
,
Math
.
min
(
newStart
,
newEnd
));
// Min/Max the values since RTL selections will start at a higher
// index than they end at.
mEditable
.
delete
(
Math
.
min
(
newStart
,
newEnd
),
Math
.
max
(
newStart
,
newEnd
));
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/XTextInputPlugin.java
0 → 100644
View file @
20a64699
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.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
,
@NonNull
TextInputChannel
textInputChannel
,
@NonNull
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.
* <p>
* See also: @{link lockPlatformViewInputConnection}.
*/
public
void
unlockPlatformViewInputConnection
()
{
isInputConnectionLocked
=
false
;
}
/**
* Detaches the text input plugin from the platform views controller.
* <p>
* 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.
* <p>
* 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 @
20a64699
...
...
@@ -15,9 +15,11 @@ import android.view.ViewGroup;
import
java.io.Serializable
;
import
java.util.Arrays
;
import
java.util.HashMap
;
import
java.util.Map
;
import
com.idlefish.flutterboost.BoostPluginRegistry
;
import
com.idlefish.flutterboost.NewFlutterBoost
;
import
com.idlefish.flutterboost.Utils
;
import
com.idlefish.flutterboost.XFlutterView
;
...
...
@@ -28,6 +30,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.ActivityPluginBinding
;
import
io.flutter.plugin.platform.PlatformPlugin
;
import
io.flutter.view.FlutterMain
;
...
...
@@ -74,12 +77,15 @@ public class FlutterActivityAndFragmentDelegate implements IFlutterViewContaine
return
flutterEngine
;
}
XFlutterView
getFlutterView
(){
return
flutterView
;
}
void
onAttach
(
@NonNull
Context
context
)
{
ensureAlive
();
initializeFlutter
(
context
);
if
(
NewFlutterBoost
.
instance
().
platform
().
whenEngineStart
()
==
NewFlutterBoost
.
ConfigBuilder
.
FLUTTER_ACTIVITY_CREATED
)
{
NewFlutterBoost
.
instance
().
doInitialFlutter
(
);
}
// When "retain instance" is true, the FlutterEngine will survive configuration
// changes. Therefore, we create a new one only if one does not already exist.
if
(
flutterEngine
==
null
)
{
...
...
@@ -94,47 +100,16 @@ public class FlutterActivityAndFragmentDelegate implements IFlutterViewContaine
// use-cases.
platformPlugin
=
host
.
providePlatformPlugin
(
host
.
getActivity
(),
flutterEngine
);
if
(
host
.
shouldAttachEngineToActivity
())
{
// Notify any plugins that are currently attached to our FlutterEngine that they
// are now attached to an Activity.
//
// Passing this Fragment's Lifecycle should be sufficient because as long as this Fragment
// is attached to its Activity, the lifecycles should be in sync. Once this Fragment is
// detached from its Activity, that Activity will be detached from the FlutterEngine, too,
// which means there shouldn't be any possibility for the Fragment Lifecycle to get out of
// sync with the Activity. We use the Fragment's Lifecycle because it is possible that the
// attached Activity is not a LifecycleOwner.
Log
.
d
(
TAG
,
"Attaching FlutterEngine to the Activity that owns this Fragment."
);
flutterEngine
.
getActivityControlSurface
().
attachToActivity
(
host
.
getActivity
(),
host
.
getLifecycle
()
);
}
host
.
configureFlutterEngine
(
flutterEngine
);
}
private
void
initializeFlutter
(
@NonNull
Context
context
)
{
FlutterMain
.
ensureInitializationComplete
(
context
.
getApplicationContext
(),
host
.
getFlutterShellArgs
().
toArray
()
);
}
private
void
setupFlutterEngine
()
{
Log
.
d
(
TAG
,
"Setting up FlutterEngine."
);
// First, check if the host wants to use a cached FlutterEngine.
// String cachedEngineId = host.getCachedEngineId();
// if (cachedEngineId != null) {
// flutterEngine = FlutterEngineCache.getInstance().get(cachedEngineId);
// isFlutterEngineFromHost = true;
// if (flutterEngine == null) {
// throw new IllegalStateException("The requested cached FlutterEngine did not exist in the FlutterEngineCache: '" + cachedEngineId + "'");
// }
// return;
// }
// Second, defer to subclasses for a custom FlutterEngine.
flutterEngine
=
host
.
provideFlutterEngine
(
host
.
getContext
());
...
...
@@ -147,7 +122,6 @@ public class FlutterActivityAndFragmentDelegate implements IFlutterViewContaine
// FlutterView.
Log
.
d
(
TAG
,
"No preferred FlutterEngine was provided. Creating a new FlutterEngine for"
+
" this NewFlutterFragment."
);
flutterEngine
=
new
FlutterEngine
(
host
.
getContext
());
isFlutterEngineFromHost
=
false
;
}
...
...
@@ -156,6 +130,11 @@ public class FlutterActivityAndFragmentDelegate implements IFlutterViewContaine
@NonNull
View
onCreateView
(
LayoutInflater
inflater
,
@Nullable
ViewGroup
container
,
@Nullable
Bundle
savedInstanceState
)
{
Log
.
v
(
TAG
,
"Creating FlutterView."
);
flutterEngine
.
getActivityControlSurface
().
attachToActivity
(
host
.
getActivity
(),
host
.
getLifecycle
()
);
mSyncer
=
NewFlutterBoost
.
instance
().
containerManager
().
generateSyncer
(
this
);
...
...
@@ -198,22 +177,24 @@ public class FlutterActivityAndFragmentDelegate implements IFlutterViewContaine
Log
.
v
(
TAG
,
"onResume()"
);
ensureAlive
();
flutterEngine
.
getLifecycleChannel
().
appIsResumed
();
BoostPluginRegistry
registry
=
(
BoostPluginRegistry
)
NewFlutterBoost
.
instance
().
getPluginRegistry
();
ActivityPluginBinding
binding
=
registry
.
getRegistrarAggregate
().
getActivityPluginBinding
();
if
(
binding
!=
null
&&(
binding
.
getActivity
()!=
this
.
host
.
getActivity
())){
flutterEngine
.
getActivityControlSurface
().
attachToActivity
(
host
.
getActivity
(),
host
.
getLifecycle
()
);
}
}
void
onPostResume
()
{
Log
.
v
(
TAG
,
"onPostResume()"
);
ensureAlive
();
if
(
flutterEngine
!=
null
)
{
if
(
platformPlugin
!=
null
)
{
// TODO(mattcarroll): find a better way to handle the update of UI overlays than calling through
// to platformPlugin. We're implicitly entangling the Window, Activity, Fragment,
// and engine all with this one call.
platformPlugin
.
updateSystemUiOverlays
();
}
}
else
{
Log
.
w
(
TAG
,
"onPostResume() invoked before NewFlutterFragment was attached to an Activity."
);
}
Utils
.
setStatusBarLightMode
(
host
.
getActivity
(),
true
);
}
...
...
@@ -229,7 +210,8 @@ public class FlutterActivityAndFragmentDelegate implements IFlutterViewContaine
void
onStop
()
{
Log
.
v
(
TAG
,
"onStop()"
);
ensureAlive
();
// flutterView.detachFromFlutterEngine();
}
void
onDestroyView
()
{
...
...
@@ -237,6 +219,14 @@ public class FlutterActivityAndFragmentDelegate implements IFlutterViewContaine
mSyncer
.
onDestroy
();
ensureAlive
();
BoostPluginRegistry
registry
=
(
BoostPluginRegistry
)
NewFlutterBoost
.
instance
().
getPluginRegistry
();
ActivityPluginBinding
binding
=
registry
.
getRegistrarAggregate
().
getActivityPluginBinding
();
if
(
binding
!=
null
&&(
binding
.
getActivity
()==
this
.
host
.
getActivity
())){
registry
.
getRegistrarAggregate
().
onDetachedFromActivityForConfigChanges
();
flutterEngine
.
getActivityControlSurface
().
detachFromActivityForConfigChanges
();
}
flutterView
.
release
();
}
...
...
@@ -244,15 +234,7 @@ public class FlutterActivityAndFragmentDelegate implements IFlutterViewContaine
Log
.
v
(
TAG
,
"onDetach()"
);
ensureAlive
();
if
(
host
.
shouldAttachEngineToActivity
())
{
// Notify plugins that they are no longer attached to an Activity.
Log
.
d
(
TAG
,
"Detaching FlutterEngine from the Activity that owns this Fragment."
);
if
(
host
.
getActivity
().
isChangingConfigurations
())
{
flutterEngine
.
getActivityControlSurface
().
detachFromActivityForConfigChanges
();
}
else
{
flutterEngine
.
getActivityControlSurface
().
detachFromActivity
();
}
}
// Null out the platformPlugin to avoid a possible retain cycle between the plugin, this Fragment,
// and this Fragment's Activity.
...
...
@@ -263,16 +245,6 @@ public class FlutterActivityAndFragmentDelegate implements IFlutterViewContaine
Utils
.
fixInputMethodManagerLeak
(
host
.
getActivity
());
// Destroy our FlutterEngine if we're not set to retain it.
// if (host.shouldDestroyEngineWithHost()) {
// flutterEngine.destroy();
//
// if (host.getCachedEngineId() != null) {
// FlutterEngineCache.getInstance().remove(host.getCachedEngineId());
// }
//
// flutterEngine = null;
// }
}
...
...
@@ -280,12 +252,6 @@ public class FlutterActivityAndFragmentDelegate implements IFlutterViewContaine
mSyncer
.
onBackPressed
();
ensureAlive
();
// if (flutterEngine != null) {
// Log.v(TAG, "Forwarding onBackPressed() to FlutterEngine.");
// flutterEngine.getNavigationChannel().popRoute();
// } else {
// Log.w(TAG, "Invoked onBackPressed() before NewFlutterFragment was attached to an Activity.");
// }
}
...
...
@@ -402,10 +368,26 @@ public class FlutterActivityAndFragmentDelegate implements IFlutterViewContaine
@Override
public
void
finishContainer
(
Map
<
String
,
Object
>
result
)
{
this
.
host
.
finishContainer
(
result
);
if
(
result
!=
null
)
{
setBoostResult
(
this
.
host
.
getActivity
(),
new
HashMap
<>(
result
));
this
.
host
.
getActivity
().
finish
();
}
else
{
this
.
host
.
getActivity
().
finish
();
}
}
public
void
setBoostResult
(
Activity
activity
,
HashMap
result
)
{
Intent
intent
=
new
Intent
();
if
(
result
!=
null
)
{
intent
.
putExtra
(
IFlutterViewContainer
.
RESULT_KEY
,
result
);
}
activity
.
setResult
(
Activity
.
RESULT_OK
,
intent
);
}
@Override
public
String
getContainerUrl
()
{
return
this
.
host
.
getContainerUrl
();
...
...
@@ -503,7 +485,6 @@ public class FlutterActivityAndFragmentDelegate implements IFlutterViewContaine
void
finishContainer
(
Map
<
String
,
Object
>
result
)
;
String
getContainerUrl
()
;
...
...
android/src/main/java/com/idlefish/flutterboost/containers/FlutterSplashView.java
View file @
20a64699
...
...
@@ -59,9 +59,8 @@ public class FlutterSplashView extends FrameLayout {
private
final
OnFirstFrameRenderedListener
onFirstFrameRenderedListener
=
new
OnFirstFrameRenderedListener
()
{
@Override
public
void
onFirstFrameRendered
()
{
if
(
splashScreen
!=
null
&&!
hasRendered
)
{
if
(
splashScreen
!=
null
)
{
transitionToFlutter
();
hasRendered
=
true
;
}
}
};
...
...
@@ -292,6 +291,9 @@ public class FlutterSplashView extends FrameLayout {
}
}
public
void
onAttach
()
{
Debuger
.
log
(
"BoostFlutterView onAttach"
);
...
...
android/src/main/java/com/idlefish/flutterboost/containers/NewBoostFlutterActivity.java
View file @
20a64699
...
...
@@ -21,6 +21,7 @@ import android.view.*;
import
android.widget.*
;
import
com.idlefish.flutterboost.NewFlutterBoost
;
import
com.idlefish.flutterboost.Utils
;
import
com.idlefish.flutterboost.XFlutterView
;
import
io.flutter.Log
;
import
io.flutter.embedding.android.DrawableSplashScreen
;
import
io.flutter.embedding.android.FlutterView
;
...
...
@@ -241,8 +242,12 @@ public class NewBoostFlutterActivity extends Activity
window
.
setStatusBarColor
(
Color
.
TRANSPARENT
);
window
.
getDecorView
().
setSystemUiVisibility
(
PlatformPlugin
.
DEFAULT_SYSTEM_UI
);
}
Utils
.
setStatusBarLightMode
(
this
,
true
);
}
protected
XFlutterView
getFlutterView
(){
return
delegate
.
getFlutterView
();
}
@Override
...
...
@@ -469,12 +474,6 @@ public class NewBoostFlutterActivity extends Activity
@Override
public
void
finishContainer
(
Map
<
String
,
Object
>
result
)
{
Activity
activity
=
this
.
getActivity
();
activity
.
finish
();
}
@Override
public
String
getContainerUrl
()
{
...
...
android/src/main/java/com/idlefish/flutterboost/containers/NewFlutterFragment.java
View file @
20a64699
...
...
@@ -15,6 +15,7 @@ import android.view.View;
import
android.view.ViewGroup
;
import
com.idlefish.flutterboost.NewFlutterBoost
;
import
com.idlefish.flutterboost.XFlutterView
;
import
io.flutter.embedding.android.*
;
import
io.flutter.embedding.engine.FlutterEngine
;
import
io.flutter.embedding.engine.FlutterShellArgs
;
...
...
@@ -233,6 +234,10 @@ public class NewFlutterFragment extends Fragment implements FlutterActivityAndFr
private
FlutterActivityAndFragmentDelegate
delegate
;
protected
XFlutterView
getFlutterView
(){
return
delegate
.
getFlutterView
();
}
public
NewFlutterFragment
()
{
// Ensure that we at least have an empty Bundle of arguments so that we don't
// need to continually check for null arguments before grabbing one.
...
...
@@ -485,16 +490,11 @@ public class NewFlutterFragment extends Fragment implements FlutterActivityAndFr
*/
@Override
public
boolean
shouldAttachEngineToActivity
()
{
return
getArguments
().
getBoolean
(
ARG_SHOULD_ATTACH_ENGINE_TO_ACTIVITY
)
;
return
true
;
}
@Override
public
void
finishContainer
(
Map
<
String
,
Object
>
result
)
{
Activity
activity
=
this
.
getActivity
();
activity
.
finish
();
}
@Override
public
String
getContainerUrl
()
{
...
...
example/android/app/src/main/java/com/taobao/idlefish/flutterboostexample/MyApplication.java
View file @
20a64699
...
...
@@ -3,19 +3,21 @@ package com.taobao.idlefish.flutterboostexample;
import
android.app.Application
;
import
android.content.Context
;
import
android.util.Log
;
import
com.idlefish.flutterboost.*
;
import
com.idlefish.flutterboost.interfaces.IContainerRecord
;
import
java.util.Map
;
import
com.idlefish.flutterboost.interfaces.INativeRouter
;
import
io.flutter.app.FlutterApplication
;
import
io.flutter.embedding.android.FlutterView
;
import
io.flutter.plugin.common.MethodChannel
;
public
class
MyApplication
extends
Application
{
public
class
MyApplication
extends
FlutterApplication
{
@Override
public
void
onCreate
()
{
super
.
onCreate
();
INativeRouter
router
=
new
INativeRouter
()
{
@Override
public
void
openContainer
(
Context
context
,
String
url
,
Map
<
String
,
Object
>
urlParams
,
int
requestCode
,
Map
<
String
,
Object
>
exts
)
{
...
...
@@ -25,12 +27,36 @@ public class MyApplication extends FlutterApplication {
};
NewFlutterBoost
.
BoostLifecycleListener
lifecycleListener
=
new
NewFlutterBoost
.
BoostLifecycleListener
()
{
@Override
public
void
onEngineCreated
()
{
}
@Override
public
void
onPluginsRegistered
()
{
MethodChannel
mMethodChannel
=
new
MethodChannel
(
NewFlutterBoost
.
instance
().
engineProvider
().
getDartExecutor
(),
"methodChannel"
);
Log
.
e
(
"MyApplication"
,
"MethodChannel create"
);
TextPlatformViewPlugin
.
register
(
NewFlutterBoost
.
instance
().
getPluginRegistry
().
registrarFor
(
"TextPlatformViewPlugin"
));
}
@Override
public
void
onEngineDestroy
()
{
}
};
Platform
platform
=
new
NewFlutterBoost
.
ConfigBuilder
(
this
,
router
)
.
isDebug
(
true
)
.
whenEngineStart
(
NewFlutterBoost
.
ConfigBuilder
.
ANY_ACTIVITY_CREATED
)
.
renderMode
(
FlutterView
.
RenderMode
.
texture
)
.
lifecycleListener
(
lifecycleListener
)
.
build
();
NewFlutterBoost
.
instance
().
init
(
platform
);
}
}
example/android/app/src/main/java/com/taobao/idlefish/flutterboostexample/TextPlatformViewPlugin.java
View file @
20a64699
package
com.taobao.idlefish.flutterboostexample
;
import
io.flutter.app.FlutterPluginRegistry
;
import
io.flutter.plugin.common.PluginRegistry
;
import
io.flutter.plugin.common.StandardMessageCodec
;
public
class
TextPlatformViewPlugin
{
public
static
void
register
(
FlutterPluginRegistry
registry
)
{
registr
y
.
getPlatformViewsController
().
get
Registry
().
registerViewFactory
(
"plugins.test/view"
,
public
static
void
register
(
PluginRegistry
.
Registrar
registrar
)
{
registr
ar
.
platformView
Registry
().
registerViewFactory
(
"plugins.test/view"
,
new
TextPlatformViewFactory
(
StandardMessageCodec
.
INSTANCE
));
}
}
example/lib/main.dart
View file @
20a64699
...
...
@@ -20,6 +20,7 @@ class _MyAppState extends State<MyApp> {
'first'
:
(
pageName
,
params
,
_
)
=>
FirstRouteWidget
(),
'second'
:
(
pageName
,
params
,
_
)
=>
SecondRouteWidget
(),
'tab'
:
(
pageName
,
params
,
_
)
=>
TabRouteWidget
(),
'platformView'
:
(
pageName
,
params
,
_
)
=>
PlatformRouteWidget
(),
'flutterFragment'
:
(
pageName
,
params
,
_
)
=>
FragmentRouteWidget
(
params
),
///可以在native层通过 getContainerParams 来传递参数
'flutterPage'
:
(
pageName
,
params
,
_
)
{
...
...
example/lib/platform_view.dart
0 → 100644
View file @
20a64699
import
'dart:async'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/services.dart'
;
typedef
void
TextViewCreatedCallback
(
TextViewController
controller
);
class
TextView
extends
StatefulWidget
{
const
TextView
({
Key
key
,
this
.
onTextViewCreated
,
})
:
super
(
key:
key
);
final
TextViewCreatedCallback
onTextViewCreated
;
@override
State
<
StatefulWidget
>
createState
()
=>
_TextViewState
();
}
class
_TextViewState
extends
State
<
TextView
>
{
@override
Widget
build
(
BuildContext
context
)
{
if
(
defaultTargetPlatform
==
TargetPlatform
.
android
)
{
return
AndroidView
(
viewType:
'plugins.test/view'
,
onPlatformViewCreated:
_onPlatformViewCreated
,
);
}
return
Text
(
'
$defaultTargetPlatform
is not yet supported by the text_view plugin'
);
}
void
_onPlatformViewCreated
(
int
id
)
{
if
(
widget
.
onTextViewCreated
==
null
)
{
return
;
}
widget
.
onTextViewCreated
(
new
TextViewController
.
_
(
id
));
}
}
class
TextViewController
{
TextViewController
.
_
(
int
id
)
:
_channel
=
new
MethodChannel
(
'plugins.felix.angelov/textview_
$id
'
);
final
MethodChannel
_channel
;
Future
<
void
>
setText
(
String
text
)
async
{
assert
(
text
!=
null
);
return
_channel
.
invokeMethod
(
'setText'
,
text
);
}
}
\ No newline at end of file
example/lib/simple_page_widgets.dart
View file @
20a64699
import
'package:flutter/cupertino.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_boost/flutter_boost.dart'
;
import
'package:flutter_boost_example/platform_view.dart'
;
class
FirstRouteWidget
extends
StatelessWidget
{
@override
...
...
@@ -68,6 +69,28 @@ class TabRouteWidget extends StatelessWidget {
}
}
class
PlatformRouteWidget
extends
StatelessWidget
{
@override
Widget
build
(
BuildContext
context
)
{
return
Scaffold
(
appBar:
AppBar
(
title:
Text
(
"Platform Route"
),
),
body:
Center
(
child:
RaisedButton
(
child:
TextView
(),
onPressed:
()
{
print
(
"open second page!"
);
FlutterBoost
.
singleton
.
open
(
"second"
).
then
((
Map
value
)
{
print
(
"call me when page is finished. did recieve second route result
$value
"
);
});
},
),
),
);
}
}
class
FlutterRouteWidget
extends
StatefulWidget
{
FlutterRouteWidget
({
this
.
params
,
this
.
message
});
final
Map
params
;
...
...
@@ -85,6 +108,10 @@ class _FlutterRouteWidgetState extends State<FlutterRouteWidget> {
final
String
message
=
widget
.
message
;
return
Scaffold
(
appBar:
AppBar
(
brightness:
Brightness
.
light
,
backgroundColor:
Colors
.
white
,
textTheme:
new
TextTheme
(
title:
TextStyle
(
color:
Colors
.
black
))
,
title:
Text
(
'flutter_boost_example'
),
),
body:
SingleChildScrollView
(
...
...
@@ -216,6 +243,21 @@ class _FlutterRouteWidgetState extends State<FlutterRouteWidget> {
MaterialPageRoute
(
builder:
(
_
)
=>
PushWidget
()));
},
),
InkWell
(
child:
Container
(
padding:
const
EdgeInsets
.
all
(
8.0
),
margin:
const
EdgeInsets
.
all
(
8.0
),
color:
Colors
.
yellow
,
child:
Text
(
'push Platform demo'
,
style:
TextStyle
(
fontSize:
22.0
,
color:
Colors
.
black
),
)),
onTap:
()
{
Navigator
.
push
(
context
,
MaterialPageRoute
(
builder:
(
_
)
=>
PlatformRouteWidget
()));
},
),
InkWell
(
child:
Container
(
padding:
const
EdgeInsets
.
all
(
8.0
),
...
...
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