0

How do you correctly close an activity when the user declines to update Google play services? I am using makeGooglePlayServicesAvailable() because it seems convenient, but I have not found many examples of its use.

I use checkGooglePlayServices() (code below) in onCreate() and onResume().

public class MainScreen extends Activity {
    private static final String TAG = "MainScreen";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState); 

        checkGooglePlayServices();
    }

    @Override
    protected void onResume() {
        super.onResume();
        checkGooglePlayServices();
    }

    void checkGooglePlayServices()
    {
        GoogleApiAvailability.getInstance()
         .makeGooglePlayServicesAvailable(this)
         .addOnSuccessListener(new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void ignored) {
                    Log.d(TAG,"makeGooglePlayServicesAvailable().onSuccess()");

                // GPS available; do something useful 

            }
        }).addOnFailureListener(this,new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {

                Log.d(TAG,"makeGooglePlayServicesAvailable().onFailure()");
                e.printStackTrace();

                Toast.makeText(MainScreen.this,
                    "Google Play services upgrade required", 
                    Toast.LENGTH_SHORT).show();

                // can't proceed without GPS; quit

                MainScreen.this.finish(); // this causes a crash
            }
        });
    }
}

The app crashes when finish() is called:

com.example E/AndroidRuntime: FATAL EXCEPTION: main
  Process: com.example, PID: 5336
  java.lang.RuntimeException: Unable to destroy activity {com.example/com.example.MainScreen}: java.lang.IllegalStateException: Task is already complete
      at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:4438)
      at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:4456)
      ...
   Caused by: java.lang.IllegalStateException: Task is already complete
      at com.google.android.gms.common.internal.zzbo.zza(Unknown Source:8)
      at com.google.android.gms.tasks.zzn.zzDH(Unknown Source:8)
      at com.google.android.gms.tasks.zzn.setException(Unknown Source:9)
      at com.google.android.gms.tasks.TaskCompletionSource.setException(Unknown Source:2)
      ...
stardt
  • 1,179
  • 1
  • 9
  • 14

2 Answers2

2

I assume that you are pressing "back" to cancel the GPS update request. The error is coming on the call to the super of onDestroy(). That seems to indicate that Android has already anticipated not being able to proceed and has already shut things down. (That is just a guess.)

Anyway, I could not determine a graceful way to close things out with the failure listener callback, but here is a slightly different approach that still uses GoogleApiAvailability. I have tested this out and it seems to work.

Replace your checkGooglePlayServices() with the following:

private static final int GPS_REQUEST_CODE = 1; // arbitrary code

void checkGooglePlayServices() {
    GoogleApiAvailability api = GoogleApiAvailability.getInstance();
    int status = api.isGooglePlayServicesAvailable(this);
    if (status != ConnectionResult.SUCCESS) {
        if (api.isUserResolvableError(status)) {
            // onActivityResult() will be called with GPS_REQUEST_CODE
            Dialog dialog = api.getErrorDialog(this, status, GPS_REQUEST_CODE,
                    new DialogInterface.OnCancelListener() {
                        @Override
                        public void onCancel(DialogInterface dialog) {
                            // GPS update was cancelled.
                            // Do toast or dialog (probably better to do) here.
                            finish();
                        }
                    });
            dialog.show();
        } else {
            // unrecoverable error
        }
    }
}

This code avoids the callbacks but will still enable you to check for the availability of GPS. Here is the documentation.

Here is the same approach in an example app.

If you want to proceed with the code you have you can do the following hack, but I do prefer the above to capture the crash and hide the nastiness from the user.

@Override
public void onDestroy() {
    try {
        super.onDestroy();
    } catch (IllegalStateException e) {
        e.printStackTrace();
    }
}
Cheticamp
  • 61,413
  • 10
  • 78
  • 131
  • Thanks this seems to be the common way to check for GPS. I'll leave the question open a bit longer in case someone has an answer about using `makeGooglePlayServicesAvailable`. What else are you suggesting to check? The documentation you linked is only about checking availability. – stardt Aug 07 '17 at 12:33
  • In case it helps someone, the solution to getting `E/WindowManager: android.view.WindowLeaked: Activity com.example.MainScreen has leaked window...` is to not run `checkGPS()` in `onCreate` though the [documentation](https://firebase.google.com/docs/cloud-messaging/android/client) says: "It is recommended to do this in two places: in the main activity's onCreate() method, and in its onResume() method." – stardt Aug 07 '17 at 12:45
  • @stardt I updated the answer. For additional things to check, I was just thinking about checking for network availability since that will be needed to update GPS. I am curious about `makeGooglePlayServicesAvailable`, though. It looks like it starts a task to check for GPS. What is the use case for this that differs from what I describe in the answer? – Cheticamp Aug 07 '17 at 12:57
  • @stardt I am thinking that the "task" that "already complete" is the task created by `makeGooglePlayServicesAvailable`. Android thinks that it is still around ready to be terminated when `onDestroy()` is invoked. I was also thinking that there was some additional work to be done after the call to the failure listener that `finish()` was preventing. I posted a delayed runnable on the main thread from the failure listener to do the "finish" to see if that would allow the cleanup, but the results were the same. Maybe someone has an idea about how to clean up that task. – Cheticamp Aug 07 '17 at 15:31
  • Your code matches my use case. I had thought that `makeGooglePlayServicesAvailable` could be used to simplify the code. – stardt Aug 07 '17 at 22:19
  • @stardt I took another quick look at this issue. Even if I remove the call to `finish()` and let the app display a blank screen, I still see the crash if I press the back button. Looks like you are having [this issue](https://github.com/microg/android_external_GmsLib/issues/9). Their resolution was to do something with the splash screen. – Cheticamp Aug 07 '17 at 23:00
  • The splash screen [solution](https://stackoverflow.com/a/42589636/870928) doesn't use `makeGooglePlayServicesAvailable`. The link you mentioned says an upgrade resolves this issue but I can't check this at the moment. – stardt Aug 07 '17 at 23:28
0

Found this snippet from https://www.codota.com/code/java/methods/com.google.android.gms.common.GoogleApiAvailability/makeGooglePlayServicesAvailable

public static void checkPlayServices(Activity activity) {
  final GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance();
  int errorCode = apiAvailability.isGooglePlayServicesAvailable(activity);
  if (errorCode != ConnectionResult.SUCCESS) {
    if (apiAvailability.isUserResolvableError(errorCode)) {
      apiAvailability.makeGooglePlayServicesAvailable(activity);
    } else {
      Toast.makeText(activity, R.string.push_error_device_not_compatible, Toast.LENGTH_SHORT).show();
    }
  }
}
arango_86
  • 4,236
  • 4
  • 40
  • 46