diff --git a/play-services-api/build.gradle b/play-services-api/build.gradle index 9b97d190..3e899acf 100644 --- a/play-services-api/build.gradle +++ b/play-services-api/build.gradle @@ -51,8 +51,8 @@ android { } dependencies { + compile project(':play-services-basement') compile project(':play-services-cast-api') - compile project(':play-services-common-api') compile project(':play-services-iid-api') compile project(':play-services-location-api') compile project(':play-services-wearable-api') diff --git a/play-services-common-api/build.gradle b/play-services-basement/build.gradle similarity index 97% rename from play-services-common-api/build.gradle rename to play-services-basement/build.gradle index f43af0a4..796ea36c 100644 --- a/play-services-common-api/build.gradle +++ b/play-services-basement/build.gradle @@ -56,5 +56,6 @@ android { } dependencies { + compile 'com.android.support:support-v4:23.4.0' compile project(':safe-parcel') } diff --git a/play-services-common-api/src/main/AndroidManifest.xml b/play-services-basement/src/main/AndroidManifest.xml similarity index 100% rename from play-services-common-api/src/main/AndroidManifest.xml rename to play-services-basement/src/main/AndroidManifest.xml diff --git a/play-services-common-api/src/main/aidl/com/google/android/gms/common/api/Status.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/api/Status.aidl similarity index 100% rename from play-services-common-api/src/main/aidl/com/google/android/gms/common/api/Status.aidl rename to play-services-basement/src/main/aidl/com/google/android/gms/common/api/Status.aidl diff --git a/play-services-common-api/src/main/aidl/com/google/android/gms/common/data/DataHolder.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/data/DataHolder.aidl similarity index 100% rename from play-services-common-api/src/main/aidl/com/google/android/gms/common/data/DataHolder.aidl rename to play-services-basement/src/main/aidl/com/google/android/gms/common/data/DataHolder.aidl diff --git a/play-services-common-api/src/main/aidl/com/google/android/gms/common/internal/GetServiceRequest.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/GetServiceRequest.aidl similarity index 100% rename from play-services-common-api/src/main/aidl/com/google/android/gms/common/internal/GetServiceRequest.aidl rename to play-services-basement/src/main/aidl/com/google/android/gms/common/internal/GetServiceRequest.aidl diff --git a/play-services-common-api/src/main/aidl/com/google/android/gms/common/internal/ICancelToken.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/ICancelToken.aidl similarity index 100% rename from play-services-common-api/src/main/aidl/com/google/android/gms/common/internal/ICancelToken.aidl rename to play-services-basement/src/main/aidl/com/google/android/gms/common/internal/ICancelToken.aidl diff --git a/play-services-common-api/src/main/aidl/com/google/android/gms/common/internal/IGmsCallbacks.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/IGmsCallbacks.aidl similarity index 100% rename from play-services-common-api/src/main/aidl/com/google/android/gms/common/internal/IGmsCallbacks.aidl rename to play-services-basement/src/main/aidl/com/google/android/gms/common/internal/IGmsCallbacks.aidl diff --git a/play-services-common-api/src/main/aidl/com/google/android/gms/common/internal/IGmsServiceBroker.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/IGmsServiceBroker.aidl similarity index 100% rename from play-services-common-api/src/main/aidl/com/google/android/gms/common/internal/IGmsServiceBroker.aidl rename to play-services-basement/src/main/aidl/com/google/android/gms/common/internal/IGmsServiceBroker.aidl diff --git a/play-services-common-api/src/main/aidl/com/google/android/gms/common/internal/ISignInButtonCreator.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/ISignInButtonCreator.aidl similarity index 100% rename from play-services-common-api/src/main/aidl/com/google/android/gms/common/internal/ISignInButtonCreator.aidl rename to play-services-basement/src/main/aidl/com/google/android/gms/common/internal/ISignInButtonCreator.aidl diff --git a/play-services-common-api/src/main/aidl/com/google/android/gms/common/internal/ValidateAccountRequest.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/internal/ValidateAccountRequest.aidl similarity index 100% rename from play-services-common-api/src/main/aidl/com/google/android/gms/common/internal/ValidateAccountRequest.aidl rename to play-services-basement/src/main/aidl/com/google/android/gms/common/internal/ValidateAccountRequest.aidl diff --git a/play-services-common-api/src/main/aidl/com/google/android/gms/common/server/FavaDiagnosticsEntity.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/common/server/FavaDiagnosticsEntity.aidl similarity index 100% rename from play-services-common-api/src/main/aidl/com/google/android/gms/common/server/FavaDiagnosticsEntity.aidl rename to play-services-basement/src/main/aidl/com/google/android/gms/common/server/FavaDiagnosticsEntity.aidl diff --git a/play-services-common-api/src/main/aidl/com/google/android/gms/dynamic/IObjectWrapper.aidl b/play-services-basement/src/main/aidl/com/google/android/gms/dynamic/IObjectWrapper.aidl similarity index 100% rename from play-services-common-api/src/main/aidl/com/google/android/gms/dynamic/IObjectWrapper.aidl rename to play-services-basement/src/main/aidl/com/google/android/gms/dynamic/IObjectWrapper.aidl diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/ConnectionResult.java b/play-services-basement/src/main/java/com/google/android/gms/common/ConnectionResult.java new file mode 100644 index 00000000..f6f75c1a --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/ConnectionResult.java @@ -0,0 +1,310 @@ +/* + * Copyright 2013-2015 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 android.app.Activity; +import android.app.PendingIntent; +import android.content.Intent; +import android.content.IntentSender; +import android.text.TextUtils; + +import com.google.android.gms.common.api.GoogleApiClient; + +import java.util.Arrays; + +/** + * Contains all possible error codes for when a client fails to connect to Google Play services. + * These error codes are used by {@link GoogleApiClient.OnConnectionFailedListener}. + */ +public class ConnectionResult { + /** + * The connection was successful. + */ + public static final int SUCCESS = 0; + /** + * Google Play services is missing on this device. The calling activity should pass this error + * code to {@link GooglePlayServicesUtil#getErrorDialog(int, Activity, int)} to get a localized + * error dialog that will resolve the error when shown. + */ + public static final int SERVICE_MISSING = 1; + /** + * The installed version of Google Play services is out of date. The calling activity should + * pass this error code to {@link GooglePlayServicesUtil#getErrorDialog(int, Activity, int)} to + * get a localized error dialog that will resolve the error when shown. + */ + public static final int SERVICE_VERSION_UPDATE_REQUIRED = 2; + /** + * The installed version of Google Play services has been disabled on this device. The calling + * activity should pass this error code to + * {@link GooglePlayServicesUtil#getErrorDialog(int, Activity, int)} to get a localized error + * dialog that will resolve the error when shown. + */ + public static final int SERVICE_DISABLED = 3; + /** + * The client attempted to connect to the service but the user is not signed in. The client may + * choose to continue without using the API or it may call + * {@link #startResolutionForResult(Activity, int)} to prompt the user to sign in. After the + * sign in activity returns with {@link Activity#RESULT_OK} further attempts to connect should + * succeed. + */ + public static final int SIGN_IN_REQUIRED = 4; + /** + * The client attempted to connect to the service with an invalid account name specified. + */ + public static final int INVALID_ACCOUNT = 5; + /** + * Completing the connection requires some form of resolution. A resolution will be available + * to be started with {@link #startResolutionForResult(Activity, int)}. If the result returned + * is {@link Activity#RESULT_OK}, then further attempts to connect should either complete or + * continue on to the next issue that needs to be resolved. + */ + public static final int RESOLUTION_REQUIRED = 6; + /** + * A network error occurred. Retrying should resolve the problem. + */ + public static final int NETWORK_ERROR = 7; + /** + * An internal error occurred. Retrying should resolve the problem. + */ + public static final int INTERNAL_ERROR = 8; + /** + * The version of the Google Play services installed on this device is not authentic. + */ + public static final int SERVICE_INVALID = 9; + /** + * The application is misconfigured. This error is not recoverable and will be treated as + * fatal. The developer should look at the logs after this to determine more actionable + * information. + */ + public static final int DEVELOPER_ERROR = 10; + /** + * The application is not licensed to the user. This error is not recoverable and will be + * treated as fatal. + */ + public static final int LICENSE_CHECK_FAILED = 11; + /** + * The client canceled the connection by calling {@link GoogleApiClient#disconnect()}. + * Only returned by {@link GoogleApiClient#blockingConnect()}. + */ + public static final int CANCELED = 13; + /** + * The timeout was exceeded while waiting for the connection to complete. Only returned by + * {@link GoogleApiClient#blockingConnect()}. + */ + public static final int TIMEOUT = 14; + /** + * An interrupt occurred while waiting for the connection complete. Only returned by + * {@link GoogleApiClient#blockingConnect()}. + */ + public static final int INTERRUPTED = 15; + /** + * One of the API components you attempted to connect to is not available. The API will not + * work on this device, and updating Google Play services will not likely solve the problem. + * Using the API on the device should be avoided. + */ + public static final int API_UNAVAILABLE = 16; + + /** + * The Drive API requires external storage (such as an SD card), but no external storage is + * mounted. This error is recoverable if the user installs external storage (if none is + * present) and ensures that it is mounted (which may involve disabling USB storage mode, + * formatting the storage, or other initialization as required by the device). + *

+ * This error should never be returned on a device with emulated external storage. On devices + * with emulated external storage, the emulated "external storage" is always present regardless + * of whether the device also has removable storage. + */ + @Deprecated + public static final int DRIVE_EXTERNAL_STORAGE_REQUIRED = 1500; + + private final int statusCode; + private final PendingIntent pendingIntent; + private final String message; + + /** + * Creates a connection result. + * + * @param statusCode The status code. + */ + public ConnectionResult(int statusCode) { + this(statusCode, null); + } + + /** + * Creates a connection result. + * + * @param statusCode The status code. + * @param pendingIntent A pending intent that will resolve the issue when started, or null. + */ + public ConnectionResult(int statusCode, PendingIntent pendingIntent) { + this(statusCode, pendingIntent, getStatusString(statusCode)); + } + + /** + * Creates a connection result. + * + * @param statusCode The status code. + * @param pendingIntent A pending intent that will resolve the issue when started, or null. + * @param message An additional error message for the connection result, or null. + */ + public ConnectionResult(int statusCode, PendingIntent pendingIntent, String message) { + this.statusCode = statusCode; + this.pendingIntent = pendingIntent; + this.message = message; + } + + static String getStatusString(int statusCode) { + switch (statusCode) { + case -1: + return "UNKNOWN"; + case 0: + return "SUCCESS"; + case 1: + return "SERVICE_MISSING"; + case 2: + return "SERVICE_VERSION_UPDATE_REQUIRED"; + case 3: + return "SERVICE_DISABLED"; + case 4: + return "SIGN_IN_REQUIRED"; + case 5: + return "INVALID_ACCOUNT"; + case 6: + return "RESOLUTION_REQUIRED"; + case 7: + return "NETWORK_ERROR"; + case 8: + return "INTERNAL_ERROR"; + case 9: + return "SERVICE_INVALID"; + case 10: + return "DEVELOPER_ERROR"; + case 11: + return "LICENSE_CHECK_FAILED"; + case 13: + return "CANCELED"; + case 14: + return "TIMEOUT"; + case 15: + return "INTERRUPTED"; + case 16: + return "API_UNAVAILABLE"; + case 17: + return "SIGN_IN_FAILED"; + case 18: + return "SERVICE_UPDATING"; + case 19: + return "SERVICE_MISSING_PERMISSION"; + case 20: + return "RESTRICTED_PROFILE"; + case 21: + return "API_VERSION_UPDATE_REQUIRED"; + case 42: + return "UPDATE_ANDROID_WEAR"; + case 99: + return "UNFINISHED"; + case 1500: + return "DRIVE_EXTERNAL_STORAGE_REQUIRED"; + default: + return "UNKNOWN_ERROR_CODE(" + statusCode + ")"; + } + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } else if (!(o instanceof ConnectionResult)) { + return false; + } else { + ConnectionResult r = (ConnectionResult)o; + return statusCode == r.statusCode && pendingIntent == null ? r.pendingIntent == null : pendingIntent.equals(r.pendingIntent) && TextUtils.equals(message, r.message); + } + } + + /** + * Indicates the type of error that interrupted connection. + * + * @return the error code, or {@link #SUCCESS} if no error occurred. + */ + public int getErrorCode() { + return statusCode; + } + + /** + * Returns an error message for connection result. + * + * @return the message + */ + public String getErrorMessage() { + return message; + } + + /** + * A pending intent to resolve the connection failure. This intent can be started with + * {@link Activity#startIntentSenderForResult(IntentSender, int, Intent, int, int, int)} to + * present UI to solve the issue. + * + * @return The pending intent to resolve the connection failure. + */ + public PendingIntent getResolution() { + return pendingIntent; + } + + @Override + public int hashCode() { + return Arrays.hashCode(new Object[]{statusCode, pendingIntent, message}); + } + + /** + * Returns {@code true} if calling {@link #startResolutionForResult(Activity, int)} will start + * any intents requiring user interaction. + * + * @return {@code true} if there is a resolution that can be started. + */ + public boolean hasResolution() { + return statusCode != 0 && pendingIntent != null; + } + + /** + * Returns {@code true} if the connection was successful. + * + * @return {@code true} if the connection was successful, {@code false} if there was an error. + */ + public boolean isSuccess() { + return statusCode == 0; + } + + /** + * Resolves an error by starting any intents requiring user interaction. See + * {@link #SIGN_IN_REQUIRED}, and {@link #RESOLUTION_REQUIRED}. + * + * @param activity An Activity context to use to resolve the issue. The activity's + * {@link Activity#onActivityResult} method will be invoked after the user + * is done. If the resultCode is {@link Activity#RESULT_OK}, the application + * should try to connect again. + * @param requestCode The request code to pass to {@link Activity#onActivityResult}. + * @throws IntentSender.SendIntentException If the resolution intent has been canceled or is no + * longer able to execute the request. + */ + public void startResolutionForResult(Activity activity, int requestCode) throws + IntentSender.SendIntentException { + if (hasResolution()) { + activity.startIntentSenderForResult(pendingIntent.getIntentSender(), requestCode, null, 0, 0, 0); + } + } +} diff --git a/play-services-common-api/src/main/java/com/google/android/gms/common/Scopes.java b/play-services-basement/src/main/java/com/google/android/gms/common/Scopes.java similarity index 100% rename from play-services-common-api/src/main/java/com/google/android/gms/common/Scopes.java rename to play-services-basement/src/main/java/com/google/android/gms/common/Scopes.java diff --git a/play-services-common-api/src/main/java/com/google/android/gms/common/api/AccountInfo.java b/play-services-basement/src/main/java/com/google/android/gms/common/api/AccountInfo.java similarity index 100% rename from play-services-common-api/src/main/java/com/google/android/gms/common/api/AccountInfo.java rename to play-services-basement/src/main/java/com/google/android/gms/common/api/AccountInfo.java diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/api/Api.java b/play-services-basement/src/main/java/com/google/android/gms/common/api/Api.java new file mode 100644 index 00000000..1b0f03f2 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/api/Api.java @@ -0,0 +1,84 @@ +/* + * Copyright 2013-2015 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.api; + +import org.microg.gms.common.PublicApi; +import org.microg.gms.common.api.ApiBuilder; + +/** + * Describes a section of the Google Play Services API that should be made available. Instances of + * this should be passed into {@link GoogleApiClient.Builder#addApi(Api)} to enable the appropriate + * parts of Google Play Services. + *

+ * Google APIs are partitioned into sections which allow your application to configure only the + * services it requires. Each Google API provides an API object which can be passed to + * {@link GoogleApiClient.Builder#addApi(Api)} in order to configure and enable that functionality + * in your {@link GoogleApiClient} instance. + *

+ * See {@link GoogleApiClient.Builder} for usage examples. + */ +@PublicApi +public final class Api { + + private final ApiBuilder builder; + + @PublicApi(exclude = true) + public Api(ApiBuilder builder) { + this.builder = builder; + } + + @PublicApi(exclude = true) + public ApiBuilder getBuilder() { + return builder; + } + + /** + * Base interface for API options. These are used to configure specific parameters for + * individual API surfaces. The default implementation has no parameters. + */ + @PublicApi + public interface ApiOptions { + /** + * Base interface for {@link ApiOptions} in {@link Api}s that have options. + */ + @PublicApi + interface HasOptions extends ApiOptions { + } + + /** + * Base interface for {@link ApiOptions} that are not required, don't exist. + */ + @PublicApi + interface NotRequiredOptions extends ApiOptions { + } + + /** + * {@link ApiOptions} implementation for {@link Api}s that do not take any options. + */ + @PublicApi + final class NoOptions implements NotRequiredOptions { + } + + /** + * Base interface for {@link ApiOptions} that are optional. + */ + @PublicApi + interface Optional extends HasOptions, NotRequiredOptions { + } + } + +} diff --git a/play-services-common-api/src/main/java/com/google/android/gms/common/api/CommonStatusCodes.java b/play-services-basement/src/main/java/com/google/android/gms/common/api/CommonStatusCodes.java similarity index 100% rename from play-services-common-api/src/main/java/com/google/android/gms/common/api/CommonStatusCodes.java rename to play-services-basement/src/main/java/com/google/android/gms/common/api/CommonStatusCodes.java diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java b/play-services-basement/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java new file mode 100644 index 00000000..87b05d6b --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java @@ -0,0 +1,494 @@ +/* + * Copyright 2013-2015 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.api; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.support.v4.app.FragmentActivity; +import android.view.View; + +import com.google.android.gms.common.ConnectionResult; + +import org.microg.gms.auth.AuthConstants; +import org.microg.gms.common.PublicApi; +import org.microg.gms.common.api.GoogleApiClientImpl; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + * The main entry point for Google Play services integration. + *

+ * GoogleApiClient is used with a variety of static methods. Some of these methods require that + * GoogleApiClient be connected, some will queue up calls before GoogleApiClient is connected; + * check the specific API documentation to determine whether you need to be connected. + *

+ * Before any operation is executed, the GoogleApiClient must be connected using the + * {@link #connect()} method. The client is not considered connected until the + * {@link ConnectionCallbacks#onConnected(Bundle)} callback has been called. + *

+ * When your app is done using this client, call {@link #disconnect()}, even if the async result + * from {@link #connect()} has not yet been delivered. + *

+ * You should instantiate a client object in your Activity's {@link Activity#onCreate(Bundle)} + * method and then call {@link #connect()} in {@link Activity#onStart()} and {@link #disconnect()} + * in {@link Activity#onStop()}, regardless of the state. + */ +@PublicApi +public interface GoogleApiClient { + /** + * Connects the client to Google Play services. Blocks until the connection either succeeds or + * fails. This is not allowed on the UI thread. + * + * @return the result of the connection + */ + ConnectionResult blockingConnect(); + + /** + * Connects the client to Google Play services. Blocks until the connection is set or failed or + * has timed out. This is not allowed on the UI thread. + * + * @param timeout the maximum time to wait + * @param unit the time unit of the {@code timeout} argument + * @return the result of the connection + */ + ConnectionResult blockingConnect(long timeout, TimeUnit unit); + + /** + * Clears the account selected by the user and reconnects the client asking the user to pick an + * account again if {@link Builder#useDefaultAccount()} was set. + * + * @return the pending result is fired once the default account has been cleared, but before + * the client is reconnected - for that {@link ConnectionCallbacks} can be used. + */ + PendingResult clearDefaultAccountAndReconnect(); + + /** + * Connects the client to Google Play services. This method returns immediately, and connects + * to the service in the background. If the connection is successful, + * {@link ConnectionCallbacks#onConnected(Bundle)} is called and enqueued items are executed. + * On a failure, {@link OnConnectionFailedListener#onConnectionFailed(ConnectionResult)} is + * called. + */ + void connect(); + + /** + * Closes the connection to Google Play services. No calls can be made using this client after + * calling this method. Any method calls that haven't executed yet will be canceled. That is + * {@link ResultCallback#onResult(Result)} won't be called, if connection to the service hasn't + * been established yet all calls already made will be canceled. + * + * @see #connect() + */ + void disconnect(); + + /** + * Checks if the client is currently connected to the service, so that requests to other + * methods will succeed. Applications should guard client actions caused by the user with a + * call to this method. + * + * @return {@code true} if the client is connected to the service. + */ + boolean isConnected(); + + /** + * Checks if the client is attempting to connect to the service. + * + * @return {@code true} if the client is attempting to connect to the service. + */ + boolean isConnecting(); + + /** + * Returns {@code true} if the specified listener is currently registered to receive connection + * events. + * + * @param listener The listener to check for. + * @return {@code true} if the specified listener is currently registered to receive connection + * events. + * @see #registerConnectionCallbacks(ConnectionCallbacks) + * @see #unregisterConnectionCallbacks(ConnectionCallbacks) + */ + boolean isConnectionCallbacksRegistered(ConnectionCallbacks listener); + + /** + * Returns {@code true} if the specified listener is currently registered to receive connection + * failed events. + * + * @param listener The listener to check for. + * @return {@code true} if the specified listener is currently registered to receive connection + * failed events. + * @see #registerConnectionFailedListener(OnConnectionFailedListener) + * @see #unregisterConnectionFailedListener(OnConnectionFailedListener) + */ + public boolean isConnectionFailedListenerRegistered(OnConnectionFailedListener listener); + + /** + * Closes the current connection to Google Play services and creates a new connection. + *

+ * This method closes the current connection then returns immediately and reconnects to the + * service in the background. + *

+ * After calling this method, your application will receive + * {@link ConnectionCallbacks#onConnected(Bundle)} if the connection is successful, or + * {@link OnConnectionFailedListener#onConnectionFailed(ConnectionResult)} if the connection + * failed. + * + * @see #connect() + * @see #disconnect() + */ + void reconnect(); + + /** + * Registers a listener to receive connection events from this {@link GoogleApiClient}. If the + * service is already connected, the listener's {@link ConnectionCallbacks#onConnected(Bundle)} + * method will be called immediately. Applications should balance calls to this method with + * calls to {@link #unregisterConnectionCallbacks(ConnectionCallbacks)} to avoid leaking + * resources. + *

+ * If the specified listener is already registered to receive connection events, this method + * will not add a duplicate entry for the same listener, but will still call the listener's + * {@link ConnectionCallbacks#onConnected(Bundle)} method if currently connected. + *

+ * Note that the order of messages received here may not be stable, so clients should not rely + * on the order that multiple listeners receive events in. + * + * @param listener the listener where the results of the asynchronous {@link #connect()} call + * are delivered. + */ + void registerConnectionCallbacks(ConnectionCallbacks listener); + + /** + * Registers a listener to receive connection failed events from this {@link GoogleApiClient}. + * Unlike {@link #registerConnectionCallbacks(ConnectionCallbacks)}, if the service is not + * already connected, the listener's + * {@link OnConnectionFailedListener#onConnectionFailed(ConnectionResult)} method will not be + * called immediately. Applications should balance calls to this method with calls to + * {@link #unregisterConnectionFailedListener(OnConnectionFailedListener)} to avoid leaking + * resources. + *

+ * If the specified listener is already registered to receive connection failed events, this + * method will not add a duplicate entry for the same listener. + *

+ * Note that the order of messages received here may not be stable, so clients should not rely + * on the order that multiple listeners receive events in. + * + * @param listener the listener where the results of the asynchronous {@link #connect()} call + * are delivered. + */ + public void registerConnectionFailedListener(OnConnectionFailedListener listener); + + /** + * Disconnects the client and stops automatic lifecycle management. Use this before creating a + * new client (which might be necessary when switching accounts, changing the set of used APIs + * etc.). + *

+ * This method must be called from the main thread. + * + * @param lifecycleActivity the activity managing the client's lifecycle. + * @throws IllegalStateException if called from outside of the main thread. + * @see Builder#enableAutoManage(FragmentActivity, int, OnConnectionFailedListener) + */ + void stopAutoManager(FragmentActivity lifecycleActivity) throws IllegalStateException; + + /** + * Removes a connection listener from this {@link GoogleApiClient}. Note that removing a + * listener does not generate any callbacks. + *

+ * If the specified listener is not currently registered to receive connection events, this + * method will have no effect. + * + * @param listener the listener to unregister. + */ + void unregisterConnectionCallbacks(ConnectionCallbacks listener); + + /** + * Removes a connection failed listener from the {@link GoogleApiClient}. Note that removing a + * listener does not generate any callbacks. + *

+ * If the specified listener is not currently registered to receive connection failed events, + * this method will have no effect. + * + * @param listener the listener to unregister. + */ + void unregisterConnectionFailedListener(OnConnectionFailedListener listener); + + /** + * Builder to configure a {@link GoogleApiClient}. + */ + @PublicApi + class Builder { + private final Context context; + private final Map apis = new HashMap(); + private final Set connectionCallbacks = new HashSet(); + private final Set connectionFailedListeners = new HashSet(); + private final Set scopes = new HashSet(); + private String accountName; + private int clientId = -1; + private FragmentActivity fragmentActivity; + private Looper looper; + private int gravityForPopups; + private OnConnectionFailedListener unresolvedConnectionFailedListener; + private View viewForPopups; + + /** + * Builder to help construct the {@link GoogleApiClient} object. + * + * @param context The context to use for the connection. + */ + public Builder(Context context) { + this.context = context; + this.looper = context.getMainLooper(); + } + + /** + * Builder to help construct the {@link GoogleApiClient} object. + * + * @param context The context to use for the connection. + * @param connectedListener The listener where the results of the asynchronous + * {@link #connect()} call are delivered. + * @param connectionFailedListener The listener which will be notified if the connection + * attempt fails. + */ + public Builder(Context context, ConnectionCallbacks connectedListener, + OnConnectionFailedListener connectionFailedListener) { + this(context); + addConnectionCallbacks(connectedListener); + addOnConnectionFailedListener(connectionFailedListener); + } + + /** + * Specify which Apis are requested by your app. See {@link Api} for more information. + * + * @param api The Api requested by your app. + * @param options Any additional parameters required for the specific AP + * @see Api + */ + public Builder addApi(Api api, O options) { + apis.put(api, options); + return this; + } + + /** + * Specify which Apis are requested by your app. See {@link Api} for more information. + * + * @param api The Api requested by your app. + * @see Api + */ + public Builder addApi(Api api) { + apis.put(api, null); + return this; + } + + /** + * Registers a listener to receive connection events from this {@link GoogleApiClient}. + * Applications should balance calls to this method with calls to + * {@link #unregisterConnectionCallbacks(ConnectionCallbacks)} to avoid + * leaking resources. + *

+ * If the specified listener is already registered to receive connection events, this + * method will not add a duplicate entry for the same listener. + *

+ * Note that the order of messages received here may not be stable, so clients should not + * rely on the order that multiple listeners receive events in. + * + * @param listener the listener where the results of the asynchronous {@link #connect()} + * call are delivered. + */ + public Builder addConnectionCallbacks(ConnectionCallbacks listener) { + connectionCallbacks.add(listener); + return this; + } + + /** + * Adds a listener to register to receive connection failed events from this + * {@link GoogleApiClient}. Applications should balance calls to this method with calls to + * {@link #unregisterConnectionFailedListener(OnConnectionFailedListener)} to avoid + * leaking resources. + *

+ * If the specified listener is already registered to receive connection failed events, + * this method will not add a duplicate entry for the same listener. + *

+ * Note that the order of messages received here may not be stable, so clients should not + * rely on the order that multiple listeners receive events in. + * + * @param listener the listener where the results of the asynchronous {@link #connect()} + * call are delivered. + */ + public Builder addOnConnectionFailedListener(OnConnectionFailedListener listener) { + connectionFailedListeners.add(listener); + return this; + } + + /** + * Specify the OAuth 2.0 scopes requested by your app. See + * {@link com.google.android.gms.common.Scopes} for more information. + * + * @param scope The OAuth 2.0 scopes requested by your app. + * @see com.google.android.gms.common.Scopes + */ + public Builder addScope(Scope scope) { + scopes.add(scope.getScopeUri()); + return this; + } + + /** + * Builds a new {@link GoogleApiClient} object for communicating with the Google APIs. + * + * @return The {@link GoogleApiClient} object. + */ + public GoogleApiClient build() { + return new GoogleApiClientImpl(context, looper, getAccountInfo(), apis, + connectionCallbacks, connectionFailedListeners, clientId); + } + + private AccountInfo getAccountInfo() { + return null; + } + + public Builder enableAutoManage(FragmentActivity fragmentActivity, int cliendId, + OnConnectionFailedListener unresolvedConnectionFailedListener) + throws NullPointerException, IllegalArgumentException, IllegalStateException { + this.fragmentActivity = fragmentActivity; + this.clientId = cliendId; + this.unresolvedConnectionFailedListener = unresolvedConnectionFailedListener; + return this; + } + + /** + * Specify an account name on the device that should be used. If this is never called, the + * client will use the current default account for Google Play services for this + * application. + * + * @param accountName The account name on the device that should be used by + * {@link GoogleApiClient}. + */ + public Builder setAccountName(String accountName) { + this.accountName = accountName; + return this; + } + + /** + * Specifies the part of the screen at which games service popups (for example, + * "welcome back" or "achievement unlocked" popups) will be displayed using gravity. + * + * @param gravityForPopups The gravity which controls the placement of games service popups. + */ + public Builder setGravityForPopups(int gravityForPopups) { + this.gravityForPopups = gravityForPopups; + return this; + } + + /** + * Sets a {@link Handler} to indicate which thread to use when invoking callbacks. Will not + * be used directly to handle callbacks. If this is not called then the application's main + * thread will be used. + */ + public Builder setHandler(Handler handler) { + this.looper = handler.getLooper(); + return this; + } + + /** + * Sets the {@link View} to use as a content view for popups. + * + * @param viewForPopups The view to use as a content view for popups. View cannot be null. + */ + public Builder setViewForPopups(View viewForPopups) { + this.viewForPopups = viewForPopups; + return this; + } + + /** + * Specify that the default account should be used when connecting to services. + */ + public Builder useDefaultAccount() { + this.accountName = AuthConstants.DEFAULT_ACCOUNT; + return this; + } + } + + /** + * Provides callbacks that are called when the client is connected or disconnected from the + * service. Most applications implement {@link #onConnected(Bundle)} to start making requests. + */ + @PublicApi + interface ConnectionCallbacks { + /** + * A suspension cause informing that the service has been killed. + */ + int CAUSE_SERVICE_DISCONNECTED = 1; + /** + * A suspension cause informing you that a peer device connection was lost. + */ + int CAUSE_NETWORK_LOST = 2; + + /** + * After calling {@link #connect()}, this method will be invoked asynchronously when the + * connect request has successfully completed. After this callback, the application can + * make requests on other methods provided by the client and expect that no user + * intervention is required to call methods that use account and scopes provided to the + * client constructor. + *

+ * Note that the contents of the {@code connectionHint} Bundle are defined by the specific + * services. Please see the documentation of the specific implementation of + * {@link GoogleApiClient} you are using for more information. + * + * @param connectionHint Bundle of data provided to clients by Google Play services. May + * be null if no content is provided by the service. + */ + void onConnected(Bundle connectionHint); + + /** + * Called when the client is temporarily in a disconnected state. This can happen if there + * is a problem with the remote service (e.g. a crash or resource problem causes it to be + * killed by the system). When called, all requests have been canceled and no outstanding + * listeners will be executed. GoogleApiClient will automatically attempt to restore the + * connection. Applications should disable UI components that require the service, and wait + * for a call to {@link #onConnected(Bundle)} to re-enable them. + * + * @param cause The reason for the disconnection. Defined by constants {@code CAUSE_*}. + */ + void onConnectionSuspended(int cause); + } + + /** + * Provides callbacks for scenarios that result in a failed attempt to connect the client to + * the service. See {@link ConnectionResult} for a list of error codes and suggestions for + * resolution. + */ + @PublicApi + interface OnConnectionFailedListener { + /** + * Called when there was an error connecting the client to the service. + * + * @param result A {@link ConnectionResult} that can be used for resolving the error, and + * deciding what sort of error occurred. To resolve the error, the resolution + * must be started from an activity with a non-negative {@code requestCode} + * passed to {@link ConnectionResult#startResolutionForResult(Activity, int)}. + * Applications should implement {@link Activity#onActivityResult} in their + * Activity to call {@link #connect()} again if the user has resolved the + * issue (resultCode is {@link Activity#RESULT_OK}). + */ + void onConnectionFailed(ConnectionResult result); + } +} diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/api/PendingResult.java b/play-services-basement/src/main/java/com/google/android/gms/common/api/PendingResult.java new file mode 100644 index 00000000..28943f18 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/api/PendingResult.java @@ -0,0 +1,59 @@ +/* + * Copyright 2013-2015 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.api; + +import java.util.concurrent.TimeUnit; + +/** + * Represents a pending result from calling an API method in Google Play services. The final result + * object from a PendingResult is of type R, which can be retrieved in one of two ways. + *

+ *

+ * After the result has been retrieved using {@link #await()} or delivered to the result callback, + * it is an error to attempt to retrieve the result again. It is the responsibility of the caller + * or callback receiver to release any resources associated with the returned result. Some result + * types may implement {@link Releasable}, in which case {@link Releasable#release()} should be + * used to free the associated resources. + *

+ * TODO: Docs + */ +public interface PendingResult { + /** + * Blocks until the task is completed. This is not allowed on the UI thread. The returned + * result object can have an additional failure mode of INTERRUPTED. + */ + public R await(); + + /** + * Blocks until the task is completed or has timed out waiting for the result. This is not + * allowed on the UI thread. The returned result object can have an additional failure mode + * of either INTERRUPTED or TIMEOUT. + */ + public R await(long time, TimeUnit unit); + + public void cancel(); + + public boolean isCanceled(); + + public void setResultCallback(ResultCallback callback, long time, TimeUnit unit); + + public void setResultCallback(ResultCallback callback); +} diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/api/Releasable.java b/play-services-basement/src/main/java/com/google/android/gms/common/api/Releasable.java new file mode 100644 index 00000000..4ea1c727 --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/api/Releasable.java @@ -0,0 +1,24 @@ +/* + * Copyright 2013-2016 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.api; + +/** + * Represents a resource, or a holder of resources, which may be released once they are no longer needed. + */ +public interface Releasable { + void release(); +} diff --git a/play-services-common-api/src/main/java/com/google/android/gms/common/api/Result.java b/play-services-basement/src/main/java/com/google/android/gms/common/api/Result.java similarity index 100% rename from play-services-common-api/src/main/java/com/google/android/gms/common/api/Result.java rename to play-services-basement/src/main/java/com/google/android/gms/common/api/Result.java diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/api/ResultCallback.java b/play-services-basement/src/main/java/com/google/android/gms/common/api/ResultCallback.java new file mode 100644 index 00000000..4695dbcb --- /dev/null +++ b/play-services-basement/src/main/java/com/google/android/gms/common/api/ResultCallback.java @@ -0,0 +1,33 @@ +/* + * Copyright 2013-2015 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.api; + +/** + * An interface for receiving a {@link Result} from a {@link PendingResult} as an asynchronous + * callback. + */ +public interface ResultCallback { + /** + * Called when the {@link Result} is ready. It is the responsibility of each callback to + * release any resources associated with the result. Some result types may implement + * {@link Releasable}, in which case {@link Releasable#release()} should be used to free the + * associated resources. + * + * @param result The result from the API call. May not be null. + */ + public void onResult(R result); +} diff --git a/play-services-common-api/src/main/java/com/google/android/gms/common/api/Scope.java b/play-services-basement/src/main/java/com/google/android/gms/common/api/Scope.java similarity index 100% rename from play-services-common-api/src/main/java/com/google/android/gms/common/api/Scope.java rename to play-services-basement/src/main/java/com/google/android/gms/common/api/Scope.java diff --git a/play-services-common-api/src/main/java/com/google/android/gms/common/api/Status.java b/play-services-basement/src/main/java/com/google/android/gms/common/api/Status.java similarity index 100% rename from play-services-common-api/src/main/java/com/google/android/gms/common/api/Status.java rename to play-services-basement/src/main/java/com/google/android/gms/common/api/Status.java diff --git a/play-services-common-api/src/main/java/com/google/android/gms/common/data/DataHolder.java b/play-services-basement/src/main/java/com/google/android/gms/common/data/DataHolder.java similarity index 100% rename from play-services-common-api/src/main/java/com/google/android/gms/common/data/DataHolder.java rename to play-services-basement/src/main/java/com/google/android/gms/common/data/DataHolder.java diff --git a/play-services-common-api/src/main/java/com/google/android/gms/common/data/Freezable.java b/play-services-basement/src/main/java/com/google/android/gms/common/data/Freezable.java similarity index 100% rename from play-services-common-api/src/main/java/com/google/android/gms/common/data/Freezable.java rename to play-services-basement/src/main/java/com/google/android/gms/common/data/Freezable.java diff --git a/play-services-common-api/src/main/java/com/google/android/gms/common/images/WebImage.java b/play-services-basement/src/main/java/com/google/android/gms/common/images/WebImage.java similarity index 100% rename from play-services-common-api/src/main/java/com/google/android/gms/common/images/WebImage.java rename to play-services-basement/src/main/java/com/google/android/gms/common/images/WebImage.java diff --git a/play-services-common-api/src/main/java/com/google/android/gms/common/internal/BinderWrapper.java b/play-services-basement/src/main/java/com/google/android/gms/common/internal/BinderWrapper.java similarity index 100% rename from play-services-common-api/src/main/java/com/google/android/gms/common/internal/BinderWrapper.java rename to play-services-basement/src/main/java/com/google/android/gms/common/internal/BinderWrapper.java diff --git a/play-services-common-api/src/main/java/com/google/android/gms/common/internal/GetServiceRequest.java b/play-services-basement/src/main/java/com/google/android/gms/common/internal/GetServiceRequest.java similarity index 100% rename from play-services-common-api/src/main/java/com/google/android/gms/common/internal/GetServiceRequest.java rename to play-services-basement/src/main/java/com/google/android/gms/common/internal/GetServiceRequest.java diff --git a/play-services-common-api/src/main/java/com/google/android/gms/common/internal/ValidateAccountRequest.java b/play-services-basement/src/main/java/com/google/android/gms/common/internal/ValidateAccountRequest.java similarity index 100% rename from play-services-common-api/src/main/java/com/google/android/gms/common/internal/ValidateAccountRequest.java rename to play-services-basement/src/main/java/com/google/android/gms/common/internal/ValidateAccountRequest.java diff --git a/play-services-common-api/src/main/java/com/google/android/gms/common/server/FavaDiagnosticsEntity.java b/play-services-basement/src/main/java/com/google/android/gms/common/server/FavaDiagnosticsEntity.java similarity index 100% rename from play-services-common-api/src/main/java/com/google/android/gms/common/server/FavaDiagnosticsEntity.java rename to play-services-basement/src/main/java/com/google/android/gms/common/server/FavaDiagnosticsEntity.java diff --git a/play-services-common-api/src/main/java/com/google/android/gms/dynamic/ObjectWrapper.java b/play-services-basement/src/main/java/com/google/android/gms/dynamic/ObjectWrapper.java similarity index 100% rename from play-services-common-api/src/main/java/com/google/android/gms/dynamic/ObjectWrapper.java rename to play-services-basement/src/main/java/com/google/android/gms/dynamic/ObjectWrapper.java diff --git a/play-services-common-api/src/main/java/org/microg/gms/auth/AuthConstants.java b/play-services-basement/src/main/java/org/microg/gms/auth/AuthConstants.java similarity index 100% rename from play-services-common-api/src/main/java/org/microg/gms/auth/AuthConstants.java rename to play-services-basement/src/main/java/org/microg/gms/auth/AuthConstants.java diff --git a/play-services-common-api/src/main/java/org/microg/gms/common/Constants.java b/play-services-basement/src/main/java/org/microg/gms/common/Constants.java similarity index 100% rename from play-services-common-api/src/main/java/org/microg/gms/common/Constants.java rename to play-services-basement/src/main/java/org/microg/gms/common/Constants.java diff --git a/play-services-common-api/src/main/java/org/microg/gms/common/GmsService.java b/play-services-basement/src/main/java/org/microg/gms/common/GmsService.java similarity index 100% rename from play-services-common-api/src/main/java/org/microg/gms/common/GmsService.java rename to play-services-basement/src/main/java/org/microg/gms/common/GmsService.java diff --git a/play-services-common-api/src/main/java/org/microg/gms/common/PublicApi.java b/play-services-basement/src/main/java/org/microg/gms/common/PublicApi.java similarity index 100% rename from play-services-common-api/src/main/java/org/microg/gms/common/PublicApi.java rename to play-services-basement/src/main/java/org/microg/gms/common/PublicApi.java diff --git a/play-services-basement/src/main/java/org/microg/gms/common/api/ApiBuilder.java b/play-services-basement/src/main/java/org/microg/gms/common/api/ApiBuilder.java new file mode 100644 index 00000000..6c12551e --- /dev/null +++ b/play-services-basement/src/main/java/org/microg/gms/common/api/ApiBuilder.java @@ -0,0 +1,30 @@ +/* + * Copyright 2013-2016 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 org.microg.gms.common.api; + +import android.content.Context; +import android.os.Looper; + +import com.google.android.gms.common.api.AccountInfo; +import com.google.android.gms.common.api.Api; +import com.google.android.gms.common.api.GoogleApiClient; + +public interface ApiBuilder { + ApiConnection build(Context context, Looper looper, O options, AccountInfo accountInfo, + GoogleApiClient.ConnectionCallbacks callbacks, + GoogleApiClient.OnConnectionFailedListener connectionFailedListener); +} diff --git a/play-services-basement/src/main/java/org/microg/gms/common/api/ApiConnection.java b/play-services-basement/src/main/java/org/microg/gms/common/api/ApiConnection.java new file mode 100644 index 00000000..019ea5d8 --- /dev/null +++ b/play-services-basement/src/main/java/org/microg/gms/common/api/ApiConnection.java @@ -0,0 +1,27 @@ +/* + * Copyright 2013-2015 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 org.microg.gms.common.api; + +public interface ApiConnection { + void connect(); + + void disconnect(); + + boolean isConnected(); + + boolean isConnecting(); +} diff --git a/play-services-basement/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java b/play-services-basement/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java new file mode 100644 index 00000000..5993b403 --- /dev/null +++ b/play-services-basement/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java @@ -0,0 +1,223 @@ +/* + * Copyright 2013-2016 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 org.microg.gms.common.api; + +import android.content.Context; +import android.os.Bundle; +import android.os.Looper; +import android.os.Message; +import android.support.v4.app.FragmentActivity; +import android.util.Log; + +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.api.AccountInfo; +import com.google.android.gms.common.api.Api; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.PendingResult; +import com.google.android.gms.common.api.Status; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +public class GoogleApiClientImpl implements GoogleApiClient { + private static final String TAG = "GmsApiClientImpl"; + + private final Context context; + private final Looper looper; + private final AccountInfo accountInfo; + private final Map apis = new HashMap(); + private final Map apiConnections = new HashMap(); + private final Handler handler; + private final Set connectionCallbacks = new HashSet(); + private final Set connectionFailedListeners = new HashSet(); + private final int clientId; + private final ConnectionCallbacks baseConnectionCallbacks = new ConnectionCallbacks() { + @Override + public void onConnected(Bundle connectionHint) { + Log.d(TAG, "ConnectionCallbacks : onConnected()"); + for (ConnectionCallbacks callback : connectionCallbacks) { + callback.onConnected(connectionHint); + } + } + + @Override + public void onConnectionSuspended(int cause) { + Log.d(TAG, "ConnectionCallbacks : onConnectionSuspended()"); + for (ConnectionCallbacks callback : connectionCallbacks) { + callback.onConnectionSuspended(cause); + } + } + }; + private final OnConnectionFailedListener baseConnectionFailedListener = new + OnConnectionFailedListener() { + @Override + public void onConnectionFailed(ConnectionResult result) { + Log.d(TAG, "OnConnectionFailedListener : onConnectionFailed()"); + for (OnConnectionFailedListener listener : connectionFailedListeners) { + listener.onConnectionFailed(result); + } + } + }; + + public GoogleApiClientImpl(Context context, Looper looper, AccountInfo accountInfo, + Map apis, + Set connectionCallbacks, + Set connectionFailedListeners, int clientId) { + this.context = context; + this.looper = looper; + this.handler = new Handler(looper); + this.accountInfo = accountInfo; + this.apis.putAll(apis); + this.connectionCallbacks.addAll(connectionCallbacks); + this.connectionFailedListeners.addAll(connectionFailedListeners); + this.clientId = clientId; + + for (Api api : apis.keySet()) { + apiConnections.put(api, api.getBuilder().build(context, looper, + apis.get(api), accountInfo, baseConnectionCallbacks, + baseConnectionFailedListener)); + } + } + + public Looper getLooper() { + return looper; + } + + public ApiConnection getApiConnection(Api api) { + return apiConnections.get(api); + } + + @Override + public ConnectionResult blockingConnect() { + return null; + } + + @Override + public ConnectionResult blockingConnect(long timeout, TimeUnit unit) { + return null; + } + + @Override + public PendingResult clearDefaultAccountAndReconnect() { + return null; + } + + @Override + public synchronized void connect() { + Log.d(TAG, "connect()"); + if (isConnected() || isConnecting()) { + Log.d(TAG, "Already connected/connecting, nothing to do"); + return; + } + for (ApiConnection connection : apiConnections.values()) { + if (!connection.isConnected()) { + connection.connect(); + } + } + } + + @Override + public synchronized void disconnect() { + Log.d(TAG, "disconnect()"); + for (ApiConnection connection : apiConnections.values()) { + if (connection.isConnected()) { + connection.disconnect(); + } + } + } + + @Override + public synchronized boolean isConnected() { + for (ApiConnection connection : apiConnections.values()) { + if (!connection.isConnected()) return false; + } + return true; + } + + @Override + public synchronized boolean isConnecting() { + for (ApiConnection connection : apiConnections.values()) { + if (connection.isConnecting()) return true; + } + return false; + } + + @Override + public boolean isConnectionCallbacksRegistered(ConnectionCallbacks listener) { + return connectionCallbacks.contains(listener); + } + + @Override + public boolean isConnectionFailedListenerRegistered( + OnConnectionFailedListener listener) { + return connectionFailedListeners.contains(listener); + } + + @Override + public synchronized void reconnect() { + Log.d(TAG, "reconnect()"); + disconnect(); + connect(); + } + + @Override + public void registerConnectionCallbacks(ConnectionCallbacks listener) { + connectionCallbacks.add(listener); + } + + @Override + public void registerConnectionFailedListener(OnConnectionFailedListener listener) { + connectionFailedListeners.add(listener); + } + + @Override + public void stopAutoManager(FragmentActivity lifecycleActivity) throws IllegalStateException { + + } + + @Override + public void unregisterConnectionCallbacks(ConnectionCallbacks listener) { + connectionCallbacks.remove(listener); + } + + @Override + public void unregisterConnectionFailedListener(OnConnectionFailedListener listener) { + connectionFailedListeners.remove(listener); + } + + private class Handler extends android.os.Handler { + private Handler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + if (msg.what == 0 && msg.obj instanceof Runnable) { + ((Runnable) msg.obj).run(); + } else { + super.handleMessage(msg); + } + } + + public void sendRunnable(Runnable runnable) { + sendMessage(obtainMessage(1, runnable)); + } + } +} diff --git a/play-services-common-api/src/main/java/org/microg/gms/gcm/GcmConstants.java b/play-services-basement/src/main/java/org/microg/gms/gcm/GcmConstants.java similarity index 100% rename from play-services-common-api/src/main/java/org/microg/gms/gcm/GcmConstants.java rename to play-services-basement/src/main/java/org/microg/gms/gcm/GcmConstants.java diff --git a/play-services-cast-api/build.gradle b/play-services-cast-api/build.gradle index 47f513a0..3ce1445d 100644 --- a/play-services-cast-api/build.gradle +++ b/play-services-cast-api/build.gradle @@ -51,5 +51,5 @@ android { } dependencies { - compile project(':play-services-common-api') + compile project(':play-services-basement') } diff --git a/play-services-iid-api/build.gradle b/play-services-iid-api/build.gradle index 47f513a0..3ce1445d 100644 --- a/play-services-iid-api/build.gradle +++ b/play-services-iid-api/build.gradle @@ -51,5 +51,5 @@ android { } dependencies { - compile project(':play-services-common-api') + compile project(':play-services-basement') } diff --git a/play-services-location-api/build.gradle b/play-services-location-api/build.gradle index 47f513a0..3ce1445d 100644 --- a/play-services-location-api/build.gradle +++ b/play-services-location-api/build.gradle @@ -51,5 +51,5 @@ android { } dependencies { - compile project(':play-services-common-api') + compile project(':play-services-basement') } diff --git a/play-services-wearable-api/build.gradle b/play-services-wearable-api/build.gradle index 47f513a0..3ce1445d 100644 --- a/play-services-wearable-api/build.gradle +++ b/play-services-wearable-api/build.gradle @@ -51,5 +51,5 @@ android { } dependencies { - compile project(':play-services-common-api') + compile project(':play-services-basement') } diff --git a/play-services-wearable-api/src/main/java/com/google/android/gms/wearable/Channel.java b/play-services-wearable-api/src/main/java/com/google/android/gms/wearable/Channel.java new file mode 100644 index 00000000..7c74ca5f --- /dev/null +++ b/play-services-wearable-api/src/main/java/com/google/android/gms/wearable/Channel.java @@ -0,0 +1,94 @@ +/* + * Copyright 2013-2016 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.wearable; + +import android.net.Uri; +import android.os.Parcelable; + +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.PendingResult; +import com.google.android.gms.common.api.Releasable; +import com.google.android.gms.common.api.Result; +import com.google.android.gms.common.api.Status; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * A channel created through {@link ChannelApi#openChannel(GoogleApiClient, String, String)}. + *

+ * The implementation of this interface is parcelable and immutable, and implements reasonable {@link #equals(Object)} + * and {@link #hashCode()} methods, so can be used in collections. + */ +public interface Channel extends Parcelable { + + PendingResult addListener(GoogleApiClient client, ChannelApi.ChannelListener listener); + + PendingResult close(GoogleApiClient client, int errorCode); + + PendingResult close(GoogleApiClient client); + + PendingResult getInputStream(GoogleApiClient client); + + PendingResult getOutputStream(GoogleApiClient client); + + String getPath(); + + PendingResult receiveFile(GoogleApiClient client, Uri uri, boolean append); + + PendingResult removeListener(GoogleApiClient client, ChannelApi.ChannelListener listener); + + PendingResult sendFile(GoogleApiClient client, Uri uri); + + PendingResult sendFile(GoogleApiClient client, Uri uri, long startOffset, long length); + + interface GetInputStreamResult extends Releasable, Result { + /** + * Returns an input stream which can read data from the remote node. The stream should be + * closed when no longer needed. This method will only return {@code null} if this result's + * {@linkplain #getStatus() status} was not {@linkplain Status#isSuccess() success}. + *

+ * The returned stream will throw {@link IOException} on read if any connection errors + * occur. This exception might be a {@link ChannelIOException}. + *

+ * Since data for this stream comes over the network, reads may block for a long time. + *

+ * Multiple calls to this method will return the same instance. + */ + InputStream getInputStream(); + } + + interface GetOutputStreamResult extends Releasable, Result { + /** + * Returns an output stream which can send data to a remote node. The stream should be + * closed when no longer needed. This method will only return {@code null} if this result's + * {@linkplain #getStatus() status} was not {@linkplain Status#isSuccess() success}. + *

+ * The returned stream will throw {@link IOException} on read if any connection errors + * occur. This exception might be a {@link ChannelIOException}. + *

+ * Since data for this stream comes over the network, reads may block for a long time. + *

+ * Data written to this stream is buffered. If you wish to send the current data without + * waiting for the buffer to fill up, {@linkplain OutputStream#flush() flush} the stream. + *

+ * Multiple calls to this method will return the same instance. + */ + OutputStream getOutputStream(); + } +} diff --git a/play-services-wearable-api/src/main/java/com/google/android/gms/wearable/ChannelApi.java b/play-services-wearable-api/src/main/java/com/google/android/gms/wearable/ChannelApi.java new file mode 100644 index 00000000..66d6ae71 --- /dev/null +++ b/play-services-wearable-api/src/main/java/com/google/android/gms/wearable/ChannelApi.java @@ -0,0 +1,185 @@ +/* + * Copyright 2013-2016 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.wearable; + +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.GoogleApiClient.Builder; +import com.google.android.gms.common.api.PendingResult; +import com.google.android.gms.common.api.Result; +import com.google.android.gms.common.api.Status; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Client interface for Wearable Channel API. Allows apps on a wearable device to send and receive + * data from other wearable nodes. + *

+ * Channels are bidirectional. Each side, both the initiator and the receiver may both read and + * write to the channel by using {@link Channel#getOutputStream(GoogleApiClient)} and {@link Channel#getInputStream(GoogleApiClient)}. + * Once a channel is established, the API for the initiator and receiver are identical. + *

+ * Channels are only available when the wearable nodes are connected. When the remote node + * disconnects, all existing channels will be closed. Any listeners (added through {@link #addListener(GoogleApiClient, ChannelListener)} + * and any installed {@link WearableListenerService}) will be notified of the channel closing. + */ +public interface ChannelApi { + /** + * Channel action for use in listener filters. + * + * @see WearableListenerService + */ + String ACTION_CHANNEL_EVENT = "com.google.android.gms.wearable.CHANNEL_EVENT"; + + /** + * Registers a listener to be notified of channel events. Calls to this method should be + * balanced with calls to {@link #removeListener(GoogleApiClient, ChannelListener)} to avoid + * leaking resources. + *

+ * Listener events will be called on the main thread, or the handler specified on {@code client} + * when it was built (using {@link Builder#setHandler(Handler)}). + *

+ * Callers wishing to be notified of events in the background should use {@link WearableListenerService}. + * + * @param client a connected client + * @param listener a listener which will be notified of changes to any channel + */ + PendingResult addListener(GoogleApiClient client, ChannelListener listener); + + /** + * Opens a channel to exchange data with a remote node. + *

+ * Channel which are no longer needed should be closed using {@link Channel#close(GoogleApiClient)}. + *

+ * This call involves a network round trip, so may be long running. {@code client} must remain + * connected during that time, or the request will be cancelled (like any other Play Services + * API calls). + * + * @param client a connected client + * @param nodeId the node ID of a wearable node, as returned from {@link NodeApi#getConnectedNodes(GoogleApiClient)} + * @param path an app-specific identifier for the channel + */ + PendingResult openChannel(GoogleApiClient client, String nodeId, String path); + + /** + * Removes a listener which was previously added through {@link #addListener(GoogleApiClient, ChannelListener)}. + * + * @param client a connected client + * @param listener a listener which was added using {@link #addListener(GoogleApiClient, ChannelListener)} + */ + PendingResult removeListener(GoogleApiClient client, ChannelListener listener); + + /** + * A listener which will be notified on changes to channels. + */ + interface ChannelListener { + /** + * Value passed to {@link #onChannelClosed(Channel, int, int)}, {@link #onInputClosed(Channel, int, int)} + * and {@link #onOutputClosed(Channel, int, int)} when the closing is due to a remote node + * being disconnected. + */ + int CLOSE_REASON_DISCONNECTED = 1; + + /** + * Value passed to {@link #onChannelClosed(Channel, int, int)}, {@link #onInputClosed(Channel, int, int)} + * and {@link #onOutputClosed(Channel, int, int)} when the stream is closed due to the + * local node calling {@link Channel#close(GoogleApiClient)} or {@link Channel#close(GoogleApiClient, int)}. + */ + int CLOSE_REASON_LOCAL_CLOSE = 3; + + /** + * Value passed to {@link #onInputClosed(Channel, int, int)} or {@link #onOutputClosed(Channel, int, int)} + * (but not {@link #onChannelClosed(Channel, int, int)}), when the stream was closed under + * normal conditions, e.g the whole file was read, or the OutputStream on the remote node + * was closed normally. + */ + int CLOSE_REASON_NORMAL = 0; + + /** + * Value passed to {@link #onChannelClosed(Channel, int, int)}, {@link #onInputClosed(Channel, int, int)} + * and {@link #onOutputClosed(Channel, int, int)} when the stream is closed due to the + * remote node calling {@link Channel#close(GoogleApiClient)} or {@link Channel#close(GoogleApiClient, int)}. + */ + int CLOSE_REASON_REMOTE_CLOSE = 2; + + /** + * Called when a channel is closed. This can happen through an explicit call to {@link Channel#close(GoogleApiClient)} + * or {@link #close(GoogleApiClient, int)} on either side of the connection, or due to + * disconnecting from the remote node. + * + * @param closeReason the reason for the channel closing. One of {@link #CLOSE_REASON_DISCONNECTED}, + * {@link #CLOSE_REASON_REMOTE_CLOSE}, or {@link #CLOSE_REASON_LOCAL_CLOSE}. + * @param appSpecificErrorCode the error code specified on {@link Channel#close(GoogleApiClient, int)}, + * or 0 if closeReason is {@link #CLOSE_REASON_DISCONNECTED}. + */ + void onChannelClosed(Channel channel, int closeReason, int appSpecificErrorCode); + + /** + * Called when a new channel is opened by a remote node. + */ + void onChannelOpened(Channel channel); + + /** + * Called when the input side of a channel is closed. + * + * @param closeReason the reason for the channel closing. One of {@link #CLOSE_REASON_DISCONNECTED}, + * {@link #CLOSE_REASON_REMOTE_CLOSE}, {@link #CLOSE_REASON_LOCAL_CLOSE} + * or {@link #CLOSE_REASON_NORMAL}. + * @param appSpecificErrorCode the error code specified on {@link Channel#close(GoogleApiClient, int)}, + * or 0 if closeReason is {@link #CLOSE_REASON_DISCONNECTED} or + * {@link #CLOSE_REASON_NORMAL}. + */ + void onInputClosed(Channel channel, @CloseReason int closeReason, int appSpecificErrorCode); + + /** + * Called when the output side of a channel is closed. + * + * @param closeReason the reason for the channel closing. One of {@link #CLOSE_REASON_DISCONNECTED}, + * {@link #CLOSE_REASON_REMOTE_CLOSE}, {@link #CLOSE_REASON_LOCAL_CLOSE} + * or {@link #CLOSE_REASON_NORMAL}. + * @param appSpecificErrorCode the error code specified on {@link Channel#close(GoogleApiClient, int)}, + * or 0 if closeReason is {@link #CLOSE_REASON_DISCONNECTED} or + * {@link #CLOSE_REASON_NORMAL}. + */ + void onOutputClosed(Channel channel, @CloseReason int closeReason, int appSpecificErrorCode); + } + + /** + * An annotation for values passed to {@link ChannelListener#onChannelClosed(Channel, int, int)}, + * and other methods on the {@link ChannelListener} interface. Annotated method parameters will + * always take one of the following values: + *

+ */ + @Retention(RetentionPolicy.SOURCE) + @interface CloseReason { + } + + /** + * Result of {@link #openChannel(GoogleApiClient, String, String)}. + */ + interface OpenChannelResult extends Result { + /** + * Returns the newly created channel, or {@code null}, if the connection couldn't be opened. + */ + Channel getChannel(); + } +} diff --git a/play-services-wearable-api/src/main/java/com/google/android/gms/wearable/internal/AmsEntityUpdate.java b/play-services-wearable-api/src/main/java/com/google/android/gms/wearable/internal/AmsEntityUpdate.java new file mode 100644 index 00000000..1e156633 --- /dev/null +++ b/play-services-wearable-api/src/main/java/com/google/android/gms/wearable/internal/AmsEntityUpdate.java @@ -0,0 +1,20 @@ +/* + * Copyright 2013-2016 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.wearable.internal; + +public interface AmsEntityUpdate { +} diff --git a/play-services-wearable-api/src/main/java/com/google/android/gms/wearable/internal/AmsEntityUpdateParcelable.java b/play-services-wearable-api/src/main/java/com/google/android/gms/wearable/internal/AmsEntityUpdateParcelable.java index e43a38bf..8533e19b 100644 --- a/play-services-wearable-api/src/main/java/com/google/android/gms/wearable/internal/AmsEntityUpdateParcelable.java +++ b/play-services-wearable-api/src/main/java/com/google/android/gms/wearable/internal/AmsEntityUpdateParcelable.java @@ -18,6 +18,6 @@ package com.google.android.gms.wearable.internal; import org.microg.safeparcel.AutoSafeParcelable; -public class AmsEntityUpdateParcelable extends AutoSafeParcelable { +public class AmsEntityUpdateParcelable extends AutoSafeParcelable implements AmsEntityUpdate { public static final Creator CREATOR = new AutoCreator(AmsEntityUpdateParcelable.class); } diff --git a/play-services-wearable-api/src/main/java/com/google/android/gms/wearable/internal/AncsNotification.java b/play-services-wearable-api/src/main/java/com/google/android/gms/wearable/internal/AncsNotification.java new file mode 100644 index 00000000..f96a605f --- /dev/null +++ b/play-services-wearable-api/src/main/java/com/google/android/gms/wearable/internal/AncsNotification.java @@ -0,0 +1,20 @@ +/* + * Copyright 2013-2016 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.wearable.internal; + +public interface AncsNotification { +} diff --git a/play-services-wearable-api/src/main/java/com/google/android/gms/wearable/internal/AncsNotificationParcelable.java b/play-services-wearable-api/src/main/java/com/google/android/gms/wearable/internal/AncsNotificationParcelable.java index 05f7c7f7..81cdb875 100644 --- a/play-services-wearable-api/src/main/java/com/google/android/gms/wearable/internal/AncsNotificationParcelable.java +++ b/play-services-wearable-api/src/main/java/com/google/android/gms/wearable/internal/AncsNotificationParcelable.java @@ -18,6 +18,6 @@ package com.google.android.gms.wearable.internal; import org.microg.safeparcel.AutoSafeParcelable; -public class AncsNotificationParcelable extends AutoSafeParcelable { +public class AncsNotificationParcelable extends AutoSafeParcelable implements AncsNotification { public static final Creator CREATOR = new AutoCreator(AncsNotificationParcelable.class); } diff --git a/play-services-wearable-api/src/main/java/com/google/android/gms/wearable/internal/ChannelEventParcelable.java b/play-services-wearable-api/src/main/java/com/google/android/gms/wearable/internal/ChannelEventParcelable.java index b562032b..93c0be72 100644 --- a/play-services-wearable-api/src/main/java/com/google/android/gms/wearable/internal/ChannelEventParcelable.java +++ b/play-services-wearable-api/src/main/java/com/google/android/gms/wearable/internal/ChannelEventParcelable.java @@ -16,8 +16,23 @@ package com.google.android.gms.wearable.internal; +import com.google.android.gms.wearable.Channel; + import org.microg.safeparcel.AutoSafeParcelable; +import org.microg.safeparcel.SafeParceled; public class ChannelEventParcelable extends AutoSafeParcelable { + + @SafeParceled(1) + private int versionCode; + @SafeParceled(2) + public Channel channel; // TODO: ChannelImpl needed! + @SafeParceled(3) + public int eventType; + @SafeParceled(4) + public int closeReason; + @SafeParceled(5) + public int appSpecificErrorCode; + public static final Creator CREATOR = new AutoCreator(ChannelEventParcelable.class); } diff --git a/settings.gradle b/settings.gradle index b13b2661..99da0fb4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1,6 @@ +include ':play-services-basement' include ':play-services-api' include ':play-services-cast-api' -include ':play-services-common-api' include ':play-services-iid-api' include ':play-services-location-api' include ':play-services-wearable-api'