@@ -14,15 +14,26 @@ static constexpr char kChannelName[] = "flutter/platform";
1414static constexpr char kBadArgumentsError [] = " Bad Arguments" ;
1515static constexpr char kUnknownClipboardFormatError [] =
1616 " Unknown Clipboard Format" ;
17+ static constexpr char kInProgressError [] = " In Progress" ;
1718static constexpr char kFailedError [] = " Failed" ;
1819static constexpr char kGetClipboardDataMethod [] = " Clipboard.getData" ;
1920static constexpr char kSetClipboardDataMethod [] = " Clipboard.setData" ;
2021static constexpr char kClipboardHasStringsMethod [] = " Clipboard.hasStrings" ;
22+ static constexpr char kExitApplicationMethod [] = " System.exitApplication" ;
23+ static constexpr char kRequestAppExitMethod [] = " System.requestAppExit" ;
2124static constexpr char kPlaySoundMethod [] = " SystemSound.play" ;
2225static constexpr char kSystemNavigatorPopMethod [] = " SystemNavigator.pop" ;
2326static constexpr char kTextKey [] = " text" ;
2427static constexpr char kValueKey [] = " value" ;
2528
29+ static constexpr char kExitTypeKey [] = " type" ;
30+ static constexpr char kExitTypeCancelable [] = " cancelable" ;
31+ static constexpr char kExitTypeRequired [] = " required" ;
32+
33+ static constexpr char kExitResponseKey [] = " response" ;
34+ static constexpr char kExitResponseCancel [] = " cancel" ;
35+ static constexpr char kExitResponseExit [] = " exit" ;
36+
2637static constexpr char kTextPlainFormat [] = " text/plain" ;
2738
2839static constexpr char kSoundTypeAlert [] = " SystemSoundType.alert" ;
@@ -32,6 +43,8 @@ struct _FlPlatformPlugin {
3243 GObject parent_instance;
3344
3445 FlMethodChannel* channel;
46+ FlMethodCall* exit_application_method_call;
47+ GCancellable* cancellable;
3548};
3649
3750G_DEFINE_TYPE (FlPlatformPlugin, fl_platform_plugin, G_TYPE_OBJECT)
@@ -140,6 +153,124 @@ static FlMethodResponse* clipboard_has_strings_async(
140153 return nullptr ;
141154}
142155
156+ // Get the exit response from a System.requestAppExit method call.
157+ static gchar* get_exit_response (FlMethodResponse* response) {
158+ if (response == nullptr ) {
159+ return nullptr ;
160+ }
161+
162+ g_autoptr (GError) error = nullptr ;
163+ g_autoptr (FlValue) result = fl_method_response_get_result (response, &error);
164+ if (result == nullptr ) {
165+ g_warning (" Error returned from System.requestAppExit: %s" , error->message );
166+ return nullptr ;
167+ }
168+ if (fl_value_get_type (result) != FL_VALUE_TYPE_MAP) {
169+ g_warning (" System.requestAppExit result argument map missing or malformed" );
170+ return nullptr ;
171+ }
172+
173+ FlValue* response_value = fl_value_lookup_string (result, kExitResponseKey );
174+ if (fl_value_get_type (response_value) != FL_VALUE_TYPE_STRING) {
175+ g_warning (" Invalid response from System.requestAppExit" );
176+ return nullptr ;
177+ }
178+ return g_strdup (fl_value_get_string (response_value));
179+ }
180+
181+ // Handle response of System.requestAppExit.
182+ static void request_app_exit_response_cb (GObject* object,
183+ GAsyncResult* result,
184+ gpointer user_data) {
185+ FlPlatformPlugin* self = FL_PLATFORM_PLUGIN (user_data);
186+
187+ g_autoptr (GError) error = nullptr ;
188+ g_autoptr (FlMethodResponse) method_response =
189+ fl_method_channel_invoke_method_finish (FL_METHOD_CHANNEL (object), result,
190+ &error);
191+ g_autofree gchar* exit_response = nullptr ;
192+ if (method_response == nullptr ) {
193+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
194+ return ;
195+ }
196+ g_warning (" Failed to complete System.requestAppExit: %s" , error->message );
197+ } else {
198+ exit_response = get_exit_response (method_response);
199+ }
200+ if (exit_response == nullptr ) {
201+ exit_response = g_strdup (kExitResponseCancel );
202+ }
203+
204+ if (g_str_equal (exit_response, kExitResponseExit )) {
205+ GApplication* app = g_application_get_default ();
206+ if (app == nullptr ) {
207+ g_warning (" Unable to quit, no GApplication" );
208+ } else {
209+ g_application_quit (app);
210+ }
211+ } else if (g_str_equal (exit_response, kExitResponseCancel )) {
212+ // Canceled - no action to take.
213+ }
214+
215+ // If request was due to a request from Flutter, pass result back.
216+ if (self->exit_application_method_call != nullptr ) {
217+ g_autoptr (FlValue) exit_result = fl_value_new_map ();
218+ fl_value_set_string_take (exit_result, kExitResponseKey ,
219+ fl_value_new_string (exit_response));
220+ g_autoptr (FlMethodResponse) exit_response =
221+ FL_METHOD_RESPONSE (fl_method_success_response_new (exit_result));
222+ if (!fl_method_call_respond (self->exit_application_method_call ,
223+ exit_response, &error)) {
224+ g_warning (" Failed to send response to System.exitApplication: %s" ,
225+ error->message );
226+ }
227+ g_clear_object (&self->exit_application_method_call );
228+ }
229+ }
230+
231+ // Send a request to Flutter to exit the application.
232+ static void request_app_exit (FlPlatformPlugin* self, const char * type) {
233+ g_autoptr (FlValue) args = fl_value_new_map ();
234+ fl_value_set_string_take (args, kExitTypeKey , fl_value_new_string (type));
235+ fl_method_channel_invoke_method (self->channel , kRequestAppExitMethod , args,
236+ self->cancellable ,
237+ request_app_exit_response_cb, self);
238+ }
239+
240+ // Called when Flutter wants to exit the application.
241+ static FlMethodResponse* system_exit_application (FlPlatformPlugin* self,
242+ FlMethodCall* method_call) {
243+ FlValue* args = fl_method_call_get_args (method_call);
244+ if (fl_value_get_type (args) != FL_VALUE_TYPE_MAP) {
245+ return FL_METHOD_RESPONSE (fl_method_error_response_new (
246+ kBadArgumentsError , " Argument map missing or malformed" , nullptr ));
247+ }
248+
249+ FlValue* type_value = fl_value_lookup_string (args, kExitTypeKey );
250+ if (type_value == nullptr ||
251+ fl_value_get_type (type_value) != FL_VALUE_TYPE_STRING) {
252+ return FL_METHOD_RESPONSE (fl_method_error_response_new (
253+ kBadArgumentsError , " Missing type argument" , nullptr ));
254+ }
255+ const char * type = fl_value_get_string (type_value);
256+
257+ // Save method call to respond to when our request to Flutter completes.
258+ if (self->exit_application_method_call != nullptr ) {
259+ return FL_METHOD_RESPONSE (fl_method_error_response_new (
260+ kInProgressError , " Request already in progress" , nullptr ));
261+ }
262+ self->exit_application_method_call =
263+ FL_METHOD_CALL (g_object_ref (method_call));
264+
265+ // Send the request back to Flutter to follow the standard process.
266+ request_app_exit (self, g_str_equal (type, kExitTypeCancelable )
267+ ? kExitTypeCancelable
268+ : kExitTypeRequired );
269+
270+ // Will respond later.
271+ return nullptr ;
272+ }
273+
143274// Called when Flutter wants to play a sound.
144275static FlMethodResponse* system_sound_play (FlPlatformPlugin* self,
145276 FlValue* args) {
@@ -192,6 +323,8 @@ static void method_call_cb(FlMethodChannel* channel,
192323 response = clipboard_get_data_async (self, method_call);
193324 } else if (strcmp (method, kClipboardHasStringsMethod ) == 0 ) {
194325 response = clipboard_has_strings_async (self, method_call);
326+ } else if (strcmp (method, kExitApplicationMethod ) == 0 ) {
327+ response = system_exit_application (self, method_call);
195328 } else if (strcmp (method, kPlaySoundMethod ) == 0 ) {
196329 response = system_sound_play (self, args);
197330 } else if (strcmp (method, kSystemNavigatorPopMethod ) == 0 ) {
@@ -208,7 +341,11 @@ static void method_call_cb(FlMethodChannel* channel,
208341static void fl_platform_plugin_dispose (GObject* object) {
209342 FlPlatformPlugin* self = FL_PLATFORM_PLUGIN (object);
210343
344+ g_cancellable_cancel (self->cancellable );
345+
211346 g_clear_object (&self->channel );
347+ g_clear_object (&self->exit_application_method_call );
348+ g_clear_object (&self->cancellable );
212349
213350 G_OBJECT_CLASS (fl_platform_plugin_parent_class)->dispose (object);
214351}
@@ -217,7 +354,9 @@ static void fl_platform_plugin_class_init(FlPlatformPluginClass* klass) {
217354 G_OBJECT_CLASS (klass)->dispose = fl_platform_plugin_dispose;
218355}
219356
220- static void fl_platform_plugin_init (FlPlatformPlugin* self) {}
357+ static void fl_platform_plugin_init (FlPlatformPlugin* self) {
358+ self->cancellable = g_cancellable_new ();
359+ }
221360
222361FlPlatformPlugin* fl_platform_plugin_new (FlBinaryMessenger* messenger) {
223362 g_return_val_if_fail (FL_IS_BINARY_MESSENGER (messenger), nullptr );
@@ -233,3 +372,9 @@ FlPlatformPlugin* fl_platform_plugin_new(FlBinaryMessenger* messenger) {
233372
234373 return self;
235374}
375+
376+ void fl_platform_plugin_request_app_exit (FlPlatformPlugin* self) {
377+ g_return_if_fail (FL_IS_PLATFORM_PLUGIN (self));
378+ // Request a cancellable exit.
379+ request_app_exit (self, kExitTypeCancelable );
380+ }
0 commit comments