10
10
import android .os .Environment ;
11
11
import android .os .Handler ;
12
12
import android .os .Looper ;
13
+ import androidx .annotation .NonNull ;
13
14
import androidx .annotation .VisibleForTesting ;
15
+ import androidx .lifecycle .DefaultLifecycleObserver ;
16
+ import androidx .lifecycle .Lifecycle ;
17
+ import androidx .lifecycle .LifecycleOwner ;
18
+ import io .flutter .embedding .engine .plugins .FlutterPlugin ;
19
+ import io .flutter .embedding .engine .plugins .activity .ActivityAware ;
20
+ import io .flutter .embedding .engine .plugins .activity .ActivityPluginBinding ;
21
+ import io .flutter .embedding .engine .plugins .lifecycle .FlutterLifecycleAdapter ;
22
+ import io .flutter .plugin .common .BinaryMessenger ;
14
23
import io .flutter .plugin .common .MethodCall ;
15
24
import io .flutter .plugin .common .MethodChannel ;
16
25
import io .flutter .plugin .common .PluginRegistry ;
17
26
import java .io .File ;
18
27
19
- public class ImagePickerPlugin implements MethodChannel .MethodCallHandler {
28
+ @ SuppressWarnings ("deprecation" )
29
+ public class ImagePickerPlugin
30
+ implements MethodChannel .MethodCallHandler , FlutterPlugin , ActivityAware {
31
+
32
+ private class LifeCycleObserver
33
+ implements Application .ActivityLifecycleCallbacks , DefaultLifecycleObserver {
34
+ private final Activity thisActivity ;
35
+
36
+ LifeCycleObserver (Activity activity ) {
37
+ this .thisActivity = activity ;
38
+ }
39
+
40
+ @ Override
41
+ public void onCreate (@ NonNull LifecycleOwner owner ) {}
42
+
43
+ @ Override
44
+ public void onStart (@ NonNull LifecycleOwner owner ) {}
45
+
46
+ @ Override
47
+ public void onResume (@ NonNull LifecycleOwner owner ) {}
48
+
49
+ @ Override
50
+ public void onPause (@ NonNull LifecycleOwner owner ) {}
51
+
52
+ @ Override
53
+ public void onStop (@ NonNull LifecycleOwner owner ) {
54
+ onActivityStopped (thisActivity );
55
+ }
56
+
57
+ @ Override
58
+ public void onDestroy (@ NonNull LifecycleOwner owner ) {
59
+ onActivityDestroyed (thisActivity );
60
+ }
61
+
62
+ @ Override
63
+ public void onActivityCreated (Activity activity , Bundle savedInstanceState ) {}
64
+
65
+ @ Override
66
+ public void onActivityStarted (Activity activity ) {}
67
+
68
+ @ Override
69
+ public void onActivityResumed (Activity activity ) {}
70
+
71
+ @ Override
72
+ public void onActivityPaused (Activity activity ) {}
73
+
74
+ @ Override
75
+ public void onActivitySaveInstanceState (Activity activity , Bundle outState ) {}
76
+
77
+ @ Override
78
+ public void onActivityDestroyed (Activity activity ) {
79
+ if (thisActivity == activity && activity .getApplicationContext () != null ) {
80
+ ((Application ) activity .getApplicationContext ())
81
+ .unregisterActivityLifecycleCallbacks (
82
+ this ); // Use getApplicationContext() to avoid casting failures
83
+ }
84
+ }
85
+
86
+ @ Override
87
+ public void onActivityStopped (Activity activity ) {
88
+ if (thisActivity == activity ) {
89
+ delegate .saveStateBeforeResult ();
90
+ }
91
+ }
92
+ }
20
93
21
94
static final String METHOD_CALL_IMAGE = "pickImage" ;
22
95
static final String METHOD_CALL_VIDEO = "pickVideo" ;
@@ -27,82 +100,130 @@ public class ImagePickerPlugin implements MethodChannel.MethodCallHandler {
27
100
private static final int SOURCE_CAMERA = 0 ;
28
101
private static final int SOURCE_GALLERY = 1 ;
29
102
30
- private final PluginRegistry . Registrar registrar ;
103
+ private MethodChannel channel ;
31
104
private ImagePickerDelegate delegate ;
32
- private Application .ActivityLifecycleCallbacks activityLifecycleCallbacks ;
105
+ private FlutterPluginBinding pluginBinding ;
106
+ private ActivityPluginBinding activityBinding ;
107
+ private Application application ;
108
+ private Activity activity ;
109
+ // This is null when not using v2 embedding;
110
+ private Lifecycle lifecycle ;
111
+ private LifeCycleObserver observer ;
33
112
34
113
public static void registerWith (PluginRegistry .Registrar registrar ) {
35
114
if (registrar .activity () == null ) {
36
115
// If a background flutter view tries to register the plugin, there will be no activity from the registrar,
37
116
// we stop the registering process immediately because the ImagePicker requires an activity.
38
117
return ;
39
118
}
40
- final ImagePickerCache cache = new ImagePickerCache (registrar .activity ());
119
+ Activity activity = registrar .activity ();
120
+ Application application = null ;
121
+ if (registrar .context () != null ) {
122
+ application = (Application ) (registrar .context ().getApplicationContext ());
123
+ }
124
+ ImagePickerPlugin plugin = new ImagePickerPlugin ();
125
+ plugin .setup (registrar .messenger (), application , activity , registrar , null );
126
+ }
41
127
42
- final MethodChannel channel = new MethodChannel (registrar .messenger (), CHANNEL );
128
+ /**
129
+ * Default constructor for the plugin.
130
+ *
131
+ * <p>Use this constructor for production code.
132
+ */
133
+ // See also: * {@link #ImagePickerPlugin(ImagePickerDelegate, Activity)} for testing.
134
+ public ImagePickerPlugin () {}
43
135
44
- final File externalFilesDirectory =
45
- registrar .activity ().getExternalFilesDir (Environment .DIRECTORY_PICTURES );
46
- final ExifDataCopier exifDataCopier = new ExifDataCopier ();
47
- final ImageResizer imageResizer = new ImageResizer (externalFilesDirectory , exifDataCopier );
48
- final ImagePickerDelegate delegate =
49
- new ImagePickerDelegate (registrar .activity (), externalFilesDirectory , imageResizer , cache );
136
+ @ VisibleForTesting
137
+ ImagePickerPlugin (final ImagePickerDelegate delegate , final Activity activity ) {
138
+ this .delegate = delegate ;
139
+ this .activity = activity ;
140
+ }
50
141
51
- registrar .addActivityResultListener (delegate );
52
- registrar .addRequestPermissionsResultListener (delegate );
53
- final ImagePickerPlugin instance = new ImagePickerPlugin (registrar , delegate );
54
- channel .setMethodCallHandler (instance );
142
+ @ Override
143
+ public void onAttachedToEngine (FlutterPluginBinding binding ) {
144
+ pluginBinding = binding ;
55
145
}
56
146
57
- @ VisibleForTesting
58
- ImagePickerPlugin (final PluginRegistry .Registrar registrar , final ImagePickerDelegate delegate ) {
59
- this .registrar = registrar ;
60
- this .delegate = delegate ;
61
- this .activityLifecycleCallbacks =
62
- new Application .ActivityLifecycleCallbacks () {
63
- @ Override
64
- public void onActivityCreated (Activity activity , Bundle savedInstanceState ) {}
147
+ @ Override
148
+ public void onDetachedFromEngine (FlutterPluginBinding binding ) {
149
+ pluginBinding = null ;
150
+ }
65
151
66
- @ Override
67
- public void onActivityStarted (Activity activity ) {}
152
+ @ Override
153
+ public void onAttachedToActivity (ActivityPluginBinding binding ) {
154
+ activityBinding = binding ;
155
+ setup (
156
+ pluginBinding .getBinaryMessenger (),
157
+ (Application ) pluginBinding .getApplicationContext (),
158
+ activityBinding .getActivity (),
159
+ null ,
160
+ activityBinding );
161
+ }
68
162
69
- @ Override
70
- public void onActivityResumed (Activity activity ) {}
163
+ @ Override
164
+ public void onDetachedFromActivity () {
165
+ tearDown ();
166
+ }
71
167
72
- @ Override
73
- public void onActivityPaused (Activity activity ) {}
168
+ @ Override
169
+ public void onDetachedFromActivityForConfigChanges () {
170
+ onDetachedFromActivity ();
171
+ }
74
172
75
- @ Override
76
- public void onActivitySaveInstanceState ( Activity activity , Bundle outState ) {
77
- if ( activity == registrar . activity ()) {
78
- delegate . saveStateBeforeResult ();
79
- }
80
- }
81
-
82
- @ Override
83
- public void onActivityDestroyed ( Activity activity ) {
84
- if ( activity == registrar . activity ()
85
- && registrar . activity (). getApplicationContext () != null ) {
86
- (( Application ) registrar . activity (). getApplicationContext ())
87
- . unregisterActivityLifecycleCallbacks (
88
- this ); // Use getApplicationContext() to avoid casting failures
89
- }
90
- }
91
-
92
- @ Override
93
- public void onActivityStopped ( Activity activity ) {}
94
- } ;
95
-
96
- if ( this . registrar != null
97
- && this . registrar . context () != null
98
- && this . registrar . context (). getApplicationContext () != null ) {
99
- (( Application ) this . registrar . context (). getApplicationContext ())
100
- . registerActivityLifecycleCallbacks (
101
- this
102
- . activityLifecycleCallbacks ); // Use getApplicationContext() to avoid casting failures.
173
+ @ Override
174
+ public void onReattachedToActivityForConfigChanges ( ActivityPluginBinding binding ) {
175
+ onAttachedToActivity ( binding );
176
+ }
177
+
178
+ private void setup (
179
+ final BinaryMessenger messenger ,
180
+ final Application application ,
181
+ final Activity activity ,
182
+ final PluginRegistry . Registrar registrar ,
183
+ final ActivityPluginBinding activityBinding ) {
184
+ this . activity = activity ;
185
+ this . application = application ;
186
+ this . delegate = constructDelegate ( activity );
187
+ channel = new MethodChannel ( messenger , CHANNEL );
188
+ channel . setMethodCallHandler ( this );
189
+ observer = new LifeCycleObserver ( activity );
190
+ if ( registrar != null ) {
191
+ // V1 embedding setup for activity listeners.
192
+ application . registerActivityLifecycleCallbacks ( observer ) ;
193
+ registrar . addActivityResultListener ( delegate );
194
+ registrar . addRequestPermissionsResultListener ( delegate );
195
+ } else {
196
+ // V2 embedding setup for activity listeners.
197
+ activityBinding . addActivityResultListener ( delegate );
198
+ activityBinding . addRequestPermissionsResultListener ( delegate );
199
+ lifecycle = FlutterLifecycleAdapter . getActivityLifecycle ( activityBinding );
200
+ lifecycle . addObserver ( observer );
103
201
}
104
202
}
105
203
204
+ private void tearDown () {
205
+ activityBinding .removeActivityResultListener (delegate );
206
+ activityBinding .removeRequestPermissionsResultListener (delegate );
207
+ activityBinding = null ;
208
+ lifecycle .removeObserver (observer );
209
+ lifecycle = null ;
210
+ delegate = null ;
211
+ channel .setMethodCallHandler (null );
212
+ channel = null ;
213
+ application .unregisterActivityLifecycleCallbacks (observer );
214
+ application = null ;
215
+ }
216
+
217
+ private final ImagePickerDelegate constructDelegate (final Activity setupActivity ) {
218
+ final ImagePickerCache cache = new ImagePickerCache (setupActivity );
219
+
220
+ final File externalFilesDirectory =
221
+ setupActivity .getExternalFilesDir (Environment .DIRECTORY_PICTURES );
222
+ final ExifDataCopier exifDataCopier = new ExifDataCopier ();
223
+ final ImageResizer imageResizer = new ImageResizer (externalFilesDirectory , exifDataCopier );
224
+ return new ImagePickerDelegate (setupActivity , externalFilesDirectory , imageResizer , cache );
225
+ }
226
+
106
227
// MethodChannel.Result wrapper that responds on the platform thread.
107
228
private static class MethodResultWrapper implements MethodChannel .Result {
108
229
private MethodChannel .Result methodResult ;
@@ -150,7 +271,7 @@ public void run() {
150
271
151
272
@ Override
152
273
public void onMethodCall (MethodCall call , MethodChannel .Result rawResult ) {
153
- if (registrar . activity () == null ) {
274
+ if (activity == null ) {
154
275
rawResult .error ("no_activity" , "image_picker plugin requires a foreground activity." , null );
155
276
return ;
156
277
}
0 commit comments