/* * Copyright (C) 2013-2017 microG Project Team * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.android.gms.common; import static com.google.android.gms.common.ConnectionResult.INTERNAL_ERROR; import static com.google.android.gms.common.ConnectionResult.INVALID_ACCOUNT; import static com.google.android.gms.common.ConnectionResult.NETWORK_ERROR; import static com.google.android.gms.common.ConnectionResult.RESOLUTION_REQUIRED; import static com.google.android.gms.common.ConnectionResult.SERVICE_DISABLED; import static com.google.android.gms.common.ConnectionResult.SERVICE_INVALID; import static com.google.android.gms.common.ConnectionResult.SERVICE_MISSING; import static com.google.android.gms.common.ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED; import static com.google.android.gms.common.ConnectionResult.SIGN_IN_REQUIRED; import static com.google.android.gms.common.ConnectionResult.SUCCESS; import android.app.Activity; import android.app.Dialog; import android.app.PendingIntent; import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; import android.util.Log; import androidx.fragment.app.FragmentActivity; import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener; import com.google.android.gms.tasks.Task; import com.google.android.gms.tasks.Tasks; import org.microg.gms.common.Constants; import org.microg.gms.common.PublicApi; @PublicApi public class GoogleApiAvailability { private static final String TAG = "GmsApiAvailability"; /** * Package name for Google Play services. */ public static final String GOOGLE_PLAY_SERVICES_PACKAGE = Constants.GMS_PACKAGE_NAME; /** * Google Play services client library version (declared in library's AndroidManifest.xml android:versionCode). */ public static final int GOOGLE_PLAY_SERVICES_VERSION_CODE = Constants.GMS_VERSION_CODE; private static GoogleApiAvailability instance; private GoogleApiAvailability() { } /** * Returns the singleton instance of GoogleApiAvailability. */ public static GoogleApiAvailability getInstance() { if (instance == null) { synchronized (GoogleApiAvailability.class) { if (instance == null) { instance = new GoogleApiAvailability(); } } } return instance; } /** * Returns a dialog to address the provided errorCode. The returned dialog displays a localized * message about the error and upon user confirmation (by tapping on dialog) will direct them * to the Play Store if Google Play services is out of date or missing, or to system settings * if Google Play services is disabled on the device. * * @param activity parent activity for creating the dialog, also used for identifying language to display dialog in. * @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call. * If errorCode is {@link ConnectionResult#SUCCESS} then null is returned. * @param requestCode The requestCode given when calling startActivityForResult. */ public Dialog getErrorDialog(Activity activity, int errorCode, int requestCode) { return getErrorDialog(activity, errorCode, requestCode, null); } /** * Returns a dialog to address the provided errorCode. The returned dialog displays a localized * message about the error and upon user confirmation (by tapping on dialog) will direct them * to the Play Store if Google Play services is out of date or missing, or to system settings * if Google Play services is disabled on the device. * * @param activity parent activity for creating the dialog, also used for identifying language to display dialog in. * @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call. * If errorCode is {@link ConnectionResult#SUCCESS} then null is returned. * @param requestCode The requestCode given when calling startActivityForResult. * @param cancelListener The {@link DialogInterface.OnCancelListener} to invoke if the dialog is canceled. */ public Dialog getErrorDialog(Activity activity, int errorCode, int requestCode, DialogInterface.OnCancelListener cancelListener) { // TODO return null; } /** * Returns a PendingIntent to address the provided connection failure. *

* If {@link ConnectionResult#hasResolution()} is true, then {@link ConnectionResult#getResolution()} * will be returned. Otherwise, the returned PendingIntent will direct the user to either the * Play Store if Google Play services is out of date or missing, or system settings if Google * Play services is disabled on the device. * * @param context parent context for creating the PendingIntent. * @param result the connection failure. If successful or the error is not resolvable by the user, null is returned. */ public PendingIntent getErrorResolutionPendingIntent(Context context, ConnectionResult result) { if (result.hasResolution()) { return result.getResolution(); } return getErrorResolutionPendingIntent(context, result.getErrorCode(), 0); } /** * Returns a PendingIntent to address the provided errorCode. It will direct the user to either * the Play Store if Google Play services is out of date or missing, or system settings if * Google Play services is disabled on the device. * * @param context parent context for creating the PendingIntent. * @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call. * If errorCode is {@link ConnectionResult#SUCCESS} then null is returned. * @param requestCode The requestCode given when calling startActivityForResult. */ public PendingIntent getErrorResolutionPendingIntent(Context context, int errorCode, int requestCode) { // TODO return null; } /** * Returns a human-readable string of the error code returned from {@link #isGooglePlayServicesAvailable(Context)}. */ public final String getErrorString(int errorCode) { return ConnectionResult.getStatusString(errorCode); } /** * Verifies that Google Play services is installed and enabled on this device, and that the * version installed on this device is no older than the one required by this client. * * @return status code indicating whether there was an error. Can be one of following in * {@link ConnectionResult}: SUCCESS, SERVICE_MISSING, SERVICE_UPDATING, * SERVICE_VERSION_UPDATE_REQUIRED, SERVICE_DISABLED, SERVICE_INVALID */ public int isGooglePlayServicesAvailable(Context context) { Log.d(TAG, "As we can't know right now if the later desired feature is available, " + "we just pretend it to be."); return SUCCESS; } /** * Verifies that Google Play services is installed and enabled on this device, and that the version installed on * this device is no older than the one required by this client or the version is not older than the one specified * in minApkVersion. * * @return status code indicating whether there was an error. Can be one of following in * {@link ConnectionResult}: SUCCESS, SERVICE_MISSING, SERVICE_UPDATING, * SERVICE_VERSION_UPDATE_REQUIRED, SERVICE_DISABLED, SERVICE_INVALID */ public int isGooglePlayServicesAvailable(Context context, int minApkVersion) { return isGooglePlayServicesAvailable(context); } /** * Determines whether an error can be resolved via user action. If true, proceed by calling * {@link #getErrorDialog(Activity, int, int)} and showing the dialog. * * @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)}, or * returned to your application via {@link OnConnectionFailedListener#onConnectionFailed(ConnectionResult)} * @return true if the error is resolvable with {@link #getErrorDialog(Activity, int, int)} */ public final boolean isUserResolvableError(int errorCode) { switch (errorCode) { case SERVICE_MISSING: case SERVICE_VERSION_UPDATE_REQUIRED: case SERVICE_DISABLED: case SERVICE_INVALID: return true; case SIGN_IN_REQUIRED: case INVALID_ACCOUNT: case RESOLUTION_REQUIRED: case NETWORK_ERROR: case INTERNAL_ERROR: default: return false; } } /** * Attempts to make Google Play services available on this device. If Play Services is already * available, the returned {@link Task} may complete immediately. *

* If it is necessary to display UI in order to complete this request (e.g. sending the user * to the Google Play store) the passed {@link Activity} will be used to display this UI. *

* It is recommended to call this method from {@link Activity#onCreate(Bundle)}. * If the passed {@link Activity} completes before the returned {@link Task} completes, the * Task will fail with a {@link java.util.concurrent.CancellationException}. *

* This method must be called from the main thread. * * @return A {@link Task}. If this Task completes without throwing an exception, Play Services * is available on this device. */ public Task makeGooglePlayServicesAvailable(Activity activity) { int status = isGooglePlayServicesAvailable(activity); if (status == SUCCESS) { return Tasks.forResult(null); } // TODO return Tasks.forResult(null); } /** * Displays a DialogFragment for an error code returned by {@link #isGooglePlayServicesAvailable(Context)}. * * @param activity parent activity for creating the dialog, also used for identifying language to display dialog in. * @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call. * If errorCode is {@link ConnectionResult#SUCCESS} then null is returned. * @param requestCode The requestCode given when calling startActivityForResult. * @return true if the dialog is shown, false otherwise * @throws RuntimeException if API level is below 11 and activity is not a {@link FragmentActivity}. * @see ErrorDialogFragment * @see SupportErrorDialogFragmet */ public boolean showErrorDialogFragment(Activity activity, int errorCode, int requestCode) { return showErrorDialogFragment(activity, errorCode, requestCode, null); } /** * Displays a DialogFragment for an error code returned by {@link #isGooglePlayServicesAvailable(Context)}. * * @param activity parent activity for creating the dialog, also used for identifying language to display dialog in. * @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call. * If errorCode is {@link ConnectionResult#SUCCESS} then null is returned. * @param requestCode The requestCode given when calling startActivityForResult. * @param cancelListener The {@link DialogInterface.OnCancelListener} to invoke if the dialog is canceled. * @return true if the dialog is shown, false otherwise * @throws RuntimeException if API level is below 11 and activity is not a {@link FragmentActivity}. * @see ErrorDialogFragment * @see SupportErrorDialogFragmet */ public boolean showErrorDialogFragment(Activity activity, int errorCode, int requestCode, DialogInterface.OnCancelListener cancelListener) { Dialog dialog = getErrorDialog(activity, errorCode, requestCode, cancelListener); if (dialog == null) { return false; } else { // TODO return false; } } /** * Displays a notification for an error code returned from * {@link #isGooglePlayServicesAvailable(Context)}, if it is resolvable by the user. *

* This method is similar to {@link #getErrorDialog(int, android.app.Activity, int)}, but is * provided for background tasks that cannot or should not display dialogs. * * @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call. * If errorCode is {@link ConnectionResult#SUCCESS} then null is returned. * @param context used for identifying language to display dialog in as well as accessing the * {@link android.app.NotificationManager}. */ public void showErrorNotification(Context context, int errorCode) { if (errorCode == RESOLUTION_REQUIRED) { Log.e(TAG, "showErrorNotification(context, errorCode) is called for RESOLUTION_REQUIRED when showErrorNotification(context, result) should be called"); } if (isUserResolvableError(errorCode)) { GooglePlayServicesUtil.showErrorNotification(errorCode, context); } } /** * Displays a notification for a connection failure, if it is resolvable by the user. * * @param context The calling context used to display the notification. * @param result The connection failure. If successful or the error is not resolvable by the * user, no notification is shown. */ public void showErrorNotification(Context context, ConnectionResult result) { PendingIntent pendingIntent = getErrorResolutionPendingIntent(context, result); if (pendingIntent != null) { // TODO } } }