From 31184fd03e862bf00dbf8e909023044bffd9dc15 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Mon, 12 Jan 2015 00:07:59 +0100 Subject: [PATCH 01/65] First snapshot --- AndroidManifest.xml | 4 + build.gradle | 29 ++ .../android/gms/common/ConnectionResult.java | 185 ++++++++ .../gms/common/GooglePlayServicesUtil.java | 81 ++++ .../google/android/gms/common/api/Api.java | 13 + .../gms/common/api/GoogleApiClient.java | 443 ++++++++++++++++++ .../android/gms/common/api/PendingResult.java | 4 + .../gms/common/api/ResultCallback.java | 4 + .../google/android/gms/common/api/Status.java | 4 + 9 files changed, 767 insertions(+) create mode 100644 AndroidManifest.xml create mode 100644 build.gradle create mode 100644 src/com/google/android/gms/common/ConnectionResult.java create mode 100644 src/com/google/android/gms/common/GooglePlayServicesUtil.java create mode 100644 src/com/google/android/gms/common/api/Api.java create mode 100644 src/com/google/android/gms/common/api/GoogleApiClient.java create mode 100644 src/com/google/android/gms/common/api/PendingResult.java create mode 100644 src/com/google/android/gms/common/api/ResultCallback.java create mode 100644 src/com/google/android/gms/common/api/Status.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml new file mode 100644 index 00000000..16e3856e --- /dev/null +++ b/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..5d2d1c52 --- /dev/null +++ b/build.gradle @@ -0,0 +1,29 @@ +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:1.0.0' + } +} + +apply plugin: 'com.android.library' + +dependencies { + compile 'com.android.support:support-v4:21.0.3' + compile project(':GmsApi') +} + +android { + compileSdkVersion 21 + buildToolsVersion "21.1.2" + lintOptions.abortOnError false + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['src'] + aidl.srcDirs = ['src'] + } + } +} diff --git a/src/com/google/android/gms/common/ConnectionResult.java b/src/com/google/android/gms/common/ConnectionResult.java new file mode 100644 index 00000000..b32466b6 --- /dev/null +++ b/src/com/google/android/gms/common/ConnectionResult.java @@ -0,0 +1,185 @@ +package com.google.android.gms.common; + +import android.app.Activity; +import android.app.PendingIntent; +import android.content.IntentSender; +import com.google.android.gms.common.api.GoogleApiClient; + +/** + * 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 PendingIntent pendingIntent; + private final int statusCode; + + /** + * 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 = statusCode; + this.pendingIntent = pendingIntent; + } + + /** + * Indicates the type of error that interrupted connection. + * + * @return the error code, or {@link #SUCCESS} if no error occurred. + */ + public int getErrorCode() { + return this.statusCode; + } + + /** + * 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 this.pendingIntent; + } + + /** + * 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 this.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/src/com/google/android/gms/common/GooglePlayServicesUtil.java b/src/com/google/android/gms/common/GooglePlayServicesUtil.java new file mode 100644 index 00000000..c4d83b21 --- /dev/null +++ b/src/com/google/android/gms/common/GooglePlayServicesUtil.java @@ -0,0 +1,81 @@ +package com.google.android.gms.common; + +import android.app.Activity; +import android.app.Dialog; +import android.app.Fragment; +import android.app.PendingIntent; +import android.content.Context; +import android.content.DialogInterface; +import android.content.pm.PackageManager; +import org.microg.gms.Constants; + +/** + * Utility class for verifying that the Google Play services APK is available and up-to-date on + * this device. The same checks are performed if one uses {@link AdvertisingIdClient} or + * {@link GoogleAuthUtil} to connect to the service. + *

+ * TODO: methods :) + */ +public class GooglePlayServicesUtil { + public static final String GMS_ERROR_DIALOG = "GooglePlayServicesErrorDialog"; + public static final String GOOGLE_PLAY_SERVICES_PACKAGE = "com.google.android.gms"; + public static final int GOOGLE_PLAY_SERVICES_VERSION_CODE = Constants.MAX_REFERENCE_VERSION; + public static final String GOOGLE_PLAY_STORE_PACKAGE = "com.android.vending"; + + public static Dialog getErrorDialog(int errorCode, Activity activity, int requestCode) { + return null; // TODO + } + + public static Dialog getErrorDialog(int errorCode, Activity activity, int requestCode, + DialogInterface.OnCancelListener cancelListener) { + return null; // TODO + } + + public static PendingIntent getErrorPendingIntent(int errorCode, Activity activity, + int requestCode) { + return null; // TODO + } + + public static String getErrorString(int errorCode) { + return null; // TODO + } + + public static String getOpenSourceSoftwareLicenseInfo(Context context) { + return null; // TODO + } + + public static Context getRemoteContext(Context context) { + return null; // TODO + } + + public static int isGooglePlayServicesAvailable(Context context) { + return 0; // TODO + } + + public static boolean isGoogleSignedUid(PackageManager packageManager, int uid) { + return false; // TODO + } + + public static boolean isUserRecoverableError(int errorCode) { + return false; // TODO + } + + public static boolean showErrorDialogFragment(int errorCode, Activity activity, + int requestCode) { + return false; // TODO + } + + public static boolean showErrorDialogFragment(int errorCode, Activity activity, + Fragment fragment, int requestCode, DialogInterface.OnCancelListener cancelListener) { + return false; // TODO + } + + public static boolean showErrorDialogFragment(int errorCode, Activity activity, int requestCode, + DialogInterface.OnCancelListener cancelListener) { + return false; // TODO + } + + public static void showErrorNotification(int errorCode, Context context) { + // TODO + } +} diff --git a/src/com/google/android/gms/common/api/Api.java b/src/com/google/android/gms/common/api/Api.java new file mode 100644 index 00000000..6560e667 --- /dev/null +++ b/src/com/google/android/gms/common/api/Api.java @@ -0,0 +1,13 @@ +package com.google.android.gms.common.api; + +public class Api { + + public interface ApiOptions { + public interface HasOptions extends ApiOptions { + + } + public interface NotRequiredOptions extends ApiOptions { + + } + } +} diff --git a/src/com/google/android/gms/common/api/GoogleApiClient.java b/src/com/google/android/gms/common/api/GoogleApiClient.java new file mode 100644 index 00000000..a6029a6d --- /dev/null +++ b/src/com/google/android/gms/common/api/GoogleApiClient.java @@ -0,0 +1,443 @@ +package com.google.android.gms.common.api; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.view.View; +import com.google.android.gms.common.ConnectionResult; + +import java.util.concurrent.TimeUnit; +import java.util.logging.Handler; + +/** + * 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. + */ +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 + */ + public 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 + */ + public 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. + */ + public 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. + */ + public 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() + */ + public 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. + */ + public 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. + */ + public 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) + */ + public 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() + */ + public 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. + */ + public 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) + */ + public 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. + */ + public 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. + */ + public void unregisterConnectionFailedListener(OnConnectionFailedListener listener); + + /** + * Builder to configure a {@link GoogleApiClient}. + */ + public class Builder { + /** + * Builder to help construct the {@link GoogleApiClient} object. + * + * @param context The context to use for the connection. + */ + public Builder(Context context) { + + } + + /** + * 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) { + // TODO + 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) { + // TODO + 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) { + // TODO + 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) { + // TODO + 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) { + // TODO + return this; + } + + /** + * Builds a new {@link GoogleApiClient} object for communicating with the Google APIs. + * + * @return The {@link GoogleApiClient} object. + */ + public GoogleApiClient build() { + return null; // TODO + } + + public Builder enableAutoManage(FragmentActivity fragmentActivity, int cliendId, + OnConnectionFailedListener unresolvedConnectionFailedListener) + throws NullPointerException, IllegalArgumentException, IllegalStateException { + // TODO + 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) { + // TODO + 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) { + // TODO + 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) { + // TODO + 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) { + // TODO + return this; + } + + /** + * Specify that the default account should be used when connecting to services. + */ + public Builder useDefaultAccount() { + // TODO + 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. + */ + public 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. + */ + public 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/src/com/google/android/gms/common/api/PendingResult.java b/src/com/google/android/gms/common/api/PendingResult.java new file mode 100644 index 00000000..a1ea2473 --- /dev/null +++ b/src/com/google/android/gms/common/api/PendingResult.java @@ -0,0 +1,4 @@ +package com.google.android.gms.common.api; + +public class PendingResult { +} diff --git a/src/com/google/android/gms/common/api/ResultCallback.java b/src/com/google/android/gms/common/api/ResultCallback.java new file mode 100644 index 00000000..1d925a8d --- /dev/null +++ b/src/com/google/android/gms/common/api/ResultCallback.java @@ -0,0 +1,4 @@ +package com.google.android.gms.common.api; + +public class ResultCallback { +} diff --git a/src/com/google/android/gms/common/api/Status.java b/src/com/google/android/gms/common/api/Status.java new file mode 100644 index 00000000..fdd78880 --- /dev/null +++ b/src/com/google/android/gms/common/api/Status.java @@ -0,0 +1,4 @@ +package com.google.android.gms.common.api; + +public class Status { +} From 8b421638000f30c21a57997bab44f0040b66550b Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Mon, 12 Jan 2015 00:52:58 +0100 Subject: [PATCH 02/65] Start Api code --- README.md | 1 + .../android/gms/common/ConnectionResult.java | 7 ++-- .../google/android/gms/common/api/Api.java | 41 +++++++++++++++++-- .../android/gms/common/api/PendingResult.java | 38 ++++++++++++++++- .../android/gms/common/api/Releasable.java | 9 ++++ .../google/android/gms/common/api/Result.java | 8 ++++ .../gms/common/api/ResultCallback.java | 14 ++++++- .../google/android/gms/common/api/Status.java | 11 ++++- 8 files changed, 119 insertions(+), 10 deletions(-) create mode 100644 README.md create mode 100644 src/com/google/android/gms/common/api/Releasable.java create mode 100644 src/com/google/android/gms/common/api/Result.java diff --git a/README.md b/README.md new file mode 100644 index 00000000..6fb94c76 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +Do not link against this repo, as it is will be renamed later diff --git a/src/com/google/android/gms/common/ConnectionResult.java b/src/com/google/android/gms/common/ConnectionResult.java index b32466b6..ae65ae68 100644 --- a/src/com/google/android/gms/common/ConnectionResult.java +++ b/src/com/google/android/gms/common/ConnectionResult.java @@ -2,6 +2,7 @@ package com.google.android.gms.common; import android.app.Activity; import android.app.PendingIntent; +import android.content.Intent; import android.content.IntentSender; import com.google.android.gms.common.api.GoogleApiClient; @@ -130,7 +131,7 @@ public class ConnectionResult { * @return the error code, or {@link #SUCCESS} if no error occurred. */ public int getErrorCode() { - return this.statusCode; + return statusCode; } /** @@ -141,7 +142,7 @@ public class ConnectionResult { * @return The pending intent to resolve the connection failure. */ public PendingIntent getResolution() { - return this.pendingIntent; + return pendingIntent; } /** @@ -160,7 +161,7 @@ public class ConnectionResult { * @return {@code true} if the connection was successful, {@code false} if there was an error. */ public boolean isSuccess() { - return this.statusCode == 0; + return statusCode == 0; } /** diff --git a/src/com/google/android/gms/common/api/Api.java b/src/com/google/android/gms/common/api/Api.java index 6560e667..5d7e45ab 100644 --- a/src/com/google/android/gms/common/api/Api.java +++ b/src/com/google/android/gms/common/api/Api.java @@ -1,13 +1,46 @@ package com.google.android.gms.common.api; -public class Api { - +/** + * 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. + */ +public final class Api { + + /** + * Base interface for API options. These are used to configure specific parameters for + * individual API surfaces. The default implementation has no parameters. + */ public interface ApiOptions { + /** + * Base interface for {@link ApiOptions} in {@link Api}s that have options. + */ public interface HasOptions extends ApiOptions { - } + + /** + * Base interface for {@link ApiOptions} that are not required, don't exist. + */ public interface NotRequiredOptions extends ApiOptions { - + } + + /** + * {@link ApiOptions} implementation for {@link Api}s that do not take any options. + */ + public class NoOptions implements NotRequiredOptions { + } + + /** + * Base interface for {@link ApiOptions} that are optional. + */ + public interface Optional extends HasOptions, NotRequiredOptions { } } } diff --git a/src/com/google/android/gms/common/api/PendingResult.java b/src/com/google/android/gms/common/api/PendingResult.java index a1ea2473..8845a8d7 100644 --- a/src/com/google/android/gms/common/api/PendingResult.java +++ b/src/com/google/android/gms/common/api/PendingResult.java @@ -1,4 +1,40 @@ package com.google.android.gms.common.api; -public class PendingResult { +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/src/com/google/android/gms/common/api/Releasable.java b/src/com/google/android/gms/common/api/Releasable.java new file mode 100644 index 00000000..ed3d08c6 --- /dev/null +++ b/src/com/google/android/gms/common/api/Releasable.java @@ -0,0 +1,9 @@ +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 { + public void release(); +} diff --git a/src/com/google/android/gms/common/api/Result.java b/src/com/google/android/gms/common/api/Result.java new file mode 100644 index 00000000..d8408112 --- /dev/null +++ b/src/com/google/android/gms/common/api/Result.java @@ -0,0 +1,8 @@ +package com.google.android.gms.common.api; + +/** + * Represents the final result of invoking an API method in Google Play Services. + */ +public interface Result { + public Status getStatus(); +} diff --git a/src/com/google/android/gms/common/api/ResultCallback.java b/src/com/google/android/gms/common/api/ResultCallback.java index 1d925a8d..ef8e8eb2 100644 --- a/src/com/google/android/gms/common/api/ResultCallback.java +++ b/src/com/google/android/gms/common/api/ResultCallback.java @@ -1,4 +1,16 @@ package com.google.android.gms.common.api; -public class ResultCallback { +/** + * 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/src/com/google/android/gms/common/api/Status.java b/src/com/google/android/gms/common/api/Status.java index fdd78880..ae65e646 100644 --- a/src/com/google/android/gms/common/api/Status.java +++ b/src/com/google/android/gms/common/api/Status.java @@ -1,4 +1,13 @@ package com.google.android.gms.common.api; -public class Status { +/** + * Represents the results of work. + *

+ * TODO: content (is this IPC API?) + */ +public final class Status implements Result { + @Override + public Status getStatus() { + return this; + } } From d2fb7d569ba160ea251b9b8edc931561c6984f2f Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Thu, 15 Jan 2015 20:11:31 +0100 Subject: [PATCH 03/65] WIP: Location API --- AndroidManifest.xml | 6 +- .../gms/common/GooglePlayServicesClient.java | 40 +++++ .../gms/common/GooglePlayServicesUtil.java | 3 +- .../google/android/gms/common/api/Api.java | 27 ++- .../gms/common/api/GoogleApiClient.java | 58 +++++-- .../google/android/gms/common/api/Result.java | 8 - .../google/android/gms/common/api/Status.java | 13 -- .../location/FusedLocationProviderApi.java | 33 ++++ .../android/gms/location/GeofencingApi.java | 4 + .../android/gms/location/LocationClient.java | 78 +++++++++ .../gms/location/LocationListener.java | 4 + .../gms/location/LocationServices.java | 28 ++++ .../common/ForwardConnectionCallbacks.java | 35 ++++ .../ForwardConnectionFailedListener.java | 31 ++++ src/org/microg/gms/common/GmsClient.java | 105 ++++++++++++ .../gms/common/MultiConnectionKeeper.java | 136 +++++++++++++++ .../gms/common/api/AbstractPendingResult.java | 95 +++++++++++ .../gms/common/api/CallbackHandler.java | 42 +++++ .../gms/common/api/GoogleApiClientImpl.java | 155 ++++++++++++++++++ .../FusedLocationProviderApiImpl.java | 72 ++++++++ .../gms/location/GeofencingApiImpl.java | 6 + .../location/GoogleLocationManagerClient.java | 35 ++++ .../gms/location/LocationClientImpl.java | 61 +++++++ 23 files changed, 1035 insertions(+), 40 deletions(-) create mode 100644 src/com/google/android/gms/common/GooglePlayServicesClient.java delete mode 100644 src/com/google/android/gms/common/api/Result.java delete mode 100644 src/com/google/android/gms/common/api/Status.java create mode 100644 src/com/google/android/gms/location/FusedLocationProviderApi.java create mode 100644 src/com/google/android/gms/location/GeofencingApi.java create mode 100644 src/com/google/android/gms/location/LocationClient.java create mode 100644 src/com/google/android/gms/location/LocationListener.java create mode 100644 src/com/google/android/gms/location/LocationServices.java create mode 100644 src/org/microg/gms/common/ForwardConnectionCallbacks.java create mode 100644 src/org/microg/gms/common/ForwardConnectionFailedListener.java create mode 100644 src/org/microg/gms/common/GmsClient.java create mode 100644 src/org/microg/gms/common/MultiConnectionKeeper.java create mode 100644 src/org/microg/gms/common/api/AbstractPendingResult.java create mode 100644 src/org/microg/gms/common/api/CallbackHandler.java create mode 100644 src/org/microg/gms/common/api/GoogleApiClientImpl.java create mode 100644 src/org/microg/gms/location/FusedLocationProviderApiImpl.java create mode 100644 src/org/microg/gms/location/GeofencingApiImpl.java create mode 100644 src/org/microg/gms/location/GoogleLocationManagerClient.java create mode 100644 src/org/microg/gms/location/LocationClientImpl.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 16e3856e..7c0e18bf 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1,4 +1,6 @@ - - + + + diff --git a/src/com/google/android/gms/common/GooglePlayServicesClient.java b/src/com/google/android/gms/common/GooglePlayServicesClient.java new file mode 100644 index 00000000..9796592f --- /dev/null +++ b/src/com/google/android/gms/common/GooglePlayServicesClient.java @@ -0,0 +1,40 @@ +package com.google.android.gms.common; + +import android.os.Bundle; + +@Deprecated +public interface GooglePlayServicesClient { + void connect(); + + void disconnect(); + + boolean isConnected(); + + boolean isConnecting(); + + void registerConnectionCallbacks(ConnectionCallbacks listener); + + boolean isConnectionCallbacksRegistered(ConnectionCallbacks listener); + + void unregisterConnectionCallbacks(ConnectionCallbacks listener); + + void registerConnectionFailedListener(OnConnectionFailedListener listener); + + boolean isConnectionFailedListenerRegistered(OnConnectionFailedListener listener); + + void unregisterConnectionFailedListener(OnConnectionFailedListener listener); + + @Deprecated + public interface OnConnectionFailedListener { + + void onConnectionFailed(ConnectionResult result); + } + + @Deprecated + public interface ConnectionCallbacks { + + void onConnected(Bundle connectionHint); + + void onDisconnected(); + } +} diff --git a/src/com/google/android/gms/common/GooglePlayServicesUtil.java b/src/com/google/android/gms/common/GooglePlayServicesUtil.java index c4d83b21..a32adf04 100644 --- a/src/com/google/android/gms/common/GooglePlayServicesUtil.java +++ b/src/com/google/android/gms/common/GooglePlayServicesUtil.java @@ -49,7 +49,8 @@ public class GooglePlayServicesUtil { } public static int isGooglePlayServicesAvailable(Context context) { - return 0; // TODO + // As we can't know right now if the later desired feature is available, we just pretend it to be. + return ConnectionResult.SUCCESS; } public static boolean isGoogleSignedUid(PackageManager packageManager, int uid) { diff --git a/src/com/google/android/gms/common/api/Api.java b/src/com/google/android/gms/common/api/Api.java index 5d7e45ab..d094fdd0 100644 --- a/src/com/google/android/gms/common/api/Api.java +++ b/src/com/google/android/gms/common/api/Api.java @@ -1,5 +1,8 @@ package com.google.android.gms.common.api; +import android.content.Context; +import android.os.Looper; + /** * 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 @@ -13,6 +16,16 @@ package com.google.android.gms.common.api; * See {@link GoogleApiClient.Builder} for usage examples. */ public final class Api { + + private final Builder builder; + + public Api(Builder builder) { + this.builder = builder; + } + + public Builder getBuilder() { + return builder; + } /** * Base interface for API options. These are used to configure specific parameters for @@ -34,7 +47,7 @@ public final class Api { /** * {@link ApiOptions} implementation for {@link Api}s that do not take any options. */ - public class NoOptions implements NotRequiredOptions { + public final class NoOptions implements NotRequiredOptions { } /** @@ -43,4 +56,16 @@ public final class Api { public interface Optional extends HasOptions, NotRequiredOptions { } } + + public interface Connection { + public void connect(); + public void disconnect(); + public boolean isConnected(); + } + + public interface Builder { + Connection build(Context context, Looper looper, O options, AccountInfo accountInfo, + GoogleApiClient.ConnectionCallbacks callbacks, + GoogleApiClient.OnConnectionFailedListener connectionFailedListener); + } } diff --git a/src/com/google/android/gms/common/api/GoogleApiClient.java b/src/com/google/android/gms/common/api/GoogleApiClient.java index a6029a6d..3f024457 100644 --- a/src/com/google/android/gms/common/api/GoogleApiClient.java +++ b/src/com/google/android/gms/common/api/GoogleApiClient.java @@ -3,12 +3,19 @@ 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.Constants; +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; -import java.util.logging.Handler; /** * The main entry point for Google Play services integration. @@ -209,13 +216,27 @@ public interface GoogleApiClient { * Builder to configure a {@link GoogleApiClient}. */ public 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(); } /** @@ -242,7 +263,7 @@ public interface GoogleApiClient { * @see Api */ public Builder addApi(Api api, O options) { - // TODO + apis.put(api, options); return this; } @@ -253,7 +274,7 @@ public interface GoogleApiClient { * @see Api */ public Builder addApi(Api api) { - // TODO + apis.put(api, null); return this; } @@ -273,7 +294,7 @@ public interface GoogleApiClient { * call are delivered. */ public Builder addConnectionCallbacks(ConnectionCallbacks listener) { - // TODO + connectionCallbacks.add(listener); return this; } @@ -293,7 +314,7 @@ public interface GoogleApiClient { * call are delivered. */ public Builder addOnConnectionFailedListener(OnConnectionFailedListener listener) { - // TODO + connectionFailedListeners.add(listener); return this; } @@ -305,7 +326,7 @@ public interface GoogleApiClient { * @see com.google.android.gms.common.Scopes */ public Builder addScope(Scope scope) { - // TODO + scopes.add(scope.getScopeUri()); return this; } @@ -315,13 +336,20 @@ public interface GoogleApiClient { * @return The {@link GoogleApiClient} object. */ public GoogleApiClient build() { - return null; // TODO + 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 { - // TODO + this.fragmentActivity = fragmentActivity; + this.clientId = cliendId; + this.unresolvedConnectionFailedListener = unresolvedConnectionFailedListener; return this; } @@ -334,7 +362,7 @@ public interface GoogleApiClient { * {@link GoogleApiClient}. */ public Builder setAccountName(String accountName) { - // TODO + this.accountName = accountName; return this; } @@ -345,7 +373,7 @@ public interface GoogleApiClient { * @param gravityForPopups The gravity which controls the placement of games service popups. */ public Builder setGravityForPopups(int gravityForPopups) { - // TODO + this.gravityForPopups = gravityForPopups; return this; } @@ -355,7 +383,7 @@ public interface GoogleApiClient { * thread will be used. */ public Builder setHandler(Handler handler) { - // TODO + this.looper = handler.getLooper(); return this; } @@ -365,7 +393,7 @@ public interface GoogleApiClient { * @param viewForPopups The view to use as a content view for popups. View cannot be null. */ public Builder setViewForPopups(View viewForPopups) { - // TODO + this.viewForPopups = viewForPopups; return this; } @@ -373,13 +401,13 @@ public interface GoogleApiClient { * Specify that the default account should be used when connecting to services. */ public Builder useDefaultAccount() { - // TODO + this.accountName = Constants.DEFAULT_ACCOUNT; return this; } } /** - * Provides callbacks that are called when the client is connected or disconnected from the + * 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. */ public interface ConnectionCallbacks { diff --git a/src/com/google/android/gms/common/api/Result.java b/src/com/google/android/gms/common/api/Result.java deleted file mode 100644 index d8408112..00000000 --- a/src/com/google/android/gms/common/api/Result.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.google.android.gms.common.api; - -/** - * Represents the final result of invoking an API method in Google Play Services. - */ -public interface Result { - public Status getStatus(); -} diff --git a/src/com/google/android/gms/common/api/Status.java b/src/com/google/android/gms/common/api/Status.java deleted file mode 100644 index ae65e646..00000000 --- a/src/com/google/android/gms/common/api/Status.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.google.android.gms.common.api; - -/** - * Represents the results of work. - *

- * TODO: content (is this IPC API?) - */ -public final class Status implements Result { - @Override - public Status getStatus() { - return this; - } -} diff --git a/src/com/google/android/gms/location/FusedLocationProviderApi.java b/src/com/google/android/gms/location/FusedLocationProviderApi.java new file mode 100644 index 00000000..5d5ba084 --- /dev/null +++ b/src/com/google/android/gms/location/FusedLocationProviderApi.java @@ -0,0 +1,33 @@ +package com.google.android.gms.location; + +import android.app.PendingIntent; +import android.location.Location; +import android.os.Looper; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.PendingResult; +import org.microg.gms.Constants; + +public interface FusedLocationProviderApi { + String KEY_LOCATION_CHANGED = "com.google.android.location.LOCATION"; + String KEY_MOCK_LOCATION = Constants.KEY_MOCK_LOCATION; + + public Location getLastLocation(GoogleApiClient client); + + public PendingResult requestLocationUpdates(GoogleApiClient client, LocationRequest request, + LocationListener listener); + + public PendingResult requestLocationUpdates(GoogleApiClient client, LocationRequest request, + LocationListener listener, Looper looper); + + public PendingResult requestLocationUpdates(GoogleApiClient client, LocationRequest request, + PendingIntent callbackIntent); + + public PendingResult removeLocationUpdates(GoogleApiClient client, LocationListener listener); + + public PendingResult removeLocationUpdates(GoogleApiClient client, + PendingIntent callbackIntent); + + public PendingResult setMockMode(GoogleApiClient client, boolean isMockMode); + + public PendingResult setMockLocation(GoogleApiClient client, Location mockLocation); +} diff --git a/src/com/google/android/gms/location/GeofencingApi.java b/src/com/google/android/gms/location/GeofencingApi.java new file mode 100644 index 00000000..fe2f8762 --- /dev/null +++ b/src/com/google/android/gms/location/GeofencingApi.java @@ -0,0 +1,4 @@ +package com.google.android.gms.location; + +public interface GeofencingApi { +} diff --git a/src/com/google/android/gms/location/LocationClient.java b/src/com/google/android/gms/location/LocationClient.java new file mode 100644 index 00000000..b3072880 --- /dev/null +++ b/src/com/google/android/gms/location/LocationClient.java @@ -0,0 +1,78 @@ +package com.google.android.gms.location; + +import android.content.Context; +import com.google.android.gms.common.GooglePlayServicesClient; +import com.google.android.gms.common.api.GoogleApiClient; +import org.microg.gms.common.ForwardConnectionCallbacks; +import org.microg.gms.common.ForwardConnectionFailedListener; + +@Deprecated +public class LocationClient implements GooglePlayServicesClient { + private GoogleApiClient googleApiClient; + + public LocationClient(Context context, ConnectionCallbacks callbacks) { + googleApiClient = new GoogleApiClient.Builder(context) + .addApi(LocationServices.API) + .addConnectionCallbacks(new ForwardConnectionCallbacks(callbacks)) + .build(); + } + + @Override + public void connect() { + googleApiClient.connect(); + } + + @Override + public void disconnect() { + googleApiClient.disconnect(); + } + + @Override + public boolean isConnected() { + return googleApiClient.isConnected(); + } + + @Override + public boolean isConnecting() { + return googleApiClient.isConnecting(); + } + + @Override + public void registerConnectionCallbacks(final ConnectionCallbacks listener) { + googleApiClient.registerConnectionCallbacks(new ForwardConnectionCallbacks(listener)); + } + + @Override + public boolean isConnectionCallbacksRegistered(ConnectionCallbacks listener) { + return googleApiClient + .isConnectionCallbacksRegistered(new ForwardConnectionCallbacks(listener)); + } + + @Override + public void unregisterConnectionCallbacks( + ConnectionCallbacks listener) { + googleApiClient.unregisterConnectionCallbacks(new ForwardConnectionCallbacks(listener)); + } + + @Override + public void registerConnectionFailedListener( + OnConnectionFailedListener listener) { + googleApiClient.registerConnectionFailedListener( + new ForwardConnectionFailedListener(listener)); + } + + @Override + public boolean isConnectionFailedListenerRegistered( + OnConnectionFailedListener listener) { + return googleApiClient.isConnectionFailedListenerRegistered( + new ForwardConnectionFailedListener(listener)); + } + + @Override + public void unregisterConnectionFailedListener( + OnConnectionFailedListener listener) { + googleApiClient.unregisterConnectionFailedListener( + new ForwardConnectionFailedListener(listener)); + } + +} diff --git a/src/com/google/android/gms/location/LocationListener.java b/src/com/google/android/gms/location/LocationListener.java new file mode 100644 index 00000000..63603d67 --- /dev/null +++ b/src/com/google/android/gms/location/LocationListener.java @@ -0,0 +1,4 @@ +package com.google.android.gms.location; + +public interface LocationListener { +} diff --git a/src/com/google/android/gms/location/LocationServices.java b/src/com/google/android/gms/location/LocationServices.java new file mode 100644 index 00000000..ced08aa6 --- /dev/null +++ b/src/com/google/android/gms/location/LocationServices.java @@ -0,0 +1,28 @@ +package com.google.android.gms.location; + +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; +import org.microg.gms.location.FusedLocationProviderApiImpl; +import org.microg.gms.location.GeofencingApiImpl; +import org.microg.gms.location.LocationClientImpl; + +/** + * The main entry point for location services integration. + */ +public class LocationServices { + public static final Api API = new Api<>( + new Api.Builder() { + @Override + public Api.Connection build(Context context, Looper looper, + Api.ApiOptions.NoOptions options, + AccountInfo accountInfo, GoogleApiClient.ConnectionCallbacks callbacks, + GoogleApiClient.OnConnectionFailedListener connectionFailedListener) { + return new LocationClientImpl(context); + } + }); + public static final FusedLocationProviderApi FusedLocationApi = new FusedLocationProviderApiImpl(); + public static final GeofencingApi GeofencingApi = new GeofencingApiImpl(); +} diff --git a/src/org/microg/gms/common/ForwardConnectionCallbacks.java b/src/org/microg/gms/common/ForwardConnectionCallbacks.java new file mode 100644 index 00000000..026b9340 --- /dev/null +++ b/src/org/microg/gms/common/ForwardConnectionCallbacks.java @@ -0,0 +1,35 @@ +package org.microg.gms.common; + +import android.os.Bundle; +import com.google.android.gms.common.GooglePlayServicesClient; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.location.LocationClient; + +public final class ForwardConnectionCallbacks implements GoogleApiClient.ConnectionCallbacks { + private final GooglePlayServicesClient.ConnectionCallbacks callbacks; + + public ForwardConnectionCallbacks(GooglePlayServicesClient.ConnectionCallbacks callbacks) { + this.callbacks = callbacks; + } + + @Override + public boolean equals(Object o) { + return o instanceof ForwardConnectionCallbacks && + callbacks.equals(((ForwardConnectionCallbacks) o).callbacks); + } + + @Override + public int hashCode() { + return callbacks.hashCode(); + } + + @Override + public void onConnected(Bundle connectionHint) { + callbacks.onConnected(connectionHint); + } + + @Override + public void onConnectionSuspended(int cause) { + callbacks.onDisconnected(); + } +} diff --git a/src/org/microg/gms/common/ForwardConnectionFailedListener.java b/src/org/microg/gms/common/ForwardConnectionFailedListener.java new file mode 100644 index 00000000..273383f9 --- /dev/null +++ b/src/org/microg/gms/common/ForwardConnectionFailedListener.java @@ -0,0 +1,31 @@ +package org.microg.gms.common; + +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.GooglePlayServicesClient; +import com.google.android.gms.common.api.GoogleApiClient; + +public final class ForwardConnectionFailedListener + implements GoogleApiClient.OnConnectionFailedListener { + private final GooglePlayServicesClient.OnConnectionFailedListener listener; + + public ForwardConnectionFailedListener( + GooglePlayServicesClient.OnConnectionFailedListener listener) { + this.listener = listener; + } + + @Override + public boolean equals(Object o) { + return o instanceof ForwardConnectionFailedListener && + listener.equals(((ForwardConnectionFailedListener) o).listener); + } + + @Override + public int hashCode() { + return listener.hashCode(); + } + + @Override + public void onConnectionFailed(ConnectionResult result) { + listener.onConnectionFailed(result); + } +} diff --git a/src/org/microg/gms/common/GmsClient.java b/src/org/microg/gms/common/GmsClient.java new file mode 100644 index 00000000..046c2e36 --- /dev/null +++ b/src/org/microg/gms/common/GmsClient.java @@ -0,0 +1,105 @@ +package org.microg.gms.common; + +import android.content.ComponentName; +import android.content.Context; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.os.IInterface; +import android.os.RemoteException; +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.GooglePlayServicesUtil; +import com.google.android.gms.common.api.Api; +import com.google.android.gms.common.internal.IGmsCallbacks; +import com.google.android.gms.common.internal.IGmsServiceBroker; + +public abstract class GmsClient implements Api.Connection { + private static final String TAG = "GmsClient"; + + private final Context context; + private ConnectionState state = ConnectionState.CONNECTED; + private ServiceConnection serviceConnection; + private I serviceInterface; + + public GmsClient(Context context) { + this.context = context; + } + + protected abstract String getActionString(); + + protected abstract void onConnectedToBroker(IGmsServiceBroker broker, GmsCallbacks callbacks) + throws RemoteException; + + protected abstract I interfaceFromBinder(IBinder binder); + + @Override + public void connect() { + state = ConnectionState.CONNECTING; + if (GooglePlayServicesUtil.isGooglePlayServicesAvailable(context) != + ConnectionResult.SUCCESS) { + state = ConnectionState.NOT_CONNECTED; + } else { + if (serviceConnection != null) { + MultiConnectionKeeper.getInstance(context) + .unbind(getActionString(), serviceConnection); + } + serviceConnection = new GmsServiceConnection(); + MultiConnectionKeeper.getInstance(context).bind(getActionString(), + serviceConnection); + } + } + + @Override + public void disconnect() { + serviceInterface = null; + if (serviceConnection != null) { + MultiConnectionKeeper.getInstance(context).unbind(getActionString(), serviceConnection); + serviceConnection = null; + } + state = ConnectionState.NOT_CONNECTED; + } + + @Override + public boolean isConnected() { + return state == ConnectionState.CONNECTED; + } + + public Context getContext() { + return context; + } + + public I getServiceInterface() { + return serviceInterface; + } + + private enum ConnectionState { + NOT_CONNECTED, CONNECTING, CONNECTED, ERROR + } + + private class GmsServiceConnection implements ServiceConnection { + + @Override + public void onServiceConnected(ComponentName componentName, IBinder iBinder) { + try { + onConnectedToBroker(IGmsServiceBroker.Stub.asInterface(iBinder), new GmsCallbacks()); + } catch (RemoteException e) { + disconnect(); + } + } + + @Override + public void onServiceDisconnected(ComponentName componentName) { + state = ConnectionState.ERROR; + } + } + + public class GmsCallbacks extends IGmsCallbacks.Stub { + + @Override + public void onPostInitComplete(int statusCode, IBinder binder, Bundle params) + throws RemoteException { + serviceInterface = interfaceFromBinder(binder); + } + } + +} diff --git a/src/org/microg/gms/common/MultiConnectionKeeper.java b/src/org/microg/gms/common/MultiConnectionKeeper.java new file mode 100644 index 00000000..459b9ccd --- /dev/null +++ b/src/org/microg/gms/common/MultiConnectionKeeper.java @@ -0,0 +1,136 @@ +package org.microg.gms.common; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static org.microg.gms.Constants.GMS_PACKAGE_NAME; + +public class MultiConnectionKeeper { + private static MultiConnectionKeeper INSTANCE; + + private final Context context; + private final Map connections = new HashMap<>(); + + public MultiConnectionKeeper(Context context) { + this.context = context; + } + + public static MultiConnectionKeeper getInstance(Context context) { + if (INSTANCE == null) + INSTANCE = new MultiConnectionKeeper(context); + return INSTANCE; + } + + public boolean bind(String action, ServiceConnection connection) { + Connection con = connections.get(action); + if (con != null) { + if (!con.forwardsConnection(connection)) { + con.addConnectionForward(connection); + if (!con.isBound()) + con.bind(); + } + } else { + con = new Connection(action); + con.addConnectionForward(connection); + con.bind(); + connections.put(action, con); + } + return con.isBound(); + } + + public void unbind(String action, ServiceConnection connection) { + Connection con = connections.get(action); + if (con != null) { + con.removeConnectionForward(connection); + if (!con.hasForwards() && con.isBound()) { + con.unbind(); + } + } + } + + public class Connection { + private final String actionString; + private final Set connectionForwards = new HashSet<>(); + private boolean bound = false; + private boolean connected = false; + private IBinder binder; + private ComponentName component; + private ServiceConnection serviceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName componentName, IBinder iBinder) { + binder = iBinder; + component = componentName; + for (ServiceConnection connection : connectionForwards) { + connection.onServiceConnected(componentName, iBinder); + } + connected = true; + } + + @Override + public void onServiceDisconnected(ComponentName componentName) { + binder = null; + component = componentName; + for (ServiceConnection connection : connectionForwards) { + connection.onServiceDisconnected(componentName); + } + connected = false; + } + }; + + public Connection(String actionString) { + this.actionString = actionString; + } + + public void bind() { + Intent intent = new Intent(actionString).setPackage(GMS_PACKAGE_NAME); + bound = context.bindService(intent, serviceConnection, + Context.BIND_ADJUST_WITH_ACTIVITY & Context.BIND_AUTO_CREATE); + if (!bound) { + context.unbindService(serviceConnection); + } + } + + public boolean isBound() { + return bound; + } + + public IBinder getBinder() { + return binder; + } + + public void unbind() { + context.unbindService(serviceConnection); + bound = false; + } + + public void addConnectionForward(ServiceConnection connection) { + connectionForwards.add(connection); + if (connected) { + connection.onServiceConnected(component, binder); + } + } + + public void removeConnectionForward(ServiceConnection connection) { + connectionForwards.remove(connection); + if (connected) { + connection.onServiceDisconnected(component); + } + } + + public boolean forwardsConnection(ServiceConnection connection) { + return connectionForwards.contains(connection); + } + + public boolean hasForwards() { + return !connectionForwards.isEmpty(); + } + } +} diff --git a/src/org/microg/gms/common/api/AbstractPendingResult.java b/src/org/microg/gms/common/api/AbstractPendingResult.java new file mode 100644 index 00000000..dfb00a86 --- /dev/null +++ b/src/org/microg/gms/common/api/AbstractPendingResult.java @@ -0,0 +1,95 @@ +package org.microg.gms.common.api; + +import android.os.Looper; +import com.google.android.gms.common.api.PendingResult; +import com.google.android.gms.common.api.Result; +import com.google.android.gms.common.api.ResultCallback; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class AbstractPendingResult implements PendingResult { + private final Object lock = new Object(); + private final CountDownLatch countDownLatch = new CountDownLatch(1); + private final CallbackHandler handler; + private boolean canceled; + private R result; + private ResultCallback resultCallback; + + public AbstractPendingResult(Looper looper) { + handler = new CallbackHandler(looper); + } + + private R getResult() { + synchronized (lock) { + return result; + } + } + + @Override + public R await() { + try { + countDownLatch.await(); + } catch (InterruptedException ignored) { + } + return getResult(); + } + + @Override + public R await(long time, TimeUnit unit) { + try { + countDownLatch.await(time, unit); + } catch (InterruptedException ignored) { + } + return getResult(); + } + + @Override + public void cancel() { + // TODO + } + + @Override + public boolean isCanceled() { + synchronized (lock) { + return canceled; + } + } + + public boolean isReady() { + return this.countDownLatch.getCount() == 0L; + } + + @Override + public void setResultCallback(ResultCallback callback, long time, TimeUnit unit) { + synchronized (lock) { + if (!isCanceled()) { + if (isReady()) { + handler.sendResultCallback(callback, getResult()); + } else { + handler.sendTimeoutResultCallback(this, unit.toMillis(time)); + } + } + } + } + + @Override + public void setResultCallback(ResultCallback callback) { + synchronized (lock) { + if (!isCanceled()) { + if (isReady()) { + handler.sendResultCallback(callback, getResult()); + } else { + resultCallback = callback; + } + } + } + } + + private void deliverResult(R result) { + this.result = result; + countDownLatch.countDown(); + + } + +} diff --git a/src/org/microg/gms/common/api/CallbackHandler.java b/src/org/microg/gms/common/api/CallbackHandler.java new file mode 100644 index 00000000..9d573abc --- /dev/null +++ b/src/org/microg/gms/common/api/CallbackHandler.java @@ -0,0 +1,42 @@ +package org.microg.gms.common.api; + +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import com.google.android.gms.common.api.Result; +import com.google.android.gms.common.api.ResultCallback; + +class CallbackHandler extends Handler { + public static final int CALLBACK_ON_COMPLETE = 1; + public static final int CALLBACK_ON_TIMEOUT = 2; + + public CallbackHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case CALLBACK_ON_COMPLETE: + OnCompleteObject o = (OnCompleteObject) msg.obj; + o.callback.onResult(o.result); + break; + case CALLBACK_ON_TIMEOUT: + // TODO + break; + } + } + + public void sendResultCallback(ResultCallback callback, R result) { + + } + + public void sendTimeoutResultCallback(AbstractPendingResult pendingResult, long millis) { + + } + + public static class OnCompleteObject { + public ResultCallback callback; + public R result; + } +} diff --git a/src/org/microg/gms/common/api/GoogleApiClientImpl.java b/src/org/microg/gms/common/api/GoogleApiClientImpl.java new file mode 100644 index 00000000..f77b5243 --- /dev/null +++ b/src/org/microg/gms/common/api/GoogleApiClientImpl.java @@ -0,0 +1,155 @@ +package org.microg.gms.common.api; + +import android.content.Context; +import android.os.Bundle; +import android.os.Looper; +import android.support.v4.app.FragmentActivity; +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.api.*; + +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 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 Set connectionCallbacks = new HashSet<>(); + private final Set connectionFailedListeners = new HashSet<>(); + private final ConnectionCallbacks baseConnectionCallbacks = new ConnectionCallbacks() { + @Override + public void onConnected(Bundle connectionHint) { + for (ConnectionCallbacks callback : connectionCallbacks) { + callback.onConnected(connectionHint); + } + } + + @Override + public void onConnectionSuspended(int cause) { + for (ConnectionCallbacks callback : connectionCallbacks) { + callback.onConnectionSuspended(cause); + } + } + }; + private final OnConnectionFailedListener baseConnectionFailedListener = new OnConnectionFailedListener() { + @Override + public void onConnectionFailed(ConnectionResult result) { + for (OnConnectionFailedListener listener : connectionFailedListeners) { + listener.onConnectionFailed(result); + } + } + }; + private final int clientId; + + public GoogleApiClientImpl(Context context, Looper looper, AccountInfo accountInfo, + Map apis, + Set connectionCallbacks, + Set connectionFailedListeners, int clientId) { + this.context = context; + this.looper = 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 Api.Connection 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 void connect() { + for (Api.Connection connection : apiConnections.values()) { + connection.connect(); + } + } + + @Override + public void disconnect() { + for (Api.Connection connection : apiConnections.values()) { + connection.disconnect(); + } + } + + @Override + public boolean isConnected() { + for (Api.Connection connection : apiConnections.values()) { + if (!connection.isConnected()) return false; + } + return true; + } + + @Override + public boolean isConnecting() { + return false; // TODO + } + + @Override + public boolean isConnectionCallbacksRegistered(ConnectionCallbacks listener) { + return connectionCallbacks.contains(listener); + } + + @Override + public boolean isConnectionFailedListenerRegistered( + OnConnectionFailedListener listener) { + return connectionFailedListeners.contains(listener); + } + + @Override + public void 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); + } +} diff --git a/src/org/microg/gms/location/FusedLocationProviderApiImpl.java b/src/org/microg/gms/location/FusedLocationProviderApiImpl.java new file mode 100644 index 00000000..eb4c516f --- /dev/null +++ b/src/org/microg/gms/location/FusedLocationProviderApiImpl.java @@ -0,0 +1,72 @@ +package org.microg.gms.location; + +import android.app.PendingIntent; +import android.location.Location; +import android.os.Looper; +import android.os.RemoteException; +import android.util.Log; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.PendingResult; +import com.google.android.gms.location.FusedLocationProviderApi; +import com.google.android.gms.location.LocationListener; +import com.google.android.gms.location.LocationRequest; + +public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { + private static final String TAG = "GmsFusedApiImpl"; + + @Override + public Location getLastLocation(GoogleApiClient client) { + try { + return LocationClientImpl.get(client).getLastLocation(); + } catch (RemoteException e) { + Log.w(TAG, e); + return null; + } + } + + @Override + public PendingResult requestLocationUpdates(GoogleApiClient client, LocationRequest request, + LocationListener listener) { + //LocationClientImpl.get(client).requestLocationUpdates(request, listener); + return null; + } + + @Override + public PendingResult requestLocationUpdates(GoogleApiClient client, LocationRequest request, + LocationListener listener, Looper looper) { + //LocationClientImpl.get(client).requestLocationUpdates(request, listener, looper); + return null; + } + + @Override + public PendingResult requestLocationUpdates(GoogleApiClient client, LocationRequest request, + PendingIntent callbackIntent) { + //LocationClientImpl.get(client).requestLocationUpdates(request, callbackIntent); + return null; + } + + @Override + public PendingResult removeLocationUpdates(GoogleApiClient client, LocationListener listener) { + //LocationClientImpl.get(client).removeLocationUpdates(listener); + return null; + } + + @Override + public PendingResult removeLocationUpdates(GoogleApiClient client, + PendingIntent callbackIntent) { + //LocationClientImpl.get(client).removeLocationUpdates(callbackIntent); + return null; + } + + @Override + public PendingResult setMockMode(GoogleApiClient client, boolean isMockMode) { + //LocationClientImpl.get(client).setMockMode(isMockMode); + return null; + } + + @Override + public PendingResult setMockLocation(GoogleApiClient client, Location mockLocation) { + //LocationClientImpl.get(client).setMockLocation(mockLocation); + return null; + } +} diff --git a/src/org/microg/gms/location/GeofencingApiImpl.java b/src/org/microg/gms/location/GeofencingApiImpl.java new file mode 100644 index 00000000..27cef98c --- /dev/null +++ b/src/org/microg/gms/location/GeofencingApiImpl.java @@ -0,0 +1,6 @@ +package org.microg.gms.location; + +import com.google.android.gms.location.GeofencingApi; + +public class GeofencingApiImpl implements GeofencingApi { +} diff --git a/src/org/microg/gms/location/GoogleLocationManagerClient.java b/src/org/microg/gms/location/GoogleLocationManagerClient.java new file mode 100644 index 00000000..a60a665b --- /dev/null +++ b/src/org/microg/gms/location/GoogleLocationManagerClient.java @@ -0,0 +1,35 @@ +package org.microg.gms.location; + +import android.content.Context; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import com.google.android.gms.common.internal.IGmsServiceBroker; +import com.google.android.gms.location.internal.IGoogleLocationManagerService; +import org.microg.gms.Constants; +import org.microg.gms.common.GmsClient; + +public class GoogleLocationManagerClient extends GmsClient { + public GoogleLocationManagerClient(Context context) { + super(context); + } + + @Override + protected String getActionString() { + return Constants.ACTION_GMS_LOCATION_MANAGER_SERVICE_START; + } + + @Override + protected IGoogleLocationManagerService interfaceFromBinder(IBinder binder) { + return IGoogleLocationManagerService.Stub.asInterface(binder); + } + + @Override + protected void onConnectedToBroker(IGmsServiceBroker broker, GmsCallbacks callbacks) + throws RemoteException { + Bundle bundle = new Bundle(); + bundle.putString("client_name", "locationServices"); + broker.getGoogleLocationManagerService(callbacks, Constants.MAX_REFERENCE_VERSION, + getContext().getPackageName(), bundle); + } +} diff --git a/src/org/microg/gms/location/LocationClientImpl.java b/src/org/microg/gms/location/LocationClientImpl.java new file mode 100644 index 00000000..e523c14c --- /dev/null +++ b/src/org/microg/gms/location/LocationClientImpl.java @@ -0,0 +1,61 @@ +package org.microg.gms.location; + +import android.app.PendingIntent; +import android.content.Context; +import android.location.Location; +import android.os.Looper; +import android.os.RemoteException; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.location.LocationListener; +import com.google.android.gms.location.LocationRequest; +import com.google.android.gms.location.LocationServices; +import org.microg.gms.common.api.GoogleApiClientImpl; + +public class LocationClientImpl extends GoogleLocationManagerClient { + public LocationClientImpl(Context context) { + super(context); + } + + public static LocationClientImpl get(GoogleApiClient apiClient) { + if (apiClient instanceof GoogleApiClientImpl) { + return (LocationClientImpl) ((GoogleApiClientImpl) apiClient) + .getApiConnection(LocationServices.API); + } + return null; + } + + public Location getLastLocation() throws RemoteException { + return getServiceInterface().getLastLocation(); + } + + public void requestLocationUpdates(LocationRequest request, LocationListener listener) + throws RemoteException { + + } + + public void requestLocationUpdates(LocationRequest request, PendingIntent pendingIntent) + throws RemoteException { + + } + + public void requestLocationUpdates(LocationRequest request, LocationListener listener, + Looper looper) throws RemoteException { + + } + + public void removeLocationUpdates(LocationListener listener) throws RemoteException { + + } + + public void removeLocationUpdates(PendingIntent pendingIntent) throws RemoteException { + + } + + public void setMockMode(boolean isMockMode) throws RemoteException { + + } + + public void setMockLocation(Location mockLocation) throws RemoteException { + + } +} From 6af3c0b70018610b057463c341f6bc2125ffe2f6 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Sun, 18 Jan 2015 23:57:09 +0100 Subject: [PATCH 04/65] Some work... --- .../google/android/gms/common/api/Api.java | 20 +---- .../gms/location/LocationServices.java | 7 +- src/org/microg/gms/common/GmsClient.java | 4 +- src/org/microg/gms/common/GmsConnector.java | 79 +++++++++++++++++++ .../gms/common/api/AbstractPendingResult.java | 7 +- src/org/microg/gms/common/api/ApiBuilder.java | 32 ++++++++ .../microg/gms/common/api/ApiConnection.java | 23 ++++++ .../gms/common/api/CallbackHandler.java | 10 ++- .../gms/common/api/GoogleApiClientImpl.java | 18 +++-- .../FusedLocationProviderApiImpl.java | 3 + 10 files changed, 173 insertions(+), 30 deletions(-) create mode 100644 src/org/microg/gms/common/GmsConnector.java create mode 100644 src/org/microg/gms/common/api/ApiBuilder.java create mode 100644 src/org/microg/gms/common/api/ApiConnection.java diff --git a/src/com/google/android/gms/common/api/Api.java b/src/com/google/android/gms/common/api/Api.java index d094fdd0..5a8d3cfc 100644 --- a/src/com/google/android/gms/common/api/Api.java +++ b/src/com/google/android/gms/common/api/Api.java @@ -1,7 +1,6 @@ package com.google.android.gms.common.api; -import android.content.Context; -import android.os.Looper; +import org.microg.gms.common.api.ApiBuilder; /** * Describes a section of the Google Play Services API that should be made available. Instances of @@ -17,13 +16,13 @@ import android.os.Looper; */ public final class Api { - private final Builder builder; + private final ApiBuilder builder; - public Api(Builder builder) { + public Api(ApiBuilder builder) { this.builder = builder; } - public Builder getBuilder() { + public ApiBuilder getBuilder() { return builder; } @@ -57,15 +56,4 @@ public final class Api { } } - public interface Connection { - public void connect(); - public void disconnect(); - public boolean isConnected(); - } - - public interface Builder { - Connection build(Context context, Looper looper, O options, AccountInfo accountInfo, - GoogleApiClient.ConnectionCallbacks callbacks, - GoogleApiClient.OnConnectionFailedListener connectionFailedListener); - } } diff --git a/src/com/google/android/gms/location/LocationServices.java b/src/com/google/android/gms/location/LocationServices.java index ced08aa6..db3f9b5b 100644 --- a/src/com/google/android/gms/location/LocationServices.java +++ b/src/com/google/android/gms/location/LocationServices.java @@ -4,6 +4,9 @@ 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 org.microg.gms.common.api.ApiConnection; + +import org.microg.gms.common.api.ApiBuilder; import com.google.android.gms.common.api.GoogleApiClient; import org.microg.gms.location.FusedLocationProviderApiImpl; import org.microg.gms.location.GeofencingApiImpl; @@ -14,9 +17,9 @@ import org.microg.gms.location.LocationClientImpl; */ public class LocationServices { public static final Api API = new Api<>( - new Api.Builder() { + new ApiBuilder() { @Override - public Api.Connection build(Context context, Looper looper, + public ApiConnection build(Context context, Looper looper, Api.ApiOptions.NoOptions options, AccountInfo accountInfo, GoogleApiClient.ConnectionCallbacks callbacks, GoogleApiClient.OnConnectionFailedListener connectionFailedListener) { diff --git a/src/org/microg/gms/common/GmsClient.java b/src/org/microg/gms/common/GmsClient.java index 046c2e36..14e00cd8 100644 --- a/src/org/microg/gms/common/GmsClient.java +++ b/src/org/microg/gms/common/GmsClient.java @@ -9,11 +9,11 @@ import android.os.IInterface; import android.os.RemoteException; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GooglePlayServicesUtil; -import com.google.android.gms.common.api.Api; +import org.microg.gms.common.api.ApiConnection; import com.google.android.gms.common.internal.IGmsCallbacks; import com.google.android.gms.common.internal.IGmsServiceBroker; -public abstract class GmsClient implements Api.Connection { +public abstract class GmsClient implements ApiConnection { private static final String TAG = "GmsClient"; private final Context context; diff --git a/src/org/microg/gms/common/GmsConnector.java b/src/org/microg/gms/common/GmsConnector.java new file mode 100644 index 00000000..af3c8699 --- /dev/null +++ b/src/org/microg/gms/common/GmsConnector.java @@ -0,0 +1,79 @@ +/* + * Copyright 2014-2015 µg 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; + +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; + +import com.google.android.gms.common.api.Api; + +import org.microg.gms.common.api.AbstractPendingResult; +import org.microg.gms.common.api.ApiConnection; + +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.Result; + +import org.microg.gms.common.api.GoogleApiClientImpl; + +public class GmsConnector { + public static + AbstractPendingResult connect(GoogleApiClient apiClient, Api api, Callback callback) { + Looper looper = ((GoogleApiClientImpl) apiClient).getLooper(); + final AbstractPendingResult result = new AbstractPendingResult<>(looper); + Message msg = new Message(); + msg.obj = new ConnectRequest((GoogleApiClientImpl) apiClient, api, result, callback); + new Handler(looper).sendMessage(msg); + return result; + } + + public static interface Callback { + public R onClientAvailable(C client) throws RemoteException; + } + + private static class Handler extends android.os.Handler { + private Handler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + ConnectRequest request = (ConnectRequest) msg.obj; + ApiConnection apiConnection = request.apiClient.getApiConnection(request.api); + apiConnection.connect(); + try { + request.result.setResult(request.callback.onClientAvailable((C) apiConnection)); + } catch (RemoteException ignored) { + + } + } + } + + private static class ConnectRequest { + GoogleApiClientImpl apiClient; + Api api; + AbstractPendingResult result; + Callback callback; + + private ConnectRequest(GoogleApiClientImpl apiClient, Api api, AbstractPendingResult result, Callback callback) { + this.apiClient = apiClient; + this.api = api; + this.result = result; + this.callback = callback; + } + } +} diff --git a/src/org/microg/gms/common/api/AbstractPendingResult.java b/src/org/microg/gms/common/api/AbstractPendingResult.java index dfb00a86..2b34ae43 100644 --- a/src/org/microg/gms/common/api/AbstractPendingResult.java +++ b/src/org/microg/gms/common/api/AbstractPendingResult.java @@ -11,13 +11,13 @@ import java.util.concurrent.TimeUnit; public class AbstractPendingResult implements PendingResult { private final Object lock = new Object(); private final CountDownLatch countDownLatch = new CountDownLatch(1); - private final CallbackHandler handler; + private final CallbackHandler handler; private boolean canceled; private R result; private ResultCallback resultCallback; public AbstractPendingResult(Looper looper) { - handler = new CallbackHandler(looper); + handler = new CallbackHandler(looper); } private R getResult() { @@ -92,4 +92,7 @@ public class AbstractPendingResult implements PendingResult } + public void setResult(R result) { + this.result = result; + } } diff --git a/src/org/microg/gms/common/api/ApiBuilder.java b/src/org/microg/gms/common/api/ApiBuilder.java new file mode 100644 index 00000000..14aba7d0 --- /dev/null +++ b/src/org/microg/gms/common/api/ApiBuilder.java @@ -0,0 +1,32 @@ +/* + * Copyright 2014-2015 µg 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; + +import org.microg.gms.common.api.ApiConnection; + +public interface ApiBuilder { + ApiConnection build(Context context, Looper looper, O options, AccountInfo accountInfo, + GoogleApiClient.ConnectionCallbacks callbacks, + GoogleApiClient.OnConnectionFailedListener connectionFailedListener); +} diff --git a/src/org/microg/gms/common/api/ApiConnection.java b/src/org/microg/gms/common/api/ApiConnection.java new file mode 100644 index 00000000..8f21c1bf --- /dev/null +++ b/src/org/microg/gms/common/api/ApiConnection.java @@ -0,0 +1,23 @@ +/* + * Copyright 2014-2015 µg 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 { + public void connect(); + public void disconnect(); + public boolean isConnected(); +} diff --git a/src/org/microg/gms/common/api/CallbackHandler.java b/src/org/microg/gms/common/api/CallbackHandler.java index 9d573abc..b2250f9e 100644 --- a/src/org/microg/gms/common/api/CallbackHandler.java +++ b/src/org/microg/gms/common/api/CallbackHandler.java @@ -28,7 +28,10 @@ class CallbackHandler extends Handler { } public void sendResultCallback(ResultCallback callback, R result) { - + Message message = new Message(); + message.what = CALLBACK_ON_COMPLETE; + message.obj = new OnCompleteObject(callback, result); + sendMessage(message); } public void sendTimeoutResultCallback(AbstractPendingResult pendingResult, long millis) { @@ -38,5 +41,10 @@ class CallbackHandler extends Handler { public static class OnCompleteObject { public ResultCallback callback; public R result; + + public OnCompleteObject(ResultCallback callback, R result) { + this.callback = callback; + this.result = result; + } } } diff --git a/src/org/microg/gms/common/api/GoogleApiClientImpl.java b/src/org/microg/gms/common/api/GoogleApiClientImpl.java index f77b5243..8a96332c 100644 --- a/src/org/microg/gms/common/api/GoogleApiClientImpl.java +++ b/src/org/microg/gms/common/api/GoogleApiClientImpl.java @@ -18,9 +18,10 @@ public class GoogleApiClientImpl implements GoogleApiClient { private final Looper looper; private final AccountInfo accountInfo; private final Map apis = new HashMap<>(); - private final Map apiConnections = new HashMap<>(); + private final Map apiConnections = new HashMap<>(); 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) { @@ -44,7 +45,6 @@ public class GoogleApiClientImpl implements GoogleApiClient { } } }; - private final int clientId; public GoogleApiClientImpl(Context context, Looper looper, AccountInfo accountInfo, Map apis, @@ -64,8 +64,12 @@ public class GoogleApiClientImpl implements GoogleApiClient { baseConnectionFailedListener)); } } - - public Api.Connection getApiConnection(Api api) { + + public Looper getLooper() { + return looper; + } + + public ApiConnection getApiConnection(Api api) { return apiConnections.get(api); } @@ -86,21 +90,21 @@ public class GoogleApiClientImpl implements GoogleApiClient { @Override public void connect() { - for (Api.Connection connection : apiConnections.values()) { + for (ApiConnection connection : apiConnections.values()) { connection.connect(); } } @Override public void disconnect() { - for (Api.Connection connection : apiConnections.values()) { + for (ApiConnection connection : apiConnections.values()) { connection.disconnect(); } } @Override public boolean isConnected() { - for (Api.Connection connection : apiConnections.values()) { + for (ApiConnection connection : apiConnections.values()) { if (!connection.isConnected()) return false; } return true; diff --git a/src/org/microg/gms/location/FusedLocationProviderApiImpl.java b/src/org/microg/gms/location/FusedLocationProviderApiImpl.java index eb4c516f..169b9788 100644 --- a/src/org/microg/gms/location/FusedLocationProviderApiImpl.java +++ b/src/org/microg/gms/location/FusedLocationProviderApiImpl.java @@ -11,6 +11,8 @@ import com.google.android.gms.location.FusedLocationProviderApi; import com.google.android.gms.location.LocationListener; import com.google.android.gms.location.LocationRequest; +import org.microg.gms.common.GmsConnector; + public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { private static final String TAG = "GmsFusedApiImpl"; @@ -27,6 +29,7 @@ public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { @Override public PendingResult requestLocationUpdates(GoogleApiClient client, LocationRequest request, LocationListener listener) { + //LocationClientImpl.get(client).requestLocationUpdates(request, listener); return null; } From a5be10d11b6b7077ea16a4bd87d4ea422913f326 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Mon, 19 Jan 2015 01:46:08 +0100 Subject: [PATCH 05/65] Location API mainly done --- .../location/FusedLocationProviderApi.java | 4 +- .../android/gms/location/LocationClient.java | 83 ++++++------- .../gms/location/LocationListener.java | 14 +++ src/main/res/values/version.xml | 20 ++++ src/org/microg/gms/common/GmsConnector.java | 42 +++---- .../api/AbstractPlayServicesClient.java | 90 ++++++++++++++ .../FusedLocationProviderApiImpl.java | 112 +++++++++++++----- .../gms/location/LocationClientImpl.java | 35 ++++-- 8 files changed, 288 insertions(+), 112 deletions(-) create mode 100644 src/main/res/values/version.xml create mode 100644 src/org/microg/gms/common/api/AbstractPlayServicesClient.java diff --git a/src/com/google/android/gms/location/FusedLocationProviderApi.java b/src/com/google/android/gms/location/FusedLocationProviderApi.java index 5d5ba084..c736e6ce 100644 --- a/src/com/google/android/gms/location/FusedLocationProviderApi.java +++ b/src/com/google/android/gms/location/FusedLocationProviderApi.java @@ -8,8 +8,8 @@ import com.google.android.gms.common.api.PendingResult; import org.microg.gms.Constants; public interface FusedLocationProviderApi { - String KEY_LOCATION_CHANGED = "com.google.android.location.LOCATION"; - String KEY_MOCK_LOCATION = Constants.KEY_MOCK_LOCATION; + public static final String KEY_LOCATION_CHANGED = "com.google.android.location.LOCATION"; + public static final String KEY_MOCK_LOCATION = Constants.KEY_MOCK_LOCATION; public Location getLastLocation(GoogleApiClient client); diff --git a/src/com/google/android/gms/location/LocationClient.java b/src/com/google/android/gms/location/LocationClient.java index b3072880..a8b79b0b 100644 --- a/src/com/google/android/gms/location/LocationClient.java +++ b/src/com/google/android/gms/location/LocationClient.java @@ -1,78 +1,63 @@ package com.google.android.gms.location; +import android.app.PendingIntent; import android.content.Context; +import android.location.Location; +import android.os.Looper; +import android.os.RemoteException; + import com.google.android.gms.common.GooglePlayServicesClient; import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.PendingResult; + import org.microg.gms.common.ForwardConnectionCallbacks; import org.microg.gms.common.ForwardConnectionFailedListener; +import org.microg.gms.common.api.AbstractPlayServicesClient; +import org.microg.gms.common.api.GoogleApiClientImpl; +import org.microg.gms.location.LocationClientImpl; @Deprecated -public class LocationClient implements GooglePlayServicesClient { - private GoogleApiClient googleApiClient; - - public LocationClient(Context context, ConnectionCallbacks callbacks) { - googleApiClient = new GoogleApiClient.Builder(context) +public class LocationClient extends AbstractPlayServicesClient { + public static final String KEY_LOCATION_CHANGED = "com.google.android.location.LOCATION"; + + public LocationClient(Context context, ConnectionCallbacks callbacks, + OnConnectionFailedListener connectionFailedListener) { + super(new GoogleApiClient.Builder(context) .addApi(LocationServices.API) .addConnectionCallbacks(new ForwardConnectionCallbacks(callbacks)) - .build(); + .addOnConnectionFailedListener(new ForwardConnectionFailedListener(connectionFailedListener)) + .build()); } - @Override - public void connect() { - googleApiClient.connect(); + public Location getLastLocation() { + return LocationServices.FusedLocationApi.getLastLocation(googleApiClient); } - @Override - public void disconnect() { - googleApiClient.disconnect(); + public PendingResult requestLocationUpdates(LocationRequest request, LocationListener listener) { + return LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, request, listener); } - @Override - public boolean isConnected() { - return googleApiClient.isConnected(); + public PendingResult requestLocationUpdates(LocationRequest request, LocationListener listener, Looper looper) { + return LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, request, listener, looper); } - @Override - public boolean isConnecting() { - return googleApiClient.isConnecting(); + public PendingResult requestLocationUpdates(LocationRequest request, PendingIntent callbackIntent) { + return LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, request, callbackIntent); } - @Override - public void registerConnectionCallbacks(final ConnectionCallbacks listener) { - googleApiClient.registerConnectionCallbacks(new ForwardConnectionCallbacks(listener)); + public PendingResult removeLocationUpdates(LocationListener listener) { + return LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, listener); } - @Override - public boolean isConnectionCallbacksRegistered(ConnectionCallbacks listener) { - return googleApiClient - .isConnectionCallbacksRegistered(new ForwardConnectionCallbacks(listener)); + public PendingResult removeLocationUpdates(PendingIntent callbackIntent) { + return LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, callbackIntent); } - @Override - public void unregisterConnectionCallbacks( - ConnectionCallbacks listener) { - googleApiClient.unregisterConnectionCallbacks(new ForwardConnectionCallbacks(listener)); + public PendingResult setMockMode(boolean isMockMode) { + return LocationServices.FusedLocationApi.setMockMode(googleApiClient, isMockMode); } - @Override - public void registerConnectionFailedListener( - OnConnectionFailedListener listener) { - googleApiClient.registerConnectionFailedListener( - new ForwardConnectionFailedListener(listener)); + public PendingResult setMockLocation(Location mockLocation) { + return LocationServices.FusedLocationApi.setMockLocation(googleApiClient, mockLocation); } - - @Override - public boolean isConnectionFailedListenerRegistered( - OnConnectionFailedListener listener) { - return googleApiClient.isConnectionFailedListenerRegistered( - new ForwardConnectionFailedListener(listener)); - } - - @Override - public void unregisterConnectionFailedListener( - OnConnectionFailedListener listener) { - googleApiClient.unregisterConnectionFailedListener( - new ForwardConnectionFailedListener(listener)); - } - } diff --git a/src/com/google/android/gms/location/LocationListener.java b/src/com/google/android/gms/location/LocationListener.java index 63603d67..86fedc42 100644 --- a/src/com/google/android/gms/location/LocationListener.java +++ b/src/com/google/android/gms/location/LocationListener.java @@ -1,4 +1,18 @@ package com.google.android.gms.location; +import android.location.Location; + +/** + * Used for receiving notifications from the {@link FusedLocationProviderApi} when the location has + * changed. The methods are called if the LocationListener has been registered with the location + * client. + */ public interface LocationListener { + + /** + * Called when the location has changed. + * + * @param location The updated location. + */ + public void onLocationChanged(Location location); } diff --git a/src/main/res/values/version.xml b/src/main/res/values/version.xml new file mode 100644 index 00000000..096db2c0 --- /dev/null +++ b/src/main/res/values/version.xml @@ -0,0 +1,20 @@ + + + + + 6599436 + diff --git a/src/org/microg/gms/common/GmsConnector.java b/src/org/microg/gms/common/GmsConnector.java index af3c8699..c222aad2 100644 --- a/src/org/microg/gms/common/GmsConnector.java +++ b/src/org/microg/gms/common/GmsConnector.java @@ -30,14 +30,24 @@ import com.google.android.gms.common.api.Result; import org.microg.gms.common.api.GoogleApiClientImpl; -public class GmsConnector { - public static - AbstractPendingResult connect(GoogleApiClient apiClient, Api api, Callback callback) { +public class GmsConnector { + private final GoogleApiClientImpl apiClient; + private final Api api; + private final Callback callback; + + public GmsConnector(GoogleApiClient apiClient, Api api, Callback callback) { + this.apiClient = (GoogleApiClientImpl) apiClient; + this.api = api; + this.callback = callback; + } + + + public AbstractPendingResult connect() { Looper looper = ((GoogleApiClientImpl) apiClient).getLooper(); final AbstractPendingResult result = new AbstractPendingResult<>(looper); Message msg = new Message(); - msg.obj = new ConnectRequest((GoogleApiClientImpl) apiClient, api, result, callback); - new Handler(looper).sendMessage(msg); + msg.obj = result; + new Handler(looper).sendMessage(msg); return result; } @@ -45,35 +55,21 @@ public class GmsConnector { public R onClientAvailable(C client) throws RemoteException; } - private static class Handler extends android.os.Handler { + private class Handler extends android.os.Handler { private Handler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { - ConnectRequest request = (ConnectRequest) msg.obj; - ApiConnection apiConnection = request.apiClient.getApiConnection(request.api); + AbstractPendingResult result = (AbstractPendingResult) msg.obj; + ApiConnection apiConnection = apiClient.getApiConnection(api); apiConnection.connect(); try { - request.result.setResult(request.callback.onClientAvailable((C) apiConnection)); + result.setResult(callback.onClientAvailable((C) apiConnection)); } catch (RemoteException ignored) { } } } - - private static class ConnectRequest { - GoogleApiClientImpl apiClient; - Api api; - AbstractPendingResult result; - Callback callback; - - private ConnectRequest(GoogleApiClientImpl apiClient, Api api, AbstractPendingResult result, Callback callback) { - this.apiClient = apiClient; - this.api = api; - this.result = result; - this.callback = callback; - } - } } diff --git a/src/org/microg/gms/common/api/AbstractPlayServicesClient.java b/src/org/microg/gms/common/api/AbstractPlayServicesClient.java new file mode 100644 index 00000000..6ff1be44 --- /dev/null +++ b/src/org/microg/gms/common/api/AbstractPlayServicesClient.java @@ -0,0 +1,90 @@ +/* + * Copyright 2014-2015 µg 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 com.google.android.gms.common.GooglePlayServicesClient; +import com.google.android.gms.common.api.GoogleApiClient; + +import org.microg.gms.common.ForwardConnectionCallbacks; +import org.microg.gms.common.ForwardConnectionFailedListener; + +public class AbstractPlayServicesClient implements GooglePlayServicesClient { + + protected final GoogleApiClient googleApiClient; + + public AbstractPlayServicesClient(GoogleApiClient googleApiClient) { + this.googleApiClient = googleApiClient; + } + + @Override + public void connect() { + googleApiClient.connect(); + } + + @Override + public void disconnect() { + googleApiClient.disconnect(); + } + + @Override + public boolean isConnected() { + return googleApiClient.isConnected(); + } + + @Override + public boolean isConnecting() { + return googleApiClient.isConnecting(); + } + + @Override + public void registerConnectionCallbacks(final ConnectionCallbacks listener) { + googleApiClient.registerConnectionCallbacks(new ForwardConnectionCallbacks(listener)); + } + + @Override + public boolean isConnectionCallbacksRegistered(ConnectionCallbacks listener) { + return googleApiClient + .isConnectionCallbacksRegistered(new ForwardConnectionCallbacks(listener)); + } + + @Override + public void unregisterConnectionCallbacks( + ConnectionCallbacks listener) { + googleApiClient.unregisterConnectionCallbacks(new ForwardConnectionCallbacks(listener)); + } + + @Override + public void registerConnectionFailedListener( + OnConnectionFailedListener listener) { + googleApiClient.registerConnectionFailedListener( + new ForwardConnectionFailedListener(listener)); + } + + @Override + public boolean isConnectionFailedListenerRegistered( + OnConnectionFailedListener listener) { + return googleApiClient.isConnectionFailedListenerRegistered( + new ForwardConnectionFailedListener(listener)); + } + + @Override + public void unregisterConnectionFailedListener( + OnConnectionFailedListener listener) { + googleApiClient.unregisterConnectionFailedListener( + new ForwardConnectionFailedListener(listener)); + } +} diff --git a/src/org/microg/gms/location/FusedLocationProviderApiImpl.java b/src/org/microg/gms/location/FusedLocationProviderApiImpl.java index 169b9788..c3f5f36f 100644 --- a/src/org/microg/gms/location/FusedLocationProviderApiImpl.java +++ b/src/org/microg/gms/location/FusedLocationProviderApiImpl.java @@ -5,17 +5,22 @@ import android.location.Location; import android.os.Looper; import android.os.RemoteException; import android.util.Log; + import com.google.android.gms.common.api.GoogleApiClient; 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 com.google.android.gms.location.FusedLocationProviderApi; import com.google.android.gms.location.LocationListener; import com.google.android.gms.location.LocationRequest; +import com.google.android.gms.location.LocationServices; import org.microg.gms.common.GmsConnector; +import org.microg.gms.common.api.ApiConnection; public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { private static final String TAG = "GmsFusedApiImpl"; - + @Override public Location getLastLocation(GoogleApiClient client) { try { @@ -27,49 +32,98 @@ public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { } @Override - public PendingResult requestLocationUpdates(GoogleApiClient client, LocationRequest request, - LocationListener listener) { - - //LocationClientImpl.get(client).requestLocationUpdates(request, listener); - return null; + public PendingResult requestLocationUpdates(GoogleApiClient client, + final LocationRequest request, + final LocationListener listener) { + return callVoid(client, new Runnable() { + @Override + public void run(LocationClientImpl client) throws RemoteException { + client.requestLocationUpdates(request, listener); + } + }); } @Override - public PendingResult requestLocationUpdates(GoogleApiClient client, LocationRequest request, - LocationListener listener, Looper looper) { - //LocationClientImpl.get(client).requestLocationUpdates(request, listener, looper); - return null; + public PendingResult requestLocationUpdates(GoogleApiClient client, + final LocationRequest request, + final LocationListener listener, + final Looper looper) { + return callVoid(client, new Runnable() { + @Override + public void run(LocationClientImpl client) throws RemoteException { + client.requestLocationUpdates(request, listener, looper); + } + }); } @Override - public PendingResult requestLocationUpdates(GoogleApiClient client, LocationRequest request, - PendingIntent callbackIntent) { - //LocationClientImpl.get(client).requestLocationUpdates(request, callbackIntent); - return null; - } - - @Override - public PendingResult removeLocationUpdates(GoogleApiClient client, LocationListener listener) { - //LocationClientImpl.get(client).removeLocationUpdates(listener); - return null; + public PendingResult requestLocationUpdates(GoogleApiClient client, + final LocationRequest request, + final PendingIntent callbackIntent) { + return callVoid(client, new Runnable() { + @Override + public void run(LocationClientImpl client) throws RemoteException { + client.requestLocationUpdates(request, callbackIntent); + } + }); } @Override public PendingResult removeLocationUpdates(GoogleApiClient client, - PendingIntent callbackIntent) { - //LocationClientImpl.get(client).removeLocationUpdates(callbackIntent); - return null; + final LocationListener listener) { + return callVoid(client, new Runnable() { + @Override + public void run(LocationClientImpl client) throws RemoteException { + client.removeLocationUpdates(listener); + } + }); } @Override - public PendingResult setMockMode(GoogleApiClient client, boolean isMockMode) { - //LocationClientImpl.get(client).setMockMode(isMockMode); - return null; + public PendingResult removeLocationUpdates(GoogleApiClient client, + final PendingIntent callbackIntent) { + return callVoid(client, new Runnable() { + @Override + public void run(LocationClientImpl client) throws RemoteException { + client.removeLocationUpdates(callbackIntent); + } + }); } @Override - public PendingResult setMockLocation(GoogleApiClient client, Location mockLocation) { - //LocationClientImpl.get(client).setMockLocation(mockLocation); - return null; + public PendingResult setMockMode(GoogleApiClient client, final boolean isMockMode) { + return callVoid(client, new Runnable() { + @Override + public void run(LocationClientImpl client) throws RemoteException { + client.setMockMode(isMockMode); + } + }); } + + @Override + public PendingResult setMockLocation(GoogleApiClient client, final Location mockLocation) { + return callVoid(client, new Runnable() { + @Override + public void run(LocationClientImpl client) throws RemoteException { + client.setMockLocation(mockLocation); + } + }); + } + + private PendingResult callVoid(GoogleApiClient client, final Runnable runnable) { + return new GmsConnector<>(client, LocationServices.API, + new GmsConnector.Callback() { + @Override + public Result onClientAvailable(LocationClientImpl client) throws + RemoteException { + runnable.run(client); + return Status.SUCCESS; + } + }).connect(); + } + + private interface Runnable { + public void run(LocationClientImpl client) throws RemoteException; + } + } diff --git a/src/org/microg/gms/location/LocationClientImpl.java b/src/org/microg/gms/location/LocationClientImpl.java index e523c14c..1bb56a9a 100644 --- a/src/org/microg/gms/location/LocationClientImpl.java +++ b/src/org/microg/gms/location/LocationClientImpl.java @@ -5,12 +5,18 @@ import android.content.Context; import android.location.Location; import android.os.Looper; import android.os.RemoteException; + import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.location.ILocationListener; import com.google.android.gms.location.LocationListener; import com.google.android.gms.location.LocationRequest; import com.google.android.gms.location.LocationServices; + import org.microg.gms.common.api.GoogleApiClientImpl; +import java.util.HashMap; +import java.util.Map; + public class LocationClientImpl extends GoogleLocationManagerClient { public LocationClientImpl(Context context) { super(context); @@ -24,38 +30,49 @@ public class LocationClientImpl extends GoogleLocationManagerClient { return null; } + private Map listenerMap = new HashMap<>(); + public Location getLastLocation() throws RemoteException { return getServiceInterface().getLastLocation(); } - public void requestLocationUpdates(LocationRequest request, LocationListener listener) + public void requestLocationUpdates(LocationRequest request, final LocationListener listener) throws RemoteException { - + ILocationListener iLocationListener = new ILocationListener.Stub() { + @Override + public void onLocationChanged(Location location) throws RemoteException { + listener.onLocationChanged(location); + } + }; + listenerMap.put(listener, iLocationListener); + getServiceInterface().requestLocationUpdatesWithPackage(request, + iLocationListener, getContext().getPackageName()); } public void requestLocationUpdates(LocationRequest request, PendingIntent pendingIntent) throws RemoteException { - + getServiceInterface().requestLocationUpdatesWithIntent(request, pendingIntent); } public void requestLocationUpdates(LocationRequest request, LocationListener listener, - Looper looper) throws RemoteException { - + Looper looper) throws RemoteException { + requestLocationUpdates(request, listener); // TODO } public void removeLocationUpdates(LocationListener listener) throws RemoteException { - + getServiceInterface().removeLocationUpdatesWithListener(listenerMap.get(listener)); + listenerMap.remove(listener); } public void removeLocationUpdates(PendingIntent pendingIntent) throws RemoteException { - + getServiceInterface().removeLocationUpdatesWithIntent(pendingIntent); } public void setMockMode(boolean isMockMode) throws RemoteException { - + getServiceInterface().setMockMode(isMockMode); } public void setMockLocation(Location mockLocation) throws RemoteException { - + getServiceInterface().setMockLocation(mockLocation); } } From 745a1d0fef02b2508da0b27fcd4ad205a46446e7 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Wed, 21 Jan 2015 23:45:21 +0100 Subject: [PATCH 06/65] README --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6fb94c76..f3f240f3 100644 --- a/README.md +++ b/README.md @@ -1 +1,5 @@ -Do not link against this repo, as it is will be renamed later +GmsLib +====== +This library is a compatibility implementation of the often used play-services library. + +It will try to use the Play Services when installed on the target device. If this is not possible, a basic fallback implementation might be used. From 863c9693ac86b9c203bfb9f6116f1528dd984aed Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Thu, 22 Jan 2015 03:07:09 +0100 Subject: [PATCH 07/65] Basic Location API working. Tested using deprecated LocationClient calls in DashClock --- .../android/gms/common/ConnectionResult.java | 1 + .../gms/common/GooglePlayServicesUtil.java | 18 +++++++---- .../google/android/gms/common/api/Api.java | 4 +-- .../gms/common/api/GoogleApiClient.java | 2 ++ .../android/gms/common/api/PendingResult.java | 7 +++-- .../gms/common/api/ResultCallback.java | 3 +- .../location/FusedLocationProviderApi.java | 2 ++ .../android/gms/location/LocationClient.java | 30 +++++++++++-------- .../gms/location/LocationServices.java | 10 ++++--- .../common/ForwardConnectionCallbacks.java | 2 +- src/org/microg/gms/common/GmsClient.java | 20 ++++++++++--- src/org/microg/gms/common/GmsConnector.java | 8 ++--- .../gms/common/MultiConnectionKeeper.java | 12 ++++++-- .../gms/common/api/AbstractPendingResult.java | 5 ++-- src/org/microg/gms/common/api/ApiBuilder.java | 6 ++-- .../microg/gms/common/api/ApiConnection.java | 2 ++ .../gms/common/api/GoogleApiClientImpl.java | 23 ++++++++++++++ .../FusedLocationProviderApiImpl.java | 16 ++++------ .../location/GoogleLocationManagerClient.java | 8 +++-- .../gms/location/LocationClientImpl.java | 11 +++++-- 20 files changed, 128 insertions(+), 62 deletions(-) diff --git a/src/com/google/android/gms/common/ConnectionResult.java b/src/com/google/android/gms/common/ConnectionResult.java index ae65ae68..155f0508 100644 --- a/src/com/google/android/gms/common/ConnectionResult.java +++ b/src/com/google/android/gms/common/ConnectionResult.java @@ -4,6 +4,7 @@ import android.app.Activity; import android.app.PendingIntent; import android.content.Intent; import android.content.IntentSender; + import com.google.android.gms.common.api.GoogleApiClient; /** diff --git a/src/com/google/android/gms/common/GooglePlayServicesUtil.java b/src/com/google/android/gms/common/GooglePlayServicesUtil.java index a32adf04..40cc045f 100644 --- a/src/com/google/android/gms/common/GooglePlayServicesUtil.java +++ b/src/com/google/android/gms/common/GooglePlayServicesUtil.java @@ -7,6 +7,8 @@ import android.app.PendingIntent; import android.content.Context; import android.content.DialogInterface; import android.content.pm.PackageManager; +import android.util.Log; + import org.microg.gms.Constants; /** @@ -17,6 +19,8 @@ import org.microg.gms.Constants; * TODO: methods :) */ public class GooglePlayServicesUtil { + private static final String TAG = "GooglePlayServicesUtil"; + public static final String GMS_ERROR_DIALOG = "GooglePlayServicesErrorDialog"; public static final String GOOGLE_PLAY_SERVICES_PACKAGE = "com.google.android.gms"; public static final int GOOGLE_PLAY_SERVICES_VERSION_CODE = Constants.MAX_REFERENCE_VERSION; @@ -27,12 +31,12 @@ public class GooglePlayServicesUtil { } public static Dialog getErrorDialog(int errorCode, Activity activity, int requestCode, - DialogInterface.OnCancelListener cancelListener) { + DialogInterface.OnCancelListener cancelListener) { return null; // TODO } public static PendingIntent getErrorPendingIntent(int errorCode, Activity activity, - int requestCode) { + int requestCode) { return null; // TODO } @@ -49,7 +53,8 @@ public class GooglePlayServicesUtil { } public static int isGooglePlayServicesAvailable(Context context) { - // As we can't know right now if the later desired feature is available, we just pretend it to be. + Log.d(TAG, "As we can't know right now if the later desired feature is available, " + + "we just pretend it to be."); return ConnectionResult.SUCCESS; } @@ -62,17 +67,18 @@ public class GooglePlayServicesUtil { } public static boolean showErrorDialogFragment(int errorCode, Activity activity, - int requestCode) { + int requestCode) { return false; // TODO } public static boolean showErrorDialogFragment(int errorCode, Activity activity, - Fragment fragment, int requestCode, DialogInterface.OnCancelListener cancelListener) { + Fragment fragment, int requestCode, + DialogInterface.OnCancelListener cancelListener) { return false; // TODO } public static boolean showErrorDialogFragment(int errorCode, Activity activity, int requestCode, - DialogInterface.OnCancelListener cancelListener) { + DialogInterface.OnCancelListener cancelListener) { return false; // TODO } diff --git a/src/com/google/android/gms/common/api/Api.java b/src/com/google/android/gms/common/api/Api.java index 5a8d3cfc..ffbeeddc 100644 --- a/src/com/google/android/gms/common/api/Api.java +++ b/src/com/google/android/gms/common/api/Api.java @@ -15,9 +15,9 @@ import org.microg.gms.common.api.ApiBuilder; * See {@link GoogleApiClient.Builder} for usage examples. */ public final class Api { - + private final ApiBuilder builder; - + public Api(ApiBuilder builder) { this.builder = builder; } diff --git a/src/com/google/android/gms/common/api/GoogleApiClient.java b/src/com/google/android/gms/common/api/GoogleApiClient.java index 3f024457..92a2ee64 100644 --- a/src/com/google/android/gms/common/api/GoogleApiClient.java +++ b/src/com/google/android/gms/common/api/GoogleApiClient.java @@ -7,7 +7,9 @@ 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.Constants; import org.microg.gms.common.api.GoogleApiClientImpl; diff --git a/src/com/google/android/gms/common/api/PendingResult.java b/src/com/google/android/gms/common/api/PendingResult.java index 8845a8d7..575fc791 100644 --- a/src/com/google/android/gms/common/api/PendingResult.java +++ b/src/com/google/android/gms/common/api/PendingResult.java @@ -21,12 +21,15 @@ import java.util.concurrent.TimeUnit; */ 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. + * 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. + * 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); diff --git a/src/com/google/android/gms/common/api/ResultCallback.java b/src/com/google/android/gms/common/api/ResultCallback.java index ef8e8eb2..08335d5b 100644 --- a/src/com/google/android/gms/common/api/ResultCallback.java +++ b/src/com/google/android/gms/common/api/ResultCallback.java @@ -1,7 +1,8 @@ package com.google.android.gms.common.api; /** - * An interface for receiving a {@link Result} from a {@link PendingResult} as an asynchronous callback. + * An interface for receiving a {@link Result} from a {@link PendingResult} as an asynchronous + * callback. */ public interface ResultCallback { /** diff --git a/src/com/google/android/gms/location/FusedLocationProviderApi.java b/src/com/google/android/gms/location/FusedLocationProviderApi.java index c736e6ce..46a91060 100644 --- a/src/com/google/android/gms/location/FusedLocationProviderApi.java +++ b/src/com/google/android/gms/location/FusedLocationProviderApi.java @@ -3,8 +3,10 @@ package com.google.android.gms.location; import android.app.PendingIntent; import android.location.Location; import android.os.Looper; + import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.PendingResult; + import org.microg.gms.Constants; public interface FusedLocationProviderApi { diff --git a/src/com/google/android/gms/location/LocationClient.java b/src/com/google/android/gms/location/LocationClient.java index a8b79b0b..130c9ed9 100644 --- a/src/com/google/android/gms/location/LocationClient.java +++ b/src/com/google/android/gms/location/LocationClient.java @@ -4,28 +4,25 @@ import android.app.PendingIntent; import android.content.Context; import android.location.Location; import android.os.Looper; -import android.os.RemoteException; -import com.google.android.gms.common.GooglePlayServicesClient; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.PendingResult; import org.microg.gms.common.ForwardConnectionCallbacks; import org.microg.gms.common.ForwardConnectionFailedListener; import org.microg.gms.common.api.AbstractPlayServicesClient; -import org.microg.gms.common.api.GoogleApiClientImpl; -import org.microg.gms.location.LocationClientImpl; @Deprecated public class LocationClient extends AbstractPlayServicesClient { public static final String KEY_LOCATION_CHANGED = "com.google.android.location.LOCATION"; public LocationClient(Context context, ConnectionCallbacks callbacks, - OnConnectionFailedListener connectionFailedListener) { + OnConnectionFailedListener connectionFailedListener) { super(new GoogleApiClient.Builder(context) .addApi(LocationServices.API) .addConnectionCallbacks(new ForwardConnectionCallbacks(callbacks)) - .addOnConnectionFailedListener(new ForwardConnectionFailedListener(connectionFailedListener)) + .addOnConnectionFailedListener(new ForwardConnectionFailedListener + (connectionFailedListener)) .build()); } @@ -33,16 +30,22 @@ public class LocationClient extends AbstractPlayServicesClient { return LocationServices.FusedLocationApi.getLastLocation(googleApiClient); } - public PendingResult requestLocationUpdates(LocationRequest request, LocationListener listener) { - return LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, request, listener); + public PendingResult requestLocationUpdates(LocationRequest request, + LocationListener listener) { + return LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, request, + listener); } - public PendingResult requestLocationUpdates(LocationRequest request, LocationListener listener, Looper looper) { - return LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, request, listener, looper); + public PendingResult requestLocationUpdates(LocationRequest request, + LocationListener listener, Looper looper) { + return LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, request, + listener, looper); } - public PendingResult requestLocationUpdates(LocationRequest request, PendingIntent callbackIntent) { - return LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, request, callbackIntent); + public PendingResult requestLocationUpdates(LocationRequest request, + PendingIntent callbackIntent) { + return LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, request, + callbackIntent); } public PendingResult removeLocationUpdates(LocationListener listener) { @@ -50,7 +53,8 @@ public class LocationClient extends AbstractPlayServicesClient { } public PendingResult removeLocationUpdates(PendingIntent callbackIntent) { - return LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, callbackIntent); + return LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, + callbackIntent); } public PendingResult setMockMode(boolean isMockMode) { diff --git a/src/com/google/android/gms/location/LocationServices.java b/src/com/google/android/gms/location/LocationServices.java index db3f9b5b..c0949f43 100644 --- a/src/com/google/android/gms/location/LocationServices.java +++ b/src/com/google/android/gms/location/LocationServices.java @@ -2,12 +2,13 @@ package com.google.android.gms.location; 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 org.microg.gms.common.api.ApiConnection; +import com.google.android.gms.common.api.GoogleApiClient; import org.microg.gms.common.api.ApiBuilder; -import com.google.android.gms.common.api.GoogleApiClient; +import org.microg.gms.common.api.ApiConnection; import org.microg.gms.location.FusedLocationProviderApiImpl; import org.microg.gms.location.GeofencingApiImpl; import org.microg.gms.location.LocationClientImpl; @@ -23,9 +24,10 @@ public class LocationServices { Api.ApiOptions.NoOptions options, AccountInfo accountInfo, GoogleApiClient.ConnectionCallbacks callbacks, GoogleApiClient.OnConnectionFailedListener connectionFailedListener) { - return new LocationClientImpl(context); + return new LocationClientImpl(context, callbacks, connectionFailedListener); } }); - public static final FusedLocationProviderApi FusedLocationApi = new FusedLocationProviderApiImpl(); + public static final FusedLocationProviderApi FusedLocationApi = new + FusedLocationProviderApiImpl(); public static final GeofencingApi GeofencingApi = new GeofencingApiImpl(); } diff --git a/src/org/microg/gms/common/ForwardConnectionCallbacks.java b/src/org/microg/gms/common/ForwardConnectionCallbacks.java index 026b9340..de4d9203 100644 --- a/src/org/microg/gms/common/ForwardConnectionCallbacks.java +++ b/src/org/microg/gms/common/ForwardConnectionCallbacks.java @@ -1,9 +1,9 @@ package org.microg.gms.common; import android.os.Bundle; + import com.google.android.gms.common.GooglePlayServicesClient; import com.google.android.gms.common.api.GoogleApiClient; -import com.google.android.gms.location.LocationClient; public final class ForwardConnectionCallbacks implements GoogleApiClient.ConnectionCallbacks { private final GooglePlayServicesClient.ConnectionCallbacks callbacks; diff --git a/src/org/microg/gms/common/GmsClient.java b/src/org/microg/gms/common/GmsClient.java index 14e00cd8..e990ab27 100644 --- a/src/org/microg/gms/common/GmsClient.java +++ b/src/org/microg/gms/common/GmsClient.java @@ -7,22 +7,31 @@ import android.os.Bundle; import android.os.IBinder; import android.os.IInterface; import android.os.RemoteException; +import android.util.Log; + import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GooglePlayServicesUtil; -import org.microg.gms.common.api.ApiConnection; +import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.internal.IGmsCallbacks; import com.google.android.gms.common.internal.IGmsServiceBroker; +import org.microg.gms.common.api.ApiConnection; + public abstract class GmsClient implements ApiConnection { private static final String TAG = "GmsClient"; private final Context context; + private final GoogleApiClient.ConnectionCallbacks callbacks; + private final GoogleApiClient.OnConnectionFailedListener connectionFailedListener; private ConnectionState state = ConnectionState.CONNECTED; private ServiceConnection serviceConnection; private I serviceInterface; - public GmsClient(Context context) { + public GmsClient(Context context, GoogleApiClient.ConnectionCallbacks callbacks, + GoogleApiClient.OnConnectionFailedListener connectionFailedListener) { this.context = context; + this.callbacks = callbacks; + this.connectionFailedListener = connectionFailedListener; } protected abstract String getActionString(); @@ -67,7 +76,7 @@ public abstract class GmsClient implements ApiConnection { public Context getContext() { return context; } - + public I getServiceInterface() { return serviceInterface; } @@ -81,7 +90,9 @@ public abstract class GmsClient implements ApiConnection { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { try { - onConnectedToBroker(IGmsServiceBroker.Stub.asInterface(iBinder), new GmsCallbacks()); + Log.d(TAG, "Connecting to broker for " + componentName); + onConnectedToBroker(IGmsServiceBroker.Stub.asInterface(iBinder), + new GmsCallbacks()); } catch (RemoteException e) { disconnect(); } @@ -99,6 +110,7 @@ public abstract class GmsClient implements ApiConnection { public void onPostInitComplete(int statusCode, IBinder binder, Bundle params) throws RemoteException { serviceInterface = interfaceFromBinder(binder); + callbacks.onConnected(params); } } diff --git a/src/org/microg/gms/common/GmsConnector.java b/src/org/microg/gms/common/GmsConnector.java index c222aad2..3510c956 100644 --- a/src/org/microg/gms/common/GmsConnector.java +++ b/src/org/microg/gms/common/GmsConnector.java @@ -21,13 +21,11 @@ import android.os.Message; import android.os.RemoteException; import com.google.android.gms.common.api.Api; - -import org.microg.gms.common.api.AbstractPendingResult; -import org.microg.gms.common.api.ApiConnection; - import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.Result; +import org.microg.gms.common.api.AbstractPendingResult; +import org.microg.gms.common.api.ApiConnection; import org.microg.gms.common.api.GoogleApiClientImpl; public class GmsConnector { @@ -43,7 +41,7 @@ public class GmsConnector connect() { - Looper looper = ((GoogleApiClientImpl) apiClient).getLooper(); + Looper looper = apiClient.getLooper(); final AbstractPendingResult result = new AbstractPendingResult<>(looper); Message msg = new Message(); msg.obj = result; diff --git a/src/org/microg/gms/common/MultiConnectionKeeper.java b/src/org/microg/gms/common/MultiConnectionKeeper.java index 459b9ccd..e7414f71 100644 --- a/src/org/microg/gms/common/MultiConnectionKeeper.java +++ b/src/org/microg/gms/common/MultiConnectionKeeper.java @@ -5,6 +5,7 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; +import android.util.Log; import java.util.HashMap; import java.util.HashSet; @@ -14,6 +15,8 @@ import java.util.Set; import static org.microg.gms.Constants.GMS_PACKAGE_NAME; public class MultiConnectionKeeper { + private static final String TAG = "GmsMultiConnectionKeeper"; + private static MultiConnectionKeeper INSTANCE; private final Context context; @@ -45,7 +48,7 @@ public class MultiConnectionKeeper { } return con.isBound(); } - + public void unbind(String action, ServiceConnection connection) { Connection con = connections.get(action); if (con != null) { @@ -68,6 +71,7 @@ public class MultiConnectionKeeper { public void onServiceConnected(ComponentName componentName, IBinder iBinder) { binder = iBinder; component = componentName; + Log.d(TAG, "bound to " + actionString); for (ServiceConnection connection : connectionForwards) { connection.onServiceConnected(componentName, iBinder); } @@ -92,7 +96,8 @@ public class MultiConnectionKeeper { public void bind() { Intent intent = new Intent(actionString).setPackage(GMS_PACKAGE_NAME); bound = context.bindService(intent, serviceConnection, - Context.BIND_ADJUST_WITH_ACTIVITY & Context.BIND_AUTO_CREATE); + Context.BIND_ADJUST_WITH_ACTIVITY | Context.BIND_AUTO_CREATE); + Log.d(TAG, "binding to " + actionString + ": " + bound); if (!bound) { context.unbindService(serviceConnection); } @@ -108,6 +113,7 @@ public class MultiConnectionKeeper { public void unbind() { context.unbindService(serviceConnection); + Log.d(TAG, "unbinding from " + actionString); bound = false; } @@ -128,7 +134,7 @@ public class MultiConnectionKeeper { public boolean forwardsConnection(ServiceConnection connection) { return connectionForwards.contains(connection); } - + public boolean hasForwards() { return !connectionForwards.isEmpty(); } diff --git a/src/org/microg/gms/common/api/AbstractPendingResult.java b/src/org/microg/gms/common/api/AbstractPendingResult.java index 2b34ae43..e52fb847 100644 --- a/src/org/microg/gms/common/api/AbstractPendingResult.java +++ b/src/org/microg/gms/common/api/AbstractPendingResult.java @@ -1,6 +1,7 @@ package org.microg.gms.common.api; import android.os.Looper; + import com.google.android.gms.common.api.PendingResult; import com.google.android.gms.common.api.Result; import com.google.android.gms.common.api.ResultCallback; @@ -85,11 +86,11 @@ public class AbstractPendingResult implements PendingResult } } } - + private void deliverResult(R result) { this.result = result; countDownLatch.countDown(); - + } public void setResult(R result) { diff --git a/src/org/microg/gms/common/api/ApiBuilder.java b/src/org/microg/gms/common/api/ApiBuilder.java index 14aba7d0..4ffffebe 100644 --- a/src/org/microg/gms/common/api/ApiBuilder.java +++ b/src/org/microg/gms/common/api/ApiBuilder.java @@ -23,10 +23,8 @@ 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 org.microg.gms.common.api.ApiConnection; - public interface ApiBuilder { ApiConnection build(Context context, Looper looper, O options, AccountInfo accountInfo, - GoogleApiClient.ConnectionCallbacks callbacks, - GoogleApiClient.OnConnectionFailedListener connectionFailedListener); + GoogleApiClient.ConnectionCallbacks callbacks, + GoogleApiClient.OnConnectionFailedListener connectionFailedListener); } diff --git a/src/org/microg/gms/common/api/ApiConnection.java b/src/org/microg/gms/common/api/ApiConnection.java index 8f21c1bf..24a80b4a 100644 --- a/src/org/microg/gms/common/api/ApiConnection.java +++ b/src/org/microg/gms/common/api/ApiConnection.java @@ -18,6 +18,8 @@ package org.microg.gms.common.api; public interface ApiConnection { public void connect(); + public void disconnect(); + public boolean isConnected(); } diff --git a/src/org/microg/gms/common/api/GoogleApiClientImpl.java b/src/org/microg/gms/common/api/GoogleApiClientImpl.java index 8a96332c..ed67ad31 100644 --- a/src/org/microg/gms/common/api/GoogleApiClientImpl.java +++ b/src/org/microg/gms/common/api/GoogleApiClientImpl.java @@ -2,7 +2,9 @@ package org.microg.gms.common.api; import android.content.Context; import android.os.Bundle; +import android.os.Handler; import android.os.Looper; +import android.os.Message; import android.support.v4.app.FragmentActivity; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.api.*; @@ -19,6 +21,7 @@ public class GoogleApiClientImpl implements GoogleApiClient { 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; @@ -52,6 +55,7 @@ public class GoogleApiClientImpl implements GoogleApiClient { 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); @@ -156,4 +160,23 @@ public class GoogleApiClientImpl implements GoogleApiClient { 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/src/org/microg/gms/location/FusedLocationProviderApiImpl.java b/src/org/microg/gms/location/FusedLocationProviderApiImpl.java index c3f5f36f..708f4263 100644 --- a/src/org/microg/gms/location/FusedLocationProviderApiImpl.java +++ b/src/org/microg/gms/location/FusedLocationProviderApiImpl.java @@ -16,7 +16,6 @@ import com.google.android.gms.location.LocationRequest; import com.google.android.gms.location.LocationServices; import org.microg.gms.common.GmsConnector; -import org.microg.gms.common.api.ApiConnection; public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { private static final String TAG = "GmsFusedApiImpl"; @@ -33,8 +32,7 @@ public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { @Override public PendingResult requestLocationUpdates(GoogleApiClient client, - final LocationRequest request, - final LocationListener listener) { + final LocationRequest request, final LocationListener listener) { return callVoid(client, new Runnable() { @Override public void run(LocationClientImpl client) throws RemoteException { @@ -45,9 +43,8 @@ public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { @Override public PendingResult requestLocationUpdates(GoogleApiClient client, - final LocationRequest request, - final LocationListener listener, - final Looper looper) { + final LocationRequest request, final LocationListener listener, + final Looper looper) { return callVoid(client, new Runnable() { @Override public void run(LocationClientImpl client) throws RemoteException { @@ -58,8 +55,7 @@ public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { @Override public PendingResult requestLocationUpdates(GoogleApiClient client, - final LocationRequest request, - final PendingIntent callbackIntent) { + final LocationRequest request, final PendingIntent callbackIntent) { return callVoid(client, new Runnable() { @Override public void run(LocationClientImpl client) throws RemoteException { @@ -70,7 +66,7 @@ public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { @Override public PendingResult removeLocationUpdates(GoogleApiClient client, - final LocationListener listener) { + final LocationListener listener) { return callVoid(client, new Runnable() { @Override public void run(LocationClientImpl client) throws RemoteException { @@ -81,7 +77,7 @@ public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { @Override public PendingResult removeLocationUpdates(GoogleApiClient client, - final PendingIntent callbackIntent) { + final PendingIntent callbackIntent) { return callVoid(client, new Runnable() { @Override public void run(LocationClientImpl client) throws RemoteException { diff --git a/src/org/microg/gms/location/GoogleLocationManagerClient.java b/src/org/microg/gms/location/GoogleLocationManagerClient.java index a60a665b..93f7fba1 100644 --- a/src/org/microg/gms/location/GoogleLocationManagerClient.java +++ b/src/org/microg/gms/location/GoogleLocationManagerClient.java @@ -4,14 +4,18 @@ import android.content.Context; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; + +import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.internal.IGmsServiceBroker; import com.google.android.gms.location.internal.IGoogleLocationManagerService; + import org.microg.gms.Constants; import org.microg.gms.common.GmsClient; public class GoogleLocationManagerClient extends GmsClient { - public GoogleLocationManagerClient(Context context) { - super(context); + public GoogleLocationManagerClient(Context context, GoogleApiClient.ConnectionCallbacks + callbacks, GoogleApiClient.OnConnectionFailedListener connectionFailedListener) { + super(context, callbacks, connectionFailedListener); } @Override diff --git a/src/org/microg/gms/location/LocationClientImpl.java b/src/org/microg/gms/location/LocationClientImpl.java index 1bb56a9a..07aac0f3 100644 --- a/src/org/microg/gms/location/LocationClientImpl.java +++ b/src/org/microg/gms/location/LocationClientImpl.java @@ -5,6 +5,7 @@ import android.content.Context; import android.location.Location; import android.os.Looper; import android.os.RemoteException; +import android.util.Log; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.location.ILocationListener; @@ -18,8 +19,12 @@ import java.util.HashMap; import java.util.Map; public class LocationClientImpl extends GoogleLocationManagerClient { - public LocationClientImpl(Context context) { - super(context); + private static final String TAG = "GmsLocationClientImpl"; + + public LocationClientImpl(Context context, GoogleApiClient.ConnectionCallbacks callbacks, + GoogleApiClient.OnConnectionFailedListener connectionFailedListener) { + super(context, callbacks, connectionFailedListener); + Log.d(TAG, ""); } public static LocationClientImpl get(GoogleApiClient apiClient) { @@ -55,7 +60,7 @@ public class LocationClientImpl extends GoogleLocationManagerClient { } public void requestLocationUpdates(LocationRequest request, LocationListener listener, - Looper looper) throws RemoteException { + Looper looper) throws RemoteException { requestLocationUpdates(request, listener); // TODO } From a2b51811289c9c224f8ea5a555a06f1a1cebe5a2 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Sat, 24 Jan 2015 19:06:41 +0100 Subject: [PATCH 08/65] Add more debug logging, fix some bugs --- .../gms/common/GooglePlayServicesUtil.java | 13 ++++--- src/org/microg/gms/common/GmsClient.java | 28 ++++++++++++--- src/org/microg/gms/common/GmsConnector.java | 9 +++-- .../gms/common/MultiConnectionKeeper.java | 14 +++++--- .../gms/common/api/AbstractPendingResult.java | 10 ++---- .../microg/gms/common/api/ApiConnection.java | 2 ++ .../gms/common/api/GoogleApiClientImpl.java | 35 +++++++++++++++---- ...andler.java => ResultCallbackHandler.java} | 12 ++++--- .../FusedLocationProviderApiImpl.java | 1 + .../location/GoogleLocationManagerClient.java | 2 +- .../gms/location/LocationClientImpl.java | 1 + 11 files changed, 90 insertions(+), 37 deletions(-) rename src/org/microg/gms/common/api/{CallbackHandler.java => ResultCallbackHandler.java} (82%) diff --git a/src/com/google/android/gms/common/GooglePlayServicesUtil.java b/src/com/google/android/gms/common/GooglePlayServicesUtil.java index 40cc045f..f1dc70c6 100644 --- a/src/com/google/android/gms/common/GooglePlayServicesUtil.java +++ b/src/com/google/android/gms/common/GooglePlayServicesUtil.java @@ -22,7 +22,7 @@ public class GooglePlayServicesUtil { private static final String TAG = "GooglePlayServicesUtil"; public static final String GMS_ERROR_DIALOG = "GooglePlayServicesErrorDialog"; - public static final String GOOGLE_PLAY_SERVICES_PACKAGE = "com.google.android.gms"; + public static final String GOOGLE_PLAY_SERVICES_PACKAGE = Constants.GMS_PACKAGE_NAME; public static final int GOOGLE_PLAY_SERVICES_VERSION_CODE = Constants.MAX_REFERENCE_VERSION; public static final String GOOGLE_PLAY_STORE_PACKAGE = "com.android.vending"; @@ -31,12 +31,12 @@ public class GooglePlayServicesUtil { } public static Dialog getErrorDialog(int errorCode, Activity activity, int requestCode, - DialogInterface.OnCancelListener cancelListener) { + DialogInterface.OnCancelListener cancelListener) { return null; // TODO } public static PendingIntent getErrorPendingIntent(int errorCode, Activity activity, - int requestCode) { + int requestCode) { return null; // TODO } @@ -67,18 +67,17 @@ public class GooglePlayServicesUtil { } public static boolean showErrorDialogFragment(int errorCode, Activity activity, - int requestCode) { + int requestCode) { return false; // TODO } public static boolean showErrorDialogFragment(int errorCode, Activity activity, - Fragment fragment, int requestCode, - DialogInterface.OnCancelListener cancelListener) { + Fragment fragment, int requestCode, DialogInterface.OnCancelListener cancelListener) { return false; // TODO } public static boolean showErrorDialogFragment(int errorCode, Activity activity, int requestCode, - DialogInterface.OnCancelListener cancelListener) { + DialogInterface.OnCancelListener cancelListener) { return false; // TODO } diff --git a/src/org/microg/gms/common/GmsClient.java b/src/org/microg/gms/common/GmsClient.java index e990ab27..c9b726f7 100644 --- a/src/org/microg/gms/common/GmsClient.java +++ b/src/org/microg/gms/common/GmsClient.java @@ -23,7 +23,7 @@ public abstract class GmsClient implements ApiConnection { private final Context context; private final GoogleApiClient.ConnectionCallbacks callbacks; private final GoogleApiClient.OnConnectionFailedListener connectionFailedListener; - private ConnectionState state = ConnectionState.CONNECTED; + private ConnectionState state = ConnectionState.NOT_CONNECTED; private ServiceConnection serviceConnection; private I serviceInterface; @@ -43,6 +43,8 @@ public abstract class GmsClient implements ApiConnection { @Override public void connect() { + Log.d(TAG, "connect()"); + if (state == ConnectionState.CONNECTED || state == ConnectionState.CONNECTING) return; state = ConnectionState.CONNECTING; if (GooglePlayServicesUtil.isGooglePlayServicesAvailable(context) != ConnectionResult.SUCCESS) { @@ -60,6 +62,12 @@ public abstract class GmsClient implements ApiConnection { @Override public void disconnect() { + Log.d(TAG, "disconnect()"); + if (state == ConnectionState.DISCONNECTING) return; + if (state == ConnectionState.CONNECTING) { + state = ConnectionState.DISCONNECTING; + return; + } serviceInterface = null; if (serviceConnection != null) { MultiConnectionKeeper.getInstance(context).unbind(getActionString(), serviceConnection); @@ -73,6 +81,11 @@ public abstract class GmsClient implements ApiConnection { return state == ConnectionState.CONNECTED; } + @Override + public boolean isConnecting() { + return state == ConnectionState.CONNECTING; + } + public Context getContext() { return context; } @@ -82,7 +95,7 @@ public abstract class GmsClient implements ApiConnection { } private enum ConnectionState { - NOT_CONNECTED, CONNECTING, CONNECTED, ERROR + NOT_CONNECTED, CONNECTING, CONNECTED, DISCONNECTING, ERROR } private class GmsServiceConnection implements ServiceConnection { @@ -90,7 +103,7 @@ public abstract class GmsClient implements ApiConnection { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { try { - Log.d(TAG, "Connecting to broker for " + componentName); + Log.d(TAG, "ServiceConnection : onServiceConnected(" + componentName + ")"); onConnectedToBroker(IGmsServiceBroker.Stub.asInterface(iBinder), new GmsCallbacks()); } catch (RemoteException e) { @@ -100,7 +113,7 @@ public abstract class GmsClient implements ApiConnection { @Override public void onServiceDisconnected(ComponentName componentName) { - state = ConnectionState.ERROR; + state = ConnectionState.NOT_CONNECTED; } } @@ -109,7 +122,14 @@ public abstract class GmsClient implements ApiConnection { @Override public void onPostInitComplete(int statusCode, IBinder binder, Bundle params) throws RemoteException { + if (state == ConnectionState.DISCONNECTING) { + state = ConnectionState.CONNECTED; + disconnect(); + return; + } + state = ConnectionState.CONNECTED; serviceInterface = interfaceFromBinder(binder); + Log.d(TAG, "GmsCallbacks : onPostInitComplete(" + serviceInterface + ")"); callbacks.onConnected(params); } } diff --git a/src/org/microg/gms/common/GmsConnector.java b/src/org/microg/gms/common/GmsConnector.java index 3510c956..fbd4e01b 100644 --- a/src/org/microg/gms/common/GmsConnector.java +++ b/src/org/microg/gms/common/GmsConnector.java @@ -19,6 +19,7 @@ package org.microg.gms.common; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.util.Log; import com.google.android.gms.common.api.Api; import com.google.android.gms.common.api.GoogleApiClient; @@ -29,6 +30,8 @@ import org.microg.gms.common.api.ApiConnection; import org.microg.gms.common.api.GoogleApiClientImpl; public class GmsConnector { + private static final String TAG = "GmsConnector"; + private final GoogleApiClientImpl apiClient; private final Api api; private final Callback callback; @@ -41,6 +44,7 @@ public class GmsConnector connect() { + Log.d(TAG, "connect()"); Looper looper = apiClient.getLooper(); final AbstractPendingResult result = new AbstractPendingResult<>(looper); Message msg = new Message(); @@ -60,11 +64,10 @@ public class GmsConnector result = (AbstractPendingResult) msg.obj; - ApiConnection apiConnection = apiClient.getApiConnection(api); - apiConnection.connect(); try { - result.setResult(callback.onClientAvailable((C) apiConnection)); + result.deliverResult(callback.onClientAvailable((C) apiClient.getApiConnection(api))); } catch (RemoteException ignored) { } diff --git a/src/org/microg/gms/common/MultiConnectionKeeper.java b/src/org/microg/gms/common/MultiConnectionKeeper.java index e7414f71..2d4fe832 100644 --- a/src/org/microg/gms/common/MultiConnectionKeeper.java +++ b/src/org/microg/gms/common/MultiConnectionKeeper.java @@ -26,13 +26,14 @@ public class MultiConnectionKeeper { this.context = context; } - public static MultiConnectionKeeper getInstance(Context context) { + public synchronized static MultiConnectionKeeper getInstance(Context context) { if (INSTANCE == null) INSTANCE = new MultiConnectionKeeper(context); return INSTANCE; } public boolean bind(String action, ServiceConnection connection) { + Log.d(TAG, "bind(" + action + ", " + connection + ")"); Connection con = connections.get(action); if (con != null) { if (!con.forwardsConnection(connection)) { @@ -50,11 +51,13 @@ public class MultiConnectionKeeper { } public void unbind(String action, ServiceConnection connection) { + Log.d(TAG, "unbind(" + action + ", " + connection + ")"); Connection con = connections.get(action); if (con != null) { con.removeConnectionForward(connection); if (!con.hasForwards() && con.isBound()) { con.unbind(); + connections.remove(action); } } } @@ -69,9 +72,10 @@ public class MultiConnectionKeeper { private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { + Log.d(TAG, "Connection(" + actionString + ") : ServiceConnection : " + + "onServiceConnected("+componentName+")"); binder = iBinder; component = componentName; - Log.d(TAG, "bound to " + actionString); for (ServiceConnection connection : connectionForwards) { connection.onServiceConnected(componentName, iBinder); } @@ -80,6 +84,8 @@ public class MultiConnectionKeeper { @Override public void onServiceDisconnected(ComponentName componentName) { + Log.d(TAG, "Connection(" + actionString + ") : ServiceConnection : " + + "onServiceDisconnected("+componentName+")"); binder = null; component = componentName; for (ServiceConnection connection : connectionForwards) { @@ -94,10 +100,10 @@ public class MultiConnectionKeeper { } public void bind() { + Log.d(TAG, "Connection(" + actionString + ") : bind()"); Intent intent = new Intent(actionString).setPackage(GMS_PACKAGE_NAME); bound = context.bindService(intent, serviceConnection, Context.BIND_ADJUST_WITH_ACTIVITY | Context.BIND_AUTO_CREATE); - Log.d(TAG, "binding to " + actionString + ": " + bound); if (!bound) { context.unbindService(serviceConnection); } @@ -112,8 +118,8 @@ public class MultiConnectionKeeper { } public void unbind() { + Log.d(TAG, "Connection(" + actionString + ") : unbind()"); context.unbindService(serviceConnection); - Log.d(TAG, "unbinding from " + actionString); bound = false; } diff --git a/src/org/microg/gms/common/api/AbstractPendingResult.java b/src/org/microg/gms/common/api/AbstractPendingResult.java index e52fb847..7e42142c 100644 --- a/src/org/microg/gms/common/api/AbstractPendingResult.java +++ b/src/org/microg/gms/common/api/AbstractPendingResult.java @@ -12,13 +12,13 @@ import java.util.concurrent.TimeUnit; public class AbstractPendingResult implements PendingResult { private final Object lock = new Object(); private final CountDownLatch countDownLatch = new CountDownLatch(1); - private final CallbackHandler handler; + private final ResultCallbackHandler handler; private boolean canceled; private R result; private ResultCallback resultCallback; public AbstractPendingResult(Looper looper) { - handler = new CallbackHandler(looper); + handler = new ResultCallbackHandler(looper); } private R getResult() { @@ -87,13 +87,9 @@ public class AbstractPendingResult implements PendingResult } } - private void deliverResult(R result) { + public void deliverResult(R result) { this.result = result; countDownLatch.countDown(); } - - public void setResult(R result) { - this.result = result; - } } diff --git a/src/org/microg/gms/common/api/ApiConnection.java b/src/org/microg/gms/common/api/ApiConnection.java index 24a80b4a..d5697e4a 100644 --- a/src/org/microg/gms/common/api/ApiConnection.java +++ b/src/org/microg/gms/common/api/ApiConnection.java @@ -22,4 +22,6 @@ public interface ApiConnection { public void disconnect(); public boolean isConnected(); + + boolean isConnecting(); } diff --git a/src/org/microg/gms/common/api/GoogleApiClientImpl.java b/src/org/microg/gms/common/api/GoogleApiClientImpl.java index ed67ad31..1884b2f1 100644 --- a/src/org/microg/gms/common/api/GoogleApiClientImpl.java +++ b/src/org/microg/gms/common/api/GoogleApiClientImpl.java @@ -2,12 +2,17 @@ package org.microg.gms.common.api; import android.content.Context; import android.os.Bundle; -import android.os.Handler; 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.*; +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; @@ -16,6 +21,8 @@ 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; @@ -28,6 +35,7 @@ public class GoogleApiClientImpl implements GoogleApiClient { private final ConnectionCallbacks baseConnectionCallbacks = new ConnectionCallbacks() { @Override public void onConnected(Bundle connectionHint) { + Log.d(TAG, "ConnectionCallbacks : onConnected()"); for (ConnectionCallbacks callback : connectionCallbacks) { callback.onConnected(connectionHint); } @@ -35,14 +43,17 @@ public class GoogleApiClientImpl implements GoogleApiClient { @Override public void onConnectionSuspended(int cause) { + Log.d(TAG, "ConnectionCallbacks : onConnectionSuspended()"); for (ConnectionCallbacks callback : connectionCallbacks) { callback.onConnectionSuspended(cause); } } }; - private final OnConnectionFailedListener baseConnectionFailedListener = new OnConnectionFailedListener() { + private final OnConnectionFailedListener baseConnectionFailedListener = new + OnConnectionFailedListener() { @Override public void onConnectionFailed(ConnectionResult result) { + Log.d(TAG, "OnConnectionFailedListener : onConnectionFailed()"); for (OnConnectionFailedListener listener : connectionFailedListeners) { listener.onConnectionFailed(result); } @@ -61,7 +72,7 @@ public class GoogleApiClientImpl implements GoogleApiClient { 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, @@ -94,15 +105,21 @@ public class GoogleApiClientImpl implements GoogleApiClient { @Override public void connect() { + Log.d(TAG, "connect()"); for (ApiConnection connection : apiConnections.values()) { - connection.connect(); + if (!connection.isConnected()) { + connection.connect(); + } } } @Override public void disconnect() { + Log.d(TAG, "disconnect()"); for (ApiConnection connection : apiConnections.values()) { - connection.disconnect(); + if (connection.isConnected()) { + connection.disconnect(); + } } } @@ -116,7 +133,10 @@ public class GoogleApiClientImpl implements GoogleApiClient { @Override public boolean isConnecting() { - return false; // TODO + for (ApiConnection connection : apiConnections.values()) { + if (connection.isConnecting()) return true; + } + return false; } @Override @@ -132,6 +152,7 @@ public class GoogleApiClientImpl implements GoogleApiClient { @Override public void reconnect() { + Log.d(TAG, "reconnect()"); disconnect(); connect(); } diff --git a/src/org/microg/gms/common/api/CallbackHandler.java b/src/org/microg/gms/common/api/ResultCallbackHandler.java similarity index 82% rename from src/org/microg/gms/common/api/CallbackHandler.java rename to src/org/microg/gms/common/api/ResultCallbackHandler.java index b2250f9e..0d5dbe98 100644 --- a/src/org/microg/gms/common/api/CallbackHandler.java +++ b/src/org/microg/gms/common/api/ResultCallbackHandler.java @@ -3,14 +3,17 @@ package org.microg.gms.common.api; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.util.Log; + import com.google.android.gms.common.api.Result; import com.google.android.gms.common.api.ResultCallback; -class CallbackHandler extends Handler { +class ResultCallbackHandler extends Handler { + private static final String TAG = "GmsResultCallbackHandler"; public static final int CALLBACK_ON_COMPLETE = 1; public static final int CALLBACK_ON_TIMEOUT = 2; - - public CallbackHandler(Looper looper) { + + public ResultCallbackHandler(Looper looper) { super(looper); } @@ -19,6 +22,7 @@ class CallbackHandler extends Handler { switch (msg.what) { case CALLBACK_ON_COMPLETE: OnCompleteObject o = (OnCompleteObject) msg.obj; + Log.d(TAG, "handleMessage() : onResult(" + o.result + ")"); o.callback.onResult(o.result); break; case CALLBACK_ON_TIMEOUT: @@ -35,7 +39,7 @@ class CallbackHandler extends Handler { } public void sendTimeoutResultCallback(AbstractPendingResult pendingResult, long millis) { - + } public static class OnCompleteObject { diff --git a/src/org/microg/gms/location/FusedLocationProviderApiImpl.java b/src/org/microg/gms/location/FusedLocationProviderApiImpl.java index 708f4263..ee2bcd25 100644 --- a/src/org/microg/gms/location/FusedLocationProviderApiImpl.java +++ b/src/org/microg/gms/location/FusedLocationProviderApiImpl.java @@ -23,6 +23,7 @@ public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { @Override public Location getLastLocation(GoogleApiClient client) { try { + Log.d(TAG, "getLastLocation(" + client + ")"); return LocationClientImpl.get(client).getLastLocation(); } catch (RemoteException e) { Log.w(TAG, e); diff --git a/src/org/microg/gms/location/GoogleLocationManagerClient.java b/src/org/microg/gms/location/GoogleLocationManagerClient.java index 93f7fba1..63ed578b 100644 --- a/src/org/microg/gms/location/GoogleLocationManagerClient.java +++ b/src/org/microg/gms/location/GoogleLocationManagerClient.java @@ -12,7 +12,7 @@ import com.google.android.gms.location.internal.IGoogleLocationManagerService; import org.microg.gms.Constants; import org.microg.gms.common.GmsClient; -public class GoogleLocationManagerClient extends GmsClient { +public abstract class GoogleLocationManagerClient extends GmsClient { public GoogleLocationManagerClient(Context context, GoogleApiClient.ConnectionCallbacks callbacks, GoogleApiClient.OnConnectionFailedListener connectionFailedListener) { super(context, callbacks, connectionFailedListener); diff --git a/src/org/microg/gms/location/LocationClientImpl.java b/src/org/microg/gms/location/LocationClientImpl.java index 07aac0f3..4ce3a12a 100644 --- a/src/org/microg/gms/location/LocationClientImpl.java +++ b/src/org/microg/gms/location/LocationClientImpl.java @@ -38,6 +38,7 @@ public class LocationClientImpl extends GoogleLocationManagerClient { private Map listenerMap = new HashMap<>(); public Location getLastLocation() throws RemoteException { + Log.d(TAG, "getLastLocation()"); return getServiceInterface().getLastLocation(); } From 50c81f4a2486d8ae720f94e6e1530f7ef64bbac8 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Sat, 24 Jan 2015 21:57:42 +0100 Subject: [PATCH 09/65] Add fallback to native location provider --- AndroidManifest.xml | 5 +- src/org/microg/gms/common/GmsClient.java | 42 ++-- .../gms/location/LocationClientImpl.java | 77 +++++-- .../location/NativeLocationClientImpl.java | 208 ++++++++++++++++++ 4 files changed, 296 insertions(+), 36 deletions(-) create mode 100644 src/org/microg/gms/location/NativeLocationClientImpl.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 7c0e18bf..b0fcdc8e 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2,5 +2,8 @@ - + + + + diff --git a/src/org/microg/gms/common/GmsClient.java b/src/org/microg/gms/common/GmsClient.java index c9b726f7..e8cd12ae 100644 --- a/src/org/microg/gms/common/GmsClient.java +++ b/src/org/microg/gms/common/GmsClient.java @@ -10,7 +10,6 @@ import android.os.RemoteException; import android.util.Log; import com.google.android.gms.common.ConnectionResult; -import com.google.android.gms.common.GooglePlayServicesUtil; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.internal.IGmsCallbacks; import com.google.android.gms.common.internal.IGmsServiceBroker; @@ -21,9 +20,9 @@ public abstract class GmsClient implements ApiConnection { private static final String TAG = "GmsClient"; private final Context context; - private final GoogleApiClient.ConnectionCallbacks callbacks; - private final GoogleApiClient.OnConnectionFailedListener connectionFailedListener; - private ConnectionState state = ConnectionState.NOT_CONNECTED; + protected final GoogleApiClient.ConnectionCallbacks callbacks; + protected final GoogleApiClient.OnConnectionFailedListener connectionFailedListener; + protected ConnectionState state = ConnectionState.NOT_CONNECTED; private ServiceConnection serviceConnection; private I serviceInterface; @@ -46,18 +45,21 @@ public abstract class GmsClient implements ApiConnection { Log.d(TAG, "connect()"); if (state == ConnectionState.CONNECTED || state == ConnectionState.CONNECTING) return; state = ConnectionState.CONNECTING; - if (GooglePlayServicesUtil.isGooglePlayServicesAvailable(context) != - ConnectionResult.SUCCESS) { - state = ConnectionState.NOT_CONNECTED; - } else { - if (serviceConnection != null) { - MultiConnectionKeeper.getInstance(context) - .unbind(getActionString(), serviceConnection); - } - serviceConnection = new GmsServiceConnection(); - MultiConnectionKeeper.getInstance(context).bind(getActionString(), - serviceConnection); + if (serviceConnection != null) { + MultiConnectionKeeper.getInstance(context) + .unbind(getActionString(), serviceConnection); } + serviceConnection = new GmsServiceConnection(); + if (!MultiConnectionKeeper.getInstance(context).bind(getActionString(), + serviceConnection)) { + state = ConnectionState.ERROR; + handleConnectionFailed(); + } + } + + public void handleConnectionFailed() { + connectionFailedListener.onConnectionFailed(new ConnectionResult(ConnectionResult + .API_UNAVAILABLE, null)); } @Override @@ -78,7 +80,7 @@ public abstract class GmsClient implements ApiConnection { @Override public boolean isConnected() { - return state == ConnectionState.CONNECTED; + return state == ConnectionState.CONNECTED || state == ConnectionState.PSEUDO_CONNECTED; } @Override @@ -86,6 +88,10 @@ public abstract class GmsClient implements ApiConnection { return state == ConnectionState.CONNECTING; } + public boolean hasError() { + return state == ConnectionState.ERROR; + } + public Context getContext() { return context; } @@ -94,8 +100,8 @@ public abstract class GmsClient implements ApiConnection { return serviceInterface; } - private enum ConnectionState { - NOT_CONNECTED, CONNECTING, CONNECTED, DISCONNECTING, ERROR + protected enum ConnectionState { + NOT_CONNECTED, CONNECTING, CONNECTED, DISCONNECTING, ERROR, PSEUDO_CONNECTED } private class GmsServiceConnection implements ServiceConnection { diff --git a/src/org/microg/gms/location/LocationClientImpl.java b/src/org/microg/gms/location/LocationClientImpl.java index 4ce3a12a..57c9c60b 100644 --- a/src/org/microg/gms/location/LocationClientImpl.java +++ b/src/org/microg/gms/location/LocationClientImpl.java @@ -3,6 +3,7 @@ package org.microg.gms.location; import android.app.PendingIntent; import android.content.Context; import android.location.Location; +import android.os.Bundle; import android.os.Looper; import android.os.RemoteException; import android.util.Log; @@ -20,6 +21,9 @@ import java.util.Map; public class LocationClientImpl extends GoogleLocationManagerClient { private static final String TAG = "GmsLocationClientImpl"; + private NativeLocationClientImpl nativeLocation = null; + private Map listenerMap = new HashMap<>(); + public LocationClientImpl(Context context, GoogleApiClient.ConnectionCallbacks callbacks, GoogleApiClient.OnConnectionFailedListener connectionFailedListener) { @@ -35,50 +39,89 @@ public class LocationClientImpl extends GoogleLocationManagerClient { return null; } - private Map listenerMap = new HashMap<>(); - public Location getLastLocation() throws RemoteException { Log.d(TAG, "getLastLocation()"); - return getServiceInterface().getLastLocation(); + if (nativeLocation != null) { + return nativeLocation.getLastLocation(); + } else { + return getServiceInterface().getLastLocation(); + } } public void requestLocationUpdates(LocationRequest request, final LocationListener listener) throws RemoteException { - ILocationListener iLocationListener = new ILocationListener.Stub() { - @Override - public void onLocationChanged(Location location) throws RemoteException { - listener.onLocationChanged(location); + if (nativeLocation != null) { + nativeLocation.requestLocationUpdates(request, listener); + } else { + if (!listenerMap.containsKey(listener)) { + listenerMap.put(listener, new ILocationListener.Stub() { + @Override + public void onLocationChanged(Location location) throws RemoteException { + listener.onLocationChanged(location); + } + }); } - }; - listenerMap.put(listener, iLocationListener); - getServiceInterface().requestLocationUpdatesWithPackage(request, - iLocationListener, getContext().getPackageName()); + getServiceInterface().requestLocationUpdatesWithPackage(request, + listenerMap.get(listener), getContext().getPackageName()); + } } public void requestLocationUpdates(LocationRequest request, PendingIntent pendingIntent) throws RemoteException { - getServiceInterface().requestLocationUpdatesWithIntent(request, pendingIntent); + if (nativeLocation != null) { + nativeLocation.requestLocationUpdates(request, pendingIntent); + } else { + getServiceInterface().requestLocationUpdatesWithIntent(request, pendingIntent); + } } public void requestLocationUpdates(LocationRequest request, LocationListener listener, Looper looper) throws RemoteException { + if (nativeLocation != null) { + nativeLocation.requestLocationUpdates(request, listener, looper); + } requestLocationUpdates(request, listener); // TODO } public void removeLocationUpdates(LocationListener listener) throws RemoteException { - getServiceInterface().removeLocationUpdatesWithListener(listenerMap.get(listener)); - listenerMap.remove(listener); + if (nativeLocation != null) { + nativeLocation.removeLocationUpdates(listener); + } else { + getServiceInterface().removeLocationUpdatesWithListener(listenerMap.get(listener)); + } } public void removeLocationUpdates(PendingIntent pendingIntent) throws RemoteException { - getServiceInterface().removeLocationUpdatesWithIntent(pendingIntent); + if (nativeLocation != null) { + nativeLocation.removeLocationUpdates(pendingIntent); + } else { + getServiceInterface().removeLocationUpdatesWithIntent(pendingIntent); + } } public void setMockMode(boolean isMockMode) throws RemoteException { - getServiceInterface().setMockMode(isMockMode); + if (nativeLocation != null) { + nativeLocation.setMockMode(isMockMode); + } else { + getServiceInterface().setMockMode(isMockMode); + } } public void setMockLocation(Location mockLocation) throws RemoteException { - getServiceInterface().setMockLocation(mockLocation); + if (nativeLocation != null) { + nativeLocation.setMockLocation(mockLocation); + } else { + getServiceInterface().setMockLocation(mockLocation); + } + } + + @Override + public void handleConnectionFailed() { + // DO NOT call super here, because fails are not really problems :) + nativeLocation = new NativeLocationClientImpl(this); + state = ConnectionState.PSEUDO_CONNECTED; + Bundle bundle = new Bundle(); + bundle.putBoolean("fallback_to_native_active", true); + callbacks.onConnected(bundle); } } diff --git a/src/org/microg/gms/location/NativeLocationClientImpl.java b/src/org/microg/gms/location/NativeLocationClientImpl.java new file mode 100644 index 00000000..1937c6c1 --- /dev/null +++ b/src/org/microg/gms/location/NativeLocationClientImpl.java @@ -0,0 +1,208 @@ +/* + * Copyright 2014-2015 µg 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.location; + +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.location.Criteria; +import android.location.Location; +import android.location.LocationManager; +import android.os.Bundle; +import android.os.Looper; +import android.util.Log; + +import com.google.android.gms.location.LocationListener; +import com.google.android.gms.location.LocationRequest; + +import java.util.HashMap; +import java.util.Map; + +public class NativeLocationClientImpl { + private final static String TAG = "GmsToNativeLocationClient"; + private final static Criteria DEFAULT_CRITERIA = new Criteria(); + private final static Map pendingCount = new HashMap<>(); + private final static Map nativePendingMap = new HashMap<>(); + private static final String EXTRA_PENDING_INTENT = "pending_intent"; + + private final Context context; + private final LocationManager locationManager; + private final Map nativeListenerMap = new HashMap<>(); + + public NativeLocationClientImpl(LocationClientImpl client) { + context = client.getContext(); + locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); + } + + private static Criteria makeNativeCriteria(LocationRequest request) { + Criteria criteria = new Criteria(); + switch (request.getPriority()) { + case LocationRequest.PRIORITY_HIGH_ACCURACY: + criteria.setAccuracy(Criteria.ACCURACY_FINE); + criteria.setPowerRequirement(Criteria.POWER_HIGH); + break; + case LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY: + default: + criteria.setAccuracy(Criteria.ACCURACY_COARSE); + criteria.setPowerRequirement(Criteria.POWER_MEDIUM); + break; + case LocationRequest.PRIORITY_NO_POWER: + case LocationRequest.PRIORITY_LOW_POWER: + criteria.setAccuracy(Criteria.ACCURACY_COARSE); + criteria.setPowerRequirement(Criteria.POWER_LOW); + } + return criteria; + } + + public Location getLastLocation() { + Log.d(TAG,"getLastLocation()"); + return locationManager.getLastKnownLocation( + locationManager.getBestProvider(DEFAULT_CRITERIA, true)); + } + + public void requestLocationUpdates(LocationRequest request, LocationListener listener) { + requestLocationUpdates(request, listener, Looper.getMainLooper()); + } + + public void requestLocationUpdates(LocationRequest request, PendingIntent pendingIntent) { + Log.d(TAG,"requestLocationUpdates()"); + Intent i = new Intent(context, NativePendingIntentForwarder.class); + Bundle bundle = new Bundle(); + bundle.putParcelable(EXTRA_PENDING_INTENT, pendingIntent); + i.putExtras(bundle); + pendingCount.put(pendingIntent, request.getNumUpdates()); + nativePendingMap.put(pendingIntent, PendingIntent.getActivity(context, 0, i, 0)); + locationManager.requestLocationUpdates(request.getInterval(), + request.getSmallestDesplacement(), makeNativeCriteria(request), + nativePendingMap.get(pendingIntent)); + } + + public void requestLocationUpdates(LocationRequest request, LocationListener listener, Looper + looper) { + Log.d(TAG,"requestLocationUpdates()"); + if (nativeListenerMap.containsKey(listener)) { + removeLocationUpdates(listener); + } + nativeListenerMap.put(listener, new NativeListener(listener, request.getNumUpdates())); + locationManager.requestLocationUpdates(request.getInterval(), + request.getSmallestDesplacement(), makeNativeCriteria(request), + nativeListenerMap.get(listener), looper); + } + + public void removeLocationUpdates(LocationListener listener) { + Log.d(TAG,"removeLocationUpdates()"); + locationManager.removeUpdates(nativeListenerMap.get(listener)); + nativeListenerMap.remove(listener); + } + + public void removeLocationUpdates(PendingIntent pendingIntent) { + Log.d(TAG,"removeLocationUpdates()"); + locationManager.removeUpdates(nativePendingMap.get(pendingIntent)); + nativePendingMap.remove(pendingIntent); + pendingCount.remove(pendingIntent); + } + + public void setMockMode(boolean isMockMode) { + Log.d(TAG,"setMockMode()"); + // not yet supported + } + + public void setMockLocation(Location mockLocation) { + Log.d(TAG,"setMockLocation()"); + // not yet supported + } + + public static class NativePendingIntentForwarder extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + if (intent.hasExtra(LocationManager.KEY_LOCATION_CHANGED)) { + PendingIntent pendingIntent = intent.getExtras().getParcelable + (EXTRA_PENDING_INTENT); + try { + pendingIntent.send(context, 0, intent); + pendingCount.put(pendingIntent, pendingCount.get(pendingIntent) - 1); + if (pendingCount.get(pendingIntent) == 0) { + ((LocationManager) context.getSystemService(Context.LOCATION_SERVICE)) + .removeUpdates(nativePendingMap.get(pendingIntent)); + nativePendingMap.remove(pendingIntent); + pendingCount.remove(pendingIntent); + } + } catch (PendingIntent.CanceledException e) { + ((LocationManager) context.getSystemService(Context.LOCATION_SERVICE)) + .removeUpdates(nativePendingMap.get(pendingIntent)); + nativePendingMap.remove(pendingIntent); + pendingCount.remove(pendingIntent); + } + } + } + } + + public class NativeListener implements android.location.LocationListener { + + private final LocationListener listener; + private int count; + + private NativeListener(LocationListener listener, int count) { + this.listener = listener; + this.count = count; + } + + @Override + public void onLocationChanged(Location location) { + listener.onLocationChanged(location); + count--; + if (count == 0) { + locationManager.removeUpdates(this); + nativeListenerMap.remove(listener); + } + } + + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { + + } + + @Override + public void onProviderEnabled(String provider) { + + } + + @Override + public void onProviderDisabled(String provider) { + + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + NativeListener that = (NativeListener) o; + + if (!listener.equals(that.listener)) return false; + + return true; + } + + @Override + public int hashCode() { + return listener.hashCode(); + } + } +} From fd9bd3950c40151020a13a33067f1a0ace9462e9 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Sun, 25 Jan 2015 01:22:44 +0100 Subject: [PATCH 10/65] Explain + usage --- .gitignore | 7 +++++++ .gitmodules | 3 +++ GmsApi | 1 + README.md | 20 ++++++++++++++++++++ settings.gradle | 1 + 5 files changed, 32 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 160000 GmsApi create mode 100644 settings.gradle diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..5a0e68a2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +out/ +build/ +*.iml +local.properties +.gradle/ +gradle/ +gradlew diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..4503545a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "GmsApi"] + path = GmsApi + url = https://github.com/microg/android_external_GmsApi.git diff --git a/GmsApi b/GmsApi new file mode 160000 index 00000000..7e2f3b8f --- /dev/null +++ b/GmsApi @@ -0,0 +1 @@ +Subproject commit 7e2f3b8f7b598ee243486e03f0637b8334be216f diff --git a/README.md b/README.md index f3f240f3..d03a1ce4 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,23 @@ GmsLib This library is a compatibility implementation of the often used play-services library. It will try to use the Play Services when installed on the target device. If this is not possible, a basic fallback implementation might be used. + +WIP +--- +This is still work in progress, and most applications will not build. +However feel free to try it out and create issues for missing method calls (please include an application to test it). + +Example: DashClock +------------------ +[DashClock](https://code.google.com/p/dashclock/) is an open source clock widget with enhanced features. +However it uses play services as location backend and thus requires proprietary libraries to compile it. + +However, it is possible to build DashClock using GmsLib, supporting all it's location features, with or without play services installed. +To do this, download and build GmsLib plus its submodules and copy the three resulting .aar files (SafeParcel.aar, GmsApi.aar, GmsLib.aar) to `$DASHCLOCK_DIR/local_aars/`. +Then replace `compile 'com.google.android.gms:play-services:4.0.30'` in `$DASHCLOCK_SRC/main/build.gradle` with + + compile(name:'GmsLib', ext:'aar') + compile(name:'GmsApi', ext:'aar') + compile(name:'SafeParcel', ext:'aar') + +and build as usual. diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000..dbfd9faf --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include ':GmsApi', ':GmsApi:SafeParcel' From e9dfb23e604fe19aea2eb0e0ab2b8f4bb34dcf72 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Sun, 25 Jan 2015 01:29:43 +0100 Subject: [PATCH 11/65] Subsubmodules are messy with git, just don't use them --- .gitmodules | 3 +++ GmsApi | 2 +- SafeParcel | 1 + settings.gradle | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) create mode 160000 SafeParcel diff --git a/.gitmodules b/.gitmodules index 4503545a..2be15080 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "GmsApi"] path = GmsApi url = https://github.com/microg/android_external_GmsApi.git +[submodule "SafeParcel"] + path = SafeParcel + url = https://github.com/microg/android_external_SafeParcel.git diff --git a/GmsApi b/GmsApi index 7e2f3b8f..c4db81d1 160000 --- a/GmsApi +++ b/GmsApi @@ -1 +1 @@ -Subproject commit 7e2f3b8f7b598ee243486e03f0637b8334be216f +Subproject commit c4db81d1fcf5c301f34e12d5675b9b50c507bd42 diff --git a/SafeParcel b/SafeParcel new file mode 160000 index 00000000..3efecf38 --- /dev/null +++ b/SafeParcel @@ -0,0 +1 @@ +Subproject commit 3efecf38ca8b1f89cd8f0c40eea4b497f1563d90 diff --git a/settings.gradle b/settings.gradle index dbfd9faf..87e5535f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':GmsApi', ':GmsApi:SafeParcel' +include ':GmsApi', ':SafeParcel' From 55a608cc8d07936750f41ba40ab574d6a91b433a Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Sun, 25 Jan 2015 01:55:26 +0100 Subject: [PATCH 12/65] Fix res path --- build.gradle | 1 + {src/main/res => res}/values/version.xml | 0 2 files changed, 1 insertion(+) rename {src/main/res => res}/values/version.xml (100%) diff --git a/build.gradle b/build.gradle index 5d2d1c52..b581ecdf 100644 --- a/build.gradle +++ b/build.gradle @@ -24,6 +24,7 @@ android { manifest.srcFile 'AndroidManifest.xml' java.srcDirs = ['src'] aidl.srcDirs = ['src'] + res.srcDirs = ['res'] } } } diff --git a/src/main/res/values/version.xml b/res/values/version.xml similarity index 100% rename from src/main/res/values/version.xml rename to res/values/version.xml From af8bc78effb0e7d3c98981a6d663f7d1c4abe7d3 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Sun, 25 Jan 2015 02:11:52 +0100 Subject: [PATCH 13/65] Add copyright notice --- .../gms/common/ForwardConnectionCallbacks.java | 16 ++++++++++++++++ .../common/ForwardConnectionFailedListener.java | 16 ++++++++++++++++ src/org/microg/gms/common/GmsClient.java | 16 ++++++++++++++++ src/org/microg/gms/common/GmsConnector.java | 3 ++- .../microg/gms/common/MultiConnectionKeeper.java | 16 ++++++++++++++++ .../gms/common/api/AbstractPendingResult.java | 16 ++++++++++++++++ .../gms/common/api/GoogleApiClientImpl.java | 16 ++++++++++++++++ .../gms/common/api/ResultCallbackHandler.java | 16 ++++++++++++++++ .../location/FusedLocationProviderApiImpl.java | 16 ++++++++++++++++ .../microg/gms/location/GeofencingApiImpl.java | 16 ++++++++++++++++ .../location/GoogleLocationManagerClient.java | 16 ++++++++++++++++ .../microg/gms/location/LocationClientImpl.java | 16 ++++++++++++++++ 12 files changed, 178 insertions(+), 1 deletion(-) diff --git a/src/org/microg/gms/common/ForwardConnectionCallbacks.java b/src/org/microg/gms/common/ForwardConnectionCallbacks.java index de4d9203..e38972ff 100644 --- a/src/org/microg/gms/common/ForwardConnectionCallbacks.java +++ b/src/org/microg/gms/common/ForwardConnectionCallbacks.java @@ -1,3 +1,19 @@ +/* + * Copyright 2014-2015 µg 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; import android.os.Bundle; diff --git a/src/org/microg/gms/common/ForwardConnectionFailedListener.java b/src/org/microg/gms/common/ForwardConnectionFailedListener.java index 273383f9..823a1991 100644 --- a/src/org/microg/gms/common/ForwardConnectionFailedListener.java +++ b/src/org/microg/gms/common/ForwardConnectionFailedListener.java @@ -1,3 +1,19 @@ +/* + * Copyright 2014-2015 µg 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; import com.google.android.gms.common.ConnectionResult; diff --git a/src/org/microg/gms/common/GmsClient.java b/src/org/microg/gms/common/GmsClient.java index e8cd12ae..800b8df8 100644 --- a/src/org/microg/gms/common/GmsClient.java +++ b/src/org/microg/gms/common/GmsClient.java @@ -1,3 +1,19 @@ +/* + * Copyright 2014-2015 µg 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; import android.content.ComponentName; diff --git a/src/org/microg/gms/common/GmsConnector.java b/src/org/microg/gms/common/GmsConnector.java index fbd4e01b..cd23afc7 100644 --- a/src/org/microg/gms/common/GmsConnector.java +++ b/src/org/microg/gms/common/GmsConnector.java @@ -67,7 +67,8 @@ public class GmsConnector result = (AbstractPendingResult) msg.obj; try { - result.deliverResult(callback.onClientAvailable((C) apiClient.getApiConnection(api))); + result.deliverResult(callback.onClientAvailable((C) apiClient.getApiConnection + (api))); } catch (RemoteException ignored) { } diff --git a/src/org/microg/gms/common/MultiConnectionKeeper.java b/src/org/microg/gms/common/MultiConnectionKeeper.java index 2d4fe832..bb454e55 100644 --- a/src/org/microg/gms/common/MultiConnectionKeeper.java +++ b/src/org/microg/gms/common/MultiConnectionKeeper.java @@ -1,3 +1,19 @@ +/* + * Copyright 2014-2015 µg 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; import android.content.ComponentName; diff --git a/src/org/microg/gms/common/api/AbstractPendingResult.java b/src/org/microg/gms/common/api/AbstractPendingResult.java index 7e42142c..7986adff 100644 --- a/src/org/microg/gms/common/api/AbstractPendingResult.java +++ b/src/org/microg/gms/common/api/AbstractPendingResult.java @@ -1,3 +1,19 @@ +/* + * Copyright 2014-2015 µg 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.os.Looper; diff --git a/src/org/microg/gms/common/api/GoogleApiClientImpl.java b/src/org/microg/gms/common/api/GoogleApiClientImpl.java index 1884b2f1..ff2fa9ce 100644 --- a/src/org/microg/gms/common/api/GoogleApiClientImpl.java +++ b/src/org/microg/gms/common/api/GoogleApiClientImpl.java @@ -1,3 +1,19 @@ +/* + * Copyright 2014-2015 µg 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; diff --git a/src/org/microg/gms/common/api/ResultCallbackHandler.java b/src/org/microg/gms/common/api/ResultCallbackHandler.java index 0d5dbe98..0c03a57b 100644 --- a/src/org/microg/gms/common/api/ResultCallbackHandler.java +++ b/src/org/microg/gms/common/api/ResultCallbackHandler.java @@ -1,3 +1,19 @@ +/* + * Copyright 2014-2015 µg 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.os.Handler; diff --git a/src/org/microg/gms/location/FusedLocationProviderApiImpl.java b/src/org/microg/gms/location/FusedLocationProviderApiImpl.java index ee2bcd25..be46f446 100644 --- a/src/org/microg/gms/location/FusedLocationProviderApiImpl.java +++ b/src/org/microg/gms/location/FusedLocationProviderApiImpl.java @@ -1,3 +1,19 @@ +/* + * Copyright 2014-2015 µg 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.location; import android.app.PendingIntent; diff --git a/src/org/microg/gms/location/GeofencingApiImpl.java b/src/org/microg/gms/location/GeofencingApiImpl.java index 27cef98c..81fcfdc7 100644 --- a/src/org/microg/gms/location/GeofencingApiImpl.java +++ b/src/org/microg/gms/location/GeofencingApiImpl.java @@ -1,3 +1,19 @@ +/* + * Copyright 2014-2015 µg 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.location; import com.google.android.gms.location.GeofencingApi; diff --git a/src/org/microg/gms/location/GoogleLocationManagerClient.java b/src/org/microg/gms/location/GoogleLocationManagerClient.java index 63ed578b..cbc24b21 100644 --- a/src/org/microg/gms/location/GoogleLocationManagerClient.java +++ b/src/org/microg/gms/location/GoogleLocationManagerClient.java @@ -1,3 +1,19 @@ +/* + * Copyright 2014-2015 µg 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.location; import android.content.Context; diff --git a/src/org/microg/gms/location/LocationClientImpl.java b/src/org/microg/gms/location/LocationClientImpl.java index 57c9c60b..a79e0c62 100644 --- a/src/org/microg/gms/location/LocationClientImpl.java +++ b/src/org/microg/gms/location/LocationClientImpl.java @@ -1,3 +1,19 @@ +/* + * Copyright 2014-2015 µg 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.location; import android.app.PendingIntent; From 2f2745d6e9b3d745b69b4892bf41a37a523e0b92 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Sun, 25 Jan 2015 02:14:45 +0100 Subject: [PATCH 14/65] Fix KEY_LOCATION_CHANGED --- .../gms/location/NativeLocationClientImpl.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/org/microg/gms/location/NativeLocationClientImpl.java b/src/org/microg/gms/location/NativeLocationClientImpl.java index 1937c6c1..5129c19f 100644 --- a/src/org/microg/gms/location/NativeLocationClientImpl.java +++ b/src/org/microg/gms/location/NativeLocationClientImpl.java @@ -27,6 +27,7 @@ import android.os.Bundle; import android.os.Looper; import android.util.Log; +import com.google.android.gms.location.FusedLocationProviderApi; import com.google.android.gms.location.LocationListener; import com.google.android.gms.location.LocationRequest; @@ -70,7 +71,7 @@ public class NativeLocationClientImpl { } public Location getLastLocation() { - Log.d(TAG,"getLastLocation()"); + Log.d(TAG, "getLastLocation()"); return locationManager.getLastKnownLocation( locationManager.getBestProvider(DEFAULT_CRITERIA, true)); } @@ -80,7 +81,7 @@ public class NativeLocationClientImpl { } public void requestLocationUpdates(LocationRequest request, PendingIntent pendingIntent) { - Log.d(TAG,"requestLocationUpdates()"); + Log.d(TAG, "requestLocationUpdates()"); Intent i = new Intent(context, NativePendingIntentForwarder.class); Bundle bundle = new Bundle(); bundle.putParcelable(EXTRA_PENDING_INTENT, pendingIntent); @@ -94,7 +95,7 @@ public class NativeLocationClientImpl { public void requestLocationUpdates(LocationRequest request, LocationListener listener, Looper looper) { - Log.d(TAG,"requestLocationUpdates()"); + Log.d(TAG, "requestLocationUpdates()"); if (nativeListenerMap.containsKey(listener)) { removeLocationUpdates(listener); } @@ -105,25 +106,25 @@ public class NativeLocationClientImpl { } public void removeLocationUpdates(LocationListener listener) { - Log.d(TAG,"removeLocationUpdates()"); + Log.d(TAG, "removeLocationUpdates()"); locationManager.removeUpdates(nativeListenerMap.get(listener)); nativeListenerMap.remove(listener); } public void removeLocationUpdates(PendingIntent pendingIntent) { - Log.d(TAG,"removeLocationUpdates()"); + Log.d(TAG, "removeLocationUpdates()"); locationManager.removeUpdates(nativePendingMap.get(pendingIntent)); nativePendingMap.remove(pendingIntent); pendingCount.remove(pendingIntent); } public void setMockMode(boolean isMockMode) { - Log.d(TAG,"setMockMode()"); + Log.d(TAG, "setMockMode()"); // not yet supported } public void setMockLocation(Location mockLocation) { - Log.d(TAG,"setMockLocation()"); + Log.d(TAG, "setMockLocation()"); // not yet supported } @@ -135,6 +136,8 @@ public class NativeLocationClientImpl { PendingIntent pendingIntent = intent.getExtras().getParcelable (EXTRA_PENDING_INTENT); try { + intent.putExtra(FusedLocationProviderApi.KEY_LOCATION_CHANGED, + intent.getParcelableExtra(LocationManager.KEY_LOCATION_CHANGED)); pendingIntent.send(context, 0, intent); pendingCount.put(pendingIntent, pendingCount.get(pendingIntent) - 1); if (pendingCount.get(pendingIntent) == 0) { From ad1d47e1cf3f45752b5ae3bf7691bfa187d4ab8d Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Mon, 26 Jan 2015 00:43:41 +0100 Subject: [PATCH 15/65] Start adding support for wearable API --- GmsApi | 2 +- .../gms/common/GooglePlayServicesUtil.java | 2 + .../google/android/gms/common/api/Api.java | 9 ++++ .../gms/common/api/GoogleApiClient.java | 5 ++ .../android/gms/location/LocationClient.java | 4 ++ .../gms/location/LocationServices.java | 21 ++------- .../google/android/gms/wearable/DataApi.java | 20 ++++++++ .../android/gms/wearable/MessageApi.java | 20 ++++++++ .../google/android/gms/wearable/NodeApi.java | 20 ++++++++ .../google/android/gms/wearable/Wearable.java | 47 +++++++++++++++++++ .../location/LocationServicesApiBuilder.java | 37 +++++++++++++++ src/org/microg/gms/wearable/DataApiImpl.java | 22 +++++++++ .../microg/gms/wearable/MessageApiImpl.java | 22 +++++++++ src/org/microg/gms/wearable/NodeApiImpl.java | 22 +++++++++ .../gms/wearable/WearableApiBuilder.java | 40 ++++++++++++++++ 15 files changed, 274 insertions(+), 19 deletions(-) create mode 100644 src/com/google/android/gms/wearable/DataApi.java create mode 100644 src/com/google/android/gms/wearable/MessageApi.java create mode 100644 src/com/google/android/gms/wearable/NodeApi.java create mode 100644 src/com/google/android/gms/wearable/Wearable.java create mode 100644 src/org/microg/gms/location/LocationServicesApiBuilder.java create mode 100644 src/org/microg/gms/wearable/DataApiImpl.java create mode 100644 src/org/microg/gms/wearable/MessageApiImpl.java create mode 100644 src/org/microg/gms/wearable/NodeApiImpl.java create mode 100644 src/org/microg/gms/wearable/WearableApiBuilder.java diff --git a/GmsApi b/GmsApi index c4db81d1..3c19d84e 160000 --- a/GmsApi +++ b/GmsApi @@ -1 +1 @@ -Subproject commit c4db81d1fcf5c301f34e12d5675b9b50c507bd42 +Subproject commit 3c19d84eaa9842a2526166a38ad445125dbd2300 diff --git a/src/com/google/android/gms/common/GooglePlayServicesUtil.java b/src/com/google/android/gms/common/GooglePlayServicesUtil.java index f1dc70c6..6da29c4f 100644 --- a/src/com/google/android/gms/common/GooglePlayServicesUtil.java +++ b/src/com/google/android/gms/common/GooglePlayServicesUtil.java @@ -10,6 +10,7 @@ import android.content.pm.PackageManager; import android.util.Log; import org.microg.gms.Constants; +import org.microg.gms.PublicApi; /** * Utility class for verifying that the Google Play services APK is available and up-to-date on @@ -19,6 +20,7 @@ import org.microg.gms.Constants; * TODO: methods :) */ public class GooglePlayServicesUtil { + @PublicApi(exclude = true) private static final String TAG = "GooglePlayServicesUtil"; public static final String GMS_ERROR_DIALOG = "GooglePlayServicesErrorDialog"; diff --git a/src/com/google/android/gms/common/api/Api.java b/src/com/google/android/gms/common/api/Api.java index ffbeeddc..3e5482a9 100644 --- a/src/com/google/android/gms/common/api/Api.java +++ b/src/com/google/android/gms/common/api/Api.java @@ -1,5 +1,6 @@ package com.google.android.gms.common.api; +import org.microg.gms.PublicApi; import org.microg.gms.common.api.ApiBuilder; /** @@ -14,14 +15,17 @@ import org.microg.gms.common.api.ApiBuilder; *

* 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; } @@ -30,28 +34,33 @@ public final class Api { * 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 public interface HasOptions extends ApiOptions { } /** * Base interface for {@link ApiOptions} that are not required, don't exist. */ + @PublicApi public interface NotRequiredOptions extends ApiOptions { } /** * {@link ApiOptions} implementation for {@link Api}s that do not take any options. */ + @PublicApi public final class NoOptions implements NotRequiredOptions { } /** * Base interface for {@link ApiOptions} that are optional. */ + @PublicApi public interface Optional extends HasOptions, NotRequiredOptions { } } diff --git a/src/com/google/android/gms/common/api/GoogleApiClient.java b/src/com/google/android/gms/common/api/GoogleApiClient.java index 92a2ee64..672a1a7e 100644 --- a/src/com/google/android/gms/common/api/GoogleApiClient.java +++ b/src/com/google/android/gms/common/api/GoogleApiClient.java @@ -11,6 +11,7 @@ import android.view.View; import com.google.android.gms.common.ConnectionResult; import org.microg.gms.Constants; +import org.microg.gms.PublicApi; import org.microg.gms.common.api.GoogleApiClientImpl; import java.util.HashMap; @@ -37,6 +38,7 @@ import java.util.concurrent.TimeUnit; * 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 @@ -217,6 +219,7 @@ public interface GoogleApiClient { /** * Builder to configure a {@link GoogleApiClient}. */ + @PublicApi public class Builder { private final Context context; private final Map apis = new HashMap<>(); @@ -412,6 +415,7 @@ public interface GoogleApiClient { * 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 public interface ConnectionCallbacks { /** * A suspension cause informing that the service has been killed. @@ -456,6 +460,7 @@ public interface GoogleApiClient { * the service. See {@link ConnectionResult} for a list of error codes and suggestions for * resolution. */ + @PublicApi public interface OnConnectionFailedListener { /** * Called when there was an error connecting the client to the service. diff --git a/src/com/google/android/gms/location/LocationClient.java b/src/com/google/android/gms/location/LocationClient.java index 130c9ed9..06b766d1 100644 --- a/src/com/google/android/gms/location/LocationClient.java +++ b/src/com/google/android/gms/location/LocationClient.java @@ -12,6 +12,10 @@ import org.microg.gms.common.ForwardConnectionCallbacks; import org.microg.gms.common.ForwardConnectionFailedListener; import org.microg.gms.common.api.AbstractPlayServicesClient; +/** + * This class is deprecated as of play services 6.5, do not use it in production systems, + * it's just a forwarder for the {@link FusedLocationProviderApi}. + */ @Deprecated public class LocationClient extends AbstractPlayServicesClient { public static final String KEY_LOCATION_CHANGED = "com.google.android.location.LOCATION"; diff --git a/src/com/google/android/gms/location/LocationServices.java b/src/com/google/android/gms/location/LocationServices.java index c0949f43..5dddd2b4 100644 --- a/src/com/google/android/gms/location/LocationServices.java +++ b/src/com/google/android/gms/location/LocationServices.java @@ -1,32 +1,17 @@ package com.google.android.gms.location; -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; -import org.microg.gms.common.api.ApiBuilder; -import org.microg.gms.common.api.ApiConnection; import org.microg.gms.location.FusedLocationProviderApiImpl; import org.microg.gms.location.GeofencingApiImpl; -import org.microg.gms.location.LocationClientImpl; +import org.microg.gms.location.LocationServicesApiBuilder; /** * The main entry point for location services integration. */ public class LocationServices { - public static final Api API = new Api<>( - new ApiBuilder() { - @Override - public ApiConnection build(Context context, Looper looper, - Api.ApiOptions.NoOptions options, - AccountInfo accountInfo, GoogleApiClient.ConnectionCallbacks callbacks, - GoogleApiClient.OnConnectionFailedListener connectionFailedListener) { - return new LocationClientImpl(context, callbacks, connectionFailedListener); - } - }); + public static final Api API = new Api<>(new + LocationServicesApiBuilder()); public static final FusedLocationProviderApi FusedLocationApi = new FusedLocationProviderApiImpl(); public static final GeofencingApi GeofencingApi = new GeofencingApiImpl(); diff --git a/src/com/google/android/gms/wearable/DataApi.java b/src/com/google/android/gms/wearable/DataApi.java new file mode 100644 index 00000000..c26bdf22 --- /dev/null +++ b/src/com/google/android/gms/wearable/DataApi.java @@ -0,0 +1,20 @@ +/* + * Copyright 2014-2015 µg 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; + +public interface DataApi { +} diff --git a/src/com/google/android/gms/wearable/MessageApi.java b/src/com/google/android/gms/wearable/MessageApi.java new file mode 100644 index 00000000..730ebe4a --- /dev/null +++ b/src/com/google/android/gms/wearable/MessageApi.java @@ -0,0 +1,20 @@ +/* + * Copyright 2014-2015 µg 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; + +public interface MessageApi { +} diff --git a/src/com/google/android/gms/wearable/NodeApi.java b/src/com/google/android/gms/wearable/NodeApi.java new file mode 100644 index 00000000..4aa57284 --- /dev/null +++ b/src/com/google/android/gms/wearable/NodeApi.java @@ -0,0 +1,20 @@ +/* + * Copyright 2014-2015 µg 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; + +public interface NodeApi { +} diff --git a/src/com/google/android/gms/wearable/Wearable.java b/src/com/google/android/gms/wearable/Wearable.java new file mode 100644 index 00000000..72003234 --- /dev/null +++ b/src/com/google/android/gms/wearable/Wearable.java @@ -0,0 +1,47 @@ +/* + * Copyright 2014-2015 µg 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.Api; +import com.google.android.gms.common.api.GoogleApiClient; + +import org.microg.gms.wearable.DataApiImpl; +import org.microg.gms.wearable.MessageApiImpl; +import org.microg.gms.wearable.NodeApiImpl; +import org.microg.gms.wearable.WearableApiBuilder; + +/** + * An API for the Android Wear platform. + */ +public class Wearable { + /** + * Token to pass to {@link GoogleApiClient.Builder#addApi(Api)} to enable the Wearable features. + */ + public static final Api API = new Api<>(new WearableApiBuilder()); + + public static final DataApi DataApi = new DataApiImpl(); + public static final MessageApi MessageApi = new MessageApiImpl(); + public static final NodeApi NodeApi = new NodeApiImpl(); + + public static class WearableOptions implements Api.ApiOptions.Optional { + public static class Builder { + public WearableOptions build() { + return new WearableOptions(); + } + } + } +} diff --git a/src/org/microg/gms/location/LocationServicesApiBuilder.java b/src/org/microg/gms/location/LocationServicesApiBuilder.java new file mode 100644 index 00000000..6876e630 --- /dev/null +++ b/src/org/microg/gms/location/LocationServicesApiBuilder.java @@ -0,0 +1,37 @@ +/* + * Copyright 2014-2015 µg 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.location; + +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; + +import org.microg.gms.common.api.ApiBuilder; +import org.microg.gms.common.api.ApiConnection; + +public class LocationServicesApiBuilder implements ApiBuilder { + @Override + public ApiConnection build(Context context, Looper looper, + Api.ApiOptions.NoOptions options, + AccountInfo accountInfo, GoogleApiClient.ConnectionCallbacks callbacks, + GoogleApiClient.OnConnectionFailedListener connectionFailedListener) { + return new LocationClientImpl(context, callbacks, connectionFailedListener); + } +} diff --git a/src/org/microg/gms/wearable/DataApiImpl.java b/src/org/microg/gms/wearable/DataApiImpl.java new file mode 100644 index 00000000..5d8f07ec --- /dev/null +++ b/src/org/microg/gms/wearable/DataApiImpl.java @@ -0,0 +1,22 @@ +/* + * Copyright 2014-2015 µg 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.wearable; + +import com.google.android.gms.wearable.DataApi; + +public class DataApiImpl implements DataApi { +} diff --git a/src/org/microg/gms/wearable/MessageApiImpl.java b/src/org/microg/gms/wearable/MessageApiImpl.java new file mode 100644 index 00000000..6f055d5e --- /dev/null +++ b/src/org/microg/gms/wearable/MessageApiImpl.java @@ -0,0 +1,22 @@ +/* + * Copyright 2014-2015 µg 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.wearable; + +import com.google.android.gms.wearable.MessageApi; + +public class MessageApiImpl implements MessageApi { +} diff --git a/src/org/microg/gms/wearable/NodeApiImpl.java b/src/org/microg/gms/wearable/NodeApiImpl.java new file mode 100644 index 00000000..bf8bf5ed --- /dev/null +++ b/src/org/microg/gms/wearable/NodeApiImpl.java @@ -0,0 +1,22 @@ +/* + * Copyright 2014-2015 µg 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.wearable; + +import com.google.android.gms.wearable.NodeApi; + +public class NodeApiImpl implements NodeApi { +} diff --git a/src/org/microg/gms/wearable/WearableApiBuilder.java b/src/org/microg/gms/wearable/WearableApiBuilder.java new file mode 100644 index 00000000..c19f5da4 --- /dev/null +++ b/src/org/microg/gms/wearable/WearableApiBuilder.java @@ -0,0 +1,40 @@ +/* + * Copyright 2014-2015 µg 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.wearable; + +import android.content.Context; +import android.os.Looper; +import android.util.Log; + +import com.google.android.gms.common.api.AccountInfo; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.wearable.Wearable; + +import org.microg.gms.common.api.ApiBuilder; +import org.microg.gms.common.api.ApiConnection; + +public class WearableApiBuilder implements ApiBuilder { + private static final String TAG = "GmsWearableApi"; + + @Override + public ApiConnection build(Context context, Looper looper, Wearable.WearableOptions options, + AccountInfo accountInfo, GoogleApiClient.ConnectionCallbacks callbacks, + GoogleApiClient.OnConnectionFailedListener connectionFailedListener) { + Log.d(TAG, "Wearables not supported"); + return null; + } +} From f70578f78b3667d57d6168f4c0b1ebea29e84ad6 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Mon, 2 Feb 2015 00:03:48 +0100 Subject: [PATCH 16/65] Fix async issues --- GmsApi | 2 +- .../android/gms/common/GooglePlayServicesUtil.java | 4 ++-- src/com/google/android/gms/common/api/Api.java | 2 +- .../android/gms/common/api/GoogleApiClient.java | 4 ++-- .../gms/location/FusedLocationProviderApi.java | 2 +- .../microg/gms/common/MultiConnectionKeeper.java | 13 +++++++++---- .../gms/location/GoogleLocationManagerClient.java | 2 +- 7 files changed, 17 insertions(+), 12 deletions(-) diff --git a/GmsApi b/GmsApi index 3c19d84e..5d2364f3 160000 --- a/GmsApi +++ b/GmsApi @@ -1 +1 @@ -Subproject commit 3c19d84eaa9842a2526166a38ad445125dbd2300 +Subproject commit 5d2364f3dcc66bdd1ae19c91d76d77d2a4a785b6 diff --git a/src/com/google/android/gms/common/GooglePlayServicesUtil.java b/src/com/google/android/gms/common/GooglePlayServicesUtil.java index 6da29c4f..01499aec 100644 --- a/src/com/google/android/gms/common/GooglePlayServicesUtil.java +++ b/src/com/google/android/gms/common/GooglePlayServicesUtil.java @@ -9,8 +9,8 @@ import android.content.DialogInterface; import android.content.pm.PackageManager; import android.util.Log; -import org.microg.gms.Constants; -import org.microg.gms.PublicApi; +import org.microg.gms.common.Constants; +import org.microg.gms.common.PublicApi; /** * Utility class for verifying that the Google Play services APK is available and up-to-date on diff --git a/src/com/google/android/gms/common/api/Api.java b/src/com/google/android/gms/common/api/Api.java index 3e5482a9..60ab9a36 100644 --- a/src/com/google/android/gms/common/api/Api.java +++ b/src/com/google/android/gms/common/api/Api.java @@ -1,6 +1,6 @@ package com.google.android.gms.common.api; -import org.microg.gms.PublicApi; +import org.microg.gms.common.PublicApi; import org.microg.gms.common.api.ApiBuilder; /** diff --git a/src/com/google/android/gms/common/api/GoogleApiClient.java b/src/com/google/android/gms/common/api/GoogleApiClient.java index 672a1a7e..e44f988e 100644 --- a/src/com/google/android/gms/common/api/GoogleApiClient.java +++ b/src/com/google/android/gms/common/api/GoogleApiClient.java @@ -10,8 +10,8 @@ import android.view.View; import com.google.android.gms.common.ConnectionResult; -import org.microg.gms.Constants; -import org.microg.gms.PublicApi; +import org.microg.gms.common.Constants; +import org.microg.gms.common.PublicApi; import org.microg.gms.common.api.GoogleApiClientImpl; import java.util.HashMap; diff --git a/src/com/google/android/gms/location/FusedLocationProviderApi.java b/src/com/google/android/gms/location/FusedLocationProviderApi.java index 46a91060..8c484537 100644 --- a/src/com/google/android/gms/location/FusedLocationProviderApi.java +++ b/src/com/google/android/gms/location/FusedLocationProviderApi.java @@ -7,7 +7,7 @@ import android.os.Looper; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.PendingResult; -import org.microg.gms.Constants; +import org.microg.gms.common.Constants; public interface FusedLocationProviderApi { public static final String KEY_LOCATION_CHANGED = "com.google.android.location.LOCATION"; diff --git a/src/org/microg/gms/common/MultiConnectionKeeper.java b/src/org/microg/gms/common/MultiConnectionKeeper.java index bb454e55..84ce091d 100644 --- a/src/org/microg/gms/common/MultiConnectionKeeper.java +++ b/src/org/microg/gms/common/MultiConnectionKeeper.java @@ -28,7 +28,7 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; -import static org.microg.gms.Constants.GMS_PACKAGE_NAME; +import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME; public class MultiConnectionKeeper { private static final String TAG = "GmsMultiConnectionKeeper"; @@ -48,7 +48,7 @@ public class MultiConnectionKeeper { return INSTANCE; } - public boolean bind(String action, ServiceConnection connection) { + public synchronized boolean bind(String action, ServiceConnection connection) { Log.d(TAG, "bind(" + action + ", " + connection + ")"); Connection con = connections.get(action); if (con != null) { @@ -66,7 +66,7 @@ public class MultiConnectionKeeper { return con.isBound(); } - public void unbind(String action, ServiceConnection connection) { + public synchronized void unbind(String action, ServiceConnection connection) { Log.d(TAG, "unbind(" + action + ", " + connection + ")"); Connection con = connections.get(action); if (con != null) { @@ -108,6 +108,7 @@ public class MultiConnectionKeeper { connection.onServiceDisconnected(componentName); } connected = false; + bound = false; } }; @@ -135,7 +136,11 @@ public class MultiConnectionKeeper { public void unbind() { Log.d(TAG, "Connection(" + actionString + ") : unbind()"); - context.unbindService(serviceConnection); + try { + context.unbindService(serviceConnection); + } catch (IllegalArgumentException e) { // not bound (whatever reason) + Log.w(TAG, e); + } bound = false; } diff --git a/src/org/microg/gms/location/GoogleLocationManagerClient.java b/src/org/microg/gms/location/GoogleLocationManagerClient.java index cbc24b21..8da05e69 100644 --- a/src/org/microg/gms/location/GoogleLocationManagerClient.java +++ b/src/org/microg/gms/location/GoogleLocationManagerClient.java @@ -25,7 +25,7 @@ import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.internal.IGmsServiceBroker; import com.google.android.gms.location.internal.IGoogleLocationManagerService; -import org.microg.gms.Constants; +import org.microg.gms.common.Constants; import org.microg.gms.common.GmsClient; public abstract class GoogleLocationManagerClient extends GmsClient { From 60d7da6dc6730f7c393eaef295bd6a9ffa2287e6 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Wed, 4 Feb 2015 23:12:23 +0100 Subject: [PATCH 17/65] Update sublib --- GmsApi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GmsApi b/GmsApi index 5d2364f3..19b0b0f1 160000 --- a/GmsApi +++ b/GmsApi @@ -1 +1 @@ -Subproject commit 5d2364f3dcc66bdd1ae19c91d76d77d2a4a785b6 +Subproject commit 19b0b0f1d782640c0e5a9a381a0191665de0b053 From 2a3cdfed4f51031d6260f4c707c88ef53dc03177 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Fri, 6 Feb 2015 13:00:31 +0100 Subject: [PATCH 18/65] Synchronize connections --- .../android/gms/location/LocationClient.java | 8 ++++ src/org/microg/gms/common/GmsClient.java | 40 ++++++++++++------- src/org/microg/gms/common/GmsConnector.java | 2 +- .../api/AbstractPlayServicesClient.java | 4 ++ .../gms/common/api/GoogleApiClientImpl.java | 14 ++++--- 5 files changed, 48 insertions(+), 20 deletions(-) diff --git a/src/com/google/android/gms/location/LocationClient.java b/src/com/google/android/gms/location/LocationClient.java index 06b766d1..04190cef 100644 --- a/src/com/google/android/gms/location/LocationClient.java +++ b/src/com/google/android/gms/location/LocationClient.java @@ -31,41 +31,49 @@ public class LocationClient extends AbstractPlayServicesClient { } public Location getLastLocation() { + assertConnected(); return LocationServices.FusedLocationApi.getLastLocation(googleApiClient); } public PendingResult requestLocationUpdates(LocationRequest request, LocationListener listener) { + assertConnected(); return LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, request, listener); } public PendingResult requestLocationUpdates(LocationRequest request, LocationListener listener, Looper looper) { + assertConnected(); return LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, request, listener, looper); } public PendingResult requestLocationUpdates(LocationRequest request, PendingIntent callbackIntent) { + assertConnected(); return LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, request, callbackIntent); } public PendingResult removeLocationUpdates(LocationListener listener) { + assertConnected(); return LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, listener); } public PendingResult removeLocationUpdates(PendingIntent callbackIntent) { + assertConnected(); return LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, callbackIntent); } public PendingResult setMockMode(boolean isMockMode) { + assertConnected(); return LocationServices.FusedLocationApi.setMockMode(googleApiClient, isMockMode); } public PendingResult setMockLocation(Location mockLocation) { + assertConnected(); return LocationServices.FusedLocationApi.setMockLocation(googleApiClient, mockLocation); } } diff --git a/src/org/microg/gms/common/GmsClient.java b/src/org/microg/gms/common/GmsClient.java index 800b8df8..80bf78cf 100644 --- a/src/org/microg/gms/common/GmsClient.java +++ b/src/org/microg/gms/common/GmsClient.java @@ -43,7 +43,7 @@ public abstract class GmsClient implements ApiConnection { private I serviceInterface; public GmsClient(Context context, GoogleApiClient.ConnectionCallbacks callbacks, - GoogleApiClient.OnConnectionFailedListener connectionFailedListener) { + GoogleApiClient.OnConnectionFailedListener connectionFailedListener) { this.context = context; this.callbacks = callbacks; this.connectionFailedListener = connectionFailedListener; @@ -57,9 +57,11 @@ public abstract class GmsClient implements ApiConnection { protected abstract I interfaceFromBinder(IBinder binder); @Override - public void connect() { + public synchronized void connect() { Log.d(TAG, "connect()"); - if (state == ConnectionState.CONNECTED || state == ConnectionState.CONNECTING) return; + if (state == ConnectionState.CONNECTED || state == ConnectionState.CONNECTING) { + Log.d(TAG, "Already connected/connecting - nothing to do"); + } state = ConnectionState.CONNECTING; if (serviceConnection != null) { MultiConnectionKeeper.getInstance(context) @@ -79,7 +81,7 @@ public abstract class GmsClient implements ApiConnection { } @Override - public void disconnect() { + public synchronized void disconnect() { Log.d(TAG, "disconnect()"); if (state == ConnectionState.DISCONNECTING) return; if (state == ConnectionState.CONNECTING) { @@ -95,16 +97,16 @@ public abstract class GmsClient implements ApiConnection { } @Override - public boolean isConnected() { + public synchronized boolean isConnected() { return state == ConnectionState.CONNECTED || state == ConnectionState.PSEUDO_CONNECTED; } @Override - public boolean isConnecting() { + public synchronized boolean isConnecting() { return state == ConnectionState.CONNECTING; } - public boolean hasError() { + public synchronized boolean hasError() { return state == ConnectionState.ERROR; } @@ -112,7 +114,13 @@ public abstract class GmsClient implements ApiConnection { return context; } - public I getServiceInterface() { + public synchronized I getServiceInterface() { + if (isConnecting()) { + // TODO: wait for connection to be established and return afterwards. + throw new IllegalStateException("Waiting for connection"); + } else if (!isConnected()) { + throw new IllegalStateException("interface only available once connected!"); + } return serviceInterface; } @@ -135,7 +143,9 @@ public abstract class GmsClient implements ApiConnection { @Override public void onServiceDisconnected(ComponentName componentName) { - state = ConnectionState.NOT_CONNECTED; + synchronized (GmsClient.this) { + state = ConnectionState.NOT_CONNECTED; + } } } @@ -144,13 +154,15 @@ public abstract class GmsClient implements ApiConnection { @Override public void onPostInitComplete(int statusCode, IBinder binder, Bundle params) throws RemoteException { - if (state == ConnectionState.DISCONNECTING) { + synchronized (GmsClient.this) { + if (state == ConnectionState.DISCONNECTING) { + state = ConnectionState.CONNECTED; + disconnect(); + return; + } state = ConnectionState.CONNECTED; - disconnect(); - return; + serviceInterface = interfaceFromBinder(binder); } - state = ConnectionState.CONNECTED; - serviceInterface = interfaceFromBinder(binder); Log.d(TAG, "GmsCallbacks : onPostInitComplete(" + serviceInterface + ")"); callbacks.onConnected(params); } diff --git a/src/org/microg/gms/common/GmsConnector.java b/src/org/microg/gms/common/GmsConnector.java index cd23afc7..90712d9a 100644 --- a/src/org/microg/gms/common/GmsConnector.java +++ b/src/org/microg/gms/common/GmsConnector.java @@ -64,7 +64,7 @@ public class GmsConnector result = (AbstractPendingResult) msg.obj; try { result.deliverResult(callback.onClientAvailable((C) apiClient.getApiConnection diff --git a/src/org/microg/gms/common/api/AbstractPlayServicesClient.java b/src/org/microg/gms/common/api/AbstractPlayServicesClient.java index 6ff1be44..0bd98141 100644 --- a/src/org/microg/gms/common/api/AbstractPlayServicesClient.java +++ b/src/org/microg/gms/common/api/AbstractPlayServicesClient.java @@ -30,6 +30,10 @@ public class AbstractPlayServicesClient implements GooglePlayServicesClient { this.googleApiClient = googleApiClient; } + public void assertConnected() { + if (!isConnected()) throw new IllegalStateException("Not connected!"); + } + @Override public void connect() { googleApiClient.connect(); diff --git a/src/org/microg/gms/common/api/GoogleApiClientImpl.java b/src/org/microg/gms/common/api/GoogleApiClientImpl.java index ff2fa9ce..10878613 100644 --- a/src/org/microg/gms/common/api/GoogleApiClientImpl.java +++ b/src/org/microg/gms/common/api/GoogleApiClientImpl.java @@ -120,8 +120,12 @@ public class GoogleApiClientImpl implements GoogleApiClient { } @Override - public void connect() { + public synchronized void connect() { Log.d(TAG, "connect()"); + if (isConnected() || isConnecting()) { + Log.d(TAG, "Already connected/connecting, noting to do"); + return; + } for (ApiConnection connection : apiConnections.values()) { if (!connection.isConnected()) { connection.connect(); @@ -130,7 +134,7 @@ public class GoogleApiClientImpl implements GoogleApiClient { } @Override - public void disconnect() { + public synchronized void disconnect() { Log.d(TAG, "disconnect()"); for (ApiConnection connection : apiConnections.values()) { if (connection.isConnected()) { @@ -140,7 +144,7 @@ public class GoogleApiClientImpl implements GoogleApiClient { } @Override - public boolean isConnected() { + public synchronized boolean isConnected() { for (ApiConnection connection : apiConnections.values()) { if (!connection.isConnected()) return false; } @@ -148,7 +152,7 @@ public class GoogleApiClientImpl implements GoogleApiClient { } @Override - public boolean isConnecting() { + public synchronized boolean isConnecting() { for (ApiConnection connection : apiConnections.values()) { if (connection.isConnecting()) return true; } @@ -167,7 +171,7 @@ public class GoogleApiClientImpl implements GoogleApiClient { } @Override - public void reconnect() { + public synchronized void reconnect() { Log.d(TAG, "reconnect()"); disconnect(); connect(); From 4b4e173addb6d4490cf613ba63136fac3fea770a Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Sat, 14 Feb 2015 23:18:28 +0100 Subject: [PATCH 19/65] Fix usage of deprecated LocationClient --- GmsApi | 2 +- SafeParcel | 2 +- build.gradle | 2 +- .../android/gms/location/LocationClient.java | 36 +++++++++---------- src/org/microg/gms/common/GmsConnector.java | 5 +-- .../api/AbstractPlayServicesClient.java | 5 +++ .../gms/common/api/GoogleApiClientImpl.java | 2 +- .../FusedLocationProviderApiImpl.java | 4 +-- 8 files changed, 32 insertions(+), 26 deletions(-) diff --git a/GmsApi b/GmsApi index 19b0b0f1..3243e7d3 160000 --- a/GmsApi +++ b/GmsApi @@ -1 +1 @@ -Subproject commit 19b0b0f1d782640c0e5a9a381a0191665de0b053 +Subproject commit 3243e7d3ecf2f7ff92279b61c4a7e7831356f5c7 diff --git a/SafeParcel b/SafeParcel index 3efecf38..a650ca5b 160000 --- a/SafeParcel +++ b/SafeParcel @@ -1 +1 @@ -Subproject commit 3efecf38ca8b1f89cd8f0c40eea4b497f1563d90 +Subproject commit a650ca5beac2a374460d820935b40f9539e692db diff --git a/build.gradle b/build.gradle index b581ecdf..15771b0c 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:1.0.0' + classpath 'com.android.tools.build:gradle:1.0.1' } } diff --git a/src/com/google/android/gms/location/LocationClient.java b/src/com/google/android/gms/location/LocationClient.java index 04190cef..b9df6c40 100644 --- a/src/com/google/android/gms/location/LocationClient.java +++ b/src/com/google/android/gms/location/LocationClient.java @@ -35,45 +35,45 @@ public class LocationClient extends AbstractPlayServicesClient { return LocationServices.FusedLocationApi.getLastLocation(googleApiClient); } - public PendingResult requestLocationUpdates(LocationRequest request, + public void requestLocationUpdates(LocationRequest request, LocationListener listener) { assertConnected(); - return LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, request, - listener); + LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, request, + listener).await(); } - public PendingResult requestLocationUpdates(LocationRequest request, + public void requestLocationUpdates(LocationRequest request, LocationListener listener, Looper looper) { assertConnected(); - return LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, request, - listener, looper); + LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, request, + listener, looper).await(); } - public PendingResult requestLocationUpdates(LocationRequest request, + public void requestLocationUpdates(LocationRequest request, PendingIntent callbackIntent) { assertConnected(); - return LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, request, - callbackIntent); + LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, request, + callbackIntent).await(); } - public PendingResult removeLocationUpdates(LocationListener listener) { + public void removeLocationUpdates(LocationListener listener) { assertConnected(); - return LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, listener); + LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, listener).await(); } - public PendingResult removeLocationUpdates(PendingIntent callbackIntent) { + public void removeLocationUpdates(PendingIntent callbackIntent) { assertConnected(); - return LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, - callbackIntent); + LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, + callbackIntent).await(); } - public PendingResult setMockMode(boolean isMockMode) { + public void setMockMode(boolean isMockMode) { assertConnected(); - return LocationServices.FusedLocationApi.setMockMode(googleApiClient, isMockMode); + LocationServices.FusedLocationApi.setMockMode(googleApiClient, isMockMode).await(); } - public PendingResult setMockLocation(Location mockLocation) { + public void setMockLocation(Location mockLocation) { assertConnected(); - return LocationServices.FusedLocationApi.setMockLocation(googleApiClient, mockLocation); + LocationServices.FusedLocationApi.setMockLocation(googleApiClient, mockLocation).await(); } } diff --git a/src/org/microg/gms/common/GmsConnector.java b/src/org/microg/gms/common/GmsConnector.java index 90712d9a..3621f8ec 100644 --- a/src/org/microg/gms/common/GmsConnector.java +++ b/src/org/microg/gms/common/GmsConnector.java @@ -45,6 +45,7 @@ public class GmsConnector connect() { Log.d(TAG, "connect()"); + apiClient.getApiConnection(api); Looper looper = apiClient.getLooper(); final AbstractPendingResult result = new AbstractPendingResult<>(looper); Message msg = new Message(); @@ -67,8 +68,8 @@ public class GmsConnector result = (AbstractPendingResult) msg.obj; try { - result.deliverResult(callback.onClientAvailable((C) apiClient.getApiConnection - (api))); + C connection = (C)apiClient.getApiConnection(api); + result.deliverResult(callback.onClientAvailable(connection)); } catch (RemoteException ignored) { } diff --git a/src/org/microg/gms/common/api/AbstractPlayServicesClient.java b/src/org/microg/gms/common/api/AbstractPlayServicesClient.java index 0bd98141..50fd1bc3 100644 --- a/src/org/microg/gms/common/api/AbstractPlayServicesClient.java +++ b/src/org/microg/gms/common/api/AbstractPlayServicesClient.java @@ -16,6 +16,8 @@ package org.microg.gms.common.api; +import android.util.Log; + import com.google.android.gms.common.GooglePlayServicesClient; import com.google.android.gms.common.api.GoogleApiClient; @@ -23,6 +25,7 @@ import org.microg.gms.common.ForwardConnectionCallbacks; import org.microg.gms.common.ForwardConnectionFailedListener; public class AbstractPlayServicesClient implements GooglePlayServicesClient { + private static final String TAG = "GmsPlatServicesClient"; protected final GoogleApiClient googleApiClient; @@ -36,11 +39,13 @@ public class AbstractPlayServicesClient implements GooglePlayServicesClient { @Override public void connect() { + Log.d(TAG, "connect()"); googleApiClient.connect(); } @Override public void disconnect() { + Log.d(TAG, "disconnect()"); googleApiClient.disconnect(); } diff --git a/src/org/microg/gms/common/api/GoogleApiClientImpl.java b/src/org/microg/gms/common/api/GoogleApiClientImpl.java index 10878613..db0c707e 100644 --- a/src/org/microg/gms/common/api/GoogleApiClientImpl.java +++ b/src/org/microg/gms/common/api/GoogleApiClientImpl.java @@ -123,7 +123,7 @@ public class GoogleApiClientImpl implements GoogleApiClient { public synchronized void connect() { Log.d(TAG, "connect()"); if (isConnected() || isConnecting()) { - Log.d(TAG, "Already connected/connecting, noting to do"); + Log.d(TAG, "Already connected/connecting, nothing to do"); return; } for (ApiConnection connection : apiConnections.values()) { diff --git a/src/org/microg/gms/location/FusedLocationProviderApiImpl.java b/src/org/microg/gms/location/FusedLocationProviderApiImpl.java index be46f446..f89a79af 100644 --- a/src/org/microg/gms/location/FusedLocationProviderApiImpl.java +++ b/src/org/microg/gms/location/FusedLocationProviderApiImpl.java @@ -129,8 +129,8 @@ public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { @Override public Result onClientAvailable(LocationClientImpl client) throws RemoteException { - runnable.run(client); - return Status.SUCCESS; + runnable.run(client); + return Status.SUCCESS; } }).connect(); } From c790480c40826cecd5d115c50d6fb7dfb6060b2b Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Fri, 13 Mar 2015 01:41:14 +0100 Subject: [PATCH 20/65] Update structure, add maven support --- .gitmodules | 7 +--- GmsApi | 1 - SafeParcel | 2 +- build.gradle | 41 ++++++------------- play-services-api | 1 + play-services-base/build.gradle | 12 ++++++ .../src/main/AndroidManifest.xml | 6 +++ .../android/gms/common/ConnectionResult.java | 0 .../gms/common/GooglePlayServicesClient.java | 0 .../gms/common/GooglePlayServicesUtil.java | 0 .../google/android/gms/common/api/Api.java | 0 .../gms/common/api/GoogleApiClient.java | 0 .../android/gms/common/api/PendingResult.java | 0 .../android/gms/common/api/Releasable.java | 0 .../gms/common/api/ResultCallback.java | 0 .../common/ForwardConnectionCallbacks.java | 0 .../ForwardConnectionFailedListener.java | 0 .../org/microg/gms/common/GmsClient.java | 0 .../org/microg/gms/common/GmsConnector.java | 0 .../gms/common/MultiConnectionKeeper.java | 0 .../gms/common/api/AbstractPendingResult.java | 0 .../api/AbstractPlayServicesClient.java | 0 .../org/microg/gms/common/api/ApiBuilder.java | 0 .../microg/gms/common/api/ApiConnection.java | 0 .../gms/common/api/GoogleApiClientImpl.java | 0 .../gms/common/api/ResultCallbackHandler.java | 0 play-services-location/build.gradle | 11 +++++ .../src/main/AndroidManifest.xml | 0 .../location/FusedLocationProviderApi.java | 0 .../android/gms/location/GeofencingApi.java | 0 .../android/gms/location/LocationClient.java | 0 .../gms/location/LocationListener.java | 0 .../gms/location/LocationServices.java | 0 .../FusedLocationProviderApiImpl.java | 0 .../gms/location/GeofencingApiImpl.java | 0 .../location/GoogleLocationManagerClient.java | 0 .../gms/location/LocationClientImpl.java | 0 .../location/LocationServicesApiBuilder.java | 0 .../location/NativeLocationClientImpl.java | 0 play-services-wearable/build.gradle | 11 +++++ .../src/main/AndroidManifest.xml | 6 +++ .../google/android/gms/wearable/DataApi.java | 0 .../android/gms/wearable/MessageApi.java | 0 .../google/android/gms/wearable/NodeApi.java | 0 .../google/android/gms/wearable/Wearable.java | 0 .../org/microg/gms/wearable/DataApiImpl.java | 0 .../microg/gms/wearable/MessageApiImpl.java | 0 .../org/microg/gms/wearable/NodeApiImpl.java | 0 .../gms/wearable/WearableApiBuilder.java | 0 play-services/build.gradle | 14 +++++++ {res => play-services/res}/values/version.xml | 0 play-services/src/main/AndroidManifest.xml | 6 +++ settings.gradle | 9 +++- 53 files changed, 91 insertions(+), 36 deletions(-) delete mode 160000 GmsApi mode change 160000 => 120000 SafeParcel create mode 160000 play-services-api create mode 100644 play-services-base/build.gradle create mode 100644 play-services-base/src/main/AndroidManifest.xml rename {src => play-services-base/src/main/java}/com/google/android/gms/common/ConnectionResult.java (100%) rename {src => play-services-base/src/main/java}/com/google/android/gms/common/GooglePlayServicesClient.java (100%) rename {src => play-services-base/src/main/java}/com/google/android/gms/common/GooglePlayServicesUtil.java (100%) rename {src => play-services-base/src/main/java}/com/google/android/gms/common/api/Api.java (100%) rename {src => play-services-base/src/main/java}/com/google/android/gms/common/api/GoogleApiClient.java (100%) rename {src => play-services-base/src/main/java}/com/google/android/gms/common/api/PendingResult.java (100%) rename {src => play-services-base/src/main/java}/com/google/android/gms/common/api/Releasable.java (100%) rename {src => play-services-base/src/main/java}/com/google/android/gms/common/api/ResultCallback.java (100%) rename {src => play-services-base/src/main/java}/org/microg/gms/common/ForwardConnectionCallbacks.java (100%) rename {src => play-services-base/src/main/java}/org/microg/gms/common/ForwardConnectionFailedListener.java (100%) rename {src => play-services-base/src/main/java}/org/microg/gms/common/GmsClient.java (100%) rename {src => play-services-base/src/main/java}/org/microg/gms/common/GmsConnector.java (100%) rename {src => play-services-base/src/main/java}/org/microg/gms/common/MultiConnectionKeeper.java (100%) rename {src => play-services-base/src/main/java}/org/microg/gms/common/api/AbstractPendingResult.java (100%) rename {src => play-services-base/src/main/java}/org/microg/gms/common/api/AbstractPlayServicesClient.java (100%) rename {src => play-services-base/src/main/java}/org/microg/gms/common/api/ApiBuilder.java (100%) rename {src => play-services-base/src/main/java}/org/microg/gms/common/api/ApiConnection.java (100%) rename {src => play-services-base/src/main/java}/org/microg/gms/common/api/GoogleApiClientImpl.java (100%) rename {src => play-services-base/src/main/java}/org/microg/gms/common/api/ResultCallbackHandler.java (100%) create mode 100644 play-services-location/build.gradle rename AndroidManifest.xml => play-services-location/src/main/AndroidManifest.xml (100%) rename {src => play-services-location/src/main/java}/com/google/android/gms/location/FusedLocationProviderApi.java (100%) rename {src => play-services-location/src/main/java}/com/google/android/gms/location/GeofencingApi.java (100%) rename {src => play-services-location/src/main/java}/com/google/android/gms/location/LocationClient.java (100%) rename {src => play-services-location/src/main/java}/com/google/android/gms/location/LocationListener.java (100%) rename {src => play-services-location/src/main/java}/com/google/android/gms/location/LocationServices.java (100%) rename {src => play-services-location/src/main/java}/org/microg/gms/location/FusedLocationProviderApiImpl.java (100%) rename {src => play-services-location/src/main/java}/org/microg/gms/location/GeofencingApiImpl.java (100%) rename {src => play-services-location/src/main/java}/org/microg/gms/location/GoogleLocationManagerClient.java (100%) rename {src => play-services-location/src/main/java}/org/microg/gms/location/LocationClientImpl.java (100%) rename {src => play-services-location/src/main/java}/org/microg/gms/location/LocationServicesApiBuilder.java (100%) rename {src => play-services-location/src/main/java}/org/microg/gms/location/NativeLocationClientImpl.java (100%) create mode 100644 play-services-wearable/build.gradle create mode 100644 play-services-wearable/src/main/AndroidManifest.xml rename {src => play-services-wearable/src/main/java}/com/google/android/gms/wearable/DataApi.java (100%) rename {src => play-services-wearable/src/main/java}/com/google/android/gms/wearable/MessageApi.java (100%) rename {src => play-services-wearable/src/main/java}/com/google/android/gms/wearable/NodeApi.java (100%) rename {src => play-services-wearable/src/main/java}/com/google/android/gms/wearable/Wearable.java (100%) rename {src => play-services-wearable/src/main/java}/org/microg/gms/wearable/DataApiImpl.java (100%) rename {src => play-services-wearable/src/main/java}/org/microg/gms/wearable/MessageApiImpl.java (100%) rename {src => play-services-wearable/src/main/java}/org/microg/gms/wearable/NodeApiImpl.java (100%) rename {src => play-services-wearable/src/main/java}/org/microg/gms/wearable/WearableApiBuilder.java (100%) create mode 100644 play-services/build.gradle rename {res => play-services/res}/values/version.xml (100%) create mode 100644 play-services/src/main/AndroidManifest.xml diff --git a/.gitmodules b/.gitmodules index 2be15080..bddcb0cc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "GmsApi"] - path = GmsApi +[submodule "play-services-api"] + path = play-services-api url = https://github.com/microg/android_external_GmsApi.git -[submodule "SafeParcel"] - path = SafeParcel - url = https://github.com/microg/android_external_SafeParcel.git diff --git a/GmsApi b/GmsApi deleted file mode 160000 index 3243e7d3..00000000 --- a/GmsApi +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3243e7d3ecf2f7ff92279b61c4a7e7831356f5c7 diff --git a/SafeParcel b/SafeParcel deleted file mode 160000 index a650ca5b..00000000 --- a/SafeParcel +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a650ca5beac2a374460d820935b40f9539e692db diff --git a/SafeParcel b/SafeParcel new file mode 120000 index 00000000..18f7aa75 --- /dev/null +++ b/SafeParcel @@ -0,0 +1 @@ +play-services-api/SafeParcel/ \ No newline at end of file diff --git a/build.gradle b/build.gradle index 15771b0c..252327cb 100644 --- a/build.gradle +++ b/build.gradle @@ -1,30 +1,15 @@ -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath 'com.android.tools.build:gradle:1.0.1' - } -} - -apply plugin: 'com.android.library' - -dependencies { - compile 'com.android.support:support-v4:21.0.3' - compile project(':GmsApi') -} - -android { - compileSdkVersion 21 - buildToolsVersion "21.1.2" - lintOptions.abortOnError false - - sourceSets { - main { - manifest.srcFile 'AndroidManifest.xml' - java.srcDirs = ['src'] - aidl.srcDirs = ['src'] - res.srcDirs = ['res'] +// Top-level build file where you can add configuration options common to all sub-projects/modules. +subprojects { + buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:1.0.1' + classpath 'com.github.dcendents:android-maven-plugin:1.2' } } -} + + group = 'org.microg.gms' + version = '1.0-SNAPSHOT' +} \ No newline at end of file diff --git a/play-services-api b/play-services-api new file mode 160000 index 00000000..d2ec5f52 --- /dev/null +++ b/play-services-api @@ -0,0 +1 @@ +Subproject commit d2ec5f52fa6c1aed0640e65130b20e3ffce5f8b0 diff --git a/play-services-base/build.gradle b/play-services-base/build.gradle new file mode 100644 index 00000000..8a308f2f --- /dev/null +++ b/play-services-base/build.gradle @@ -0,0 +1,12 @@ +apply plugin: 'com.android.library' +apply plugin: 'com.github.dcendents.android-maven' + +dependencies { + compile 'com.android.support:support-v4:22.0.0' + compile project(':play-services-api') +} + +android { + compileSdkVersion 22 + buildToolsVersion "21.1.2" +} diff --git a/play-services-base/src/main/AndroidManifest.xml b/play-services-base/src/main/AndroidManifest.xml new file mode 100644 index 00000000..9fa720e3 --- /dev/null +++ b/play-services-base/src/main/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/src/com/google/android/gms/common/ConnectionResult.java b/play-services-base/src/main/java/com/google/android/gms/common/ConnectionResult.java similarity index 100% rename from src/com/google/android/gms/common/ConnectionResult.java rename to play-services-base/src/main/java/com/google/android/gms/common/ConnectionResult.java diff --git a/src/com/google/android/gms/common/GooglePlayServicesClient.java b/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesClient.java similarity index 100% rename from src/com/google/android/gms/common/GooglePlayServicesClient.java rename to play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesClient.java diff --git a/src/com/google/android/gms/common/GooglePlayServicesUtil.java b/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesUtil.java similarity index 100% rename from src/com/google/android/gms/common/GooglePlayServicesUtil.java rename to play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesUtil.java diff --git a/src/com/google/android/gms/common/api/Api.java b/play-services-base/src/main/java/com/google/android/gms/common/api/Api.java similarity index 100% rename from src/com/google/android/gms/common/api/Api.java rename to play-services-base/src/main/java/com/google/android/gms/common/api/Api.java diff --git a/src/com/google/android/gms/common/api/GoogleApiClient.java b/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java similarity index 100% rename from src/com/google/android/gms/common/api/GoogleApiClient.java rename to play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java diff --git a/src/com/google/android/gms/common/api/PendingResult.java b/play-services-base/src/main/java/com/google/android/gms/common/api/PendingResult.java similarity index 100% rename from src/com/google/android/gms/common/api/PendingResult.java rename to play-services-base/src/main/java/com/google/android/gms/common/api/PendingResult.java diff --git a/src/com/google/android/gms/common/api/Releasable.java b/play-services-base/src/main/java/com/google/android/gms/common/api/Releasable.java similarity index 100% rename from src/com/google/android/gms/common/api/Releasable.java rename to play-services-base/src/main/java/com/google/android/gms/common/api/Releasable.java diff --git a/src/com/google/android/gms/common/api/ResultCallback.java b/play-services-base/src/main/java/com/google/android/gms/common/api/ResultCallback.java similarity index 100% rename from src/com/google/android/gms/common/api/ResultCallback.java rename to play-services-base/src/main/java/com/google/android/gms/common/api/ResultCallback.java diff --git a/src/org/microg/gms/common/ForwardConnectionCallbacks.java b/play-services-base/src/main/java/org/microg/gms/common/ForwardConnectionCallbacks.java similarity index 100% rename from src/org/microg/gms/common/ForwardConnectionCallbacks.java rename to play-services-base/src/main/java/org/microg/gms/common/ForwardConnectionCallbacks.java diff --git a/src/org/microg/gms/common/ForwardConnectionFailedListener.java b/play-services-base/src/main/java/org/microg/gms/common/ForwardConnectionFailedListener.java similarity index 100% rename from src/org/microg/gms/common/ForwardConnectionFailedListener.java rename to play-services-base/src/main/java/org/microg/gms/common/ForwardConnectionFailedListener.java diff --git a/src/org/microg/gms/common/GmsClient.java b/play-services-base/src/main/java/org/microg/gms/common/GmsClient.java similarity index 100% rename from src/org/microg/gms/common/GmsClient.java rename to play-services-base/src/main/java/org/microg/gms/common/GmsClient.java diff --git a/src/org/microg/gms/common/GmsConnector.java b/play-services-base/src/main/java/org/microg/gms/common/GmsConnector.java similarity index 100% rename from src/org/microg/gms/common/GmsConnector.java rename to play-services-base/src/main/java/org/microg/gms/common/GmsConnector.java diff --git a/src/org/microg/gms/common/MultiConnectionKeeper.java b/play-services-base/src/main/java/org/microg/gms/common/MultiConnectionKeeper.java similarity index 100% rename from src/org/microg/gms/common/MultiConnectionKeeper.java rename to play-services-base/src/main/java/org/microg/gms/common/MultiConnectionKeeper.java diff --git a/src/org/microg/gms/common/api/AbstractPendingResult.java b/play-services-base/src/main/java/org/microg/gms/common/api/AbstractPendingResult.java similarity index 100% rename from src/org/microg/gms/common/api/AbstractPendingResult.java rename to play-services-base/src/main/java/org/microg/gms/common/api/AbstractPendingResult.java diff --git a/src/org/microg/gms/common/api/AbstractPlayServicesClient.java b/play-services-base/src/main/java/org/microg/gms/common/api/AbstractPlayServicesClient.java similarity index 100% rename from src/org/microg/gms/common/api/AbstractPlayServicesClient.java rename to play-services-base/src/main/java/org/microg/gms/common/api/AbstractPlayServicesClient.java diff --git a/src/org/microg/gms/common/api/ApiBuilder.java b/play-services-base/src/main/java/org/microg/gms/common/api/ApiBuilder.java similarity index 100% rename from src/org/microg/gms/common/api/ApiBuilder.java rename to play-services-base/src/main/java/org/microg/gms/common/api/ApiBuilder.java diff --git a/src/org/microg/gms/common/api/ApiConnection.java b/play-services-base/src/main/java/org/microg/gms/common/api/ApiConnection.java similarity index 100% rename from src/org/microg/gms/common/api/ApiConnection.java rename to play-services-base/src/main/java/org/microg/gms/common/api/ApiConnection.java diff --git a/src/org/microg/gms/common/api/GoogleApiClientImpl.java b/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java similarity index 100% rename from src/org/microg/gms/common/api/GoogleApiClientImpl.java rename to play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java diff --git a/src/org/microg/gms/common/api/ResultCallbackHandler.java b/play-services-base/src/main/java/org/microg/gms/common/api/ResultCallbackHandler.java similarity index 100% rename from src/org/microg/gms/common/api/ResultCallbackHandler.java rename to play-services-base/src/main/java/org/microg/gms/common/api/ResultCallbackHandler.java diff --git a/play-services-location/build.gradle b/play-services-location/build.gradle new file mode 100644 index 00000000..f79b92c5 --- /dev/null +++ b/play-services-location/build.gradle @@ -0,0 +1,11 @@ +apply plugin: 'com.android.library' +apply plugin: 'com.github.dcendents.android-maven' + +dependencies { + compile project(':play-services-base') +} + +android { + compileSdkVersion 22 + buildToolsVersion "22.0.0" +} \ No newline at end of file diff --git a/AndroidManifest.xml b/play-services-location/src/main/AndroidManifest.xml similarity index 100% rename from AndroidManifest.xml rename to play-services-location/src/main/AndroidManifest.xml diff --git a/src/com/google/android/gms/location/FusedLocationProviderApi.java b/play-services-location/src/main/java/com/google/android/gms/location/FusedLocationProviderApi.java similarity index 100% rename from src/com/google/android/gms/location/FusedLocationProviderApi.java rename to play-services-location/src/main/java/com/google/android/gms/location/FusedLocationProviderApi.java diff --git a/src/com/google/android/gms/location/GeofencingApi.java b/play-services-location/src/main/java/com/google/android/gms/location/GeofencingApi.java similarity index 100% rename from src/com/google/android/gms/location/GeofencingApi.java rename to play-services-location/src/main/java/com/google/android/gms/location/GeofencingApi.java diff --git a/src/com/google/android/gms/location/LocationClient.java b/play-services-location/src/main/java/com/google/android/gms/location/LocationClient.java similarity index 100% rename from src/com/google/android/gms/location/LocationClient.java rename to play-services-location/src/main/java/com/google/android/gms/location/LocationClient.java diff --git a/src/com/google/android/gms/location/LocationListener.java b/play-services-location/src/main/java/com/google/android/gms/location/LocationListener.java similarity index 100% rename from src/com/google/android/gms/location/LocationListener.java rename to play-services-location/src/main/java/com/google/android/gms/location/LocationListener.java diff --git a/src/com/google/android/gms/location/LocationServices.java b/play-services-location/src/main/java/com/google/android/gms/location/LocationServices.java similarity index 100% rename from src/com/google/android/gms/location/LocationServices.java rename to play-services-location/src/main/java/com/google/android/gms/location/LocationServices.java diff --git a/src/org/microg/gms/location/FusedLocationProviderApiImpl.java b/play-services-location/src/main/java/org/microg/gms/location/FusedLocationProviderApiImpl.java similarity index 100% rename from src/org/microg/gms/location/FusedLocationProviderApiImpl.java rename to play-services-location/src/main/java/org/microg/gms/location/FusedLocationProviderApiImpl.java diff --git a/src/org/microg/gms/location/GeofencingApiImpl.java b/play-services-location/src/main/java/org/microg/gms/location/GeofencingApiImpl.java similarity index 100% rename from src/org/microg/gms/location/GeofencingApiImpl.java rename to play-services-location/src/main/java/org/microg/gms/location/GeofencingApiImpl.java diff --git a/src/org/microg/gms/location/GoogleLocationManagerClient.java b/play-services-location/src/main/java/org/microg/gms/location/GoogleLocationManagerClient.java similarity index 100% rename from src/org/microg/gms/location/GoogleLocationManagerClient.java rename to play-services-location/src/main/java/org/microg/gms/location/GoogleLocationManagerClient.java diff --git a/src/org/microg/gms/location/LocationClientImpl.java b/play-services-location/src/main/java/org/microg/gms/location/LocationClientImpl.java similarity index 100% rename from src/org/microg/gms/location/LocationClientImpl.java rename to play-services-location/src/main/java/org/microg/gms/location/LocationClientImpl.java diff --git a/src/org/microg/gms/location/LocationServicesApiBuilder.java b/play-services-location/src/main/java/org/microg/gms/location/LocationServicesApiBuilder.java similarity index 100% rename from src/org/microg/gms/location/LocationServicesApiBuilder.java rename to play-services-location/src/main/java/org/microg/gms/location/LocationServicesApiBuilder.java diff --git a/src/org/microg/gms/location/NativeLocationClientImpl.java b/play-services-location/src/main/java/org/microg/gms/location/NativeLocationClientImpl.java similarity index 100% rename from src/org/microg/gms/location/NativeLocationClientImpl.java rename to play-services-location/src/main/java/org/microg/gms/location/NativeLocationClientImpl.java diff --git a/play-services-wearable/build.gradle b/play-services-wearable/build.gradle new file mode 100644 index 00000000..5e701f76 --- /dev/null +++ b/play-services-wearable/build.gradle @@ -0,0 +1,11 @@ +apply plugin: 'com.android.library' +apply plugin: 'com.github.dcendents.android-maven' + +dependencies { + compile project(':play-services-base') +} + +android { + compileSdkVersion 21 + buildToolsVersion "21.1.2" +} diff --git a/play-services-wearable/src/main/AndroidManifest.xml b/play-services-wearable/src/main/AndroidManifest.xml new file mode 100644 index 00000000..9fa720e3 --- /dev/null +++ b/play-services-wearable/src/main/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/src/com/google/android/gms/wearable/DataApi.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataApi.java similarity index 100% rename from src/com/google/android/gms/wearable/DataApi.java rename to play-services-wearable/src/main/java/com/google/android/gms/wearable/DataApi.java diff --git a/src/com/google/android/gms/wearable/MessageApi.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageApi.java similarity index 100% rename from src/com/google/android/gms/wearable/MessageApi.java rename to play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageApi.java diff --git a/src/com/google/android/gms/wearable/NodeApi.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/NodeApi.java similarity index 100% rename from src/com/google/android/gms/wearable/NodeApi.java rename to play-services-wearable/src/main/java/com/google/android/gms/wearable/NodeApi.java diff --git a/src/com/google/android/gms/wearable/Wearable.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/Wearable.java similarity index 100% rename from src/com/google/android/gms/wearable/Wearable.java rename to play-services-wearable/src/main/java/com/google/android/gms/wearable/Wearable.java diff --git a/src/org/microg/gms/wearable/DataApiImpl.java b/play-services-wearable/src/main/java/org/microg/gms/wearable/DataApiImpl.java similarity index 100% rename from src/org/microg/gms/wearable/DataApiImpl.java rename to play-services-wearable/src/main/java/org/microg/gms/wearable/DataApiImpl.java diff --git a/src/org/microg/gms/wearable/MessageApiImpl.java b/play-services-wearable/src/main/java/org/microg/gms/wearable/MessageApiImpl.java similarity index 100% rename from src/org/microg/gms/wearable/MessageApiImpl.java rename to play-services-wearable/src/main/java/org/microg/gms/wearable/MessageApiImpl.java diff --git a/src/org/microg/gms/wearable/NodeApiImpl.java b/play-services-wearable/src/main/java/org/microg/gms/wearable/NodeApiImpl.java similarity index 100% rename from src/org/microg/gms/wearable/NodeApiImpl.java rename to play-services-wearable/src/main/java/org/microg/gms/wearable/NodeApiImpl.java diff --git a/src/org/microg/gms/wearable/WearableApiBuilder.java b/play-services-wearable/src/main/java/org/microg/gms/wearable/WearableApiBuilder.java similarity index 100% rename from src/org/microg/gms/wearable/WearableApiBuilder.java rename to play-services-wearable/src/main/java/org/microg/gms/wearable/WearableApiBuilder.java diff --git a/play-services/build.gradle b/play-services/build.gradle new file mode 100644 index 00000000..4d357641 --- /dev/null +++ b/play-services/build.gradle @@ -0,0 +1,14 @@ +apply plugin: 'com.android.library' +apply plugin: 'com.github.dcendents.android-maven' + +dependencies { + compile 'com.android.support:support-v4:21.0.3' + compile project(':play-services-base') + compile project(':play-services-location') + compile project(':play-services-wearable') +} + +android { + compileSdkVersion 21 + buildToolsVersion "22.0.0" +} diff --git a/res/values/version.xml b/play-services/res/values/version.xml similarity index 100% rename from res/values/version.xml rename to play-services/res/values/version.xml diff --git a/play-services/src/main/AndroidManifest.xml b/play-services/src/main/AndroidManifest.xml new file mode 100644 index 00000000..9fa720e3 --- /dev/null +++ b/play-services/src/main/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/settings.gradle b/settings.gradle index 87e5535f..c87b1990 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,8 @@ -include ':GmsApi', ':SafeParcel' +include ':SafeParcel' +include ':play-services-api' + +include ':play-services-base' +include ':play-services-location' +include ':play-services-wearable' + +include ':play-services' From d47deb067f661d777ff02ce04e9910cd2f205555 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Fri, 13 Mar 2015 02:24:01 +0100 Subject: [PATCH 21/65] Various fixes for gradle/maven --- .gitignore | 1 + play-services-api | 2 +- play-services-base/src/main/AndroidManifest.xml | 2 ++ .../src/main}/res/values/version.xml | 0 play-services-location/src/main/AndroidManifest.xml | 2 +- play-services-wearable/src/main/AndroidManifest.xml | 4 +++- play-services/build.gradle | 1 - play-services/src/main/AndroidManifest.xml | 4 +++- 8 files changed, 11 insertions(+), 5 deletions(-) rename {play-services => play-services-base/src/main}/res/values/version.xml (100%) diff --git a/.gitignore b/.gitignore index 5a0e68a2..769ee68c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ out/ build/ +.idea/ *.iml local.properties .gradle/ diff --git a/play-services-api b/play-services-api index d2ec5f52..dbae4add 160000 --- a/play-services-api +++ b/play-services-api @@ -1 +1 @@ -Subproject commit d2ec5f52fa6c1aed0640e65130b20e3ffce5f8b0 +Subproject commit dbae4add94505fb0765ca9a18896848df6ae6ae4 diff --git a/play-services-base/src/main/AndroidManifest.xml b/play-services-base/src/main/AndroidManifest.xml index 9fa720e3..d3685676 100644 --- a/play-services-base/src/main/AndroidManifest.xml +++ b/play-services-base/src/main/AndroidManifest.xml @@ -3,4 +3,6 @@ package="com.google.android.gms"> + + diff --git a/play-services/res/values/version.xml b/play-services-base/src/main/res/values/version.xml similarity index 100% rename from play-services/res/values/version.xml rename to play-services-base/src/main/res/values/version.xml diff --git a/play-services-location/src/main/AndroidManifest.xml b/play-services-location/src/main/AndroidManifest.xml index b0fcdc8e..32fad327 100644 --- a/play-services-location/src/main/AndroidManifest.xml +++ b/play-services-location/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ + package="org.microg.gms.location"> diff --git a/play-services-wearable/src/main/AndroidManifest.xml b/play-services-wearable/src/main/AndroidManifest.xml index 9fa720e3..33723d79 100644 --- a/play-services-wearable/src/main/AndroidManifest.xml +++ b/play-services-wearable/src/main/AndroidManifest.xml @@ -1,6 +1,8 @@ + package="org.microg.gms.wearable"> + + diff --git a/play-services/build.gradle b/play-services/build.gradle index 4d357641..1646fc2f 100644 --- a/play-services/build.gradle +++ b/play-services/build.gradle @@ -2,7 +2,6 @@ apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' dependencies { - compile 'com.android.support:support-v4:21.0.3' compile project(':play-services-base') compile project(':play-services-location') compile project(':play-services-wearable') diff --git a/play-services/src/main/AndroidManifest.xml b/play-services/src/main/AndroidManifest.xml index 9fa720e3..336be92b 100644 --- a/play-services/src/main/AndroidManifest.xml +++ b/play-services/src/main/AndroidManifest.xml @@ -1,6 +1,8 @@ + package="org.microg.gms"> + + From 76602a343f6655be951b71277cbba97146139ca5 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Fri, 13 Mar 2015 03:08:00 +0100 Subject: [PATCH 22/65] Update DashClock example --- README.md | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d03a1ce4..1154cef8 100644 --- a/README.md +++ b/README.md @@ -15,11 +15,32 @@ Example: DashClock However it uses play services as location backend and thus requires proprietary libraries to compile it. However, it is possible to build DashClock using GmsLib, supporting all it's location features, with or without play services installed. -To do this, download and build GmsLib plus its submodules and copy the three resulting .aar files (SafeParcel.aar, GmsApi.aar, GmsLib.aar) to `$DASHCLOCK_DIR/local_aars/`. -Then replace `compile 'com.google.android.gms:play-services:4.0.30'` in `$DASHCLOCK_SRC/main/build.gradle` with +To do this, download and build GmsLib plus its submodules and install it to the local gradle repository: - compile(name:'GmsLib', ext:'aar') - compile(name:'GmsApi', ext:'aar') - compile(name:'SafeParcel', ext:'aar') + $ git clone https://github.com/microg/android_external_GmsLib.git GmsLib + $ cd GmsLib + $ git submodule update --init --recursive + $ gradle install -and build as usual. +Then update the main/build.gradle to point to non-google gms in local maven: + + repositories { + + maven { url "${System.env.HOME}/.m2/repository" } // This can be mavenLocal() since Gradle 2.0 + mavenCentral() + flatDir { + dirs '../local_aars' + } + } + + dependencies { + compile 'com.android.support:support-v13:22.0.0' + - compile 'com.google.android.gms:play-services:4.0.30' + + compile 'org.microg.gms:play-services:1.0-SNAPSHOT' + //compile 'com.mobeta.android.dslv:drag-sort-listview:0.6.1-SNAPSHOT-AAR' + compile 'com.mobeta.android.dslv:drag-sort-listview:0.6.1-SNAPSHOT-AAR@aar' + compile project(':api') + } + +Afterwards you can compile dashclock the usual way: + + $ gradle :main:assembleDebug From c24447d5d32e4c35cf412a16ad7e5353ed33c5bf Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Fri, 13 Mar 2015 03:15:24 +0100 Subject: [PATCH 23/65] Allow sub-projects to exist stand-alone --- build.gradle | 10 ---------- play-services-base/build.gradle | 12 +++++++++++- play-services-location/build.gradle | 10 ++++++++++ play-services-wearable/build.gradle | 14 ++++++++++++-- play-services/build.gradle | 12 +++++++++++- 5 files changed, 44 insertions(+), 14 deletions(-) diff --git a/build.gradle b/build.gradle index 252327cb..135b35aa 100644 --- a/build.gradle +++ b/build.gradle @@ -1,15 +1,5 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. subprojects { - buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath 'com.android.tools.build:gradle:1.0.1' - classpath 'com.github.dcendents:android-maven-plugin:1.2' - } - } - group = 'org.microg.gms' version = '1.0-SNAPSHOT' } \ No newline at end of file diff --git a/play-services-base/build.gradle b/play-services-base/build.gradle index 8a308f2f..c6903999 100644 --- a/play-services-base/build.gradle +++ b/play-services-base/build.gradle @@ -1,3 +1,13 @@ +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:1.0.1' + classpath 'com.github.dcendents:android-maven-plugin:1.2' + } +} + apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' @@ -8,5 +18,5 @@ dependencies { android { compileSdkVersion 22 - buildToolsVersion "21.1.2" + buildToolsVersion "22.0.0" } diff --git a/play-services-location/build.gradle b/play-services-location/build.gradle index f79b92c5..918f297c 100644 --- a/play-services-location/build.gradle +++ b/play-services-location/build.gradle @@ -1,3 +1,13 @@ +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:1.0.1' + classpath 'com.github.dcendents:android-maven-plugin:1.2' + } +} + apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' diff --git a/play-services-wearable/build.gradle b/play-services-wearable/build.gradle index 5e701f76..923eb4b0 100644 --- a/play-services-wearable/build.gradle +++ b/play-services-wearable/build.gradle @@ -1,3 +1,13 @@ +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:1.0.1' + classpath 'com.github.dcendents:android-maven-plugin:1.2' + } +} + apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' @@ -6,6 +16,6 @@ dependencies { } android { - compileSdkVersion 21 - buildToolsVersion "21.1.2" + compileSdkVersion 22 + buildToolsVersion "22.0.0" } diff --git a/play-services/build.gradle b/play-services/build.gradle index 1646fc2f..1f73cbcc 100644 --- a/play-services/build.gradle +++ b/play-services/build.gradle @@ -1,3 +1,13 @@ +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:1.0.1' + classpath 'com.github.dcendents:android-maven-plugin:1.2' + } +} + apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' @@ -8,6 +18,6 @@ dependencies { } android { - compileSdkVersion 21 + compileSdkVersion 22 buildToolsVersion "22.0.0" } From 588df8f17bcaa3e1be3686374d502c49b370fb91 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Sat, 14 Mar 2015 22:21:35 +0100 Subject: [PATCH 24/65] Update api --- .gitmodules | 4 ++-- SafeParcel | 1 - build.gradle | 4 ++-- extern/GmsApi | 1 + play-services-api | 2 +- safe-parcel | 1 + settings.gradle | 3 ++- 7 files changed, 9 insertions(+), 7 deletions(-) delete mode 120000 SafeParcel create mode 160000 extern/GmsApi mode change 160000 => 120000 play-services-api create mode 120000 safe-parcel diff --git a/.gitmodules b/.gitmodules index bddcb0cc..634ab4ca 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "play-services-api"] - path = play-services-api +[submodule "GmsApi"] + path = extern/GmsApi url = https://github.com/microg/android_external_GmsApi.git diff --git a/SafeParcel b/SafeParcel deleted file mode 120000 index 18f7aa75..00000000 --- a/SafeParcel +++ /dev/null @@ -1 +0,0 @@ -play-services-api/SafeParcel/ \ No newline at end of file diff --git a/build.gradle b/build.gradle index 135b35aa..4cb7b45f 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. subprojects { - group = 'org.microg.gms' + group = 'org.microg' version = '1.0-SNAPSHOT' -} \ No newline at end of file +} diff --git a/extern/GmsApi b/extern/GmsApi new file mode 160000 index 00000000..c0c4a78e --- /dev/null +++ b/extern/GmsApi @@ -0,0 +1 @@ +Subproject commit c0c4a78eab3064cdd7545cb7fcdddfe93ca1674d diff --git a/play-services-api b/play-services-api deleted file mode 160000 index dbae4add..00000000 --- a/play-services-api +++ /dev/null @@ -1 +0,0 @@ -Subproject commit dbae4add94505fb0765ca9a18896848df6ae6ae4 diff --git a/play-services-api b/play-services-api new file mode 120000 index 00000000..ce1f09ca --- /dev/null +++ b/play-services-api @@ -0,0 +1 @@ +extern/GmsApi/play-services-api \ No newline at end of file diff --git a/safe-parcel b/safe-parcel new file mode 120000 index 00000000..5c222c0b --- /dev/null +++ b/safe-parcel @@ -0,0 +1 @@ +extern/GmsApi/safe-parcel \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index c87b1990..9ec62cf1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,5 @@ -include ':SafeParcel' +include ':safe-parcel' + include ':play-services-api' include ':play-services-base' From a5813321171b4e606443708ae27cde72a7c88722 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Sat, 28 Mar 2015 23:21:09 +0100 Subject: [PATCH 25/65] Update api, add gcm basics --- extern/GmsApi | 2 +- .../android/gms/gcm/GoogleCloudMessaging.java | 115 ++++++++++++++++++ 2 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 play-services-base/src/main/java/com/google/android/gms/gcm/GoogleCloudMessaging.java diff --git a/extern/GmsApi b/extern/GmsApi index c0c4a78e..c43facae 160000 --- a/extern/GmsApi +++ b/extern/GmsApi @@ -1 +1 @@ -Subproject commit c0c4a78eab3064cdd7545cb7fcdddfe93ca1674d +Subproject commit c43facae3dba6fcf4139c6a36e3a6f364d6db057 diff --git a/play-services-base/src/main/java/com/google/android/gms/gcm/GoogleCloudMessaging.java b/play-services-base/src/main/java/com/google/android/gms/gcm/GoogleCloudMessaging.java new file mode 100644 index 00000000..91f00fd5 --- /dev/null +++ b/play-services-base/src/main/java/com/google/android/gms/gcm/GoogleCloudMessaging.java @@ -0,0 +1,115 @@ +/* + * Copyright 2013-2015 µg 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.gcm; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; + +import java.io.IOException; + +public class GoogleCloudMessaging { + + /** + * The GCM {@link #register(String...)} and {@link #unregister()} methods are blocking. You + * should not run them in the main thread or in broadcast receivers. + */ + public static final String ERROR_MAIN_THREAD = "MAIN_THREAD"; + + /** + * The device can't read the response, or there was a 500/503 from the server that can be + * retried later. The application should use exponential back off and retry. + */ + public static final String ERROR_SERVICE_NOT_AVAILABLE = "SERVICE_NOT_AVAILABLE"; + + /** + * Returned by {@link #getMessageType(Intent)} to indicate that the server deleted some + * pending messages because they were collapsible. + */ + public static final String MESSAGE_TYPE_DELETED = "deleted_messages"; + + /** + * Returned by {@link #getMessageType(Intent)} to indicate a regular message. + */ + public static final String MESSAGE_TYPE_MESSAGE = "gcm"; + + /** + * Returned by {@link #getMessageType(Intent)} to indicate a send error. The intent includes + * the message ID of the message and an error code. + */ + public static final String MESSAGE_TYPE_SEND_ERROR = "send_error"; + + /** + * Return the singleton instance of GCM. + */ + public static synchronized GoogleCloudMessaging getInstance(Context context) { + return null; + } + + /** + * Must be called when your application is done using GCM, to release internal resources. + */ + public void close() { + + } + + /** + * Return the message type from an intent passed into a client app's broadcast receiver. + * There are two general categories of messages passed from the server: regular GCM messages, + * and special GCM status messages. The possible types are: + *

+ * You can use this method to filter based on message type. Since it is likely that GCM will + * be extended in the future with new message types, just ignore any message types you're not + * interested in, or that you don't recognize. + * + * @param intent + * @return + */ + public String getMessageType(Intent intent) { + return null; + } + + public String register(String... senderIds) { + return null; + } + + public void send(String to, String msgId, long timeToLive, Bundle data) { + + } + + public void send(String to, String msgId, Bundle data) { + + } + + /** + * Unregister the application. Calling unregister() stops any messages from the server. + * This is a blocking call—you shouldn't call it from the UI thread. + * You should rarely (if ever) need to call this method. Not only is it expensive in terms of + * resources, but it invalidates your registration ID, which you should never change + * unnecessarily. A better approach is to simply have your server stop sending messages. + * Only use unregister if you want to change your sender ID. + * + * @throws IOException if we can't connect to server to unregister. + */ + public void unregister() throws IOException { + + } +} From 1374dfa01201561418230b4c5afeacb5a249b4d8 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Tue, 31 Mar 2015 23:56:13 +0200 Subject: [PATCH 26/65] Add numerous wearable APIs --- extern/GmsApi | 2 +- .../android/gms/common/data/DataBuffer.java | 89 +++++ .../microg/gms/common/api/ApiConnection.java | 6 +- .../FusedLocationProviderApiImpl.java | 2 +- .../google/android/gms/wearable/DataApi.java | 145 +++++++ .../android/gms/wearable/DataEvent.java | 50 +++ .../android/gms/wearable/DataEventBuffer.java | 48 +++ .../android/gms/wearable/DataItemBuffer.java | 45 +++ .../google/android/gms/wearable/DataMap.java | 369 ++++++++++++++++++ .../android/gms/wearable/DataMapItem.java | 45 +++ .../android/gms/wearable/MessageApi.java | 68 ++++ .../android/gms/wearable/MessageEvent.java | 42 ++ .../google/android/gms/wearable/NodeApi.java | 78 ++++ .../gms/wearable/PutDataMapRequest.java | 83 ++++ .../gms/wearable/WearableListenerService.java | 58 +++ .../org/microg/gms/wearable/DataApiImpl.java | 53 +++ .../microg/gms/wearable/MessageApiImpl.java | 17 + .../org/microg/gms/wearable/NodeApiImpl.java | 22 ++ 18 files changed, 1217 insertions(+), 5 deletions(-) create mode 100644 play-services-base/src/main/java/com/google/android/gms/common/data/DataBuffer.java create mode 100644 play-services-wearable/src/main/java/com/google/android/gms/wearable/DataEvent.java create mode 100644 play-services-wearable/src/main/java/com/google/android/gms/wearable/DataEventBuffer.java create mode 100644 play-services-wearable/src/main/java/com/google/android/gms/wearable/DataItemBuffer.java create mode 100644 play-services-wearable/src/main/java/com/google/android/gms/wearable/DataMap.java create mode 100644 play-services-wearable/src/main/java/com/google/android/gms/wearable/DataMapItem.java create mode 100644 play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageEvent.java create mode 100644 play-services-wearable/src/main/java/com/google/android/gms/wearable/PutDataMapRequest.java create mode 100644 play-services-wearable/src/main/java/com/google/android/gms/wearable/WearableListenerService.java diff --git a/extern/GmsApi b/extern/GmsApi index c43facae..70419935 160000 --- a/extern/GmsApi +++ b/extern/GmsApi @@ -1 +1 @@ -Subproject commit c43facae3dba6fcf4139c6a36e3a6f364d6db057 +Subproject commit 704199355e1c6f8b14402e01063fbae7b187b35b diff --git a/play-services-base/src/main/java/com/google/android/gms/common/data/DataBuffer.java b/play-services-base/src/main/java/com/google/android/gms/common/data/DataBuffer.java new file mode 100644 index 00000000..0d406cbd --- /dev/null +++ b/play-services-base/src/main/java/com/google/android/gms/common/data/DataBuffer.java @@ -0,0 +1,89 @@ +/* + * Copyright 2013-2015 µg 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.data; + +import com.google.android.gms.common.api.Releasable; + +import org.microg.gms.common.PublicApi; + +import java.util.Iterator; + +/** + * TODO + */ +@PublicApi +public abstract class DataBuffer implements Releasable, Iterable { + + private DataHolder dataHolder; + + @PublicApi(exclude = true) + public DataBuffer(DataHolder dataHolder) { + this.dataHolder = dataHolder; + } + + /** + * @deprecated use {@link #release()} instead + */ + @Deprecated + public final void close() { + release(); + } + + /** + * Get the item at the specified position. Note that the objects returned from subsequent + * invocations of this method for the same position may not be identical objects, but will be + * equal in value. + * + * @param position The position of the item to retrieve. + * @return the item at {@code position} in this buffer. + */ + public abstract T get(int position); + + public int getCount() { + return dataHolder == null ? 0 : dataHolder.getCount(); + } + + /** + * @deprecated {@link #release()} is idempotent, and so is safe to call multiple times + */ + @Deprecated + public boolean isClosed() { + return false; + } + + @Override + public Iterator iterator() { + return null; + } + + /** + * Releases resources used by the buffer. This method is idempotent. + */ + @Override + public void release() { + + } + + /** + * In order to use this one should correctly override setDataRow(int) in his DataBufferRef + * implementation. Be careful: there will be single DataBufferRef while iterating. + * If you are not sure - DO NOT USE this iterator. + */ + public Iterator singleRefIterator() { + return null; + } +} diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/ApiConnection.java b/play-services-base/src/main/java/org/microg/gms/common/api/ApiConnection.java index d5697e4a..77c9af9d 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/api/ApiConnection.java +++ b/play-services-base/src/main/java/org/microg/gms/common/api/ApiConnection.java @@ -17,11 +17,11 @@ package org.microg.gms.common.api; public interface ApiConnection { - public void connect(); + void connect(); - public void disconnect(); + void disconnect(); - public boolean isConnected(); + boolean isConnected(); boolean isConnecting(); } diff --git a/play-services-location/src/main/java/org/microg/gms/location/FusedLocationProviderApiImpl.java b/play-services-location/src/main/java/org/microg/gms/location/FusedLocationProviderApiImpl.java index f89a79af..08e90c5b 100644 --- a/play-services-location/src/main/java/org/microg/gms/location/FusedLocationProviderApiImpl.java +++ b/play-services-location/src/main/java/org/microg/gms/location/FusedLocationProviderApiImpl.java @@ -136,7 +136,7 @@ public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { } private interface Runnable { - public void run(LocationClientImpl client) throws RemoteException; + void run(LocationClientImpl client) throws RemoteException; } } diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataApi.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataApi.java index c26bdf22..25ef04bc 100644 --- a/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataApi.java +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataApi.java @@ -16,5 +16,150 @@ package com.google.android.gms.wearable; +import android.net.Uri; +import android.os.ParcelFileDescriptor; + +import com.google.android.gms.common.api.GoogleApiClient; +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 com.google.android.gms.common.data.Freezable; + +import org.microg.gms.common.PublicApi; + +import java.io.InputStream; + +/** + * Exposes an API for components to read or write data items and assets. + *

+ * A {@link DataItem} is synchronized across all devices in an Android Wear network. It is possible + * to set data items while not connected to any nodes. Those data items will be synchronized when + * the nodes eventually come online. + *

+ * Data items are private to the application that created them, and are only accessible by that + * application on other nodes. They should generally be small in size, relying on assets for the + * transfer of larger, more persistent data objects such as images. + *

+ * Each data item is identified by a URI, accessible with {@link DataItem#getUri()}, that indicates + * the item's creator and path. Fully specified URIs follow the following format: + * {@code wear:///}, where is the node ID of the wearable node that + * created the data item, and is an application-defined path. This means that given a data + * item's URI, calling {@link Uri#getHost()} will return the creator's node ID. + *

+ * In some of the methods below (such as {@link #getDataItems(GoogleApiClient, Uri)}), it is + * possible to omit the node ID from the URI, and only leave a path. In that case, the URI may + * refer to multiple data items, since multiple nodes may create data items with the same path. + * Partially specified data item URIs follow the following format: + * {@ocde wear:/} + * Note the single / after wear:. + */ +@PublicApi public interface DataApi { + /** + * Registers a listener to receive data item changed and deleted events. This call should be + * balanced with a call to {@link #removeListener(GoogleApiClient, DataListener)}, to avoid + * leaking resources. + *

+ * The listener will be notified of changes initiated by this node. + */ + PendingResult addListener(GoogleApiClient client, DataListener listener); + + /** + * Removes all specified data items from the Android Wear network. + *

+ * If uri is fully specified, this method will delete at most one data item. If {@code uri} + * contains no host, multiple data items may be deleted, since different nodes may create data + * items with the same path. See {@link DataApi} for details of the URI format. + */ + PendingResult deleteDataItems(GoogleApiClient client, Uri uri); + + /** + * Retrieves a single {@link DataItem} from the Android Wear network. A fully qualified URI + * must be specified. The URI's host must be the ID of the node that created the item. + *

+ * See {@link DataApi} for details of the URI format. + */ + PendingResult getDataItem(GoogleApiClient client, Uri uri); + + /** + * Retrieves all data items from the Android Wear network. + *

+ * Callers must call {@link DataItemBuffer#release()} on the returned buffer when finished + * processing results. + */ + PendingResult getDataItems(GoogleApiClient client); + + /** + * Retrieves all data items matching the provided URI, from the Android Wear network. + *

+ * The URI must contain a path. If {@code uri} is fully specified, at most one data item will + * be returned. If uri contains no host, multiple data items may be returned, since different + * nodes may create data items with the same path. See {@link DataApi} for details of the URI + * format. + *

+ * Callers must call {@link DataItemBuffer#release()} on the returned buffer when finished + * processing results. + */ + PendingResult getDataItems(GoogleApiClient client, Uri uri); + + /** + * Retrieves a {@link ParcelFileDescriptor} pointing at the bytes of an asset. Only assets + * previously stored in a {@link DataItem} may be retrieved. + */ + PendingResult getFdForAsset(GoogleApiClient client, DataItemAsset asset); + + /** + * Retrieves a {@link ParcelFileDescriptor} pointing at the bytes of an asset. Only assets + * previously stored in a {@link DataItem} may be retrieved. + */ + PendingResult getFdForAsset(GoogleApiClient client, Asset asset); + + /** + * Adds a {@link DataItem} to the Android Wear network. The updated item is synchronized across + * all devices. + */ + PendingResult putDataItem(GoogleApiClient client, PutDataRequest request); + + /** + * Removes a data listener which was previously added through + * {@link #addListener(GoogleApiClient, DataListener)}. + */ + PendingResult removeListener(GoogleApiClient client, DataListener listener); + + interface DataItemResult extends Result { + /** + * @return data item, or {@code null} if the item does not exit. + */ + DataItem getDataItem(); + } + + interface DataListener { + /** + * Notification that a set of data items have been changed or deleted. The data buffer is + * released upon completion of this method. If a caller wishes to use the events outside + * this callback, they should be sure to {@link Freezable#freeze()} the DataEvent objects + * they wish to use. + */ + void onDataChanged(DataEventBuffer dataEvents); + } + + interface DeleteDataItemsResult extends Result { + /** + * @return the number of items deleted by + * {@link DataApi#deleteDataItems(GoogleApiClient, Uri)}. + */ + int getNumDeleted(); + } + + interface GetFdForAssetResult extends Result { + /** + * @return a file descriptor for the requested asset. + */ + ParcelFileDescriptor getFd(); + + /** + * @return an input stream wrapping the file descriptor. When this input stream is closed, the file descriptor is, as well. + */ + InputStream getInputStream(); + } } diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataEvent.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataEvent.java new file mode 100644 index 00000000..0d72101e --- /dev/null +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataEvent.java @@ -0,0 +1,50 @@ +/* + * Copyright 2013-2015 µg 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.data.Freezable; + +import org.microg.gms.common.PublicApi; + +/** + * Data interface for data events. + */ +@PublicApi +public interface DataEvent extends Freezable { + + /** + * Indicates that the enclosing {@link DataEvent} was triggered by a data item being added or + * changed. + */ + int TYPE_CHANGED = 1; + + /** + * Indicates that the enclosing {@link DataEvent} was triggered by a data item being deleted. + */ + int TYPE_DELETED = 2; + + /** + * @return the data item modified in this event. An event of {@link #TYPE_DELETED} will only + * have its {@link DataItem#getUri} populated. + */ + DataItem getDataItem(); + + /** + * @return the type of event this is. One of {@link #TYPE_CHANGED}, {@link #TYPE_DELETED}. + */ + int getType(); +} diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataEventBuffer.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataEventBuffer.java new file mode 100644 index 00000000..1d4f6a45 --- /dev/null +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataEventBuffer.java @@ -0,0 +1,48 @@ +/* + * Copyright 2013-2015 µg 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.Result; +import com.google.android.gms.common.api.Status; +import com.google.android.gms.common.data.DataBuffer; +import com.google.android.gms.common.data.DataHolder; + +import org.microg.gms.common.PublicApi; + +/** + * Data structure holding references to a set of events. + */ +@PublicApi +public class DataEventBuffer extends DataBuffer implements Result { + private Status status; + + @PublicApi(exclude = true) + public DataEventBuffer(DataHolder dataHolder) { + super(dataHolder); + status = new Status(dataHolder.statusCode); + } + + @Override + public DataEvent get(int position) { + return null; + } + + @Override + public Status getStatus() { + return null; + } +} diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataItemBuffer.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataItemBuffer.java new file mode 100644 index 00000000..991b79f4 --- /dev/null +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataItemBuffer.java @@ -0,0 +1,45 @@ +/* + * Copyright 2013-2015 µg 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.Result; +import com.google.android.gms.common.api.Status; +import com.google.android.gms.common.data.DataBuffer; +import com.google.android.gms.common.data.DataHolder; + +import org.microg.gms.common.PublicApi; + +@PublicApi +public class DataItemBuffer extends DataBuffer implements Result { + private Status status; + + @PublicApi(exclude = true) + public DataItemBuffer(DataHolder dataHolder) { + super(dataHolder); + status = new Status(dataHolder.statusCode); + } + + @Override + public DataItem get(int position) { + return null; + } + + @Override + public Status getStatus() { + return null; + } +} diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataMap.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataMap.java new file mode 100644 index 00000000..aad96e14 --- /dev/null +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataMap.java @@ -0,0 +1,369 @@ +/* + * Copyright 2013-2015 µg 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.os.Bundle; + +import org.microg.gms.common.PublicApi; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * A map of data supported by {@link PutDataMapRequest} and {@link DataMapItem}s. DataMap may + * convert to and from Bundles, but will drop any types not explicitly supported by DataMap in the + * conversion process. + */ +@PublicApi +public class DataMap { + public static String TAG = "GmsDataMap"; + + private Map data = new HashMap<>(); + private Map types = new HashMap<>(); + + public DataMap() { + + } + + /** + * @return an ArrayList of DataMaps from an ArrayList of Bundles. Any elements in the Bundles not supported by DataMap will be dropped. + */ + public static ArrayList arrayListFromBundleArrayList(ArrayList bundleArrayList) { + ArrayList res = new ArrayList<>(); + for (Bundle bundle : bundleArrayList) { + res.add(fromBundle(bundle)); + } + return res; + } + + /** + * Removes all elements from the mapping of this DataMap. + */ + public void clear() { + data.clear(); + } + + /** + * @return true if the given key is contained in the mapping of this DataMap. + */ + public boolean containsKey(String key) { + return data.containsKey(key); + } + + /** + * @return true if the given Object is a DataMap equivalent to this one. + */ + @Override + public boolean equals(Object o) { + return o instanceof DataMap && data.equals(((DataMap) o).data); + } + + /** + * @return a DataMap from a Bundle. The input Bundle is expected to contain only elements + * supported by DataMap. Any elements in the Bundle not supported by DataMap will be dropped. + */ + public static DataMap fromBundle(Bundle bundle) { + DataMap res = new DataMap(); + if (bundle != null) { + for (String key : bundle.keySet()) { + res.data.put(key, bundle.get(key)); + } + } + return res; + } + + /** + * @return a DataMap from a byte[]. + */ + public static DataMap fromByteArray(byte[] bytes) { + return null; // TODO + } + + /** + * @return the entry with the given key as an object, or null + */ + public T get(String key) { + return (T) data.get(key); + } + + public Asset getAsset(String key) { + return types.get(key) == StoredType.Asset ? (Asset) data.get(key) : null; + } + + public boolean getBoolean(String key) { + return getBoolean(key, false); + } + + public boolean getBoolean(String key, boolean defaultValue) { + return types.get(key) == StoredType.Boolean ? (Boolean) data.get(key) : defaultValue; + } + + public byte getByte(String key) { + return getByte(key, (byte) 0); + } + + public byte getByte(String key, byte defaultValue) { + return types.get(key) == StoredType.Byte ? (Byte) data.get(key) : defaultValue; + } + + public byte[] getByteArray(String key) { + return types.get(key) == StoredType.ByteArray ? (byte[]) data.get(key) : null; + } + + public DataMap getDataMap(String key) { + return types.get(key) == StoredType.DataMap ? (DataMap) data.get(key) : null; + } + + public ArrayList getDataMapArrayList(String key) { + return types.get(key) == StoredType.DataMapArrayList ? (ArrayList) data.get(key) : null; + } + + public double getDouble(String key) { + return getDouble(key, 0.0); + } + + public double getDouble(String key, double defaultValue) { + return types.get(key) == StoredType.Double ? (Double) data.get(key) : defaultValue; + } + + public float getFloat(String key) { + return getFloat(key, 0.0f); + } + + public float getFloat(String key, float defaultValue) { + return types.get(key) == StoredType.Float ? (Float) data.get(key) : defaultValue; + } + + public float[] getFloatArray(String key) { + return types.get(key) == StoredType.FloatArray ? (float[]) data.get(key) : null; + } + + public int getInt(String key) { + return getInt(key, 0); + } + + public int getInt(String key, int defaultValue) { + return types.get(key) == StoredType.Integer ? (Integer) data.get(key) : defaultValue; + } + + public ArrayList getIntegerArrayList(String key) { + return types.get(key) == StoredType.IntegerArrayList ? (ArrayList) data.get(key) : null; + } + + public long getLong(String key) { + return getLong(key, 0L); + } + + public long getLong(String key, long defaultValue) { + return types.get(key) == StoredType.Long ? (Long) data.get(key) : defaultValue; + } + + public long[] getLongArray(String key) { + return types.get(key) == StoredType.LongArray ? (long[]) data.get(key) : null; + } + + public String getString(String key) { + return getString(key, null); + } + + public String getString(String key, String defaultValue) { + return types.get(key) == StoredType.String ? (String) data.get(key) : defaultValue; + } + + public String[] getStringArray(String key) { + return types.get(key) == StoredType.StringArray ? (String[]) data.get(key) : null; + } + + public ArrayList getStringArrayList(String key) { + return types.get(key) == StoredType.StringArrayList ? (ArrayList) data.get(key) : null; + } + + public int hashCode() { + return data.hashCode(); + } + + public boolean isEmpty() { + return data.isEmpty(); + } + + public Set keySet() { + return data.keySet(); + } + + public void putAll(DataMap dataMap) { + for (String key : dataMap.keySet()) { + data.put(key, dataMap.data.get(key)); + types.put(key, dataMap.types.get(key)); + } + } + + public void putBoolean(String key, boolean value) { + data.put(key, value); + types.put(key, StoredType.Boolean); + } + + public void putByte(String key, byte value) { + data.put(key, value); + types.put(key, StoredType.Byte); + } + + public void putByteArray(String key, byte[] value) { + data.put(key, value); + types.put(key, StoredType.ByteArray); + } + + public void putDataMap(String key, DataMap value) { + data.put(key, value); + types.put(key, StoredType.DataMap); + } + + public void putDataMapArrayList(String key, ArrayList value) { + data.put(key, value); + types.put(key, StoredType.DataMapArrayList); + } + + public void putDouble(String key, double value) { + data.put(key, value); + types.put(key, StoredType.Double); + } + + public void putFloat(String key, float value) { + data.put(key, value); + types.put(key, StoredType.Float); + } + + public void putFloatArray(String key, float[] value) { + data.put(key, value); + types.put(key, StoredType.FloatArray); + } + + public void putInt(String key, int value) { + data.put(key, value); + types.put(key, StoredType.Integer); + } + + public void putIntegerArrayList(String key, ArrayList value) { + data.put(key, value); + types.put(key, StoredType.IntegerArrayList); + } + + public void putLong(String key, long value) { + data.put(key, value); + types.put(key, StoredType.Long); + } + + public void putLongArray(String key, long[] value) { + data.put(key, value); + types.put(key, StoredType.LongArray); + } + + public void putString(String key, String value) { + data.put(key, value); + types.put(key, StoredType.String); + } + + public void putStringArray(String key, String[] value) { + data.put(key, value); + types.put(key, StoredType.StringArray); + } + + public void putStringArrayList(String key, ArrayList value) { + data.put(key, value); + types.put(key, StoredType.StringArrayList); + } + + public Object remove(String key) { + types.remove(key); + return data.remove(key); + } + + public int size() { + return data.size(); + } + + public Bundle toBundle() { + Bundle bundle = new Bundle(); + for (String key : data.keySet()) { + switch (types.get(key)) { + case Asset: + bundle.putParcelable(key, (Asset)data.get(key)); + break; + case Boolean: + bundle.putBoolean(key, (Boolean) data.get(key)); + break; + case Byte: + bundle.putByte(key, (Byte) data.get(key)); + break; + case ByteArray: + bundle.putByteArray(key, (byte[]) data.get(key)); + break; + case DataMap: + bundle.putBundle(key, ((DataMap) data.get(key)).toBundle()); + break; + case DataMapArrayList: + // TODO + break; + case Double: + bundle.putDouble(key, (Double) data.get(key)); + break; + case Float: + bundle.putFloat(key, (Float) data.get(key)); + break; + case FloatArray: + bundle.putFloatArray(key, (float[]) data.get(key)); + break; + case Integer: + bundle.putInt(key, (Integer) data.get(key)); + break; + case IntegerArrayList: + bundle.putIntegerArrayList(key, (ArrayList) data.get(key)); + break; + case Long: + bundle.putLong(key, (Long) data.get(key)); + break; + case LongArray: + bundle.putLongArray(key, (long[]) data.get(key)); + break; + case String: + bundle.putString(key, (String) data.get(key)); + break; + case StringArray: + bundle.putStringArray(key, (String[]) data.get(key)); + break; + case StringArrayList: + bundle.putStringArrayList(key, (ArrayList) data.get(key)); + break; + } + } + return bundle; + } + + public byte[] toByteArray() { + return null; // TODO + } + + public String toString() { + return "DataMap{size=" + size() + "}"; + } + + private enum StoredType { + Asset, Boolean, Byte, ByteArray, DataMap, DataMapArrayList, Double, Float, FloatArray, + Integer, IntegerArrayList, Long, LongArray, String, StringArray, StringArrayList + } +} diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataMapItem.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataMapItem.java new file mode 100644 index 00000000..7eb6fb0f --- /dev/null +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataMapItem.java @@ -0,0 +1,45 @@ +/* + * Copyright 2013-2015 µg 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 org.microg.gms.common.PublicApi; + +/** + * Creates a new dataItem-like object containing structured and serializable data. + */ +@PublicApi +public class DataMapItem { + /** + * Provides a {@link DataMapItem} wrapping a dataItem. + * + * @param dataItem the base for the wrapped {@link DataMapItem}. {@code dataItem} should not + * be modified after wrapping it. + */ + public static DataMapItem fromDataItem(DataItem dataItem) { + return null; + } + + public DataMap getDataMap() { + return null; + } + + public Uri getUri() { + return null; + } +} diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageApi.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageApi.java index 730ebe4a..999d615f 100644 --- a/play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageApi.java +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageApi.java @@ -16,5 +16,73 @@ package com.google.android.gms.wearable; +import com.google.android.gms.common.api.GoogleApiClient; +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 org.microg.gms.common.PublicApi; + +@PublicApi public interface MessageApi { + + /** + * A value returned by {@link SendMessageResult#getRequestId()} when + * {@link #sendMessage(GoogleApiClient, String, String, byte[])} fails. + */ + int UNKNOWN_REQUEST_ID = -1; + + /** + * Registers a listener to be notified of received messages. Calls to this method should + * balanced with {@link #removeListener(GoogleApiClient, MessageListener)} to avoid leaking + * resources. + *

+ * Callers wishing to be notified of events in the background should use {@link WearableListenerService}. + */ + PendingResult addListener(GoogleApiClient client, MessageListener listener); + + /** + * Removes a message listener which was previously added through + * {@link #addListener(GoogleApiClient, MessageListener)}. + */ + PendingResult removeListener(GoogleApiClient client, MessageListener listener); + + /** + * Sends {@code byte[]} data to the specified node. + * + * @param nodeId identifier for a particular node on the Android Wear network. Valid targets + * may be obtained through {@link NodeApi#getConnectedNodes(GoogleApiClient)} + * or from the host in {@link DataItem#getUri()}. + * @param path identifier used to specify a particular endpoint at the receiving node + * @param data small array of information to pass to the target node. Generally not larger + * than 100k + */ + PendingResult sendMessage(GoogleApiClient client, String nodeId, + String path, byte[] data); + + /** + * Used with {@link MessageApi#addListener(GoogleApiClient, MessageListener)} to receive + * message events. + *

+ * Callers wishing to be notified of events in the background should use + * {@link WearableListenerService}. + */ + interface MessageListener { + /** + * Notification that a message has been received. + */ + void onMessageReceived(MessageEvent messageEvent); + } + + /** + * Contains the request id assigned to the message. On failure, the id will be + * {@link MessageApi#UNKNOWN_REQUEST_ID} and the status will be unsuccessful. + */ + interface SendMessageResult extends Result { + /** + * @return an ID used to identify the sent message. If {@link #getStatus()} is not + * successful, this value will be {@link MessageApi#UNKNOWN_REQUEST_ID}. + */ + int getRequestId(); + } } diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageEvent.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageEvent.java new file mode 100644 index 00000000..c5dd0d33 --- /dev/null +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageEvent.java @@ -0,0 +1,42 @@ +/* + * Copyright 2013-2015 µg 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 org.microg.gms.common.PublicApi; + +@PublicApi +public interface MessageEvent { + /** + * @return the data passed by the message. + */ + byte[] getData(); + + /** + * @return the path the message is being delivered to + */ + String getPath(); + + /** + * @return the request id of the message, generated by the sender + */ + int getRequestId(); + + /** + * @return the node ID of the sender. + */ + String getSourceNodeId(); +} diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/NodeApi.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/NodeApi.java index 4aa57284..86088446 100644 --- a/play-services-wearable/src/main/java/com/google/android/gms/wearable/NodeApi.java +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/NodeApi.java @@ -16,5 +16,83 @@ package com.google.android.gms.wearable; +import com.google.android.gms.common.api.GoogleApiClient; +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 org.microg.gms.common.PublicApi; + +import java.util.List; + +/** + * Exposes an API for to learn about local or connected Nodes. + *

+ * Node events are delivered to all applications on a device. + */ +@PublicApi public interface NodeApi { + + /** + * Registers a listener to receive all node events. Calls to this method should balanced with + * {@link #removeListener(GoogleApiClient, NodeListener)}, to avoid leaking resources. + *

+ * Callers wishing to be notified of node events in the background should use WearableListenerService. + */ + PendingResult addListener(GoogleApiClient client, NodeListener listener); + + /** + * Gets a list of nodes to which this device is currently connected. + *

+ * The returned list will not include the {@link #getLocalNode(GoogleApiClient) local node}. + */ + PendingResult getConnectedNodes(GoogleApiClient client); + + /** + * Gets the {@link Node} that refers to this device. The information in the returned Node + * can be passed to other devices using the {@link MessageApi}, for example. + */ + PendingResult getLocalNode(GoogleApiClient client); + + /** + * Removes a listener which was previously added through + * {@link #addListener(GoogleApiClient, NodeListener)}. + */ + PendingResult removeListener(GoogleApiClient client, NodeListener listener); + + + /** + * Contains a list of connected nodes. + */ + interface GetConnectedNodesResult extends Result { + /** + * @return a list of connected nodes. This list doesn't include the local node. + */ + List getNodes(); + } + + /** + * Contains the name and id that represents this device. + */ + interface GetLocalNodeResult extends Result { + /** + * @return a {@link Node} object which represents this device. + */ + Node getNode(); + } + + /** + * Used with {@link NodeApi#addListener(GoogleApiClient, NodeListener)} to receive node events. + */ + interface NodeListener { + /** + * Notification that a peer has been connected. + */ + void onPeerConnected(Node peer); + + /** + * Notification that a peer has been disconnected. + */ + void onPeerDisconnected(Node peer); + } } diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/PutDataMapRequest.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/PutDataMapRequest.java new file mode 100644 index 00000000..f70e3c6c --- /dev/null +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/PutDataMapRequest.java @@ -0,0 +1,83 @@ +/* + * Copyright 2013-2015 µg 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 org.microg.gms.common.PublicApi; + +/** + * PutDataMapRequest is a DataMap-aware version of {@link PutDataRequest}. + */ +@PublicApi +public class PutDataMapRequest { + + private DataMapItem dataMapItem; + + private PutDataMapRequest(DataMapItem dataMapItem) { + this.dataMapItem = dataMapItem; + } + + /** + * Creates a {@link PutDataRequest} containing the data and assets in this + * {@link PutDataMapRequest}. + */ + public PutDataRequest asPutDataRequest() { + // TODO + return new PutDataRequest(); + } + + /** + * Creates a {@link PutDataMapRequest} with the provided, complete, path. + */ + public static PutDataMapRequest create(String path) { + // TODO + return new PutDataMapRequest(null); + } + + /** + * Creates a {@link PutDataMapRequest} from a {@link DataMapItem} using the provided source. + */ + public static PutDataMapRequest createFromDataMapItem(DataMapItem source) { + return new PutDataMapRequest(source); + } + + /** + * Creates a {@link PutDataMapRequest} with a randomly generated id prefixed with the provided + * path. + */ + public static PutDataMapRequest createWithAutoAppendedId(String pathPrefix) { + // TODO + return new PutDataMapRequest(null); + } + + /** + * @return the structured data associated with this data item. + */ + public DataMap getDataMap() { + return dataMapItem.getDataMap(); + } + + /** + * @return a {@link Uri} for the pending data item. If this is a modification of an existing + * data item, {@link Uri#getHost()} will return the id of the node that originally created it. + * Otherwise, a new data item will be created with the requesting device's node. + */ + public Uri getUri() { + return dataMapItem.getUri(); + } +} diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/WearableListenerService.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/WearableListenerService.java new file mode 100644 index 00000000..61a552d0 --- /dev/null +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/WearableListenerService.java @@ -0,0 +1,58 @@ +/* + * Copyright 2013-2015 µg 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.app.Service; +import android.content.Intent; +import android.os.IBinder; + +public abstract class WearableListenerService extends Service implements DataApi.DataListener, MessageApi.MessageListener, NodeApi.NodeListener { + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onCreate() { + super.onCreate(); + } + + @Override + public void onDataChanged(DataEventBuffer dataEvents) { + + } + + @Override + public void onDestroy() { + super.onDestroy(); + } + + @Override + public void onMessageReceived(MessageEvent messageEvent) { + + } + + @Override + public void onPeerConnected(Node peer) { + + } + + @Override + public void onPeerDisconnected(Node peer) { + + } +} diff --git a/play-services-wearable/src/main/java/org/microg/gms/wearable/DataApiImpl.java b/play-services-wearable/src/main/java/org/microg/gms/wearable/DataApiImpl.java index 5d8f07ec..bdfa778e 100644 --- a/play-services-wearable/src/main/java/org/microg/gms/wearable/DataApiImpl.java +++ b/play-services-wearable/src/main/java/org/microg/gms/wearable/DataApiImpl.java @@ -16,7 +16,60 @@ package org.microg.gms.wearable; +import android.net.Uri; + +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 com.google.android.gms.wearable.Asset; import com.google.android.gms.wearable.DataApi; +import com.google.android.gms.wearable.DataItemAsset; +import com.google.android.gms.wearable.DataItemBuffer; +import com.google.android.gms.wearable.PutDataRequest; public class DataApiImpl implements DataApi { + @Override + public PendingResult addListener(GoogleApiClient client, DataListener listener) { + return null; + } + + @Override + public PendingResult deleteDataItems(GoogleApiClient client, Uri uri) { + return null; + } + + @Override + public PendingResult getDataItem(GoogleApiClient client, Uri uri) { + return null; + } + + @Override + public PendingResult getDataItems(GoogleApiClient client) { + return null; + } + + @Override + public PendingResult getDataItems(GoogleApiClient client, Uri uri) { + return null; + } + + @Override + public PendingResult getFdForAsset(GoogleApiClient client, DataItemAsset asset) { + return null; + } + + @Override + public PendingResult getFdForAsset(GoogleApiClient client, Asset asset) { + return null; + } + + @Override + public PendingResult putDataItem(GoogleApiClient client, PutDataRequest request) { + return null; + } + + @Override + public PendingResult removeListener(GoogleApiClient client, DataListener listener) { + return null; + } } diff --git a/play-services-wearable/src/main/java/org/microg/gms/wearable/MessageApiImpl.java b/play-services-wearable/src/main/java/org/microg/gms/wearable/MessageApiImpl.java index 6f055d5e..667eaa77 100644 --- a/play-services-wearable/src/main/java/org/microg/gms/wearable/MessageApiImpl.java +++ b/play-services-wearable/src/main/java/org/microg/gms/wearable/MessageApiImpl.java @@ -16,7 +16,24 @@ package org.microg.gms.wearable; +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 com.google.android.gms.wearable.MessageApi; public class MessageApiImpl implements MessageApi { + @Override + public PendingResult addListener(GoogleApiClient client, MessageListener listener) { + return null; + } + + @Override + public PendingResult removeListener(GoogleApiClient client, MessageListener listener) { + return null; + } + + @Override + public PendingResult sendMessage(GoogleApiClient client, String nodeId, String path, byte[] data) { + return null; + } } diff --git a/play-services-wearable/src/main/java/org/microg/gms/wearable/NodeApiImpl.java b/play-services-wearable/src/main/java/org/microg/gms/wearable/NodeApiImpl.java index bf8bf5ed..2aca31ee 100644 --- a/play-services-wearable/src/main/java/org/microg/gms/wearable/NodeApiImpl.java +++ b/play-services-wearable/src/main/java/org/microg/gms/wearable/NodeApiImpl.java @@ -16,7 +16,29 @@ package org.microg.gms.wearable; +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 com.google.android.gms.wearable.NodeApi; public class NodeApiImpl implements NodeApi { + @Override + public PendingResult addListener(GoogleApiClient client, NodeListener listener) { + return null; + } + + @Override + public PendingResult getConnectedNodes(GoogleApiClient client) { + return null; + } + + @Override + public PendingResult getLocalNode(GoogleApiClient client) { + return null; + } + + @Override + public PendingResult removeListener(GoogleApiClient client, NodeListener listener) { + return null; + } } From 0ea075dd1179382d7d4a127133162dfd376ce6ce Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Wed, 1 Apr 2015 00:13:40 +0200 Subject: [PATCH 27/65] Small fixes --- .../gms/common/MultiConnectionKeeper.java | 17 ++++++++++++----- .../android/gms/wearable/PutDataMapRequest.java | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/play-services-base/src/main/java/org/microg/gms/common/MultiConnectionKeeper.java b/play-services-base/src/main/java/org/microg/gms/common/MultiConnectionKeeper.java index 84ce091d..d88bc5ed 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/MultiConnectionKeeper.java +++ b/play-services-base/src/main/java/org/microg/gms/common/MultiConnectionKeeper.java @@ -16,6 +16,7 @@ package org.microg.gms.common; +import android.annotation.SuppressLint; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -28,10 +29,12 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import static android.os.Build.VERSION.SDK_INT; +import static android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH; import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME; public class MultiConnectionKeeper { - private static final String TAG = "GmsMultiConnectionKeeper"; + private static final String TAG = "GmsMultiConKeeper"; private static MultiConnectionKeeper INSTANCE; @@ -89,7 +92,7 @@ public class MultiConnectionKeeper { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { Log.d(TAG, "Connection(" + actionString + ") : ServiceConnection : " + - "onServiceConnected("+componentName+")"); + "onServiceConnected(" + componentName + ")"); binder = iBinder; component = componentName; for (ServiceConnection connection : connectionForwards) { @@ -101,7 +104,7 @@ public class MultiConnectionKeeper { @Override public void onServiceDisconnected(ComponentName componentName) { Log.d(TAG, "Connection(" + actionString + ") : ServiceConnection : " + - "onServiceDisconnected("+componentName+")"); + "onServiceDisconnected(" + componentName + ")"); binder = null; component = componentName; for (ServiceConnection connection : connectionForwards) { @@ -116,11 +119,15 @@ public class MultiConnectionKeeper { this.actionString = actionString; } + @SuppressLint("InlinedApi") public void bind() { Log.d(TAG, "Connection(" + actionString + ") : bind()"); Intent intent = new Intent(actionString).setPackage(GMS_PACKAGE_NAME); - bound = context.bindService(intent, serviceConnection, - Context.BIND_ADJUST_WITH_ACTIVITY | Context.BIND_AUTO_CREATE); + int flags = Context.BIND_AUTO_CREATE; + if (SDK_INT >= ICE_CREAM_SANDWICH) { + flags |= Context.BIND_ADJUST_WITH_ACTIVITY; + } + bound = context.bindService(intent, serviceConnection, flags); if (!bound) { context.unbindService(serviceConnection); } diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/PutDataMapRequest.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/PutDataMapRequest.java index f70e3c6c..339c23ef 100644 --- a/play-services-wearable/src/main/java/com/google/android/gms/wearable/PutDataMapRequest.java +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/PutDataMapRequest.java @@ -38,7 +38,7 @@ public class PutDataMapRequest { */ public PutDataRequest asPutDataRequest() { // TODO - return new PutDataRequest(); + return PutDataRequest.create(null); } /** From 0f8febb7056490d38f2e8e900cc863cbba038386 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Wed, 8 Apr 2015 23:10:50 +0200 Subject: [PATCH 28/65] Update sublib --- extern/GmsApi | 2 +- .../org/microg/gms/location/GoogleLocationManagerClient.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/extern/GmsApi b/extern/GmsApi index 70419935..a0e9645c 160000 --- a/extern/GmsApi +++ b/extern/GmsApi @@ -1 +1 @@ -Subproject commit 704199355e1c6f8b14402e01063fbae7b187b35b +Subproject commit a0e9645c796dfd8af38f8cdedf21f5a299c9c5b0 diff --git a/play-services-location/src/main/java/org/microg/gms/location/GoogleLocationManagerClient.java b/play-services-location/src/main/java/org/microg/gms/location/GoogleLocationManagerClient.java index 8da05e69..68f8d4c9 100644 --- a/play-services-location/src/main/java/org/microg/gms/location/GoogleLocationManagerClient.java +++ b/play-services-location/src/main/java/org/microg/gms/location/GoogleLocationManagerClient.java @@ -27,6 +27,7 @@ import com.google.android.gms.location.internal.IGoogleLocationManagerService; import org.microg.gms.common.Constants; import org.microg.gms.common.GmsClient; +import org.microg.gms.common.Services; public abstract class GoogleLocationManagerClient extends GmsClient { public GoogleLocationManagerClient(Context context, GoogleApiClient.ConnectionCallbacks @@ -36,7 +37,7 @@ public abstract class GoogleLocationManagerClient extends GmsClient Date: Wed, 15 Apr 2015 01:19:40 +0200 Subject: [PATCH 29/65] Update tools to 22.0.1 --- extern/GmsApi | 2 +- play-services-base/build.gradle | 5 ++++- play-services-location/build.gradle | 7 +++++-- play-services-wearable/build.gradle | 5 ++++- play-services/build.gradle | 5 ++++- 5 files changed, 18 insertions(+), 6 deletions(-) diff --git a/extern/GmsApi b/extern/GmsApi index a0e9645c..6743f1d4 160000 --- a/extern/GmsApi +++ b/extern/GmsApi @@ -1 +1 @@ -Subproject commit a0e9645c796dfd8af38f8cdedf21f5a299c9c5b0 +Subproject commit 6743f1d4b1a3cf0202b9d62f2ebc5dfb385b5576 diff --git a/play-services-base/build.gradle b/play-services-base/build.gradle index c6903999..f0caf02f 100644 --- a/play-services-base/build.gradle +++ b/play-services-base/build.gradle @@ -18,5 +18,8 @@ dependencies { android { compileSdkVersion 22 - buildToolsVersion "22.0.0" + buildToolsVersion "22.0.1" + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_6 + } } diff --git a/play-services-location/build.gradle b/play-services-location/build.gradle index 918f297c..041610d5 100644 --- a/play-services-location/build.gradle +++ b/play-services-location/build.gradle @@ -17,5 +17,8 @@ dependencies { android { compileSdkVersion 22 - buildToolsVersion "22.0.0" -} \ No newline at end of file + buildToolsVersion "22.0.1" + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_6 + } +} diff --git a/play-services-wearable/build.gradle b/play-services-wearable/build.gradle index 923eb4b0..041610d5 100644 --- a/play-services-wearable/build.gradle +++ b/play-services-wearable/build.gradle @@ -17,5 +17,8 @@ dependencies { android { compileSdkVersion 22 - buildToolsVersion "22.0.0" + buildToolsVersion "22.0.1" + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_6 + } } diff --git a/play-services/build.gradle b/play-services/build.gradle index 1f73cbcc..75aa8dd3 100644 --- a/play-services/build.gradle +++ b/play-services/build.gradle @@ -19,5 +19,8 @@ dependencies { android { compileSdkVersion 22 - buildToolsVersion "22.0.0" + buildToolsVersion "22.0.1" + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_6 + } } From 1df0066367669d470cb44154e251c6796a04659f Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Thu, 1 Oct 2015 21:31:52 +0200 Subject: [PATCH 30/65] Update Gms API --- extern/GmsApi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/GmsApi b/extern/GmsApi index 6743f1d4..607fb4ae 160000 --- a/extern/GmsApi +++ b/extern/GmsApi @@ -1 +1 @@ -Subproject commit 6743f1d4b1a3cf0202b9d62f2ebc5dfb385b5576 +Subproject commit 607fb4ae5f7950223a957cd3b7d8406c8d019340 From d8a5f3351f201b21bb13ed2864d019347d877552 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Thu, 1 Oct 2015 21:36:38 +0200 Subject: [PATCH 31/65] Add basic play-services-cast --- build.gradle | 16 ++ play-services-cast/build.gradle | 40 +++++ .../src/main/AndroidManifest.xml | 24 +++ .../com/google/android/gms/cast/Cast.java | 153 ++++++++++++++++++ .../gms/cast/CastMediaControlIntent.java | 64 ++++++++ .../android/gms/cast/CastPresentation.java | 33 ++++ play-services/build.gradle | 39 +++-- play-services/src/main/AndroidManifest.xml | 16 ++ settings.gradle | 17 ++ 9 files changed, 391 insertions(+), 11 deletions(-) create mode 100644 play-services-cast/build.gradle create mode 100644 play-services-cast/src/main/AndroidManifest.xml create mode 100644 play-services-cast/src/main/java/com/google/android/gms/cast/Cast.java create mode 100644 play-services-cast/src/main/java/com/google/android/gms/cast/CastMediaControlIntent.java create mode 100644 play-services-cast/src/main/java/com/google/android/gms/cast/CastPresentation.java diff --git a/build.gradle b/build.gradle index 4cb7b45f..49dfafa1 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,19 @@ +/* + * 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. + */ + // Top-level build file where you can add configuration options common to all sub-projects/modules. subprojects { group = 'org.microg' diff --git a/play-services-cast/build.gradle b/play-services-cast/build.gradle new file mode 100644 index 00000000..2d7747bc --- /dev/null +++ b/play-services-cast/build.gradle @@ -0,0 +1,40 @@ +/* + * 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. + */ + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:1.3.0' + classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' + } +} + +apply plugin: 'com.android.library' +apply plugin: 'com.github.dcendents.android-maven' + +android { + compileSdkVersion 23 + buildToolsVersion "23.0.1" + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_6 + } +} + +dependencies { + compile project(':play-services-base') +} diff --git a/play-services-cast/src/main/AndroidManifest.xml b/play-services-cast/src/main/AndroidManifest.xml new file mode 100644 index 00000000..bb22a334 --- /dev/null +++ b/play-services-cast/src/main/AndroidManifest.xml @@ -0,0 +1,24 @@ + + + + + + + + + diff --git a/play-services-cast/src/main/java/com/google/android/gms/cast/Cast.java b/play-services-cast/src/main/java/com/google/android/gms/cast/Cast.java new file mode 100644 index 00000000..c84bab98 --- /dev/null +++ b/play-services-cast/src/main/java/com/google/android/gms/cast/Cast.java @@ -0,0 +1,153 @@ +/* + * 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.cast; + +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.Result; +import com.google.android.gms.common.api.Status; + +import java.io.IOException; + +public class Cast { + + public static final Api API = new Api(null); // TODO + public static final Cast.CastApi CastApi = null; // TODO + + public interface ApplicationConnectionResult extends Result { + ApplicationMetadata getApplicationMetadata(); + + String getApplicationStatus(); + + String getSessionId(); + + boolean getWasLaunched(); + } + + public interface CastApi { + int getActiveInputState(GoogleApiClient client); + + ApplicationMetadata getApplicationMetadata(GoogleApiClient client); + + String getApplicationStatus(GoogleApiClient client); + + int getStandbyState(GoogleApiClient client); + + double getVolume(GoogleApiClient client); + + boolean isMute(GoogleApiClient client); + + PendingResult joinApplication(GoogleApiClient client); + + PendingResult joinApplication(GoogleApiClient client, String applicationId, String sessionId); + + PendingResult joinApplication(GoogleApiClient client, String applicationId); + + PendingResult launchApplication(GoogleApiClient client, String applicationId, LaunchOptions launchOptions); + + PendingResult launchApplication(GoogleApiClient client, String applicationId); + + @Deprecated + PendingResult launchApplication(GoogleApiClient client, String applicationId, boolean relaunchIfRunning); + + PendingResult leaveApplication(GoogleApiClient client); + + void removeMessageReceivedCallbacks(GoogleApiClient client, String namespace) throws IOException; + + void requestStatus(GoogleApiClient client) throws IOException; + + PendingResult sendMessage(GoogleApiClient client, String namespace, String message); + + void setMessageReceivedCallbacks(GoogleApiClient client, String namespace, Cast.MessageReceivedCallback callbacks) throws IOException; + + void setMute(GoogleApiClient client, boolean mute) throws IOException; + + void setVolume(GoogleApiClient client, double volume) throws IOException; + + PendingResult stopApplication(GoogleApiClient client); + + PendingResult stopApplication(GoogleApiClient client, String sessionId); + } + + public static class CastOptions implements Api.ApiOptions.HasOptions { + private final CastDevice castDevice; + private final Listener castListener; + private final boolean verboseLoggingEnabled; + + public CastOptions(CastDevice castDevice, Listener castListener, boolean verboseLoggingEnabled) { + this.castDevice = castDevice; + this.castListener = castListener; + this.verboseLoggingEnabled = verboseLoggingEnabled; + } + + @Deprecated + public static Builder builder(CastDevice castDevice, Listener castListener) { + return new Builder(castDevice, castListener); + } + + public static class Builder { + private final CastDevice castDevice; + private final Listener castListener; + private boolean verboseLoggingEnabled; + + public Builder(CastDevice castDevice, Listener castListener) { + this.castDevice = castDevice; + this.castListener = castListener; + } + + public CastOptions build() { + return new CastOptions(castDevice, castListener, verboseLoggingEnabled); + } + + public Builder setVerboseLoggingEnabled(boolean verboseLoggingEnabled) { + this.verboseLoggingEnabled = verboseLoggingEnabled; + return this; + } + } + } + + public static class Listener { + public void onActiveInputStateChanged(int activeInputState) { + + } + + public void onApplicationDisconnected(int statusCode) { + + } + + public void onApplicationMetadataChanged(ApplicationMetadata applicationMetadata) { + + } + + public void onApplicationStatusChanged() { + + } + + public void onStandbyStateChanged(int standbyState) { + + } + + public void onVolumeChanged() { + + } + } + + public interface MessageReceivedCallback { + void onMessageReceived(CastDevice castDevice, String namespace, String message); + } +} diff --git a/play-services-cast/src/main/java/com/google/android/gms/cast/CastMediaControlIntent.java b/play-services-cast/src/main/java/com/google/android/gms/cast/CastMediaControlIntent.java new file mode 100644 index 00000000..e06b87fa --- /dev/null +++ b/play-services-cast/src/main/java/com/google/android/gms/cast/CastMediaControlIntent.java @@ -0,0 +1,64 @@ +/* + * 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.cast; + +import java.util.Collection; +import java.util.Locale; + +public class CastMediaControlIntent { + public static final String ACTION_SYNC_STATUS = "com.google.android.gms.cast.ACTION_SYNC_STATUS"; + @Deprecated + public static final String CATEGORY_CAST = "com.google.android.gms.cast.CATEGORY_CAST"; + + public static final String EXTRA_CAST_APPLICATION_ID = "com.google.android.gms.cast.EXTRA_CAST_APPLICATION_ID"; + public static final String EXTRA_CAST_LANGUAGE_CODE = "com.google.android.gms.cast.EXTRA_CAST_LANGUAGE_CODE"; + public static final String EXTRA_CAST_RELAUNCH_APPLICATION = "com.google.android.gms.cast.EXTRA_CAST_RELAUNCH_APPLICATION"; + public static final String EXTRA_CAST_STOP_APPLICATION_WHEN_SESSION_ENDS = "com.google.android.gms.cast.EXTRA_CAST_STOP_APPLICATION_WHEN_SESSION_ENDS"; + public static final String EXTRA_CUSTOM_DATA = "com.google.android.gms.cast.EXTRA_CUSTOM_DATA"; + public static final String EXTRA_DEBUG_LOGGING_ENABLED = "com.google.android.gms.cast.EXTRA_DEBUG_LOGGING_ENABLED"; + public static final String EXTRA_ERROR_CODE = "com.google.android.gms.cast.EXTRA_ERROR_CODE"; + + public static final String DEFAULT_MEDIA_RECEIVER_APPLICATION_ID = "CC1AD845"; + + public static final int ERROR_CODE_REQUEST_FAILED = 1; + public static final int ERROR_CODE_SESSION_START_FAILED = 2; + public static final int ERROR_CODE_TEMPORARILY_DISCONNECTED = 3; + + public static String categoryForCast(String applicationId) { + return CATEGORY_CAST; // TODO + } + + public static String categoryForCast(String applicationId, Collection namespaces) { + return CATEGORY_CAST; // TODO + } + + public static String categoryForCast(Collection namespaces) { + return CATEGORY_CAST; // TODO + } + + public static String categoryForRemotePlayback(String applicationId) { + return CATEGORY_CAST; // TODO + } + + public static String categoryForRemotePlayback() { + return CATEGORY_CAST; // TODO + } + + public static String languageTagForLocale(Locale locale) { + return CATEGORY_CAST; // TODO + } +} diff --git a/play-services-cast/src/main/java/com/google/android/gms/cast/CastPresentation.java b/play-services-cast/src/main/java/com/google/android/gms/cast/CastPresentation.java new file mode 100644 index 00000000..4eef8806 --- /dev/null +++ b/play-services-cast/src/main/java/com/google/android/gms/cast/CastPresentation.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.cast; + +import android.annotation.TargetApi; +import android.app.Presentation; +import android.content.Context; +import android.view.Display; + +@TargetApi(17) +public class CastPresentation extends Presentation { + public CastPresentation(Context outerContext, Display display) { + super(outerContext, display); + } + + public CastPresentation(Context outerContext, Display display, int theme) { + super(outerContext, display, theme); + } +} diff --git a/play-services/build.gradle b/play-services/build.gradle index 75aa8dd3..3d51b56e 100644 --- a/play-services/build.gradle +++ b/play-services/build.gradle @@ -1,26 +1,43 @@ +/* + * 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. + */ + buildscript { repositories { - mavenCentral() + jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.0.1' - classpath 'com.github.dcendents:android-maven-plugin:1.2' + classpath 'com.android.tools.build:gradle:1.3.0' + classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' } } apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' -dependencies { - compile project(':play-services-base') - compile project(':play-services-location') - compile project(':play-services-wearable') -} - android { - compileSdkVersion 22 - buildToolsVersion "22.0.1" + compileSdkVersion 23 + buildToolsVersion "23.0.1" compileOptions { sourceCompatibility JavaVersion.VERSION_1_6 } } + +dependencies { + compile project(':play-services-base') + compile project(':play-services-cast') + compile project(':play-services-location') + compile project(':play-services-wearable') +} diff --git a/play-services/src/main/AndroidManifest.xml b/play-services/src/main/AndroidManifest.xml index 336be92b..3ef46ecf 100644 --- a/play-services/src/main/AndroidManifest.xml +++ b/play-services/src/main/AndroidManifest.xml @@ -1,4 +1,20 @@ + + diff --git a/settings.gradle b/settings.gradle index 9ec62cf1..4adc2f63 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,8 +1,25 @@ +/* + * 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. + */ + include ':safe-parcel' include ':play-services-api' include ':play-services-base' +include ':play-services-cast' include ':play-services-location' include ':play-services-wearable' From 41a6ee844f3be0a3a0417714a01387ddec6df122 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Thu, 1 Oct 2015 21:37:50 +0200 Subject: [PATCH 32/65] Cleanup for Java 1.6 compatibility, Update copyright --- play-services-base/build.gradle | 36 ++++++++++----- .../src/main/AndroidManifest.xml | 16 +++++++ .../android/gms/common/ConnectionResult.java | 16 +++++++ .../gms/common/GooglePlayServicesClient.java | 16 +++++++ .../gms/common/GooglePlayServicesUtil.java | 17 ++++++- .../google/android/gms/common/api/Api.java | 16 +++++++ .../gms/common/api/GoogleApiClient.java | 24 ++++++++-- .../android/gms/common/api/PendingResult.java | 16 +++++++ .../android/gms/common/api/Releasable.java | 16 +++++++ .../gms/common/api/ResultCallback.java | 16 +++++++ .../android/gms/common/data/DataBuffer.java | 2 +- .../android/gms/gcm/GoogleCloudMessaging.java | 2 +- .../common/ForwardConnectionCallbacks.java | 2 +- .../ForwardConnectionFailedListener.java | 2 +- .../java/org/microg/gms/common/GmsClient.java | 2 +- .../org/microg/gms/common/GmsConnector.java | 4 +- .../gms/common/MultiConnectionKeeper.java | 6 +-- .../gms/common/api/AbstractPendingResult.java | 2 +- .../api/AbstractPlayServicesClient.java | 2 +- .../org/microg/gms/common/api/ApiBuilder.java | 2 +- .../microg/gms/common/api/ApiConnection.java | 2 +- .../gms/common/api/GoogleApiClientImpl.java | 10 ++--- .../gms/common/api/ResultCallbackHandler.java | 2 +- .../src/main/res/values/version.xml | 2 +- play-services-location/build.gradle | 34 ++++++++++---- .../src/main/AndroidManifest.xml | 16 +++++++ .../location/FusedLocationProviderApi.java | 44 +++++++++++++------ .../android/gms/location/GeofencingApi.java | 16 +++++++ .../android/gms/location/LocationClient.java | 16 +++++++ .../gms/location/LocationListener.java | 16 +++++++ .../gms/location/LocationServices.java | 22 ++++++++-- .../FusedLocationProviderApiImpl.java | 5 ++- .../gms/location/GeofencingApiImpl.java | 2 +- .../location/GoogleLocationManagerClient.java | 2 +- .../gms/location/LocationClientImpl.java | 4 +- .../location/LocationServicesApiBuilder.java | 2 +- .../location/NativeLocationClientImpl.java | 10 ++--- play-services-wearable/build.gradle | 34 ++++++++++---- .../src/main/AndroidManifest.xml | 16 +++++++ .../google/android/gms/wearable/DataApi.java | 3 +- .../android/gms/wearable/DataEvent.java | 2 +- .../android/gms/wearable/DataEventBuffer.java | 2 +- .../android/gms/wearable/DataItemBuffer.java | 2 +- .../google/android/gms/wearable/DataMap.java | 8 ++-- .../android/gms/wearable/DataMapItem.java | 2 +- .../android/gms/wearable/MessageApi.java | 2 +- .../android/gms/wearable/MessageEvent.java | 2 +- .../google/android/gms/wearable/NodeApi.java | 2 +- .../gms/wearable/PutDataMapRequest.java | 6 ++- .../google/android/gms/wearable/Wearable.java | 4 +- .../gms/wearable/WearableListenerService.java | 2 +- .../org/microg/gms/wearable/DataApiImpl.java | 4 +- .../microg/gms/wearable/MessageApiImpl.java | 2 +- .../org/microg/gms/wearable/NodeApiImpl.java | 2 +- .../gms/wearable/WearableApiBuilder.java | 2 +- 55 files changed, 411 insertions(+), 106 deletions(-) diff --git a/play-services-base/build.gradle b/play-services-base/build.gradle index f0caf02f..fe05a441 100644 --- a/play-services-base/build.gradle +++ b/play-services-base/build.gradle @@ -1,25 +1,41 @@ +/* + * 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. + */ + buildscript { repositories { - mavenCentral() + jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.0.1' - classpath 'com.github.dcendents:android-maven-plugin:1.2' + classpath 'com.android.tools.build:gradle:1.3.0' + classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' } } apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' -dependencies { - compile 'com.android.support:support-v4:22.0.0' - compile project(':play-services-api') -} - android { - compileSdkVersion 22 - buildToolsVersion "22.0.1" + compileSdkVersion 23 + buildToolsVersion "23.0.1" compileOptions { sourceCompatibility JavaVersion.VERSION_1_6 } } + +dependencies { + compile 'com.android.support:support-v4:23.0.1' + compile project(':play-services-api') +} diff --git a/play-services-base/src/main/AndroidManifest.xml b/play-services-base/src/main/AndroidManifest.xml index d3685676..8b7acc02 100644 --- a/play-services-base/src/main/AndroidManifest.xml +++ b/play-services-base/src/main/AndroidManifest.xml @@ -1,4 +1,20 @@ + + diff --git a/play-services-base/src/main/java/com/google/android/gms/common/ConnectionResult.java b/play-services-base/src/main/java/com/google/android/gms/common/ConnectionResult.java index 155f0508..dbd87044 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/ConnectionResult.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/ConnectionResult.java @@ -1,3 +1,19 @@ +/* + * 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; diff --git a/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesClient.java b/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesClient.java index 9796592f..b9cece5c 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesClient.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesClient.java @@ -1,3 +1,19 @@ +/* + * 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.os.Bundle; diff --git a/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesUtil.java b/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesUtil.java index 01499aec..faaa6adc 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesUtil.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesUtil.java @@ -1,3 +1,19 @@ +/* + * 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; @@ -20,7 +36,6 @@ import org.microg.gms.common.PublicApi; * TODO: methods :) */ public class GooglePlayServicesUtil { - @PublicApi(exclude = true) private static final String TAG = "GooglePlayServicesUtil"; public static final String GMS_ERROR_DIALOG = "GooglePlayServicesErrorDialog"; diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/Api.java b/play-services-base/src/main/java/com/google/android/gms/common/api/Api.java index 60ab9a36..8a55af20 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/api/Api.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/api/Api.java @@ -1,3 +1,19 @@ +/* + * 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; diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java b/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java index e44f988e..c3e44885 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java @@ -1,3 +1,19 @@ +/* + * 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; @@ -222,10 +238,10 @@ public interface GoogleApiClient { @PublicApi public 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 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; diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/PendingResult.java b/play-services-base/src/main/java/com/google/android/gms/common/api/PendingResult.java index 575fc791..28943f18 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/api/PendingResult.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/api/PendingResult.java @@ -1,3 +1,19 @@ +/* + * 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; diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/Releasable.java b/play-services-base/src/main/java/com/google/android/gms/common/api/Releasable.java index ed3d08c6..6856634a 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/api/Releasable.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/api/Releasable.java @@ -1,3 +1,19 @@ +/* + * 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; /** diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/ResultCallback.java b/play-services-base/src/main/java/com/google/android/gms/common/api/ResultCallback.java index 08335d5b..4695dbcb 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/api/ResultCallback.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/api/ResultCallback.java @@ -1,3 +1,19 @@ +/* + * 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; /** diff --git a/play-services-base/src/main/java/com/google/android/gms/common/data/DataBuffer.java b/play-services-base/src/main/java/com/google/android/gms/common/data/DataBuffer.java index 0d406cbd..485e7bc9 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/data/DataBuffer.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/data/DataBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 µg Project Team + * 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. diff --git a/play-services-base/src/main/java/com/google/android/gms/gcm/GoogleCloudMessaging.java b/play-services-base/src/main/java/com/google/android/gms/gcm/GoogleCloudMessaging.java index 91f00fd5..b867ae7a 100644 --- a/play-services-base/src/main/java/com/google/android/gms/gcm/GoogleCloudMessaging.java +++ b/play-services-base/src/main/java/com/google/android/gms/gcm/GoogleCloudMessaging.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 µg Project Team + * 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. diff --git a/play-services-base/src/main/java/org/microg/gms/common/ForwardConnectionCallbacks.java b/play-services-base/src/main/java/org/microg/gms/common/ForwardConnectionCallbacks.java index e38972ff..62497285 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/ForwardConnectionCallbacks.java +++ b/play-services-base/src/main/java/org/microg/gms/common/ForwardConnectionCallbacks.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 µg Project Team + * 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. diff --git a/play-services-base/src/main/java/org/microg/gms/common/ForwardConnectionFailedListener.java b/play-services-base/src/main/java/org/microg/gms/common/ForwardConnectionFailedListener.java index 823a1991..9645b957 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/ForwardConnectionFailedListener.java +++ b/play-services-base/src/main/java/org/microg/gms/common/ForwardConnectionFailedListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 µg Project Team + * 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. diff --git a/play-services-base/src/main/java/org/microg/gms/common/GmsClient.java b/play-services-base/src/main/java/org/microg/gms/common/GmsClient.java index 80bf78cf..2d88a881 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/GmsClient.java +++ b/play-services-base/src/main/java/org/microg/gms/common/GmsClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 µg Project Team + * 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. diff --git a/play-services-base/src/main/java/org/microg/gms/common/GmsConnector.java b/play-services-base/src/main/java/org/microg/gms/common/GmsConnector.java index 3621f8ec..efb66edd 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/GmsConnector.java +++ b/play-services-base/src/main/java/org/microg/gms/common/GmsConnector.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 µg Project Team + * 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. @@ -47,7 +47,7 @@ public class GmsConnector result = new AbstractPendingResult<>(looper); + final AbstractPendingResult result = new AbstractPendingResult(looper); Message msg = new Message(); msg.obj = result; new Handler(looper).sendMessage(msg); diff --git a/play-services-base/src/main/java/org/microg/gms/common/MultiConnectionKeeper.java b/play-services-base/src/main/java/org/microg/gms/common/MultiConnectionKeeper.java index d88bc5ed..75b34039 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/MultiConnectionKeeper.java +++ b/play-services-base/src/main/java/org/microg/gms/common/MultiConnectionKeeper.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 µg Project Team + * 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. @@ -39,7 +39,7 @@ public class MultiConnectionKeeper { private static MultiConnectionKeeper INSTANCE; private final Context context; - private final Map connections = new HashMap<>(); + private final Map connections = new HashMap(); public MultiConnectionKeeper(Context context) { this.context = context; @@ -83,7 +83,7 @@ public class MultiConnectionKeeper { public class Connection { private final String actionString; - private final Set connectionForwards = new HashSet<>(); + private final Set connectionForwards = new HashSet(); private boolean bound = false; private boolean connected = false; private IBinder binder; diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/AbstractPendingResult.java b/play-services-base/src/main/java/org/microg/gms/common/api/AbstractPendingResult.java index 7986adff..013cc8d3 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/api/AbstractPendingResult.java +++ b/play-services-base/src/main/java/org/microg/gms/common/api/AbstractPendingResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 µg Project Team + * 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. diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/AbstractPlayServicesClient.java b/play-services-base/src/main/java/org/microg/gms/common/api/AbstractPlayServicesClient.java index 50fd1bc3..32d817da 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/api/AbstractPlayServicesClient.java +++ b/play-services-base/src/main/java/org/microg/gms/common/api/AbstractPlayServicesClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 µg Project Team + * 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. diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/ApiBuilder.java b/play-services-base/src/main/java/org/microg/gms/common/api/ApiBuilder.java index 4ffffebe..b20899df 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/api/ApiBuilder.java +++ b/play-services-base/src/main/java/org/microg/gms/common/api/ApiBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 µg Project Team + * 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. diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/ApiConnection.java b/play-services-base/src/main/java/org/microg/gms/common/api/ApiConnection.java index 77c9af9d..019ea5d8 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/api/ApiConnection.java +++ b/play-services-base/src/main/java/org/microg/gms/common/api/ApiConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 µg Project Team + * 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. diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java b/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java index db0c707e..b47982ca 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java +++ b/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 µg Project Team + * 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. @@ -42,11 +42,11 @@ public class GoogleApiClientImpl implements GoogleApiClient { 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 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 Set connectionCallbacks = new HashSet(); + private final Set connectionFailedListeners = new HashSet(); private final int clientId; private final ConnectionCallbacks baseConnectionCallbacks = new ConnectionCallbacks() { @Override diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/ResultCallbackHandler.java b/play-services-base/src/main/java/org/microg/gms/common/api/ResultCallbackHandler.java index 0c03a57b..071a6d74 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/api/ResultCallbackHandler.java +++ b/play-services-base/src/main/java/org/microg/gms/common/api/ResultCallbackHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 µg Project Team + * 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. diff --git a/play-services-base/src/main/res/values/version.xml b/play-services-base/src/main/res/values/version.xml index 096db2c0..aca0d060 100644 --- a/play-services-base/src/main/res/values/version.xml +++ b/play-services-base/src/main/res/values/version.xml @@ -1,6 +1,6 @@ + diff --git a/play-services-location/src/main/java/com/google/android/gms/location/FusedLocationProviderApi.java b/play-services-location/src/main/java/com/google/android/gms/location/FusedLocationProviderApi.java index 8c484537..572a6ae1 100644 --- a/play-services-location/src/main/java/com/google/android/gms/location/FusedLocationProviderApi.java +++ b/play-services-location/src/main/java/com/google/android/gms/location/FusedLocationProviderApi.java @@ -1,3 +1,19 @@ +/* + * 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.location; import android.app.PendingIntent; @@ -10,26 +26,26 @@ import com.google.android.gms.common.api.PendingResult; import org.microg.gms.common.Constants; public interface FusedLocationProviderApi { - public static final String KEY_LOCATION_CHANGED = "com.google.android.location.LOCATION"; - public static final String KEY_MOCK_LOCATION = Constants.KEY_MOCK_LOCATION; + String KEY_LOCATION_CHANGED = "com.google.android.location.LOCATION"; + String KEY_MOCK_LOCATION = Constants.KEY_MOCK_LOCATION; - public Location getLastLocation(GoogleApiClient client); + Location getLastLocation(GoogleApiClient client); - public PendingResult requestLocationUpdates(GoogleApiClient client, LocationRequest request, - LocationListener listener); + PendingResult requestLocationUpdates(GoogleApiClient client, LocationRequest request, + LocationListener listener); - public PendingResult requestLocationUpdates(GoogleApiClient client, LocationRequest request, - LocationListener listener, Looper looper); + PendingResult requestLocationUpdates(GoogleApiClient client, LocationRequest request, + LocationListener listener, Looper looper); - public PendingResult requestLocationUpdates(GoogleApiClient client, LocationRequest request, - PendingIntent callbackIntent); + PendingResult requestLocationUpdates(GoogleApiClient client, LocationRequest request, + PendingIntent callbackIntent); - public PendingResult removeLocationUpdates(GoogleApiClient client, LocationListener listener); + PendingResult removeLocationUpdates(GoogleApiClient client, LocationListener listener); - public PendingResult removeLocationUpdates(GoogleApiClient client, - PendingIntent callbackIntent); + PendingResult removeLocationUpdates(GoogleApiClient client, + PendingIntent callbackIntent); - public PendingResult setMockMode(GoogleApiClient client, boolean isMockMode); + PendingResult setMockMode(GoogleApiClient client, boolean isMockMode); - public PendingResult setMockLocation(GoogleApiClient client, Location mockLocation); + PendingResult setMockLocation(GoogleApiClient client, Location mockLocation); } diff --git a/play-services-location/src/main/java/com/google/android/gms/location/GeofencingApi.java b/play-services-location/src/main/java/com/google/android/gms/location/GeofencingApi.java index fe2f8762..b2b581bf 100644 --- a/play-services-location/src/main/java/com/google/android/gms/location/GeofencingApi.java +++ b/play-services-location/src/main/java/com/google/android/gms/location/GeofencingApi.java @@ -1,3 +1,19 @@ +/* + * 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.location; public interface GeofencingApi { diff --git a/play-services-location/src/main/java/com/google/android/gms/location/LocationClient.java b/play-services-location/src/main/java/com/google/android/gms/location/LocationClient.java index b9df6c40..ae75d4c0 100644 --- a/play-services-location/src/main/java/com/google/android/gms/location/LocationClient.java +++ b/play-services-location/src/main/java/com/google/android/gms/location/LocationClient.java @@ -1,3 +1,19 @@ +/* + * 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.location; import android.app.PendingIntent; diff --git a/play-services-location/src/main/java/com/google/android/gms/location/LocationListener.java b/play-services-location/src/main/java/com/google/android/gms/location/LocationListener.java index 86fedc42..805b543a 100644 --- a/play-services-location/src/main/java/com/google/android/gms/location/LocationListener.java +++ b/play-services-location/src/main/java/com/google/android/gms/location/LocationListener.java @@ -1,3 +1,19 @@ +/* + * 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.location; import android.location.Location; diff --git a/play-services-location/src/main/java/com/google/android/gms/location/LocationServices.java b/play-services-location/src/main/java/com/google/android/gms/location/LocationServices.java index 5dddd2b4..dc575bb9 100644 --- a/play-services-location/src/main/java/com/google/android/gms/location/LocationServices.java +++ b/play-services-location/src/main/java/com/google/android/gms/location/LocationServices.java @@ -1,3 +1,19 @@ +/* + * 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.location; import com.google.android.gms.common.api.Api; @@ -10,9 +26,7 @@ import org.microg.gms.location.LocationServicesApiBuilder; * The main entry point for location services integration. */ public class LocationServices { - public static final Api API = new Api<>(new - LocationServicesApiBuilder()); - public static final FusedLocationProviderApi FusedLocationApi = new - FusedLocationProviderApiImpl(); + public static final Api API = new Api(new LocationServicesApiBuilder()); + public static final FusedLocationProviderApi FusedLocationApi = new FusedLocationProviderApiImpl(); public static final GeofencingApi GeofencingApi = new GeofencingApiImpl(); } diff --git a/play-services-location/src/main/java/org/microg/gms/location/FusedLocationProviderApiImpl.java b/play-services-location/src/main/java/org/microg/gms/location/FusedLocationProviderApiImpl.java index 08e90c5b..a9d6bf12 100644 --- a/play-services-location/src/main/java/org/microg/gms/location/FusedLocationProviderApiImpl.java +++ b/play-services-location/src/main/java/org/microg/gms/location/FusedLocationProviderApiImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 µg Project Team + * 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. @@ -22,6 +22,7 @@ import android.os.Looper; import android.os.RemoteException; import android.util.Log; +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.Result; @@ -124,7 +125,7 @@ public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { } private PendingResult callVoid(GoogleApiClient client, final Runnable runnable) { - return new GmsConnector<>(client, LocationServices.API, + return new GmsConnector(client, LocationServices.API, new GmsConnector.Callback() { @Override public Result onClientAvailable(LocationClientImpl client) throws diff --git a/play-services-location/src/main/java/org/microg/gms/location/GeofencingApiImpl.java b/play-services-location/src/main/java/org/microg/gms/location/GeofencingApiImpl.java index 81fcfdc7..7b6512b0 100644 --- a/play-services-location/src/main/java/org/microg/gms/location/GeofencingApiImpl.java +++ b/play-services-location/src/main/java/org/microg/gms/location/GeofencingApiImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 µg Project Team + * 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. diff --git a/play-services-location/src/main/java/org/microg/gms/location/GoogleLocationManagerClient.java b/play-services-location/src/main/java/org/microg/gms/location/GoogleLocationManagerClient.java index 68f8d4c9..aab38b0d 100644 --- a/play-services-location/src/main/java/org/microg/gms/location/GoogleLocationManagerClient.java +++ b/play-services-location/src/main/java/org/microg/gms/location/GoogleLocationManagerClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 µg Project Team + * 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. diff --git a/play-services-location/src/main/java/org/microg/gms/location/LocationClientImpl.java b/play-services-location/src/main/java/org/microg/gms/location/LocationClientImpl.java index a79e0c62..e3593198 100644 --- a/play-services-location/src/main/java/org/microg/gms/location/LocationClientImpl.java +++ b/play-services-location/src/main/java/org/microg/gms/location/LocationClientImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 µg Project Team + * 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. @@ -38,7 +38,7 @@ import java.util.Map; public class LocationClientImpl extends GoogleLocationManagerClient { private static final String TAG = "GmsLocationClientImpl"; private NativeLocationClientImpl nativeLocation = null; - private Map listenerMap = new HashMap<>(); + private Map listenerMap = new HashMap(); public LocationClientImpl(Context context, GoogleApiClient.ConnectionCallbacks callbacks, diff --git a/play-services-location/src/main/java/org/microg/gms/location/LocationServicesApiBuilder.java b/play-services-location/src/main/java/org/microg/gms/location/LocationServicesApiBuilder.java index 6876e630..218feebd 100644 --- a/play-services-location/src/main/java/org/microg/gms/location/LocationServicesApiBuilder.java +++ b/play-services-location/src/main/java/org/microg/gms/location/LocationServicesApiBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 µg Project Team + * 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. diff --git a/play-services-location/src/main/java/org/microg/gms/location/NativeLocationClientImpl.java b/play-services-location/src/main/java/org/microg/gms/location/NativeLocationClientImpl.java index 5129c19f..500e5e05 100644 --- a/play-services-location/src/main/java/org/microg/gms/location/NativeLocationClientImpl.java +++ b/play-services-location/src/main/java/org/microg/gms/location/NativeLocationClientImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 µg Project Team + * 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. @@ -35,15 +35,15 @@ import java.util.HashMap; import java.util.Map; public class NativeLocationClientImpl { - private final static String TAG = "GmsToNativeLocationClient"; + private final static String TAG = "GmsToNativeLocClient"; private final static Criteria DEFAULT_CRITERIA = new Criteria(); - private final static Map pendingCount = new HashMap<>(); - private final static Map nativePendingMap = new HashMap<>(); + private final static Map pendingCount = new HashMap(); + private final static Map nativePendingMap = new HashMap(); private static final String EXTRA_PENDING_INTENT = "pending_intent"; private final Context context; private final LocationManager locationManager; - private final Map nativeListenerMap = new HashMap<>(); + private final Map nativeListenerMap = new HashMap(); public NativeLocationClientImpl(LocationClientImpl client) { context = client.getContext(); diff --git a/play-services-wearable/build.gradle b/play-services-wearable/build.gradle index 041610d5..2d7747bc 100644 --- a/play-services-wearable/build.gradle +++ b/play-services-wearable/build.gradle @@ -1,24 +1,40 @@ +/* + * 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. + */ + buildscript { repositories { - mavenCentral() + jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.0.1' - classpath 'com.github.dcendents:android-maven-plugin:1.2' + classpath 'com.android.tools.build:gradle:1.3.0' + classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' } } apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' -dependencies { - compile project(':play-services-base') -} - android { - compileSdkVersion 22 - buildToolsVersion "22.0.1" + compileSdkVersion 23 + buildToolsVersion "23.0.1" compileOptions { sourceCompatibility JavaVersion.VERSION_1_6 } } + +dependencies { + compile project(':play-services-base') +} diff --git a/play-services-wearable/src/main/AndroidManifest.xml b/play-services-wearable/src/main/AndroidManifest.xml index 33723d79..bb22a334 100644 --- a/play-services-wearable/src/main/AndroidManifest.xml +++ b/play-services-wearable/src/main/AndroidManifest.xml @@ -1,4 +1,20 @@ + + diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataApi.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataApi.java index 25ef04bc..fffa4079 100644 --- a/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataApi.java +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataApi.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 µg Project Team + * 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. @@ -24,6 +24,7 @@ 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 com.google.android.gms.common.data.Freezable; +import com.google.android.gms.wearable.internal.PutDataRequest; import org.microg.gms.common.PublicApi; diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataEvent.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataEvent.java index 0d72101e..3a665646 100644 --- a/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataEvent.java +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 µg Project Team + * 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. diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataEventBuffer.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataEventBuffer.java index 1d4f6a45..b5cc0952 100644 --- a/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataEventBuffer.java +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataEventBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 µg Project Team + * 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. diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataItemBuffer.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataItemBuffer.java index 991b79f4..56b71acf 100644 --- a/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataItemBuffer.java +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataItemBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 µg Project Team + * 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. diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataMap.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataMap.java index aad96e14..0cbb2c63 100644 --- a/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataMap.java +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataMap.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 µg Project Team + * 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. @@ -34,8 +34,8 @@ import java.util.Set; public class DataMap { public static String TAG = "GmsDataMap"; - private Map data = new HashMap<>(); - private Map types = new HashMap<>(); + private Map data = new HashMap(); + private Map types = new HashMap(); public DataMap() { @@ -45,7 +45,7 @@ public class DataMap { * @return an ArrayList of DataMaps from an ArrayList of Bundles. Any elements in the Bundles not supported by DataMap will be dropped. */ public static ArrayList arrayListFromBundleArrayList(ArrayList bundleArrayList) { - ArrayList res = new ArrayList<>(); + ArrayList res = new ArrayList(); for (Bundle bundle : bundleArrayList) { res.add(fromBundle(bundle)); } diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataMapItem.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataMapItem.java index 7eb6fb0f..3dadd29d 100644 --- a/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataMapItem.java +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataMapItem.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 µg Project Team + * 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. diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageApi.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageApi.java index 999d615f..e2016f73 100644 --- a/play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageApi.java +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageApi.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 µg Project Team + * 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. diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageEvent.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageEvent.java index c5dd0d33..5c772334 100644 --- a/play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageEvent.java +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 µg Project Team + * 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. diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/NodeApi.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/NodeApi.java index 86088446..4e9e19ae 100644 --- a/play-services-wearable/src/main/java/com/google/android/gms/wearable/NodeApi.java +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/NodeApi.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 µg Project Team + * 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. diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/PutDataMapRequest.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/PutDataMapRequest.java index 339c23ef..6a171425 100644 --- a/play-services-wearable/src/main/java/com/google/android/gms/wearable/PutDataMapRequest.java +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/PutDataMapRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 µg Project Team + * 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. @@ -18,6 +18,8 @@ package com.google.android.gms.wearable; import android.net.Uri; +import com.google.android.gms.wearable.internal.PutDataRequest; + import org.microg.gms.common.PublicApi; /** @@ -38,7 +40,7 @@ public class PutDataMapRequest { */ public PutDataRequest asPutDataRequest() { // TODO - return PutDataRequest.create(null); + return PutDataRequest.create((Uri) null); } /** diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/Wearable.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/Wearable.java index 72003234..420652a3 100644 --- a/play-services-wearable/src/main/java/com/google/android/gms/wearable/Wearable.java +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/Wearable.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 µg Project Team + * 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. @@ -31,7 +31,7 @@ public class Wearable { /** * Token to pass to {@link GoogleApiClient.Builder#addApi(Api)} to enable the Wearable features. */ - public static final Api API = new Api<>(new WearableApiBuilder()); + public static final Api API = new Api(new WearableApiBuilder()); public static final DataApi DataApi = new DataApiImpl(); public static final MessageApi MessageApi = new MessageApiImpl(); diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/WearableListenerService.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/WearableListenerService.java index 61a552d0..5817c5d3 100644 --- a/play-services-wearable/src/main/java/com/google/android/gms/wearable/WearableListenerService.java +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/WearableListenerService.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 µg Project Team + * 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. diff --git a/play-services-wearable/src/main/java/org/microg/gms/wearable/DataApiImpl.java b/play-services-wearable/src/main/java/org/microg/gms/wearable/DataApiImpl.java index bdfa778e..f7a09865 100644 --- a/play-services-wearable/src/main/java/org/microg/gms/wearable/DataApiImpl.java +++ b/play-services-wearable/src/main/java/org/microg/gms/wearable/DataApiImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 µg Project Team + * 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. @@ -25,7 +25,7 @@ import com.google.android.gms.wearable.Asset; import com.google.android.gms.wearable.DataApi; import com.google.android.gms.wearable.DataItemAsset; import com.google.android.gms.wearable.DataItemBuffer; -import com.google.android.gms.wearable.PutDataRequest; +import com.google.android.gms.wearable.internal.PutDataRequest; public class DataApiImpl implements DataApi { @Override diff --git a/play-services-wearable/src/main/java/org/microg/gms/wearable/MessageApiImpl.java b/play-services-wearable/src/main/java/org/microg/gms/wearable/MessageApiImpl.java index 667eaa77..3312ad45 100644 --- a/play-services-wearable/src/main/java/org/microg/gms/wearable/MessageApiImpl.java +++ b/play-services-wearable/src/main/java/org/microg/gms/wearable/MessageApiImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 µg Project Team + * 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. diff --git a/play-services-wearable/src/main/java/org/microg/gms/wearable/NodeApiImpl.java b/play-services-wearable/src/main/java/org/microg/gms/wearable/NodeApiImpl.java index 2aca31ee..4cf51a46 100644 --- a/play-services-wearable/src/main/java/org/microg/gms/wearable/NodeApiImpl.java +++ b/play-services-wearable/src/main/java/org/microg/gms/wearable/NodeApiImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 µg Project Team + * 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. diff --git a/play-services-wearable/src/main/java/org/microg/gms/wearable/WearableApiBuilder.java b/play-services-wearable/src/main/java/org/microg/gms/wearable/WearableApiBuilder.java index c19f5da4..1d103230 100644 --- a/play-services-wearable/src/main/java/org/microg/gms/wearable/WearableApiBuilder.java +++ b/play-services-wearable/src/main/java/org/microg/gms/wearable/WearableApiBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 µg Project Team + * 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. From 2c18bafa4b37a4fb0f290d2ed6e32af34f9b519b Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Fri, 2 Oct 2015 03:22:45 +0200 Subject: [PATCH 33/65] Update Cast API --- extern/GmsApi | 2 +- .../google/android/gms/common/api/Api.java | 8 +- .../microg/gms/common/DummyApiConnection.java | 43 ++++++ .../com/google/android/gms/cast/Cast.java | 76 +++++++++- .../gms/cast/CastMediaControlIntent.java | 64 --------- .../android/gms/cast/CastRemoteDisplay.java | 76 ++++++++++ .../gms/cast/CastRemoteDisplayApi.java | 26 ++++ .../cast/CastRemoteDisplayLocalService.java | 28 ++++ .../org/microg/gms/cast/CastApiBuilder.java | 34 +++++ .../java/org/microg/gms/cast/CastApiImpl.java | 134 ++++++++++++++++++ .../org/microg/gms/cast/CastClientImpl.java | 29 ++++ .../gms/cast/CastRemoteDisplayApiBuilder.java | 35 +++++ .../gms/cast/CastRemoteDisplayApiImpl.java | 34 +++++ 13 files changed, 517 insertions(+), 72 deletions(-) create mode 100644 play-services-base/src/main/java/org/microg/gms/common/DummyApiConnection.java delete mode 100644 play-services-cast/src/main/java/com/google/android/gms/cast/CastMediaControlIntent.java create mode 100644 play-services-cast/src/main/java/com/google/android/gms/cast/CastRemoteDisplay.java create mode 100644 play-services-cast/src/main/java/com/google/android/gms/cast/CastRemoteDisplayApi.java create mode 100644 play-services-cast/src/main/java/com/google/android/gms/cast/CastRemoteDisplayLocalService.java create mode 100644 play-services-cast/src/main/java/org/microg/gms/cast/CastApiBuilder.java create mode 100644 play-services-cast/src/main/java/org/microg/gms/cast/CastApiImpl.java create mode 100644 play-services-cast/src/main/java/org/microg/gms/cast/CastClientImpl.java create mode 100644 play-services-cast/src/main/java/org/microg/gms/cast/CastRemoteDisplayApiBuilder.java create mode 100644 play-services-cast/src/main/java/org/microg/gms/cast/CastRemoteDisplayApiImpl.java diff --git a/extern/GmsApi b/extern/GmsApi index 607fb4ae..bb403701 160000 --- a/extern/GmsApi +++ b/extern/GmsApi @@ -1 +1 @@ -Subproject commit 607fb4ae5f7950223a957cd3b7d8406c8d019340 +Subproject commit bb4037017c447d8129abf7c5cbaa73e527da5631 diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/Api.java b/play-services-base/src/main/java/com/google/android/gms/common/api/Api.java index 8a55af20..1b0f03f2 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/api/Api.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/api/Api.java @@ -56,28 +56,28 @@ public final class Api { * Base interface for {@link ApiOptions} in {@link Api}s that have options. */ @PublicApi - public interface HasOptions extends ApiOptions { + interface HasOptions extends ApiOptions { } /** * Base interface for {@link ApiOptions} that are not required, don't exist. */ @PublicApi - public interface NotRequiredOptions extends ApiOptions { + interface NotRequiredOptions extends ApiOptions { } /** * {@link ApiOptions} implementation for {@link Api}s that do not take any options. */ @PublicApi - public final class NoOptions implements NotRequiredOptions { + final class NoOptions implements NotRequiredOptions { } /** * Base interface for {@link ApiOptions} that are optional. */ @PublicApi - public interface Optional extends HasOptions, NotRequiredOptions { + interface Optional extends HasOptions, NotRequiredOptions { } } diff --git a/play-services-base/src/main/java/org/microg/gms/common/DummyApiConnection.java b/play-services-base/src/main/java/org/microg/gms/common/DummyApiConnection.java new file mode 100644 index 00000000..355a1675 --- /dev/null +++ b/play-services-base/src/main/java/org/microg/gms/common/DummyApiConnection.java @@ -0,0 +1,43 @@ +/* + * 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; + +import org.microg.gms.common.api.ApiConnection; + +public class DummyApiConnection implements ApiConnection { + private boolean connected = false; + + @Override + public void connect() { + connected = true; + } + + @Override + public void disconnect() { + connected = false; + } + + @Override + public boolean isConnected() { + return connected; + } + + @Override + public boolean isConnecting() { + return false; + } +} diff --git a/play-services-cast/src/main/java/com/google/android/gms/cast/Cast.java b/play-services-cast/src/main/java/com/google/android/gms/cast/Cast.java index c84bab98..18065ded 100644 --- a/play-services-cast/src/main/java/com/google/android/gms/cast/Cast.java +++ b/play-services-cast/src/main/java/com/google/android/gms/cast/Cast.java @@ -16,18 +16,88 @@ package com.google.android.gms.cast; +import android.os.Bundle; + 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.Result; import com.google.android.gms.common.api.Status; +import org.microg.gms.cast.CastApiBuilder; +import org.microg.gms.cast.CastApiImpl; +import org.microg.gms.common.PublicApi; + import java.io.IOException; -public class Cast { +@PublicApi +public final class Cast { - public static final Api API = new Api(null); // TODO - public static final Cast.CastApi CastApi = null; // TODO + /** + * A constant indicating that the Google Cast device is not the currently active video input. + */ + public static final int ACTIVE_INPUT_STATE_NO = 0; + + /** + * A constant indicating that it is not known (and/or not possible to know) whether the Google Cast device is + * the currently active video input. Active input state can only be reported when the Google Cast device is + * connected to a TV or AVR with CEC support. + */ + public static final int ACTIVE_INPUT_STATE_UNKNOWN = -1; + + /** + * A constant indicating that the Google Cast device is the currently active video input. + */ + public static final int ACTIVE_INPUT_STATE_YES = 1; + + /** + * A boolean extra for the connection hint bundle passed to + * {@link GoogleApiClient.ConnectionCallbacks#onConnected(Bundle)} that indicates that the connection was + * re-established, but the receiver application that was in use at the time of the connection loss is no longer + * running on the receiver. + */ + public static final String EXTRA_APP_NO_LONGER_RUNNING = "com.google.android.gms.cast.EXTRA_APP_NO_LONGER_RUNNING"; + + /** + * The maximum raw message length (in bytes) that is supported by a Cast channel. + */ + public static final int MAX_MESSAGE_LENGTH = 65536; + + /** + * The maximum length (in characters) of a namespace name. + */ + public static final int MAX_NAMESPACE_LENGTH = 128; + + /** + * A constant indicating that the Google Cast device is not currently in standby. + */ + public static final int STANDBY_STATE_NO = 0; + + /** + * A constant indicating that it is not known (and/or not possible to know) whether the Google Cast device is + * currently in standby. Standby state can only be reported when the Google Cast device is connected to a TV or + * AVR with CEC support. + */ + public static final int STANDBY_STATE_UNKNOWN = -1; + + /** + * A constant indicating that the Google Cast device is currently in standby. + */ + public static final int STANDBY_STATE_YES = 1; + + + /** + * Token to pass to {@link GoogleApiClient.Builder#addApi(Api)} to enable the Cast features. + */ + public static final Api API = new Api(new CastApiBuilder()); + + /** + * An implementation of the CastApi interface. The interface is used to interact with a cast device. + */ + public static final Cast.CastApi CastApi = new CastApiImpl(); + + private Cast() { + } public interface ApplicationConnectionResult extends Result { ApplicationMetadata getApplicationMetadata(); diff --git a/play-services-cast/src/main/java/com/google/android/gms/cast/CastMediaControlIntent.java b/play-services-cast/src/main/java/com/google/android/gms/cast/CastMediaControlIntent.java deleted file mode 100644 index e06b87fa..00000000 --- a/play-services-cast/src/main/java/com/google/android/gms/cast/CastMediaControlIntent.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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.cast; - -import java.util.Collection; -import java.util.Locale; - -public class CastMediaControlIntent { - public static final String ACTION_SYNC_STATUS = "com.google.android.gms.cast.ACTION_SYNC_STATUS"; - @Deprecated - public static final String CATEGORY_CAST = "com.google.android.gms.cast.CATEGORY_CAST"; - - public static final String EXTRA_CAST_APPLICATION_ID = "com.google.android.gms.cast.EXTRA_CAST_APPLICATION_ID"; - public static final String EXTRA_CAST_LANGUAGE_CODE = "com.google.android.gms.cast.EXTRA_CAST_LANGUAGE_CODE"; - public static final String EXTRA_CAST_RELAUNCH_APPLICATION = "com.google.android.gms.cast.EXTRA_CAST_RELAUNCH_APPLICATION"; - public static final String EXTRA_CAST_STOP_APPLICATION_WHEN_SESSION_ENDS = "com.google.android.gms.cast.EXTRA_CAST_STOP_APPLICATION_WHEN_SESSION_ENDS"; - public static final String EXTRA_CUSTOM_DATA = "com.google.android.gms.cast.EXTRA_CUSTOM_DATA"; - public static final String EXTRA_DEBUG_LOGGING_ENABLED = "com.google.android.gms.cast.EXTRA_DEBUG_LOGGING_ENABLED"; - public static final String EXTRA_ERROR_CODE = "com.google.android.gms.cast.EXTRA_ERROR_CODE"; - - public static final String DEFAULT_MEDIA_RECEIVER_APPLICATION_ID = "CC1AD845"; - - public static final int ERROR_CODE_REQUEST_FAILED = 1; - public static final int ERROR_CODE_SESSION_START_FAILED = 2; - public static final int ERROR_CODE_TEMPORARILY_DISCONNECTED = 3; - - public static String categoryForCast(String applicationId) { - return CATEGORY_CAST; // TODO - } - - public static String categoryForCast(String applicationId, Collection namespaces) { - return CATEGORY_CAST; // TODO - } - - public static String categoryForCast(Collection namespaces) { - return CATEGORY_CAST; // TODO - } - - public static String categoryForRemotePlayback(String applicationId) { - return CATEGORY_CAST; // TODO - } - - public static String categoryForRemotePlayback() { - return CATEGORY_CAST; // TODO - } - - public static String languageTagForLocale(Locale locale) { - return CATEGORY_CAST; // TODO - } -} diff --git a/play-services-cast/src/main/java/com/google/android/gms/cast/CastRemoteDisplay.java b/play-services-cast/src/main/java/com/google/android/gms/cast/CastRemoteDisplay.java new file mode 100644 index 00000000..c6a4bc96 --- /dev/null +++ b/play-services-cast/src/main/java/com/google/android/gms/cast/CastRemoteDisplay.java @@ -0,0 +1,76 @@ +/* + * 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.cast; + +import android.view.Display; + +import com.google.android.gms.common.api.Api; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.Result; +import com.google.android.gms.common.api.Status; + +import org.microg.gms.cast.CastRemoteDisplayApiBuilder; +import org.microg.gms.cast.CastRemoteDisplayApiImpl; +import org.microg.gms.common.PublicApi; + +@PublicApi +public final class CastRemoteDisplay { + /** + * Token to pass to {@link GoogleApiClient.Builder#addApi(Api)} to enable the CastRemoteDisplay features. + */ + public static final Api API = new Api(new CastRemoteDisplayApiBuilder()); + + /** + * An implementation of the CastRemoteDisplayAPI interface. The interface is used to interact with a cast device. + */ + public static final CastRemoteDisplayApi CastApi = new CastRemoteDisplayApiImpl(); + + private CastRemoteDisplay() { + } + + public static final class CastRemoteDisplayOptions implements Api.ApiOptions.HasOptions { + private CastDevice castDevice; + private CastRemoteDisplaySessionCallbacks callbacks; + + private CastRemoteDisplayOptions(CastDevice castDevice, CastRemoteDisplaySessionCallbacks callbacks) { + this.castDevice = castDevice; + this.callbacks = callbacks; + } + + public static final class Builder { + private CastDevice castDevice; + private CastRemoteDisplaySessionCallbacks callbacks; + + public Builder(CastDevice castDevice, CastRemoteDisplaySessionCallbacks callbacks) { + this.castDevice = castDevice; + this.callbacks = callbacks; + } + + public CastRemoteDisplayOptions build() { + return new CastRemoteDisplayOptions(castDevice, callbacks); + } + } + } + + public interface CastRemoteDisplaySessionCallbacks { + void onRemoteDisplayEnded(Status status); + } + + public interface CastRemoteDisplaySessionResult extends Result { + Display getPresentationDisplay(); + } +} diff --git a/play-services-cast/src/main/java/com/google/android/gms/cast/CastRemoteDisplayApi.java b/play-services-cast/src/main/java/com/google/android/gms/cast/CastRemoteDisplayApi.java new file mode 100644 index 00000000..3e1c8774 --- /dev/null +++ b/play-services-cast/src/main/java/com/google/android/gms/cast/CastRemoteDisplayApi.java @@ -0,0 +1,26 @@ +/* + * 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.cast; + +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.PendingResult; + +public interface CastRemoteDisplayApi { + PendingResult startRemoteDisplay(GoogleApiClient apiClient, String applicationId); + + PendingResult stopRemoteDisplay(GoogleApiClient apiClient); +} diff --git a/play-services-cast/src/main/java/com/google/android/gms/cast/CastRemoteDisplayLocalService.java b/play-services-cast/src/main/java/com/google/android/gms/cast/CastRemoteDisplayLocalService.java new file mode 100644 index 00000000..66dd6fef --- /dev/null +++ b/play-services-cast/src/main/java/com/google/android/gms/cast/CastRemoteDisplayLocalService.java @@ -0,0 +1,28 @@ +/* + * 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.cast; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +public class CastRemoteDisplayLocalService extends Service { + @Override + public IBinder onBind(Intent intent) { + return null; + } +} diff --git a/play-services-cast/src/main/java/org/microg/gms/cast/CastApiBuilder.java b/play-services-cast/src/main/java/org/microg/gms/cast/CastApiBuilder.java new file mode 100644 index 00000000..ba8704d9 --- /dev/null +++ b/play-services-cast/src/main/java/org/microg/gms/cast/CastApiBuilder.java @@ -0,0 +1,34 @@ +/* + * 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.cast; + +import android.content.Context; +import android.os.Looper; + +import com.google.android.gms.cast.Cast; +import com.google.android.gms.common.api.AccountInfo; +import com.google.android.gms.common.api.GoogleApiClient; + +import org.microg.gms.common.api.ApiBuilder; +import org.microg.gms.common.api.ApiConnection; + +public class CastApiBuilder implements ApiBuilder{ + @Override + public ApiConnection build(Context context, Looper looper, Cast.CastOptions options, AccountInfo accountInfo, GoogleApiClient.ConnectionCallbacks callbacks, GoogleApiClient.OnConnectionFailedListener connectionFailedListener) { + return new CastClientImpl(context, options, callbacks, connectionFailedListener); + } +} diff --git a/play-services-cast/src/main/java/org/microg/gms/cast/CastApiImpl.java b/play-services-cast/src/main/java/org/microg/gms/cast/CastApiImpl.java new file mode 100644 index 00000000..668773e3 --- /dev/null +++ b/play-services-cast/src/main/java/org/microg/gms/cast/CastApiImpl.java @@ -0,0 +1,134 @@ +/* + * 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.cast; + +import com.google.android.gms.cast.ApplicationMetadata; +import com.google.android.gms.cast.Cast; +import com.google.android.gms.cast.LaunchOptions; +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.io.IOException; + +// TODO +public class CastApiImpl implements Cast.CastApi { + @Override + public int getActiveInputState(GoogleApiClient client) { + return 0; + } + + @Override + public ApplicationMetadata getApplicationMetadata(GoogleApiClient client) { + return null; + } + + @Override + public String getApplicationStatus(GoogleApiClient client) { + return null; + } + + @Override + public int getStandbyState(GoogleApiClient client) { + return 0; + } + + @Override + public double getVolume(GoogleApiClient client) { + return 0; + } + + @Override + public boolean isMute(GoogleApiClient client) { + return false; + } + + @Override + public PendingResult joinApplication(GoogleApiClient client) { + return null; + } + + @Override + public PendingResult joinApplication(GoogleApiClient client, String applicationId, String sessionId) { + return null; + } + + @Override + public PendingResult joinApplication(GoogleApiClient client, String applicationId) { + return null; + } + + @Override + public PendingResult launchApplication(GoogleApiClient client, String applicationId, LaunchOptions launchOptions) { + return null; + } + + @Override + public PendingResult launchApplication(GoogleApiClient client, String applicationId) { + return null; + } + + @Override + public PendingResult launchApplication(GoogleApiClient client, String applicationId, boolean relaunchIfRunning) { + return null; + } + + @Override + public PendingResult leaveApplication(GoogleApiClient client) { + return null; + } + + @Override + public void removeMessageReceivedCallbacks(GoogleApiClient client, String namespace) throws IOException { + + } + + @Override + public void requestStatus(GoogleApiClient client) throws IOException { + + } + + @Override + public PendingResult sendMessage(GoogleApiClient client, String namespace, String message) { + return null; + } + + @Override + public void setMessageReceivedCallbacks(GoogleApiClient client, String namespace, Cast.MessageReceivedCallback callbacks) throws IOException { + + } + + @Override + public void setMute(GoogleApiClient client, boolean mute) throws IOException { + + } + + @Override + public void setVolume(GoogleApiClient client, double volume) throws IOException { + + } + + @Override + public PendingResult stopApplication(GoogleApiClient client) { + return null; + } + + @Override + public PendingResult stopApplication(GoogleApiClient client, String sessionId) { + return null; + } +} diff --git a/play-services-cast/src/main/java/org/microg/gms/cast/CastClientImpl.java b/play-services-cast/src/main/java/org/microg/gms/cast/CastClientImpl.java new file mode 100644 index 00000000..071ef58d --- /dev/null +++ b/play-services-cast/src/main/java/org/microg/gms/cast/CastClientImpl.java @@ -0,0 +1,29 @@ +/* + * 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.cast; + +import android.content.Context; + +import com.google.android.gms.cast.Cast; +import com.google.android.gms.common.api.GoogleApiClient; + +import org.microg.gms.common.DummyApiConnection; + +public class CastClientImpl extends DummyApiConnection { + public CastClientImpl(Context context, Cast.CastOptions options, GoogleApiClient.ConnectionCallbacks callbacks, GoogleApiClient.OnConnectionFailedListener connectionFailedListener) { + } +} diff --git a/play-services-cast/src/main/java/org/microg/gms/cast/CastRemoteDisplayApiBuilder.java b/play-services-cast/src/main/java/org/microg/gms/cast/CastRemoteDisplayApiBuilder.java new file mode 100644 index 00000000..29f5810c --- /dev/null +++ b/play-services-cast/src/main/java/org/microg/gms/cast/CastRemoteDisplayApiBuilder.java @@ -0,0 +1,35 @@ +/* + * 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.cast; + +import android.content.Context; +import android.os.Looper; + +import com.google.android.gms.cast.CastRemoteDisplay; +import com.google.android.gms.common.api.AccountInfo; +import com.google.android.gms.common.api.GoogleApiClient; + +import org.microg.gms.common.DummyApiConnection; +import org.microg.gms.common.api.ApiBuilder; +import org.microg.gms.common.api.ApiConnection; + +public class CastRemoteDisplayApiBuilder implements ApiBuilder { + @Override + public ApiConnection build(Context context, Looper looper, CastRemoteDisplay.CastRemoteDisplayOptions options, AccountInfo accountInfo, GoogleApiClient.ConnectionCallbacks callbacks, GoogleApiClient.OnConnectionFailedListener connectionFailedListener) { + return new DummyApiConnection(); + } +} diff --git a/play-services-cast/src/main/java/org/microg/gms/cast/CastRemoteDisplayApiImpl.java b/play-services-cast/src/main/java/org/microg/gms/cast/CastRemoteDisplayApiImpl.java new file mode 100644 index 00000000..6803ce46 --- /dev/null +++ b/play-services-cast/src/main/java/org/microg/gms/cast/CastRemoteDisplayApiImpl.java @@ -0,0 +1,34 @@ +/* + * 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.cast; + +import com.google.android.gms.cast.CastRemoteDisplay; +import com.google.android.gms.cast.CastRemoteDisplayApi; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.PendingResult; + +public class CastRemoteDisplayApiImpl implements CastRemoteDisplayApi { + @Override + public PendingResult startRemoteDisplay(GoogleApiClient apiClient, String applicationId) { + return null; + } + + @Override + public PendingResult stopRemoteDisplay(GoogleApiClient apiClient) { + return null; + } +} From d0f7742d21870f9262486f49c3fea3938f325af0 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Fri, 2 Oct 2015 03:36:08 +0200 Subject: [PATCH 34/65] Add Travis CI --- .travis.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..1b3be818 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,17 @@ +language: android +git: + submodules: false +before_install: + - git submodule update --init --recursive +script: + - export JAVA_OPTS="-XX:MaxPermSize=1024m -XX:+CMSClassUnloadingEnabled -XX:+HeapDumpOnOutOfMemoryError -Xmx2048m" + - export TERM=dumb + - echo sdk.dir $ANDROID_HOME > local.properties + - ./gradlew assembleDebug -x lint +android: + components: + - extra-android-m2repository + - build-tools-23.0.1 + - android-23 + + From 99d95d5b729c958102978e0b0e7d96332b49c82f Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Fri, 2 Oct 2015 03:38:07 +0200 Subject: [PATCH 35/65] Add Gradle Wrapper --- .gitignore | 2 - gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 49896 bytes gradle/wrapper/gradle-wrapper.properties | 22 +++ gradlew | 164 +++++++++++++++++++++++ 4 files changed, 186 insertions(+), 2 deletions(-) create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100755 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew diff --git a/.gitignore b/.gitignore index 769ee68c..bec3b98b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,3 @@ build/ *.iml local.properties .gradle/ -gradle/ -gradlew diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..8c0fb64a8698b08ecc4158d828ca593c4928e9dd GIT binary patch literal 49896 zcmagFb986H(k`5d^NVfUwr$(C?M#x1ZQHiZiEVpg+jrjgoQrerx!>1o_ul)D>ebz~ zs=Mmxr&>W81QY-S1PKWQ%N-;H^tS;2*XwVA`dej1RRn1z<;3VgfE4~kaG`A%QSPsR z#ovnZe+tS9%1MfeDyz`RirvdjPRK~p(#^q2(^5@O&NM19EHdvN-A&StN>0g6QA^VN z0Gx%Gq#PD$QMRFzmK+utjS^Y1F0e8&u&^=w5K<;4Rz|i3A=o|IKLY+g`iK6vfr9?+ z-`>gmU&i?FGSL5&F?TXFu`&Js6h;15QFkXp2M1H9|Eq~bpov-GU(uz%mH0n55wUl- zv#~ccAz`F5wlQ>e_KlJS3@{)B?^v*EQM=IxLa&76^y51a((wq|2-`qON>+4dLc{Oo z51}}o^Zen(oAjxDK7b++9_Yg`67p$bPo3~BCpGM7uAWmvIhWc5Gi+gQZ|Pwa-Gll@<1xmcPy z|NZmu6m)g5Ftu~BG&Xdxclw7Cij{xbBMBn-LMII#Slp`AElb&2^Hw+w>(3crLH!;I zN+Vk$D+wP1#^!MDCiad@vM>H#6+`Ct#~6VHL4lzmy;lSdk>`z6)=>Wh15Q2)dQtGqvn0vJU@+(B5{MUc*qs4!T+V=q=wy)<6$~ z!G>e_4dN@lGeF_$q9`Ju6Ncb*x?O7=l{anm7Eahuj_6lA{*#Gv*TaJclevPVbbVYu z(NY?5q+xxbO6%g1xF0r@Ix8fJ~u)VRUp`S%&rN$&e!Od`~s+64J z5*)*WSi*i{k%JjMSIN#X;jC{HG$-^iX+5f5BGOIHWAl*%15Z#!xntpk($-EGKCzKa zT7{siZ9;4TICsWQ$pu&wKZQTCvpI$Xvzwxoi+XkkpeE&&kFb!B?h2hi%^YlXt|-@5 zHJ~%AN!g_^tmn1?HSm^|gCE#!GRtK2(L{9pL#hp0xh zME}|DB>(5)`iE7CM)&_+S}-Bslc#@B5W4_+k4Cp$l>iVyg$KP>CN?SVGZ(&02>iZK zB<^HP$g$Lq*L$BWd?2(F?-MUbNWTJVQdW7$#8a|k_30#vHAD1Z{c#p;bETk0VnU5A zBgLe2HFJ3032$G<`m*OB!KM$*sdM20jm)It5OSru@tXpK5LT>#8)N!*skNu1$TpIw zufjjdp#lyH5bZ%|Iuo|iu9vG1HrIVWLH>278xo>aVBkPN3V$~!=KnlXQ4eDqS7%E% zQ!z^$Q$b^6Q)g#cLpwur(|<0gWHo6A6jc;n`t(V9T;LzTAU{IAu*uEQ%Ort1k+Kn+f_N`9|bxYC+~Z1 zCC1UCWv*Orx$_@ydv9mIe(liLfOr7mhbV@tKw{6)q^1DH1nmvZ0cj215R<~&I<4S| zgnr;9Cdjqpz#o8i0CQjtl`}{c*P)aSdH|abxGdrR)-3z+02-eX(k*B)Uqv6~^nh** z zGh0A%o~bd$iYvP!egRY{hObDIvy_vXAOkeTgl5o!33m!l4VLm@<-FwT0+k|yl~vUh z@RFcL4=b(QQQmwQ;>FS_e96dyIU`jmR%&&Amxcb8^&?wvpK{_V_IbmqHh);$hBa~S z;^ph!k~noKv{`Ix7Hi&;Hq%y3wpqUsYO%HhI3Oe~HPmjnSTEasoU;Q_UfYbzd?Vv@ zD6ztDG|W|%xq)xqSx%bU1f>fF#;p9g=Hnjph>Pp$ZHaHS@-DkHw#H&vb1gARf4A*zm3Z75QQ6l( z=-MPMjish$J$0I49EEg^Ykw8IqSY`XkCP&TC?!7zmO`ILgJ9R{56s-ZY$f> zU9GwXt`(^0LGOD9@WoNFK0owGKDC1)QACY_r#@IuE2<`tep4B#I^(PRQ_-Fw(5nws zpkX=rVeVXzR;+%UzoNa;jjx<&@ABmU5X926KsQsz40o*{@47S2 z)p9z@lt=9?A2~!G*QqJWYT5z^CTeckRwhSWiC3h8PQ0M9R}_#QC+lz>`?kgy2DZio zz&2Ozo=yTXVf-?&E;_t`qY{Oy>?+7+I= zWl!tZM_YCLmGXY1nKbIHc;*Mag{Nzx-#yA{ zTATrWj;Nn;NWm6_1#0zy9SQiQV=38f(`DRgD|RxwggL(!^`}lcDTuL4RtLB2F5)lt z=mNMJN|1gcui=?#{NfL{r^nQY+_|N|6Gp5L^vRgt5&tZjSRIk{_*y<3^NrX6PTkze zD|*8!08ZVN)-72TA4Wo3B=+Rg1sc>SX9*X>a!rR~ntLVYeWF5MrLl zA&1L8oli@9ERY|geFokJq^O$2hEpVpIW8G>PPH0;=|7|#AQChL2Hz)4XtpAk zNrN2@Ju^8y&42HCvGddK3)r8FM?oM!3oeQ??bjoYjl$2^3|T7~s}_^835Q(&b>~3} z2kybqM_%CIKk1KSOuXDo@Y=OG2o!SL{Eb4H0-QCc+BwE8x6{rq9j$6EQUYK5a7JL! z`#NqLkDC^u0$R1Wh@%&;yj?39HRipTeiy6#+?5OF%pWyN{0+dVIf*7@T&}{v%_aC8 zCCD1xJ+^*uRsDT%lLxEUuiFqSnBZu`0yIFSv*ajhO^DNoi35o1**16bg1JB z{jl8@msjlAn3`qW{1^SIklxN^q#w|#gqFgkAZ4xtaoJN*u z{YUf|`W)RJfq)@6F&LfUxoMQz%@3SuEJHU;-YXb7a$%W=2RWu5;j44cMjC0oYy|1! zed@H>VQ!7=f~DVYkWT0nfQfAp*<@FZh{^;wmhr|K(D)i?fq9r2FEIatP=^0(s{f8GBn<8T zVz_@sKhbLE&d91L-?o`13zv6PNeK}O5dv>f{-`!ms#4U+JtPV=fgQ5;iNPl9Hf&9( zsJSm5iXIqN7|;I5M08MjUJ{J2@M3 zYN9ft?xIjx&{$K_>S%;Wfwf9N>#|ArVF^shFb9vS)v9Gm00m_%^wcLxe;gIx$7^xR zz$-JDB|>2tnGG@Rrt@R>O40AreXSU|kB3Bm)NILHlrcQ&jak^+~b`)2;otjI(n8A_X~kvp4N$+4|{8IIIv zw*(i}tt+)Kife9&xo-TyoPffGYe;D0a%!Uk(Nd^m?SvaF-gdAz4~-DTm3|Qzf%Pfd zC&tA;D2b4F@d23KV)Csxg6fyOD2>pLy#n+rU&KaQU*txfUj&D3aryVj!Lnz*;xHvl zzo}=X>kl0mBeSRXoZ^SeF94hlCU*cg+b}8p#>JZvWj8gh#66A0ODJ`AX>rubFqbBw z-WR3Z5`33S;7D5J8nq%Z^JqvZj^l)wZUX#7^q&*R+XVPln{wtnJ~;_WQzO{BIFV55 zLRuAKXu+A|7*2L*<_P${>0VdVjlC|n^@lRi}r?wnzQQm z3&h~C3!4C`w<92{?Dpea@5nLP2RJrxvCCBh%Tjobl2FupWZfayq_U$Q@L%$uEB6#X zrm_1TZA8FEtkd`tg)a_jaqnv3BC_O*AUq-*RNLOT)$>2D!r>FZdH&$x5G_FiAPaw4 zgK*7>(qd6R?+M3s@h>Z|H%7eGPxJWn_U$w`fb(Mp+_IK2Kj37YT#Xe5e6KS-_~mW} z`NXEovDJh7n!#q4b+=ne<7uB7Y2(TAR<3@PS&o3P$h#cZ-xF$~JiH6_gsv9v(#ehK zhSB_#AI%lF#+!MB5DMUN+Zhf}=t~{B|Fn{rGM?dOaSvX!D{oGXfS*%~g`W84JJAy4 zMdS?9Bb$vx?`91$J`pD-MGCTHNxU+SxLg&QY+*b_pk0R=A`F}jw$pN*BNM8`6Y=cm zgRh#vab$N$0=XjH6vMyTHQg*+1~gwOO9yhnzZx#e!1H#|Mr<`jJGetsM;$TnciSPJ z5I-R0)$)0r8ABy-2y&`2$33xx#%1mp+@1Vr|q_e=#t7YjjWXH#3F|Fu<G#+-tE2K7 zOJkYxNa74@UT_K4CyJ%mR9Yfa$l=z}lB(6)tZ1Ksp2bv$^OUn3Oed@=Q0M}imYTwX zQoO^_H7SKzf_#kPgKcs%r4BFUyAK9MzfYReHCd=l)YJEgPKq-^z3C%4lq%{&8c{2CGQ3jo!iD|wSEhZ# zjJoH87Rt{4*M_1GdBnBU3trC*hn@KCFABd=Zu`hK;@!TW`hp~;4Aac@24m|GI)Ula z4y%}ClnEu;AL4XVQ6^*!()W#P>BYC@K5mw7c4X|Hk^(mS9ZtfMsVLoPIiwI?w_X0- z#vyiV5q9(xq~fS`_FiUZw->8Awktga>2SrWyvZ|h@LVFtnY#T z%OX30{yiSov4!43kFd(8)cPRMyrN z={af_ONd;m=`^wc7lL|b7V!;zmCI}&8qz=?-6t=uOV;X>G{8pAwf9UJ`Hm=ubIbgR zs6bw3pFeQHL`1P1m5fP~fL*s?rX_|8%tB`Phrij^Nkj{o0oCo*g|ELexQU+2gt66=7}w5A+Qr}mHXC%)(ODT# zK#XTuzqOmMsO~*wgoYjDcy)P7G`5x7mYVB?DOXV^D3nN89P#?cp?A~c%c$#;+|10O z8z(C>mwk#A*LDlpv2~JXY_y_OLZ*Mt)>@gqKf-Ym+cZ{8d%+!1xNm3_xMygTp-!A5 zUTpYFd=!lz&4IFq)Ni7kxLYWhd0o2)ngenV-QP@VCu;147_Lo9f~=+=Nw$6=xyZzp zn7zAe41Sac>O60(dgwPd5a^umFVSH;<7vN>o;}YlMYhBZFZ}-sz`P^3oAI>SCZy&zUtwKSewH;CYysPQN7H>&m215&e2J? zY}>5N-LhaDeRF~C0cB>M z7@y&xh9q??*EIKnh*;1)n-WuSl6HkrI?OUiS^lx$Sr2C-jUm6zhd{nd(>#O8k9*kF zPom7-%w1NjFpj7WP=^!>Vx^6SG^r`r+M&s7V(uh~!T7aE;_ubqNSy)<5(Vi)-^Mp9 zEH@8Vs-+FEeJK%M0z3FzqjkXz$n~BzrtjQv`LagAMo>=?dO8-(af?k@UpL5J#;18~ zHCnWuB(m6G6a2gDq2s`^^5km@A3Rqg-oHZ68v5NqVc zHX_Iw!OOMhzS=gfR7k;K1gkEwuFs|MYTeNhc0js>Wo#^=wX4T<`p zR2$8p6%A9ZTac;OvA4u#Oe3(OUep%&QgqpR8-&{0gjRE()!Ikc?ClygFmGa(7Z^9X zWzmV0$<8Uh)#qaH1`2YCV4Zu6@~*c*bhtHXw~1I6q4I>{92Eq+ZS@_nSQU43bZyidk@hd$j-_iL=^^2CwPcaXnBP;s;b zA4C!k+~rg4U)}=bZ2q*)c4BZ#a&o!uJo*6hK3JRBhOOUQ6fQI;dU#3v>_#yi62&Sp z-%9JJxwIfQ`@w(_qH0J0z~(lbh`P zHoyp2?Oppx^WXwD<~20v!lYm~n53G1w*Ej z9^B*j@lrd>XGW43ff)F;5k|HnGGRu=wmZG9c~#%vDWQHlOIA9(;&TBr#yza{(?k0> zcGF&nOI}JhuPl`kLViBEd)~p2nY9QLdX42u9C~EUWsl-@CE;05y@^V1^wM$ z&zemD1oZd$Z))kEw9)_Mf+X#nT?}n({(+aXHK2S@j$MDsdrw-iLb?#r{?Vud?I5+I zVQ8U?LXsQ}8-)JBGaoawyOsTTK_f8~gFFJ&lhDLs8@Rw$ey-wr&eqSEU^~1jtHmz6 z!D2g4Yh?3VE*W8=*r&G`?u?M~AdO;uTRPfE(@=Gkg z7gh=EGu!6VJJ?S_>|5ZwY?dGFBp3B9m4J1=7u=HcGjsCW+y6`W?OWxfH?S#X8&Zk& zvz6tWcnaS1@~3FTH}q_*$)AjYA_j;yl0H0{I(CW7Rq|;5Q2>Ngd(tmJDp+~qHe_8y zPU_fiCrn!SJ3x&>o6;WDnjUVEt`2fhc9+uLI>99(l$(>Tzwpbh>O775OA5i`jaBdp zXnCwUgomyF3K$0tXzgQhSAc!6nhyRh_$fP}Rd$|*Y7?ah(JrN=I7+)+Hp4BLJJ2P~ zFD!)H^uR2*m7GQZpLUVS#R3^?2wCd}(gcFcz!u5KN9ldNJdh@%onf06z9m~T0n;dqg6@?>G@S|rPO*Kj>{su+R|7bH>osA&uD4eqxtr**k($ii`uO? z7-&VkiL4Rp3S&e+T}2Z#;NtWHZco(v8O3QMvN0g7l8GV|U2>x-DbamkZo5)bjaSFR zr~Y9(EvF9{o*@|nBPj+e5o$_K`%TH1hD=|its}|qS^o6EQu_gOuDUH=Dtzik;P7G$ zq%_T<>9O}bGIB?;IQ*H`BJ5NWF6+XLv@G7aZwcy(&BoepG~u`aIcG>y+;J7+L=wTZ zB=%n@O}=+mjBO%1lMo6C0@1*+mhBqqY((%QMUBhyeC~r*5WVqzisOXFncr*5Lr0q6 zyPU&NOV}Vt2jl>&yig4I6j93?D>Ft=keRh=Y;3*^Z-I26nkZ#Jj5OJ89_?@#9lNjp z#gfAO6i937)~I|98P%xAWxwmk(F&@lTMx63*FZ~2b{NHU+}EV8+kMAB0bM*Zn#&7ubt98!PT^ZcMOfwMgkYz6+;?CKbvV zQ}Z@s_3JcMPhF&y1?}9uZFIBiPR3g7lf=+XEr9Bl%zRfGcaKb*ZQq5b35ZkR@=JEw zP#iqgh2^#@VA-h)>r`7R-$1_ddGr&oWWV$rx;pkG0Yohp9p@In_p)hKvMo@qIv zcN2t{23&^Nj=Y&gX;*vJ;kjM zHE2`jtjVRRn;=WqVAY&m$z=IoKa{>DgJ;To@OPqNbh=#jiS$WE+O4TZIOv?niWs47 zQfRBG&WGmU~>2O{}h17wXGEnigSIhCkg%N~|e?hG8a- zG!Wv&NMu5z!*80>;c^G9h3n#e>SBt5JpCm0o-03o2u=@v^n+#6Q^r#96J5Q=Dd=>s z(n0{v%yj)=j_Je2`DoyT#yykulwTB+@ejCB{dA7VUnG>4`oE?GFV4sx$5;%9&}yxfz<-wWk|IlA|g&! zN_Emw#w*2GT=f95(%Y1#Viop;Yro3SqUrW~2`Fl?Ten{jAt==a>hx$0$zXN`^7>V_ zG*o7iqeZV)txtHUU2#SDTyU#@paP;_yxp!SAG##cB= zr@LoQg4f~Uy5QM++W`WlbNrDa*U;54`3$T;^YVNSHX4?%z|`B~i7W+kl0wBB`8|(l zAyI6dXL&-Sei0=f#P^m`z=JJ`=W;PPX18HF;5AaB%Zlze`#pz;t#7Bzq0;k8IyvdK=R zBW+4GhjOv+oNq^~#!5(+pDz)Ku{u60bVjyym8Or8L;iqR|qTcxEKTRm^Y%QjFYU=ab+^a|!{!hYc+= z%Qc02=prKpzD+jiiOwzyb(dELO|-iyWzizeLugO!<1(j|3cbR!8Ty1$C|l@cWoi?v zLe<5+(Z-eH++=fX**O-I8^ceYZgiA!!dH+7zfoP-Q+@$>;ab&~cLFg!uOUX7h0r== z`@*QP9tnV1cu1!9pHc43C!{3?-GUBJEzI(&#~vY9MEUcRNR*61)mo!RG>_Yb^rNN7 zR9^bI45V?3Lq`^^BMD!GONuO4NH#v9OP3@s%6*Ha3#S*;f z6JEi)qW#Iq#5BtIXT9Gby|H?NJG}DN#Li82kZ_Rt1=T0Z@U6OAdyf}4OD|Sk^2%-1 zzgvqZ@b6~kL!^sZLO$r{s!3fQ5bHW}8r$uTVS*iw1u8^9{YlPp_^Xm5IN zF|@)ZOReX zB*#tEbWEX~@f)ST|s$oUKS@drycE1tYtdJ9b*(uFTxNZ{n3BI*kF7wXgT6+@PI@vwH7iQS{1T!Nauk>fm8gOLe`->Pi~ z8)3=UL_$OLl2n7QZlHt846nkYFu4V};3LpYA%5VaF#a2#d2g0&ZO~3WA%1XlerVpg zCAlM;(9OqH@`(>Tha{*@R%twB!}1ng4V=^+R`Q{#fkRk)C|suozf-uCXrkIH2SC^C z6wlxR`yS;-U#uu#`OnD%U<41%C4mp>LYLPIbgVO~WsT1if)Y)T*8nUB`2*(B;U_ha1NWv2`GqrZ z3MWWpT3tZ!*N@d*!j3=@K4>X*gX4A^@QPAz24?7u90AXaLiFq=Z$|5p$Ok2|YCX_Z zFgNPiY2r_Bg2BQE!0z=_N*G?%0cNITmAru*!Mws=F+F&Qw!&1?DBN{vSy%IvGRV@1 zS->PARgL^XS!-aZj zi@`~LhWfD!H-L0kNv=Jil9zR0>jZLqu)cLq?$yXVyk%EteKcWbe^qh#spHJPa#?92 za(N(Kw0se^$7nQUQZBet;C_Dj5(2_?TdrXFYwmebq}YGQbN5Ex7M zGSCX~Ey;5AqAzEDNr%p^!cuG?&wIeY&Bm5guVg>8F=!nT%7QZTGR(uGM&IZuMw0V_ zhPiIFWm?H?aw*(v6#uVT@NEzi2h5I$cZ-n0~m$tmwdMTjG*of^Y%1 zW?Y%o*-_iMqEJhXo^!Qo?tGFUn1Mb|urN4_;a)9bila2}5rBS#hZ5wV+t1xbyF1TW zj+~cdjbcMgY$zTOq6;ODaxzNA@PZIXX(-=cT8DBd;9ihfqqtbDr9#gXGtK24BPxjZ z9+Xp>W1(s)->-}VX~BoQv$I|-CBdO`gULrvNL>;@*HvTdh@wyNf}~IB5mFnTitX2i z;>W>tlQyc2)T4Mq+f!(i3#KuK-I8Kj3Wm(UYx?KWWt8DEPR_Jdb9CE~Fjc7Rkh#gh zowNv()KRO@##-C+ig0l!^*ol!Bj%d32_N*~d!|&>{t!k3lc?6VrdlCCb1?qyoR42m zv;4KdwCgvMT*{?tJKa(T?cl|b;k4P>c&O@~g71K5@}ys$)?}WSxD;<5%4wEz7h=+q ztLumn6>leWdDk#*@{=v9p)MsvuJMyf_VEs;pJh?i3z7_W@Q|3p$a}P@MQ-NpMtDUBgH!h4Ia#L&POr4Qw0Tqdw^}gCmQAB z8Dgkzn?V!_@04(cx0~-pqJOpeP1_}@Ml3pCb45EJoghLows9ET13J8kt0;m$6-jO( z4F|p+JFD1NT%4bpn4?&)d+~<360$z5on`eS6{H`S>t`VS$>(D`#mC*XK6zULj1Da# zpV$gw$2Ui{07NiYJQQNK;rOepRxA>soNK~B2;>z;{Ovx`k}(dlOHHuNHfeR}7tmIp zcM}q4*Fq8vSNJYi@4-;}`@bC?nrUy`3jR%HXhs79qWI5;hyTpH5%n-NcKu&j(aGwT z1~{geeq?Jd>>HL+?2`0K8dB2pvTS=LO~tb~vx_<=iN8^rW!y@~lBTAaxHmvVQJSeJ z!cb9ffMdP1lgI=>QJN{XpM4{reRrdIt|v|0-8!p}M*Qw^uV1@Ho-YsNd0!a(os$F* zT0tGHA#0%u0j*%S>kL*73@~7|iP;;!JbWSTA@`#VHv_l_%Z7CgX@>dhg_ zgn0|U)SY~U-E5{QiT@(uPp#1jaz!(_3^Cbz2 z4ZgWWz=PdGCiGznk{^4TBfx_;ZjAHQ>dB4YI}zfEnTbf60lR%=@VWt0yc=fd38Ig* z)Q38#e9^+tA7K}IDG5Z~>JE?J+n%0_-|i2{E*$jb4h?|_^$HRHjVkiyX6@Y+)0C2a zA+eegpT1dUpqQFIwx;!ayQcWQBQTj1n5&h<%Lggt@&tE19Rm~Rijtqw6nmYip_xg0 zO_IYpU304embcWP+**H|Z5~%R*mqq+y{KbTVqugkb)JFSgjVljsR{-c>u+{?moCCl zTL)?85;LXk0HIDC3v*|bB-r_z%zvL6Dp__L*A~Z*o?$rm>cYux&)W=6#+Cb}TF&Kd zdCgz3(ZrNA>-V>$C{a^Y^2F!l_%3lFe$s(IOfLBLEJ4Mcd!y&Ah9r)7q?oc z5L(+S8{AhZ)@3bw0*8(}Xw{94Vmz6FrK&VFrJN;xB96QmqYEibFz|yHgUluA-=+yS}I-+#_Pk zN67-#8W(R^e7f!;i0tXbJgMmJZH%yEwn*-}5ew13D<_FYWnt?{Mv1+MI~u;FN~?~m z{hUnlD1|RkN}c1HQ6l@^WYbHAXPJ^m0te1woe;LDJ}XEJqh1tPf=sD0%b+OuR1aCoP>I>GBn4C24Zu$D)qg=gq;D??5 zUSj%;-Hvk_ffj-+SI{ZCp`gZcNu=L@_N}kCcs?TyMr-37fhy$?a<7lt1`fZw<%$8@B6(Wgo!#!z9z{ab|x`+&;kP!(gfdY}A-GP&4Cbh-S< z1(kmgnMyB2z3ipEj5;4<{(=&<7a>A_Jl`ujUKYV@%k(oD=cD7W@8~5O=R*zdjM_y; zXwme~0wo0aDa~9rDnjF=B}Bbj|DHRQjN|?@(F^=bVFdr!#mwr|c0843k>%~5J|7|v zSY=T)iPU6rEAwrM(xTZwPio%D4y9Z4kL0bMLKvu4yd)0ZJA3<;>a2q~rEfcREn}~1 zCJ~3c?Afvx?3^@+!lnf(kB6YwfsJ*u^y7kZA?VmM%nBmaMspWu?WXq4)jQsq`9EbT zlF2zJ)wXuAF*2u|yd5hNrG>~|i}R&ZyeetTQ!?Hz6xGZZb3W6|vR>Hq=}*m=V=Lsp zUOMxh;ZfP4za~C{Ppn^%rhitvpnu^G{Z#o-r?TdEgSbtK_+~_iD49xM;$}X*mJF02|WBL{SDqK9}p4N!G$3m=x#@T+4QcapM{4j|Q zwO!(hldpuSW#by!zHEP@tzIC|KdD z%BJzQ7Ho1(HemWm`Z8m_D#*`PZ-(R%sZmPrS$aHS#WPjH3EDitxN|DY+ zYC|3S?PQ3NNYau$Qk8f>{w}~xCX;;CE=7;Kp4^xXR8#&^L+y-jep7oO^wnQ840tg1 zuN17QKsfdqZPlB8OzwF+)q#IsmenEmIbRAJHJ$JjxzawKpk8^sBm3iy=*kB%LppNb zhSdk`^n?01FKQ;=iU+McN7Mk0^`KE>mMe1CQ2a_R26_}^$bogFm=2vqJake7x)KN( zYz;gRPL+r4*KD>1U+DU+1jh{mT8#P#(z9^(aDljpeN{mRmx{AZX&hXKXNuxj3x*RrpjvOaZ#`1EqK!$+8=0yv8}=;>f=E?5tGbRUd4%?QL zy$kq6mZeF%k6E1&8nwAYMd!-lRkhQTob$7s`*XqcHs;l~mHV}fx&0I&i!CHaPVSM{ zHdRh7a>hP)t@YTrWm9y zl-ENWSVzlKVvTdWK>)enmGCEw(WYS=FtY{srdE{Z(3~4svwd)ct;`6Y{^qiW+9E@A ztzd?lj5F#k`=E1U-n*1JJc0{x{0q!_tkD<_S6bGsW)^RxGu%Rj^Mvw|R0WP1SqvAI zs(MiAd@Y5x!UKu376&|quQNxir;{Iz(+}3k-GNb29HaQh?K30u=6sXpIc?j0hF{VY zM$Do*>pN)eRljAOgpx7fMfSrnZ7>fi@@>Jh;qxj1#-Vj}JC3E^GCbC(r55_AG>6cq z4ru34FtVuBt)bkX4>ZFWjToyu)VA>IE6hXc+^(3ruUaKRqHnx3z)(GXetm;^0D95s zQ&drwfjhM4*|q=;i5Io0eDf?I{p}qo@7i7abHX5qLu~VDwYf4bmV~-^M_U?DL(+cG z{AyE^a|*73Ft)o5k-p)+GLXj#q01VlJ9#ZJkf|+c%6qfRgVp&6NsU3~F?!uh}HJm73xq>v$h zYoW3wJE6n9P|;{8U<^%UE2wjR4x^G_Nc$J(i)!>;g4`CCh2z^Dth#ah#<`#axDR?F z4>~hnN2%B2ZUuU6j>m1Qjj~5jQSdA&Q#7hOky#=Ue)}7LPJ!8nbZO_0Sw{G>>M7&E zb1dy|0Zi$(ubk`4^XkVI%4WIpe?Bh!D~IjvZs14yHw=aQ8-`N-=P*?Kzi&eRGZ_6Z zT>eis`!Dy3eT3=vt#Lbc+;}i5XJf7zM3QneL{t?w=U<1rk7+z2Cu^|~=~54tAeSYF zsXHsU;nM0dpK>+71yo(NFLV-^Lf7%U?Q$*q{^j04Gl71ya2)^j`nmJ$cmI9eFMjp+ z#)jKmi4lZc<;l>!={@jTm%?!5jS;6;c*Ml55~r6Y?22B^K3bPhKQ(ICc&z%w<4W1= zjTTtz_}IA$%kCqU)h#$!Yq>>2mVG}qYL}!avmCWYV}x4!YEeq)pgTp| zR;+skHuc7YXRLrcbYXt>?@pa{l^2pL>RrZ!22zMmi1ZR?nkaWF*`@XFK4jGh&Em3vn(l z3~^Q9&tM^eV=f^lccCUc9v02z%^n5VV6s$~k0uq5B#Ipd6`M1Kptg^v<2jiNdlAWQ z_MmtNEaeYIHaiuaFQdG&df7miiB5lZkSbg&kxY*Eh|KTW`Tk~VwKC~+-GoYE+pvwc{+nIEizq6!xP>7ZQ(S2%48l$Y98L zvs7s<&0ArXqOb*GdLH0>Yq-f!{I~e~Z@FUIPm?jzqFZvz9VeZLYNGO}>Vh<=!Er7W zS!X6RF^et7)IM1pq57z*^hP5w7HKSDd8jHX!*gkKrGc-GssrNu5H%7-cNE{h$!aEQK3g*qy;= z)}pxO8;}nLVYm_24@iEs8)R7i;Th0n4->&$8m6(LKCRd(yn7KY%QHu_f=*#e`H^U( z{u!`9JaRD?Z?23fEXrjx>A@+a!y-_oaDB)o@2s{2%A97-ctFfrN0cXQ@6aGH`X~Nr z144?qk;MzDU-cgQOLfT3-ZR#hKmYtKG*iGf4ZJ`|`9!^SkBDUUSJCba)>mM!)k~(z zdjUqB`)~!UObMHB1b$UItM$<0kwlqHH;c z=)+~bkOcIT7vI0Iy(wD)vsg9|oi##%Rgrq`Ek;pN)}lbpz`iv{F4K*{ZZ?Zjixxxr zY|SPl2NsXH+5pimj+MvbZ_+HrfvdC13|9Zs)Y=nW$z<0mhl}%irBSm5T3ZrN#2AhY z_ZrTmS(L`U#y}VZ@~QL9wUS6AnU*7LWS02Xyz`b>%rTml#Wb0yr>@c(Ym*40g;P{V zjV1XSHdU>oY!&Jh7MzhzUV8(9E+yl5UJYga>=0Ldjwtc`5!1>LxaB-kVW;IlSPs+0 zUBx=m8OKVp<`frNvMK>WMO(iKY%PuvqD+PK*vP6f?_o!O)MCW5Ic zv(%f5PLHyOJ2h@Yn_to@54Yq;fdoy40&sbe3A$4uUXHsHP_~K}h#)p&TyOx(~JE?y(IBAQKl}~VQjVC-c6oZwmESL;`Xth?2)-b6ImNcJi z;w|`Q*k?`L(+Dp}t(FocvzWB(%~9$EAB6_J6CrA}hMj-Vy*6iA$FdV}!lvk%6}M)4 zTf<)EbXr9^hveAav1yA?>O0aNEpv0&rju{(Gt|dP=AP%)uQm~OE7@+wEhILrRLt&E zoEsF^nz>4yK1|EOU*kM+9317S;+bb7?TJM2UUpc!%sDp}7!<`i=W!ot8*C&fpj>mk#qt~GCeqcy)?W6sl>eUnR%yCBR&Ow-rc|q;lhnI+f-%`6Xf)% zIYZru;27%vA{Qi2=J`PQC<28;tFx(V^sgXf>)8WNxxQwT14M9I6- z+V0@tiCiDkv`7r-06sJS8@s|Lf>mV+8h}SPT4ZGPSMaFK7_SMXH$3KN7b2V?iV-jA zh1!Z>2tv^HVbHnNUAf-wQW#zMV(h8=3x2Swd|-%AczEIWLcm~EAu7rc3s%56b;7ME zj}$pe#fc^314Mb9i)xH^_#({)tTD4hsoz!7XcHUh9*G|}?k=D?9LBkTm2?fgaIG(%%$DL#}a-_990rQBU+M;jrf zCcvgM`+oyZmsUqc?lly9axZfO)02l$TMS#I+jHYY`Uk!gtDv|@GBQ||uaG^n*QR3Q z@tV?D;R;KmkxSDQh<2DkDC1?m?jTvf2i^T;+}aYhzL?ymNZmdns2e)}2V>tDCRw{= zTV3q3ZQDkdZQHi3?y{@8Y@1!SZQHi(y7|qSx$~Vl=iX<2`@y3eSYpsBV zI`Q-6;)B=p(ZbX55C*pu1C&yqS|@Pytis3$VDux0kxKK}2tO&GC;cH~759o?W2V)2 z)`;U(nCHBE!-maQz%z#zoRNpJR+GmJ!3N^@cA>0EGg?OtgM_h|j1X=!4N%!`g~%hdI3%yz&wq4rYChPIGnSg{H%i>96! z-(@qsCOfnz7ozXoUXzfzDmr>gg$5Z1DK$z#;wn9nnfJhy6T5-oi9fT^_CY%VrL?l} zGvnrMZP_P|XC$*}{V}b^|Hc38YaZQESOWqA1|tiXKtIxxiQ%Zthz?_wfx@<8I{XUW z+LH%eO9RxR_)8gia6-1>ZjZB2(=`?uuX|MkX082Dz*=ep%hMwK$TVTyr2*|gDy&QOWu zorR#*(SDS{S|DzOU$<-I#JTKxj#@0(__e&GRz4NuZZLUS8}$w+$QBgWMMaKge*2-) zrm62RUyB?YSUCWTiP_j-thgG>#(ZEN+~bMuqT~i3;Ri`l${s0OCvCM>sqtIX?Cy`8 zm)MRz-s^YOw>9`aR#J^tJz6$S-et%elmR2iuSqMd(gr6a#gA_+=N(I6%Cc+-mg$?_1>PlK zbgD2`hLZ?z4S~uhJf=rraLBL?H#c$cXyqt{u^?#2vX2sFb z^EU-9jmp{IZ~^ii@+7ogf!n_QawvItcLiC}w^$~vgEi(mX79UwDdBg`IlF42E5lWE zbSibqoIx*0>WWMT{Z_NadHkSg8{YW4*mZ@6!>VP>ey}2PuGwo%>W7FwVv7R!OD32n zW6ArEJX8g_aIxkbBl^YeTy5mhl1kFGI#n>%3hI>b(^`1uh}2+>kKJh0NUC|1&(l)D zh3Barl&yHRG+Le2#~u>KoY-#GSF>v)>xsEp%zgpq4;V6upzm3>V&yk^AD}uIF{vIn zRN-^d4(Sk6ioqcK@EObsAi#Z-u&Hh#kZdv1rjm4u=$2QF<6$mgJ4BE0yefFI zT7HWn?f668n!;x>!CrbdA~lDfjX?)315k1fMR~lG)|X_o()w|NX&iYUTKxI2TLl|r z{&TWcBxP>*;|XSZ1GkL&lSg?XL9rR4Ub&4&03kf};+6$F)%2rsI%9W_i_P|P%Z^b@ zDHH2LV*jB@Izq0~E4F^j04+C|SFiV8{!bth%bz(KfCg42^ zGz5P7xor$)I4VX}Cf6|DqZ$-hG7(}91tg#AknfMLFozF1-R~KS3&5I0GNb`P1+hIB z?OPmW8md3RB6v#N{4S5jm@$WTT{Sg{rVEs*)vA^CQLx?XrMKM@*gcB3mk@j#l0(~2 z9I=(Xh8)bcR(@8=&9sl1C?1}w(z+FA2`Z^NXw1t(!rpYH3(gf7&m=mm3+-sls8vRq z#E(Os4ZNSDdxRo&`NiRpo)Ai|7^GziBL6s@;1DZqlN@P_rfv4Ce1={V2BI~@(;N`A zMqjHDayBZ);7{j>)-eo~ZwBHz0eMGRu`43F`@I0g!%s~ANs>Vum~RicKT1sUXnL=gOG zDR`d=#>s?m+Af1fiaxYxSx{c5@u%@gvoHf#s6g>u57#@#a2~fNvb%uTYPfBoT_$~a^w96(}#d;-wELAoaiZCbM zxY4fKlS6-l1!b1!yra|`LOQoJB))=CxUAYqFcTDThhA?d}6FD$gYlk**!# zD=!KW>>tg1EtmSejwz{usaTPgyQm~o+NDg`MvNo)*2eWX*qAQ)4_I?Pl__?+UL>zU zvoT(dQ)pe9z1y}qa^fi-NawtuXXM>*o6Al~8~$6e>l*vX)3pB_2NFKR#2f&zqbDp7 z5aGX%gMYRH3R1Q3LS91k6-#2tzadzwbwGd{Z~z+fBD5iJ6bz4o1Rj#7cBL|x8k%jO z{cW0%iYUcCODdCIB(++gAsK(^OkY5tbWY;)>IeTp{{d~Y#hpaDa-5r#&Ha?+G{tn~ zb(#A1=WG1~q1*ReXb4CcR7gFcFK*I6Lr8bXLt9>9IybMR&%ZK15Pg4p_(v5Sya_70 ziuUYG@EBKKbKYLWbDZ)|jXpJJZ&bB|>%8bcJ7>l2>hXuf-h5Bm+ zHZ55e9(Sg>G@8a`P@3e2(YWbpKayoLQ}ar?bOh2hs89=v+ifONL~;q(d^X$7qfw=; zENCt`J*+G;dV_85dL3Tm5qz2K4m$dvUXh>H*6A@*)DSZ2og!!0GMoCPTbcd!h z@fRl3f;{F%##~e|?vw6>4VLOJXrgF2O{)k7={TiDIE=(Dq*Qy@oTM*zDr{&ElSiYM zp<=R4r36J69aTWU+R9Hfd$H5gWmJ?V){KU3!FGyE(^@i!wFjeZHzi@5dLM387u=ld zDuI1Y9aR$wW>s#I{2!yLDaVkbP0&*0Rw%6bi(LtieJQ4(1V!z!ec zxPd)Ro0iU%RP#L|_l?KE=8&DRHK>jyVOYvhGeH+Dg_E%lgA(HtS6e$v%D7I;JSA2x zJyAuin-tvpN9g7>R_VAk2y;z??3BAp?u`h-AVDA;hP#m+Ie`7qbROGh%_UTW#R8yfGp<`u zT0}L)#f%(XEE)^iXVkO8^cvjflS zqgCxM310)JQde*o>fUl#>ZVeKsgO|j#uKGi)nF_ur&_f+8#C0&TfHnfsLOL|l(2qn zzdv^wdTi|o>$q(G;+tkTKrC4rE)BY?U`NHrct*gVx&Fq2&`!3htkZEOfODxftr4Te zoseFuag=IL1Nmq45nu|G#!^@0vYG5IueVyabw#q#aMxI9byjs99WGL*y)AKSaV(zx z_`(}GNM*1y<}4H9wYYSFJyg9J)H?v((!TfFaWx(sU*fU823wPgN}sS|an>&UvI;9B(IW(V)zPBm!iHD} z#^w74Lpmu7Q-GzlVS%*T-z*?q9;ZE1rs0ART4jnba~>D}G#opcQ=0H)af6HcoRn+b z<2rB{evcd1C9+1D2J<8wZ*NxIgjZtv5GLmCgt?t)h#_#ke{c+R6mv6))J@*}Y25ef z&~LoA&qL-#o=tcfhjH{wqDJ;~-TG^?2bCf~s0k4Rr!xwz%Aef_LeAklxE=Yzv|3jf zgD0G~)e9wr@)BCjlY84wz?$NS8KC9I$wf(T&+79JjF#n?BTI)Oub%4wiOcqw+R`R_q<`dcuoF z%~hKeL&tDFFYqCY)LkC&5y(k7TTrD>35rIAx}tH4k!g9bwYVJ>Vdir4F$T*wC@$08 z9Vo*Q0>*RcvK##h>MGUhA9xix+?c1wc6xJhn)^9;@BE6i*Rl8VQdstnLOP1mq$2;!bfASHmiW7|=fA{k$rs^-8n{D6_ z!O0=_K}HvcZJLSOC6z-L^pl3Gg>8-rU#Sp1VHMqgXPE@9x&IHe;K3;!^SQLDP1Gk&szPtk| z!gP;D7|#y~yVQ?sOFiT*V(Z-}5w1H6Q_U5JM#iW16yZiFRP1Re z6d4#47#NzEm};1qRP9}1;S?AECZC5?6r)p;GIW%UGW3$tBN7WTlOy|7R1?%A<1!8Z zWcm5P6(|@=;*K&3_$9aiP>2C|H*~SEHl}qnF*32RcmCVYu#s!C?PGvhf1vgQ({MEQ z0-#j>--RMe{&5&$0wkE87$5Ic5_O3gm&0wuE-r3wCp?G1zA70H{;-u#8CM~=RwB~( zn~C`<6feUh$bdO1%&N3!qbu6nGRd5`MM1E_qrbKh-8UYp5Bn)+3H>W^BhAn;{BMii zQ6h=TvFrK)^wKK>Ii6gKj}shWFYof%+9iCj?ME4sR7F+EI)n8FL{{PKEFvB65==*@ ztYjjVTJCuAFf8I~yB-pN_PJtqH&j$`#<<`CruB zL=_u3WB~-;t3q)iNn0eU(mFTih<4nOAb>1#WtBpLi(I)^zeYIHtkMGXCMx+I zxn4BT0V=+JPzPeY=!gAL9H~Iu%!rH0-S@IcG%~=tB#6 z3?WE7GAfJ{>GE{?Cn3T!QE}GK9b*EdSJ02&x@t|}JrL{^wrM@w^&})o;&q816M5`} zv)GB;AU7`haa1_vGQ}a$!m-zkV(+M>q!vI0Swo18{;<>GYZw7-V-`G#FZ z;+`vsBihuCk1RFz1IPbPX8$W|nDk6yiU8Si40!zy{^nmv_P1=2H*j<^as01|W>BQS zU)H`NU*-*((5?rqp;kgu@+hDpJ;?p8CA1d65)bxtJikJal(bvzdGGk}O*hXz+<}J? zLcR+L2OeA7Hg4Ngrc@8htV!xzT1}8!;I6q4U&S$O9SdTrot<`XEF=(`1{T&NmQ>K7 zMhGtK9(g1p@`t)<)=eZjN8=Kn#0pC2gzXjXcadjHMc_pfV(@^3541)LC1fY~k2zn&2PdaW`RPEHoKW^(p_b=LxpW&kF?v&nzb z1`@60=JZj9zNXk(E6D5D}(@k4Oi@$e2^M%grhlEuRwVGjDDay$Qpj z`_X-Y_!4e-Y*GVgF==F0ow5MlTTAsnKR;h#b0TF>AyJe`6r|%==oiwd6xDy5ky6qQ z)}Rd0f)8xoNo)1jj59p;ChIv4Eo7z*{m2yXq6)lJrnziw9jn%Ez|A-2Xg4@1)ET2u zIX8`u5M4m=+-6?`S;?VDFJkEMf+=q?0D7?rRv)mH=gptBFJGuQo21rlIyP>%ymGWk z=PsJ>>q~i>EN~{zO0TklBIe(8i>xkd=+U@;C{SdQ`E03*KXmWm4v#DEJi_-F+3lrR z;0al0yXA&axWr)U%1VZ@(83WozZbaogIoGYpl!5vz@Tz5?u36m;N=*f0UY$ssXR!q zWj~U)qW9Q9Fg9UW?|XPnelikeqa9R^Gk77PgEyEqW$1j=P@L z*ndO!fwPeq_7J_H1Sx>#L$EO_;MfYj{lKuD8ZrUtgQLUUEhvaXA$)-<61v`C=qUhI zioV&KR#l50fn!-2VT`aMv|LycLOFPT{rRSRGTBMc)A`Cl%K&4KIgMf}G%Qpb2@cB* zw8obt-BI3q8Lab!O<#zeaz{P-lI2l`2@qrjD+Qy)^VKks5&SeT(I)i?&Kf59{F`Rw zuh7Q>SQNwqLO%cu2lzcJ7eR*3!g}U)9=EQ}js-q{d%h!wl6X3%H0Z2^8f&^H;yqti4z6TNWc& zDUU8YV(ZHA*34HHaj#C43PFZq7a>=PMmj4+?C4&l=Y-W1D#1VYvJ1~K%$&g-o*-heAgLXXIGRhU zufonwl1R<@Kc8dPKkb`i5P9VFT_NOiRA=#tM0WX2Zut)_ zLjAlJS1&nnrL8x8!o$G+*z|kmgv4DMjvfnvH)7s$X=-nQC3(eU!ioQwIkaXrl+58 z@v)uj$7>i`^#+Xu%21!F#AuX|6lD-uelN9ggShOX&ZIN+G#y5T0q+RL*(T(EP)(nP744-ML= z+Rs3|2`L4I;b=WHwvKX_AD56GU+z92_Q9D*P|HjPYa$yW0o|NO{>4B1Uvq!T;g_N- zAbNf%J0QBo1cL@iahigvWJ9~A4-glDJEK?>9*+GI6)I~UIWi>7ybj#%Po}yT6d6Li z^AGh(W{NJwz#a~Qs!IvGKjqYir%cY1+8(5lFgGvl(nhFHc7H2^A(P}yeOa_;%+bh` zcql{#E$kdu?yhRNS$iE@F8!9E5NISAlyeuOhRD)&xMf0gz^J927u5aK|P- z>B%*9vSHy?L_q)OD>4+P;^tz4T>d(rqGI7Qp@@@EQ-v9w-;n;7N05{)V4c7}&Y^!`kH3}Q z4RtMV6gAARY~y$hG7uSbU|4hRMn97Dv0$Le@1jDIq&DKy{D$FOjqw{NruxivljBGw zP4iM(4Nrz^^~;{QBD7TVrb6PB=B$<-e9!0QeE8lcZLdDeb?Gv$ePllO2jgy&FSbW* zSDjDUV^=`S(Oo0;k(Idvzh}aXkfO)F6AqB?wWqYJw-1wOn5!{-ghaHb^v|B^92LmQ9QZj zHA&X)fd%B$^+TQaM@FPXM$$DdW|Vl)4bM-#?Slb^qUX1`$Yh6Lhc4>9J$I4ba->f3 z9CeGO>T!W3w(){M{OJ+?9!MK68KovK#k9TSX#R?++W4A+N>W8nnk**6AB)e;rev=$ zN_+(?(YEX;vsZ{EkEGw%J#iJYgR8A}p+iW;c@V>Z1&K->wI>!x-+!0*pn|{f=XA7J zfjw88LeeJgs4YI?&dHkBL|PRX`ULOIZlnniTUgo-k`2O2RXx4FC76;K^|ZC6WOAEw zz~V0bZ29xe=!#Xk?*b{sjw+^8l0Koy+e7HjWXgmPa4sITz+$VP!YlJ$eyfi3^6gGx6jZLpbUzX;!Z6K}aoc!1CRi zB6Lhwt%-GMcUW;Yiy6Y7hX(2oksbsi;Z6k*=;y;1!taBcCNBXkhuVPTi+1N*z*}bf z`R=&hH*Ck5oWz>FR~>MO$3dbDSJ!y|wrff-H$y(5KadrA_PR|rR>jS=*9&J*ykWLr z-1Z^QOxE=!6I z%Bozo)mW7#2Hd$-`hzg=F@6*cNz^$#BbGlIf${ZV1ADc}sNl=B72g`41|F7JtZ^BT z+y}nqn3Ug`2scS_{MjykPW2~*k$i6PhvvxJCW;n!SK5B8Rpm41fCEdy=ea-4F`rN5 zF>ClKp#4?}pI7eR#6U|}t`DA!GQJB7nT$HVV*{qPjIRU1Ou3W;I^pCt54o|ZHvWaH zooFx9L%#yv)!P;^er5LCU$5@qXMhJ-*T5Ah8|}byGNU5oMp3V)yR;hWJKojJEregX z<1UPt%&~=5OuP(|B{ty);vLdoe7o^?`tkQa7zoXKAW6D@lc+FTzucotaOfJ!(Bm zHE8f8j@6||lH`y2<&hP}Q1wr(=6ze0D6NRL{7QaE1=nTAzqjIeD}Be&@#_d*dyurz z&L7xo-D9!dS`i>^GaIPArR@r=N#-ppIh!UBcb!N*?nLUO+*%C>_dCF1IH)q>5oT(t zjQo{AoDB;mWL;3&;vTt?;bvJSj>^Gq4Jrh}S}D>G)+b!>oRDWI?c_d77$kF5ms{Gx zak*>~*5AvaB-Xl)IgdZ^Cupv6HxQ0 zM(KPaDpPsPOd)e)aFw}|=tfzg@J1P8oJx2ZBY=g4>_G(Hkgld(u&~jN((eJ}5@b1} zI(P7j443AZj*I@%q!$JQ2?DZV47U!|Tt6_;tlb`mSP3 z74DE4#|1FMDqwYbT4P6#wSI%s?*wDc>)MR$4z9ZtJg04+CTUds>1JSDwI}=vpRoRR zLqx(Tvf34CvkTMOPkoH~$CG~fSZb;(2S4Q6Vpe9G83V={hwQ>acu+MCX)@0i>Vd`% z4I8Ye+7&Kcbh(*bN1etKmrpN)v|=eI+$oD=zzii6nP&w|kn2Y-f!(v<aE zKmOz#{6PZB(8zD={il`RO6D}v(@mN_66KXUAEefgg|;VmBfP?UrfB$&zaRw7oanna zkNmVGz4Vhd!vZSnp1(&_5^t;eSv6O771BloJAHi=Pnn+aa6y(e2iiE97uZ{evzQ^8 z*lN@ZYx<-hLXP^IuYLGf<01O*>nDp0fo;;Iyt`JADrxt7-jEF(vv_btyp6CT8=@5t zm`I0lW+2+_xj2CRL|40kcYysuyYeiGihGe&a)yilqP}5h+^)m8$=mzrUe`$(?BIY> zfF7-V10Gu0CkWF)wz04&hhI>es0NS7d`cnT`4y8K!wUAKv$H09fa>KeNQvwUNDT1zn}_*RHykC$CD%*h7vRCQ&Z z4&N-!L>(@8i?K$l5)13n0%VPPV`iG7Q$2{1T3JypLSvN%1kX73goBIOEmg=Uf$9e? zm}g>JFu}EQKH>|K!)m9teoCmTc`y2Ll}msZYyy0Pkqjeid66>DP_?C{KCw94lHvLW z-+X!2YSm70s833lH0o+|A%Xwsw`@8lE3ia0n_Dve;LC7@I+i~@%$lD|3fNf&R6ob6 z@iGfx^OC4s`$|vO!0jTWwVpX;X^EqJF{i324I>N=f@u+rTN+xJGGR0LsCQc;iFD=F zbZJrgOpS;04o^wP7HF5QBaJ$KJgS2V4u02ViWD=6+7rcu`uc&MOoyf%ZBU|gQZkUg z<}ax>*Fo?d*77Ia)+{(`X45{a8>Bi$u-0BWSteyp#GJnTs?&k&<0NeHA$Qb3;SAJK zl}H*~eyD-0qHI3SEcn`_7d zq@YRsFdBig+k490BZSQwW)j}~GvM7x>2ymO4zakaHZ!q6C2{fz^NvvD8+e%7?BQBH z-}%B{oROo2+|6g%#+XmyyIJrK_(uEbg%MHlBn3^!&hWi+9c0iqM69enep#5FvV_^r z?Yr(k*5FbG{==#CGI1zU0Wk{V?UGhBBfv9HP9A-AmcJmL^f4S zY3E2$WQa&n#WRQ5DOqty_Pu z-NWQGCR^Hnu^Vo2rm`-M>zzf|uMCUd1X0{wISJL2Pp=AO5 zF@(50!g|SYw3n<_VP0T~`WUjtY**6Npphr5bD%i3#*p7h8$#;XTLJAt5J-x~O1~`z z`2C~P4%XSI(JbrEmVMEwqdsa^aqXWg;A6KBn^jDxTl!}Q!^WhprL$kb(Iqq zUS`i$tIPs#hdE-zAaMGoxcG?Z;RO2L0Y|gcjV_)FFo|e)MtTl`msLTwq>po$`H6_U zhdWK97~M>idl9GE_WgobQkK_P85H_0jN?s3O)+m&68B`_;FnbZ3W*Qm++ghSs7|T4b7m~VVV%j0gl`Iw!?+-9#Lsb!j3O%fSTVuK z37V>qM81D+Atl};23`TqEAfEkQDpz$-1$e__>X2jN>xh@Sq)I6sj@< ziJ^66GSmW9c%F7eu6&_t$UaLXF4KweZecS1ZiHPWy-$e_7`jVk74OS*!z=l#(CQ^K zW-ke|g^&0o=hn+4uh-8lUh0>!VIXXnQXwKr>`94+2~<;+`k z$|}QZ>#pm2g}8k*;)`@EnM~ZQtci%_$ink9t6`HP{gn}P1==;WDAld3JX?k%^GcTU za>m|CH|UsyFhyJBwG5=`6562hkVRMQ=_ron-Vlm$4bG^GFz|Jh5mM{J1`!!hAr~8F^w> z^YhQ=c|bFn_6~9X$v(30v$5IX;#Nl-XXRPgs{g_~RS*znH^6Vhe}8>T?aMA|qfnWO zQpf(wr^PfygfM+m2u!9}F|frrZPBQ!dh(varsYo!tCV)WA(Wn^_t=WR_G7cQU`AGx zrK^B6<}9+$w;$vra)QWMKf_Tnqg93AMVZ6Qd=q6rdB{;ZhsoT zWy9QhnpEnc@Dauz4!8gq zqDanAX#$^vf-4~ZqUJtSe?SO+Hmb?)l2#}v(8}2+P{ZZuhlib0$3G0|a5?JR>QgUUP$HTE5hb`h>imq#7P+Y*-UVLm@9km|V# zoigziFt$bxgQMwqKKhd!c--&ciywIED>faY3zHLrA{V#IA)!mq!FXxf?1coGK~N(b zjwu*@2B1^(bzFVBJO`4EJ$=it!a0kbgUvPL;Er(0io{W4G7Bkqh)=g)uS|l0YfD}f zaCJwY7vR-D=P9M68`cmtmQ^!F-$lt@0S|9G7cHgT13A0xMv)HmH#Z<4{~iYo_VOD{ z5!kU+>mUOvHouw+-y?*cNlUlDwD#;6ZvAIc$YcwG&qKZFh>EtM(Eda+w)E$HcfZyB zG*$<*ae_ApE%gxWx%O^~XMnRSNLv!y`g99F(J_m)spJAc95P|_joOIoru%atbw z9PYgkcE*8x#)-W{>96KDl&74iW<#wrK)1s zxzU{`rW5af+dT6Z@_1dG<}CtDMT`EGVEXSL_5D9)Z;6UJe-TW7)M?bY%E;8G?Yc!$ zic;F5=#dba^P~7f#qvC}Nd#XEo2r_UlgfR_`B2^W0QjXU?RAi$>f&{G_Lu8Fp0qDp z?vAdm%z#3kcZmaJ@afooB=A@>8_N~O9Yzu=ZCEikM>UgU+{%>pPvmSNzGk@*jnc5~ z(Z#H4OL^gw>)gqZ!9X|3i4LAdp9vo)?F9QCR3##{BHoZ73Uk^Ha={2rc*TBijfKH- z=$cZQdc<5%*$kVo|{+bL3 zEoU&tq*YPR)^y-SISeQNQ)YZ9v>Hm4O=J)lf(y=Yu1ao&zj#5GVGxyj%V%vl9}dw< zO;@NRd4qe@Et}E@Q;SChBR2QPKll1{*5*jT*<$$5TywvC77vt=1=0xZ46>_17YzbiBoDffH(1_qFP7v2SVhZmA_7JDB50t#C39 z8V<9(E?bVWI<7d6MzcS^w!XmZ**{AO!~DZNU)pgr=yY1 zT@!AapE;yg&hmj*g{I3vd## zx+d%^O?d%%?Dba|l~X6ZOW|>FPsrjPjn-h4swysH!RNJUWofC?K(^0uHrBPrH5#W> zMn8^@USzjUucqo%+5&))Dnnw`5l1mp>roaA99Nkk4keZl2wAF7oa(!x?@8uGWzc5Q zM}g`}zf-D@B6lVFYWmmJ8a+_%z8g$C7Ww~PD9&jki08NY!b!fK288R;E?e3Z+Pk{is%HxQU`xu9+y5 zq?DWJD7kKp(B2J$t5Ij8-)?g!T9_n<&0L8F5-D0dp>9!Qnl#E{eDtkNo#lw6rMJG$ z9Gz_Z&a_6ie?;F1Y^6I$Mg9_sml@-z6t!YLr=ml<6{^U~UIbZUUa_zy>fBtR3Rpig zc1kLSJj!rEJILzL^uE1mQ}hjMCkA|ZlWVC9T-#=~ip%McP%6QscEGlYLuUxDUC=aX zCK@}@!_@~@z;70I+Hp5#Tq4h#d4r!$Np1KhXkAGlY$ap7IZ9DY})&(xoTyle8^dBXbQUhPE6ehWHrfMh&0=d<)E2+pxvWo=@`^ zIk@;-$}a4zJmK;rnaC)^a1_a_ie7OE*|hYEq1<6EG>r}!XI9+(j>oe!fVBG%7d}?U z#ja?T@`XO(;q~fe2CfFm-g8FbVD;O7y9c;J)k0>#q7z-%oMy4l+ zW>V~Y?s`NoXkBeHlXg&u*8B7)B%alfYcCriYwFQWeZ6Qre!4timF`d$=YN~_fPM5Kc8P;B-WIDrg^-j=|{Szq6(TC)oa!V7y zLmMFN1&0lM`+TC$7}on;!51{d^&M`UW ztI$U4S&}_R?G;2sI)g4)uS-t}sbnRoXVwM!&vi3GfYsU?fSI5Hn2GCOJ5IpPZ%Y#+ z=l@;;{XiY_r#^RJSr?s1) z4b@ve?p5(@YTD-<%79-%w)Iv@!Nf+6F4F1`&t~S{b4!B3fl-!~58a~Uj~d4-xRt`k zsmGHs$D~Wr&+DWK$cy07NH@_z(Ku8gdSN989efXqpreBSw$I%17RdxoE<5C^N&9sk!s2b9*#}#v@O@Hgm z2|U7Gs*@hu1JO$H(Mk)%buh~*>paY&Z|_AKf-?cz6jlT-v6 zF>l9?C6EBRpV2&c1~{1$VeSA|G7T(VqyzZr&G>vm87oBq2S%H0D+RbZm}Z`t5Hf$C zFn7X*;R_D^ z#Ug0tYczRP$s!6w<27;5Mw0QT3uNO5xY($|*-DoR1cq8H9l}_^O(=g5jLnbU5*SLx zGpjfy(NPyjL`^Oln_$uI6(aEh(iS4G=$%0;n39C(iw79RlXG>W&8;R1h;oVaODw2nw^v{~`j(1K8$ z5pHKrj2wJhMfw0Sos}kyOS48Dw_~=ka$0ZPb!9=_FhfOx9NpMxd80!a-$dKOmOGDW zi$G74Sd(-u8c!%35lL|GkyxZdlYUCML{V-Ovq{g}SXea9t`pYM^ioot&1_(85oVZ6 zUhCw#HkfCg7mRT3|>99{swr3FlA@_$RnE?714^o;vps4j4}u=PfUAd zMmV3j;Rogci^f!ms$Z;gqiy7>soQwo7clLNJ4=JAyrz;=*Yhe8q7*$Du970BXW89Xyq92M4GSkNS-6uVN~Y4r7iG>{OyW=R?@DmRoi9GS^QtbP zFy2DB`|uZTv8|ow|Jcz6?C=10U$*_l2oWiacRwyoLafS!EO%Lv8N-*U8V+2<_~eEA zgPG-klSM19k%(%;3YM|>F||hE4>7GMA(GaOvZBrE{$t|Hvg(C2^PEsi4+)w#P4jE2XDi2SBm1?6NiSkOp-IT<|r}L9)4tLI_KJ*GKhv16IV}An+Jyx z=Mk`vCXkt-qg|ah5=GD;g5gZQugsv!#)$@ zkE=6=6W9u9VWiGjr|MgyF<&XcKX&S3oN{c{jt-*1HHaQgY({yjZiWW97rha^TxZy< z2%-5X;0EBP>(Y9|x*603*Pz-eMF5*#4M;F`QjTBH>rrO$r3iz5 z?_nHysyjnizhZQMXo1gz7b{p`yZ8Q78^ zFJ3&CzM9fzAqb6ac}@00d*zjW`)TBzL=s$M`X*0{z8$pkd2@#4CGyKEhzqQR!7*Lo@mhw`yNEE6~+nF3p;Qp;x#-C)N5qQD)z#rmZ#)g*~Nk z)#HPdF_V$0wlJ4f3HFy&fTB#7Iq|HwGdd#P3k=p3dcpfCfn$O)C7;y;;J4Za_;+DEH%|8nKwnWcD zBgHX)JrDRqtn(hC+?fV5QVpv1^3=t2!q~AVwMBXohuW@6p`!h>>C58%sth4+Baw|u zh&>N1`t(FHKv(P+@nT$Mvcl){&d%Y5dx|&jkUxjpUO3ii1*^l$zCE*>59`AvAja%`Bfry-`?(Oo?5wY|b4YM0lC?*o7_G$QC~QwKslQTWac z#;%`sWIt8-mVa1|2KH=u!^ukn-3xyQcm4@|+Ra&~nNBi0F81BZT$XgH@$2h2wk2W% znpo1OZuQ1N>bX52II+lsnQ`WVUxmZ?4fR_f0243_m`mbc3`?iy*HBJI)p2 z`GQ{`uS;@;e1COn-vgE2D!>EheLBCF-+ok-x5X8Cu>4H}98dH^O(VlqQwE>jlLcs> zNG`aSgDNHnH8zWw?h!tye^aN|%>@k;h`Z_H6*py3hHO^6PE1-GSbkhG%wg;+vVo&dc)3~9&` zPtZtJyCqCdrFUIEt%Gs_?J``ycD16pKm^bZn>4xq3i>9{b`Ri6yH|K>kfC; zI5l&P)4NHPR)*R0DUcyB4!|2cir(Y1&Bsn3X8v4D(#QW8Dtv@D)CCO zadQC85Zy=Rkrhm9&csynbm>B_nwMTFah9ETdNcLU@J{haekA|9*DA2pY&A|FS*L!*O+>@Q$00FeL+2lg2NWLITxH5 z0l;yj=vQWI@q~jVn~+5MG!mV@Y`gE958tV#UcO#56hn>b69 zM;lq+P@MW=cIvIXkQmKS$*7l|}AW%6zETA2b`qD*cL z(=k4-4=t6FzQo#uMXVwF{4HvE%%tGbiOlO)Q3Y6D<5W$ z9pm>%TBUI99MC`N9S$crpOCr4sWJHP)$Zg#NXa~j?WeVo03P3}_w%##A@F|Bjo-nNxJZX%lbcyQtG8sO zWKHes>38e-!hu1$6VvY+W-z?<942r=i&i<88UGWdQHuMQjWC-rs$7xE<_-PNgC z_aIqBfG^4puRkogKc%I-rLIVF=M8jCh?C4!M|Q=_kO&3gwwjv$ay{FUDs?k7xr%jD zHreor1+#e1_;6|2wGPtz$``x}nzWQFj8V&Wm8Tu#oaqM<$BLh+Xis=Tt+bzEpC}w) z_c&qJ6u&eWHDb<>p;%F_>|`0p6kXYpw0B_3sIT@!=fWHH`M{FYdkF}*CxT|`v%pvx z#F#^4tdS0|O9M1#db%MF(5Opy;i( zL(Pc2aM4*f_Bme@o{xMrsO=)&>YKQw+)P-`FwEHR4vjU>#9~X7ElQ#sRMjR^Cd)wl zg^67Bgn9CK=WP%Ar>T4J!}DcLDe z=ehSmTp##KyQ78cmArL=IjOD6+n@jHCbOatm)#4l$t5YV?q-J86T&;>lEyK&9(XLh zr{kPuX+P8LN%rd%8&&Ia)iKX_%=j`Mr*)c)cO1`-B$XBvoT3yQCDKA>8F0KL$GpHL zPe?6dkE&T+VX=uJOjXyrq$BQ`a8H@wN1%0nw4qBI$2zBx)ID^6;Ux+? zu{?X$_1hoz9d^jkDJpT-N6+HDNo%^MQ2~yqsSBJj4@5;|1@w+BE04#@Jo4I63<~?O?ok%g%vQakTJKpMsk&oeVES1>cnaF7ZkFpqN6lx` zzD+YhR%wq2DP0fJCNC}CXK`g{AA6*}!O}%#0!Tdho4ooh&a5&{xtcFmjO4%Kj$f(1 zTk||{u|*?tAT{{<)?PmD_$JVA;dw;UF+x~|!q-EE*Oy?gFIlB*^``@ob2VL?rogtP z0M34@?2$;}n;^OAV2?o|zHg`+@Adk+&@Syd!rS zWvW$e5w{onua4sp+jHuJ&olMz#V53Z5y-FkcJDz>Wk%_J>COk5<0ya*aZLZl9LH}A zJhJ`Q-n9K+c8=0`FWE^x^xn4Fa7PDUc;v2+us(dSaoIUR4D#QQh91R!${|j{)=Zy1 zG;hqgdhSklM-VKL6HNC3&B(p1B)2Nshe7)F=-HBe=8o%OhK1MN*Gq6dBuPvqDRVJ{ z;zVNY?wSB%W0s^OMR_HL(Ws)va7eWGF*MWx<1wG7hZ}o=B62D?i|&0b14_7UG287YDr%?aYMMpeCkY1i`b+H!J9sqrvKc#Y6c8At@QiLSwj)@ifz~Z|c$lOMA@?cPqFRmZ%_>bz2X4(B=`^3;MDjsEeAO=? zSoD&+L>A|fGt7+6kF2@LqhL06sD%|~YsIe=EcWqy{e_61N_D(*CacnMvyXMjP87HI z4PT6!$fzxx{}=>jeqzkkoN+!r9e|@lZUN4pn(T28v`k=_vIhTn^i9O3qTqd)-%!QQ zYB6*6B@&b(!#X4C~59SLZuorNU_wWZA36{>O%iX)VS5NNZh49C_ppI>?)wwml}_0MLzOXT>lmo#&Ew6d?mu8~~I_^4VGBQtCAke;RQa5DL` z1PFDPsKb3CS$v;RhlQ1J@AHa1VRuuxp}NOIvrC>4$$A0Ix0VpAc0lfG%8{mR{TRQ( zbXM#1Tci3H*Wt>cVuMta^6^z`=^B@j+YhJqq9?>zZPxyg2U(wvod=uwJs{8gtpyab zXHQX<0FOGW6+dw&%c_qMUOI^+Rnb?&HB7Fee|33p4#8i>%_ev(aTm7N1f#6lV%28O zQ`tQh$VDjy8x(Lh#$rg1Kco$Bw%gULq+lc4$&HFGvLMO30QBSDvZ#*~hEHVZ`5=Kw z3y^9D512@P%d~s{x!lrHeL4!TzL`9(ITC97`Cwnn8PSdxPG@0_v{No|kfu3DbtF}K zuoP+88j4dP+Bn7hlGwU$BJy+LN6g&d3HJWMAd1P9xCXG-_P)raipYg5R{KQO$j;I9 z1y1cw#13K|&kfsRZ@qQC<>j=|OC?*v1|VrY$s=2!{}e33aQcZghqc@YsHKq^)kpkg z>B;CWNX+K=u|y#N)O>n5YuyvPl5cO6B^scmG?J zC8ix)E1PlhNaw8FpD+b|D$z`Id^4)rJe78MNiBga?Z- z0$L&MRTieSB1_E#KaN*H#Ns1}?zOA%Ybr{G+Sn3moXTVZj=L`nt?D&-MjOMz-Yq&@ z$P3h23d_F8Dcf*?txX7}p>nM*s+65t z1il8bHHsBynUK|aEXSjzY6sz1nZ%|%XeWTcGLRyRl@q4YAR)JovbdTTY&7u>@}28A zgV^Npp?}I!?3K7IXu9ml-Lw;w@9m zBYTeU+Seh8uJ-w?4e_6byq0f7>O3xm(hO}Y=fgU5^vW|>0yQ^0+?}LT55ei$i zzlU-iRbd8TRX9Ept%h%ariV=%u%F@@FA>U*XdAalcH%>#5_a&w)g`uW%3}m?vP- zc5}DkuF6ruKDwEYj+2YTSQ9=rkp19U5P@(zRm(nLod(sG9{~nw1BUoS2OFDXa{xfw zZ~UaZLFUZxfQ*9?_X?*~`d;nn-BbaefLJ`DT13KF6?T5Mnt;v5d>H}s)aAIzJcs#B z|CuXPJKww}hWBKsUfks#Kh$)ptp?5U1b@ttXFRbe_BZ&_R9XC6CA4WhWhMUE9Y2H4 z{w#CBCR<)Fd1M;mx*m?Z=L-^1kv1WKtqG(BjMiR4M^5yN4rlFM6oGUS2Wf~7Z@e*- ze84Vr`Bmi!(a1y}-m^HHMpbAiKPVEv|(7=|}D#Ihfk+-S5Hlkfch02z&$(zS3vrYz2g*ic{xBy~*gIp(eG}^gMc7 zPu2Eivnp@BH3SOgx!aJXttx*()!=2)%Bf$Gs^4cCs@)=(PJNxhH5lVY&qSZYaa?A^LhZW`B9(N?fx<^gCb(VE%3QpA*_Pohgp6vCB36iVaq zc1TI%L2Le?kuv?6Dq`H+W>AqnjyEzUBK948|DB|)U0_4DzWF#7L{agwo%y$hC>->r z4|_g_6ZC!n2=GF4RqVh6$$reQ(bG0K)i9(oC1t6kY)R@DNxicxGxejwL2sB<>l#w4 zE$QkyFI^(kZ#eE5srv*JDRIqRp2Totc8I%{jWhC$GrPWVc&gE1(8#?k!xDEQ)Tu~e zdU@aD8enALmN@%1FmWUz;4p}41)@c>Fg}1vv~q>xD}KC#sF|L&FU);^Ye|Q;1#^ps z)WmmdQI2;%?S%6i86-GD88>r|(nJackvJ#50vG6fm$1GWf*f6>oBiDKG0Kkwb17KPnS%7CKb zB7$V58cTd8x*NXg=uEX8Man_cDu;)4+P}BuCvYH6P|`x-#CMOp;%u$e z&BZNHgXz-KlbLp;j)si^~BI{!yNLWs5fK+!##G;yVWq|<>7TlosfaWN-;C@oag~V`3rZM_HN`kpF`u1p# ztNTl4`j*Lf>>3NIoiu{ZrM9&E5H~ozq-Qz@Lkbp-xdm>FbHQ2KCc8WD7kt?=R*kG# z!rQ178&ZoU(~U<;lsg@n216Ze3rB2FwqjbZ=u|J?nN%<4J9(Bl(90xevE|7ejUYm9 zg@E_xX}u2d%O1mpA2XzjRwWinvSeg)gHABeMH(2!A^g@~4l%8e0WWAkBvv60Cr>TR zQB1%EQ zUoZeUdqjh+1gFo6h~C~z#A57mf5ibmq$y_uVtA_kWv8X)CzfVEooDaY!#P?5$Y zGPKXbE<75nc%D-|w4OrP#;87oL@2^4+sxKah;a-5&z_&SUf~-z(1}bP=tM^GYtR3a z!x4zjSa^)KWG6jxfUI#{<26g$iAI;o_+B{LXY@WfWEdEl6%#8s3@b`?&Tm#aSK!~| z^%DdrXnijW`d!ajWuKApw&{L+WCPpFialo&^dZ9jC7A%BO`2ZF&YUDe;Yu|zFuv`2 z)BE*7Lkay)M7uohJ)446X``0x0%PzPTWY92`1Oq4a2D_7V0wypPnXFR)WM0IlFgg@ zqz#hv2xJEQL8eu}O;e(w4rSA?5|eZHbS6jENytJBq59?bOf>Wrl8ySZH36H(6fGR#vHM6q zn}!7!I@4$*+LFXs{x?|=q2*QtYT%Lw3+5(8uc0j8o3}TrG(zSV#>4wo6~)u|R+Yx# z?0$AspZDjv{dfv417~C17Oy%Fal{%+B6H(NX`$Bl>II-L3N3 zZc+sKZbqewU*&_Xt;9k=%4*aVYBvE1n&JZS7Uqjd%n8nOQmzh^x#vWK{;In~=QO)g zT-n3OU(1@3QfL|$g1d2xeBb@O15Rl01+hmpup2De7p%Yrd$E7(In!*R+;IJZh}v!svi z;7N~pq8KZDXXap0qd_D=Y^B)rz4S0^SF=&v6YYTAV$ad43#x!+n~-6< zK{8*vWoAdW(gGGt&URD}@g6tMoY(+Lw=vvxhfIIK9AjvNF_(W}1Rxn(mp;tJfDV<0 zbJN0t(@Xb8UeO{&T{$$uDrs7)j$}=?WsuDl+T2N5Y<4TMHGOMcocPr$%~(yvtKv(n z`U96d!D0cb9>Dx2zz$m&lAhazs%UeR^K*gb>d8CPs+?qlpfA;t{InXa)^2ryC(FU(Zc6Xbnnh`lg`K&g^JeS>}^c0MJKUCfV+~ zV(EN0Z5ztoN;hqcj!8V+VRbSltJ<~|y`U+9#wv|~H zNE!j9uXa=dec@JQSgJ6N6@Il&tzCBJv9#ldR`Lm*<)YwH4tdlAlG0Fl8Nfa(J~c%DQ2AA-}x8D=p(l#n1+hgx;N;1Aq?lq@{Lt9FKu89CjnnHD1G_@p;%Lp`+b@ttb33!E_Xt;QUD9~nRQl&xAro9-{+&6^ljK2f-d>&qy&d#0xwH z@slNv@ULKp!Cf*JHuS@#4c?F->WjPc)yiuSargAIEg>muRxzY?Hzdq@G5CS)U1*Et zE2SLh=@DI1J(guiy2Igq(?(xI9WL%g^f@{5Hmr|!Qz4`vn|LjrtO=b~I6~5EU5Fxy z;-#<)6w#w=DkpSthAu+E;OL?!?6C9Mwt*o(@68(Jhvs-eX4V z=d=>HI|`3J%H5X|gSrC8KH^IL?h5=3ID6svwHH@(wRbSG`Zsor^q4`3PCn#-(YX?< z_q8+T)51$E0xyKR{L!LN(G=+9K6$3#PDT^IAe|Igkx=!4#rqKWoXiZdh`&ocjp=Ok zemJe6*{it~>;sr(B0fSmp(S#*y5I0)OOz~Oe6Im+($S}e3tyx7Y6pA8vKCBmSEQDa zLfkm*;uMbTLpcR0)tF_v-lbK%`5>POyI2E(!)2=Rj0p;WKi=|UNt6HsQv0xR3QIK9 zsew(AFyzH!7Azxum{%VC^`cqhGdGbABGQ4cYdNBPTx+XpJ=NUEDeP^e^w^AOE1pQI zP{Us-sk!v$gj}@684E!uWjzvpoF|%v-6hwnitN1sCSg@(>RDCVgU8Ile_-xX`hL6u zzI4*Q)AVu(-ef8{#~P9STQ5t|qIMRoh&S?7Oq+cL6vxG?{NUr@k(~7^%w)P6nPbDa~4Jw}*p-|cT4p1?)!c0FoB(^DNJ+FDg+LoP6=RgB7Or673WD5MG&C!4< zerd6q$ODkBvFoy*%cpHGKSt z3uDC6Sc=xvv@kDzRD)aIO`x}BaWLycA%(w-D`Pd+uL*rL|etagQ;U&xt_9?7#}=}5HI)cU-0 z%pMA`>Xb7s)|Y)4HKSZOu;{lg=KjeIyXb0{@EM`FTDkLRH`!W%z*lQJ74P%Ka76)H zblrSIzf+dMWbO`g;=(b@{pS)zUcO&GrIFe%&?YeX4r8B2bBArB%-5ZrQ+vonr%AYy z1+u0*K{UVUmV>h5vD!F;6}a%KdMZQLs04oGkpiaC)zI( zT2U9qta5o|6Y+It1)sE8>u&0)W~l$NX@ZQ8UZfB=`($EW6?FT%{EoRhOrb9)z@3r8y?Z99FNLDE;7V=Q zotj&igu*Rh^VQn3MQKBq!T{yTwGhn1YL6k*?j?{_ek5xe8#i#GG4S-a_Re2lssG!} z`Y-d0BcOdB@!m?4y&hMN68}#0-IIlm_xO)d#}ugX{q^OZe{-@LeJyv`cY&ze4t2~! zKb{qX-j;kt{?gC(vW%}X4pm@1F?~LH{^Q8d@X$dy@5ff~p!J3zmA>H`A)y+6RB_h* zZfIO+bd=*LiymRw{asW%xxaVl33_xtdVrrqIPn zc@y8oMJvNtgcO~4i0`f)GCFkWY8EF?4duLVjHTdb6oYLnO9}Q-pe{CKQJL)hV8)JI z$mVA0Dq&7Z1TbYdSC(WbJ+IBjXngZTu&I+vHF|>Zo$757{8lL;8Zr-Exkf?3jzN5k z_d9I>{>^J?!l)< zNd$7E9FVrta}3qy3L7Ys$^fRWNuu^hs^{*eXvazd&+Q*?lTfc>2+EdP(o0P_Z05HX zVKsfFAQ{t^CRu~Dw(CuJ>tvx*p$5@flA>QRl455b&{*U?xU8`)nF2T$uu_(l8VNtq z?pBiRQIckGzk8W&SFSB=g6eG`ZC;6v9w`?eF*S}3E@N`2ropeHP)E}o?qJkyVEI;K$!)bWY zt9>4WmDVJh7U~m$|K`T#hF!v|znj^=M;69uXrFys#51XT;DbMr4H)>7UQ1e2(cuQf z4kr~Tt1tpBB2GaJ(|j~lHgW40EgMMVqR6eJoJig1SBg|2=$~4I3P0eP$q%_`sS&4~ z26=&a&tLjQbch1`cVXa-2fTl1y8}->|Nqu?uVrNTov!=VKh)g89wUPTgAzkSKZ57_ zr=B^mcldE3K04t4{;RaG53&9yovq;@aR#VHx+R1^^*kr-vEEd!uea68Z<{R%_DD6fn&T4 zu;fDj07L-(_fLSJGdkeh&c&7A(ZLj`7iwnkAcqUexU;WjUkqeg1m1-IUZTIZA(4dtr2Gr`e{BIejlCgS<33MB=1!8?a74!F%=Uo7N`F@k} ze+1C_eU4Y_$mvdjci zwEtCIphA2PBzBhng5=M#e4r%)RW5rVD|_`PvY$7BK`}w~d>%0O9sY#*LUAq=^OjMF^PY5m<7!=s5jyRfosCQAo#hL`h5vN-M}6Q z0Li}){5?wi8)GVHNkF|U9*8V5ej)nhb^TLw1KqiPK(@{P1^L&P=`ZNt?_+}&0(8Uh zfyyZFPgMV7ECt;Jdw|`|{}b$w4&x77VxR>8wUs|GQ5FBf1UlvasqX$qfk5rI4>Wfr zztH>y`=daAef**C12yJ7;LDf&3;h3X+5@dGPy@vS(RSs3CWimbTp=g \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" From 5b281f81673d60b7ad0bdc88206372eddec5b329 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Sat, 3 Oct 2015 22:46:14 +0200 Subject: [PATCH 36/65] Update sublib --- extern/GmsApi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/GmsApi b/extern/GmsApi index bb403701..cb0458f1 160000 --- a/extern/GmsApi +++ b/extern/GmsApi @@ -1 +1 @@ -Subproject commit bb4037017c447d8129abf7c5cbaa73e527da5631 +Subproject commit cb0458f17e55e713b10fe5611890887d0c8b910e From 5990c8cc6eca8136f5f5661cabe354a8437d341c Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Tue, 20 Oct 2015 19:01:28 +0200 Subject: [PATCH 37/65] Update sublib --- extern/GmsApi | 2 +- .../org/microg/gms/location/GoogleLocationManagerClient.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extern/GmsApi b/extern/GmsApi index cb0458f1..d2b942d8 160000 --- a/extern/GmsApi +++ b/extern/GmsApi @@ -1 +1 @@ -Subproject commit cb0458f17e55e713b10fe5611890887d0c8b910e +Subproject commit d2b942d87f5b5d0547e7e5f279bcc734d69cdef4 diff --git a/play-services-location/src/main/java/org/microg/gms/location/GoogleLocationManagerClient.java b/play-services-location/src/main/java/org/microg/gms/location/GoogleLocationManagerClient.java index aab38b0d..b012cf97 100644 --- a/play-services-location/src/main/java/org/microg/gms/location/GoogleLocationManagerClient.java +++ b/play-services-location/src/main/java/org/microg/gms/location/GoogleLocationManagerClient.java @@ -37,7 +37,7 @@ public abstract class GoogleLocationManagerClient extends GmsClient Date: Tue, 17 Nov 2015 17:22:32 +0100 Subject: [PATCH 38/65] Move MessageEvent to play-services-api --- extern/GmsApi | 2 +- .../android/gms/wearable/MessageEvent.java | 42 ------------------- 2 files changed, 1 insertion(+), 43 deletions(-) delete mode 100644 play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageEvent.java diff --git a/extern/GmsApi b/extern/GmsApi index d2b942d8..f0ec7e60 160000 --- a/extern/GmsApi +++ b/extern/GmsApi @@ -1 +1 @@ -Subproject commit d2b942d87f5b5d0547e7e5f279bcc734d69cdef4 +Subproject commit f0ec7e606f3e6e3a219eb87266c4dae1828b5083 diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageEvent.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageEvent.java deleted file mode 100644 index 5c772334..00000000 --- a/play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageEvent.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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.wearable; - -import org.microg.gms.common.PublicApi; - -@PublicApi -public interface MessageEvent { - /** - * @return the data passed by the message. - */ - byte[] getData(); - - /** - * @return the path the message is being delivered to - */ - String getPath(); - - /** - * @return the request id of the message, generated by the sender - */ - int getRequestId(); - - /** - * @return the node ID of the sender. - */ - String getSourceNodeId(); -} From 276288751397b63730bdb220d129eabb7ffca2d3 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Thu, 14 Apr 2016 22:06:42 +0200 Subject: [PATCH 39/65] Update Gradle, push unfinished GCM --- build.gradle | 11 +- extern/GmsApi | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- play-services-base/build.gradle | 13 +- .../gms/common/api/GoogleApiClient.java | 4 +- play-services-cast/build.gradle | 11 +- .../src/main/AndroidManifest.xml | 2 +- play-services-gcm/build.gradle | 43 +++ .../src/main/AndroidManifest.xml | 24 ++ .../android/gms/gcm/GcmListenerService.java | 195 ++++++++++++ .../android/gms/gcm/GcmNetworkManager.java | 244 ++++++++++++++ .../com/google/android/gms/gcm/GcmPubSub.java | 120 +++++++ .../google/android/gms/gcm/GcmReceiver.java | 49 +++ .../android/gms/gcm/GcmTaskService.java | 149 +++++++++ .../android/gms/gcm/GoogleCloudMessaging.java | 301 ++++++++++++++++++ .../google/android/gms/gcm/OneoffTask.java | 221 +++++++++++++ .../google/android/gms/gcm/PeriodicTask.java | 235 ++++++++++++++ .../java/com/google/android/gms/gcm/Task.java | 271 ++++++++++++++++ .../google/android/gms/gcm/TaskParams.java | 49 +++ .../google/android/gms/iid/InstanceID.java | 166 ++++++++++ .../gms/iid/InstanceIDListenerService.java | 138 ++++++++ play-services-location/build.gradle | 11 +- .../location/FusedLocationProviderApi.java | 4 +- .../location/GoogleLocationManagerClient.java | 4 +- play-services-wearable/build.gradle | 11 +- play-services/build.gradle | 12 +- settings.gradle | 1 + 27 files changed, 2262 insertions(+), 31 deletions(-) create mode 100644 play-services-gcm/build.gradle create mode 100644 play-services-gcm/src/main/AndroidManifest.xml create mode 100644 play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmListenerService.java create mode 100644 play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmNetworkManager.java create mode 100644 play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmPubSub.java create mode 100644 play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmReceiver.java create mode 100644 play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmTaskService.java create mode 100644 play-services-gcm/src/main/java/com/google/android/gms/gcm/GoogleCloudMessaging.java create mode 100644 play-services-gcm/src/main/java/com/google/android/gms/gcm/OneoffTask.java create mode 100644 play-services-gcm/src/main/java/com/google/android/gms/gcm/PeriodicTask.java create mode 100644 play-services-gcm/src/main/java/com/google/android/gms/gcm/Task.java create mode 100644 play-services-gcm/src/main/java/com/google/android/gms/gcm/TaskParams.java create mode 100644 play-services-gcm/src/main/java/com/google/android/gms/iid/InstanceID.java create mode 100644 play-services-gcm/src/main/java/com/google/android/gms/iid/InstanceIDListenerService.java diff --git a/build.gradle b/build.gradle index 49dfafa1..c6c27343 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,16 @@ */ // Top-level build file where you can add configuration options common to all sub-projects/modules. +String getMyVersionName() { + def stdout = new ByteArrayOutputStream() + if (rootProject.file("gradlew").exists()) + exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout } + else // automatic build system, don't tag dirty + exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout } + return stdout.toString().trim().substring(1) +} + subprojects { group = 'org.microg' - version = '1.0-SNAPSHOT' + version = getMyVersionName() } diff --git a/extern/GmsApi b/extern/GmsApi index f0ec7e60..6aa11065 160000 --- a/extern/GmsApi +++ b/extern/GmsApi @@ -1 +1 @@ -Subproject commit f0ec7e606f3e6e3a219eb87266c4dae1828b5083 +Subproject commit 6aa110657beec0b3e6c26d1030943e0b97683335 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 426c09a0..90babf29 100755 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -19,4 +19,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.12-all.zip diff --git a/play-services-base/build.gradle b/play-services-base/build.gradle index fe05a441..52ff3bd9 100644 --- a/play-services-base/build.gradle +++ b/play-services-base/build.gradle @@ -19,23 +19,26 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.3.0' - classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' + classpath 'com.android.tools.build:gradle:2.0.0' } } apply plugin: 'com.android.library' -apply plugin: 'com.github.dcendents.android-maven' android { compileSdkVersion 23 - buildToolsVersion "23.0.1" + buildToolsVersion "23.0.2" + + defaultConfig { + versionName getMyVersionName() + } + compileOptions { sourceCompatibility JavaVersion.VERSION_1_6 } } dependencies { - compile 'com.android.support:support-v4:23.0.1' + compile 'com.android.support:support-v4:23.3.0' compile project(':play-services-api') } diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java b/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java index c3e44885..7140ca96 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java @@ -26,7 +26,7 @@ import android.view.View; import com.google.android.gms.common.ConnectionResult; -import org.microg.gms.common.Constants; +import org.microg.gms.auth.AuthConstants; import org.microg.gms.common.PublicApi; import org.microg.gms.common.api.GoogleApiClientImpl; @@ -422,7 +422,7 @@ public interface GoogleApiClient { * Specify that the default account should be used when connecting to services. */ public Builder useDefaultAccount() { - this.accountName = Constants.DEFAULT_ACCOUNT; + this.accountName = AuthConstants.DEFAULT_ACCOUNT; return this; } } diff --git a/play-services-cast/build.gradle b/play-services-cast/build.gradle index 2d7747bc..f900225d 100644 --- a/play-services-cast/build.gradle +++ b/play-services-cast/build.gradle @@ -19,17 +19,20 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.3.0' - classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' + classpath 'com.android.tools.build:gradle:2.0.0' } } apply plugin: 'com.android.library' -apply plugin: 'com.github.dcendents.android-maven' android { compileSdkVersion 23 - buildToolsVersion "23.0.1" + buildToolsVersion "23.0.2" + + defaultConfig { + versionName getMyVersionName() + } + compileOptions { sourceCompatibility JavaVersion.VERSION_1_6 } diff --git a/play-services-cast/src/main/AndroidManifest.xml b/play-services-cast/src/main/AndroidManifest.xml index bb22a334..2031102a 100644 --- a/play-services-cast/src/main/AndroidManifest.xml +++ b/play-services-cast/src/main/AndroidManifest.xml @@ -16,7 +16,7 @@ --> + package="org.microg.gms.cast"> diff --git a/play-services-gcm/build.gradle b/play-services-gcm/build.gradle new file mode 100644 index 00000000..b3a6c833 --- /dev/null +++ b/play-services-gcm/build.gradle @@ -0,0 +1,43 @@ +/* + * 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. + */ + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.0.0' + } +} + +apply plugin: 'com.android.library' + +android { + compileSdkVersion 23 + buildToolsVersion "23.0.2" + + defaultConfig { + versionName getMyVersionName() + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_6 + } +} + +dependencies { + compile project(':play-services-base') +} \ No newline at end of file diff --git a/play-services-gcm/src/main/AndroidManifest.xml b/play-services-gcm/src/main/AndroidManifest.xml new file mode 100644 index 00000000..baaa3ad3 --- /dev/null +++ b/play-services-gcm/src/main/AndroidManifest.xml @@ -0,0 +1,24 @@ + + + + + + + + + diff --git a/play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmListenerService.java b/play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmListenerService.java new file mode 100644 index 00000000..8a14ff78 --- /dev/null +++ b/play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmListenerService.java @@ -0,0 +1,195 @@ +/* + * 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.gcm; + +import android.app.PendingIntent; +import android.app.Service; +import android.content.Intent; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.IBinder; +import android.support.v4.os.AsyncTaskCompat; +import android.util.Log; + +import org.microg.gms.common.PublicApi; + +import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_RECEIVE; +import static org.microg.gms.gcm.GcmConstants.ACTION_NOTIFICATION_OPEN; +import static org.microg.gms.gcm.GcmConstants.EXTRA_ERROR; +import static org.microg.gms.gcm.GcmConstants.EXTRA_FROM; +import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSAGE_ID; +import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSAGE_TYPE; +import static org.microg.gms.gcm.GcmConstants.EXTRA_PENDING_INTENT; +import static org.microg.gms.gcm.GcmConstants.MESSAGE_TYPE_DELETED_MESSAGE; +import static org.microg.gms.gcm.GcmConstants.MESSAGE_TYPE_GCM; +import static org.microg.gms.gcm.GcmConstants.MESSAGE_TYPE_SEND_ERROR; +import static org.microg.gms.gcm.GcmConstants.MESSAGE_TYPE_SEND_EVENT; + +/** + * Base class for communicating with Google Cloud Messaging. + *

+ * It also provides functionality such as automatically displaying + * notifications when requested by app server. + *

+ * Override base class methods to handle any events required by the application. + * Methods are invoked asynchronously. + *

+ * Include the following in the manifest: + *

+ * 
+ *     
+ *         
+ *     
+ * 
+ */ +@PublicApi +public abstract class GcmListenerService extends Service { + private static final String TAG = "GcmListenerService"; + + private final Object lock = new Object(); + private int startId; + private int counter = 0; + + public final IBinder onBind(Intent intent) { + return null; + } + + /** + * Called when GCM server deletes pending messages due to exceeded + * storage limits, for example, when the device cannot be reached + * for an extended period of time. + *

+ * It is recommended to retrieve any missing messages directly from the + * app server. + */ + public void onDeletedMessages() { + // To be overwritten + } + + /** + * Called when a message is received. + * + * @param from describes message sender. + * @param data message data as String key/value pairs. + */ + public void onMessageReceived(String from, Bundle data) { + // To be overwritten + } + + /** + * Called when an upstream message has been successfully sent to the + * GCM connection server. + * + * @param msgId of the upstream message sent using + * {@link com.google.android.gms.gcm.GoogleCloudMessaging#send(java.lang.String, java.lang.String, android.os.Bundle)}. + */ + public void onMessageSent(String msgId) { + // To be overwritten + } + + /** + * Called when there was an error sending an upstream message. + * + * @param msgId of the upstream message sent using + * {@link com.google.android.gms.gcm.GoogleCloudMessaging#send(java.lang.String, java.lang.String, android.os.Bundle)}. + * @param error description of the error. + */ + public void onSendError(String msgId, String error) { + // To be overwritten + } + + public final int onStartCommand(final Intent intent, int flags, int startId) { + synchronized (lock) { + this.startId = startId; + this.counter++; + } + + if (intent != null) { + if (ACTION_NOTIFICATION_OPEN.equals(intent.getAction())) { + handlePendingNotification(intent); + finishCounter(); + GcmReceiver.completeWakefulIntent(intent); + } else if (ACTION_C2DM_RECEIVE.equals(intent.getAction())) { + AsyncTaskCompat.executeParallel(new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + handleC2dmMessage(intent); + return null; + } + }); + } else { + Log.w(TAG, "Unknown intent action: " + intent.getAction()); + + } + + return START_REDELIVER_INTENT; + } else { + finishCounter(); + return START_NOT_STICKY; + } + } + + private void handleC2dmMessage(Intent intent) { + try { + String messageType = intent.getStringExtra(EXTRA_MESSAGE_TYPE); + if (messageType == null || MESSAGE_TYPE_GCM.equals(messageType)) { + String from = intent.getStringExtra(EXTRA_FROM); + Bundle data = intent.getExtras(); + data.remove(EXTRA_MESSAGE_TYPE); + data.remove("android.support.content.wakelockid"); // WakefulBroadcastReceiver.EXTRA_WAKE_LOCK_ID + data.remove(EXTRA_FROM); + onMessageReceived(from, data); + } else if (MESSAGE_TYPE_DELETED_MESSAGE.equals(messageType)) { + onDeletedMessages(); + } else if (MESSAGE_TYPE_SEND_EVENT.equals(messageType)) { + onMessageSent(intent.getStringExtra(EXTRA_MESSAGE_ID)); + } else if (MESSAGE_TYPE_SEND_ERROR.equals(messageType)) { + onSendError(intent.getStringExtra(EXTRA_MESSAGE_ID), intent.getStringExtra(EXTRA_ERROR)); + } else { + Log.w(TAG, "Unknown message type: " + messageType); + } + finishCounter(); + } finally { + GcmReceiver.completeWakefulIntent(intent); + } + } + + private void handlePendingNotification(Intent intent) { + PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_PENDING_INTENT); + if (pendingIntent != null) { + try { + pendingIntent.send(); + } catch (PendingIntent.CanceledException e) { + Log.w(TAG, "Notification cancelled", e); + } + } else { + Log.w(TAG, "Notification was null"); + } + } + + private void finishCounter() { + synchronized (lock) { + this.counter--; + if (counter == 0) { + stopSelfResult(startId); + } + } + } + +} \ No newline at end of file diff --git a/play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmNetworkManager.java b/play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmNetworkManager.java new file mode 100644 index 00000000..3c2dc39b --- /dev/null +++ b/play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmNetworkManager.java @@ -0,0 +1,244 @@ +/* + * 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.gcm; + +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.Bundle; +import android.text.TextUtils; + +import java.util.List; + +import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME; +import static org.microg.gms.gcm.GcmConstants.ACTION_SCHEDULE; +import static org.microg.gms.gcm.GcmConstants.ACTION_TASK_READY; +import static org.microg.gms.gcm.GcmConstants.EXTRA_COMPONENT; +import static org.microg.gms.gcm.GcmConstants.EXTRA_SCHEDULER_ACTION; +import static org.microg.gms.gcm.GcmConstants.EXTRA_TAG; +import static org.microg.gms.gcm.GcmConstants.SCHEDULER_ACTION_CANCEL; +import static org.microg.gms.gcm.GcmConstants.SCHEDULER_ACTION_CANCEL_ALL; +import static org.microg.gms.gcm.GcmConstants.SCHEDULER_ACTION_SCHEDULE; + +/** + * Class to create apps with robust "send-to-sync", which is the mechanism to sync data with + * servers where new information is available. + *

+ * You can use the API to schedule network-oriented tasks, and let Google Play services batch + * network operations across the system. This greatly simplifies the implementation of common + * patterns, such as waiting for network connectivity, network retries, and backoff. + *

+ * Tasks must be scheduled based on an execution window in time. During this execution window + * the scheduler will use its discretion in picking an optimal execution time, based on network + * availability (whether the device has connectivity), network activity (whether packages are + * actively being transferred). and load (how many other pending tasks are available for + * execution at that point in time). If none of these factors are influential, the + * scheduler will always wait until the end of the specified window. + *

+ * To receive the notification from the scheduler that a task is ready to be executed, your + * client app must implement a {@link com.google.android.gms.gcm.GcmTaskService} and filter + * on the action {@link com.google.android.gms.gcm.GcmTaskService#SERVICE_ACTION_EXECUTE_TASK}. + *

+ * Note that tags of arbitrary length are not allowed; if the tag you + * provide is greater than 100 characters an exception will be thrown when you try to create your + * {@link com.google.android.gms.gcm.Task} object. + *

+ * The service should be protected by the permission + * com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE which is used by Google Play + * Services. This prevents other code from invoking the broadcast receiver. Here is an excerpt from + * a sample manifest: + *

+ * 
+ *     
+ *        
+ *     
+ * 
+ * 
+ * An execution contains the tag identifier which your client app provides. This identifies + * one "task", or piece of work, that you mean to perform. Consider the tag to be the key to which + * your task logic is paired. + *
+ * // Schedule a task to occur between five and fifteen seconds from now:
+ * OneoffTask myTask = new OneoffTask.Builder()
+ *         .setService(MyGcmTaskService.class)
+ *         .setExecutionWindow(
+ *             5 * DateUtil.MINUTE_IN_SECONDS, 15 * DateUtil.MINUTE_IN_SECONDS)
+ *         .setTag("test-upload")
+ *         .build();
+ * GcmNetworkManager.get(this).schedule(myTask);
+ * ...
+ * // Implement service logic to be notified when the task elapses:
+ * MyUploadService extends GcmTaskService {
+ *
+ *     @Override public int onRunTask(TaskParams params) {
+ *         // Do some upload work.
+ *         return GcmNetworkManager.RESULT_SUCCESS;
+ *     }
+ * }
+ * 
+ * To help in debugging your tasks, run + * adb shell dumpsys activity service GcmService --endpoints [...] + * If you want to execute your task immediately (for debugging) you can execute tasks from the + * command line via: + * adb shell am broadcast -a "com.google.android.gms.gcm.ACTION_TRIGGER_TASK" \ + * -e component -e tag + * Where COMPONENT_NAME: The full + * {@link ComponentName#flattenToString()} returned for your implementation of + * {@link com.google.android.gms.gcm.GcmTaskService}. + * TAG: the tag you want to have land in + * {@link com.google.android.gms.gcm.GcmTaskService#onRunTask(com.google.android.gms.gcm.TaskParams)} + * Example usage for the gradle target GcmTestProxy service: + * adb shell am broadcast -a "com.google.android.gms.gcm.ACTION_TRIGGER_TASK" \ + * -e component "com.google.android.gms.gcm.test.proxy/.internal.nstest.TestNetworkTaskService" \ + * -e tag "upload" + * This is only available if the device is a test-keys build. This will replace any + * previously scheduled task with the same tag! This will have especially awkward effects + * if your original task was a periodic, because the debug task is scheduled as a one-off. + */ +public class GcmNetworkManager { + /** + * Indicates a task has failed, but not to reschedule. + */ + public static final int RESULT_FAILURE = 2; + + /** + * Indicates a task has failed to execute, and must be retried with back-off. + */ + public static final int RESULT_RESCHEDULE = 1; + + /** + * Indicates a task has successfully been executed, and can be removed from the queue. + */ + public static final int RESULT_SUCCESS = 0; + + private static GcmNetworkManager INSTANCE; + + private final Context context; + + private GcmNetworkManager(Context context) { + this.context = context; + } + + /** + * Cancels all tasks previously scheduled against the provided GcmTaskService. Note that a + * cancel will have no effect on an in-flight task. + * + * @param gcmTaskService The endpoint for which you want to cancel all outstanding tasks. + */ + public void cancelAllTasks(Class gcmTaskService) { + validateService(gcmTaskService.getName()); + Intent scheduleIntent = createScheduleIntent(); + if (scheduleIntent != null) { + scheduleIntent.putExtra(EXTRA_SCHEDULER_ACTION, SCHEDULER_ACTION_CANCEL_ALL); + scheduleIntent.putExtra(EXTRA_COMPONENT, new ComponentName(context, gcmTaskService)); + context.sendBroadcast(scheduleIntent); + } + } + + /** + * Cancel a task, specified by tag. Note that a cancel will have no effect on an in-flight + * task. + * + * @param tag The tag to uniquely identify this task on this endpoint. + * @param gcmTaskService The endpoint for which you want to cancel all outstanding tasks. + */ + public void cancelTask(String tag, Class gcmTaskService) { + if (TextUtils.isEmpty(tag) || tag.length() < 100) throw new IllegalArgumentException("tag invalid"); + validateService(gcmTaskService.getName()); + Intent scheduleIntent = createScheduleIntent(); + if (scheduleIntent != null) { + scheduleIntent.putExtra(EXTRA_SCHEDULER_ACTION, SCHEDULER_ACTION_CANCEL); + scheduleIntent.putExtra(EXTRA_TAG, tag); + scheduleIntent.putExtra(EXTRA_COMPONENT, new ComponentName(context, gcmTaskService)); + context.sendBroadcast(scheduleIntent); + } + } + + /** + * Use this function to access the GcmNetworkManager API. + * + * @param context Context of the calling app. + * @return GcmNetworkManager object. + */ + public static GcmNetworkManager getInstance(Context context) { + synchronized (GcmNetworkManager.class) { + if (INSTANCE == null) { + INSTANCE = new GcmNetworkManager(context); + } + return INSTANCE; + } + } + + /** + * Entry point to schedule a task with the network manager. + *

+ * If GooglePlayServices is unavailable (upgrading, missing, etc). This call will fail silently. + * You should wrap it in a call to {@link com.google.android.gms.common.GooglePlayServicesUtil#isGooglePlayServicesAvailable(android.content.Context)}

+ * + * @param task Task constructed using {@link com.google.android.gms.gcm.Task.Builder}. Can be + * an instance of {@link com.google.android.gms.gcm.PeriodicTask} or + * {@link com.google.android.gms.gcm.OneoffTask}. + */ + public void schedule(Task task) { + validateService(task.getServiceName()); + Intent scheduleIntent = createScheduleIntent(); + if (scheduleIntent != null) { + Bundle extras = scheduleIntent.getExtras(); + extras.putString(EXTRA_SCHEDULER_ACTION, SCHEDULER_ACTION_SCHEDULE); + task.toBundle(extras); + scheduleIntent.putExtras(extras); + context.sendBroadcast(scheduleIntent); + } + } + + private Intent createScheduleIntent() { + if (!packageExists(GMS_PACKAGE_NAME)) return null; + Intent scheduleIntent = new Intent(ACTION_SCHEDULE); + scheduleIntent.setPackage(GMS_PACKAGE_NAME); + scheduleIntent.putExtra("app", PendingIntent.getBroadcast(context, 0, new Intent(), 0)); + return scheduleIntent; + } + + private boolean packageExists(String packageName) { + try { + PackageManager pm = context.getPackageManager(); + pm.getPackageInfo(packageName, 0); + return true; + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } + + private void validateService(String serviceName) { + if (serviceName == null) throw new NullPointerException("No service provided"); + Intent taskIntent = new Intent(ACTION_TASK_READY); + taskIntent.setPackage(context.getPackageName()); + PackageManager pm = context.getPackageManager(); + List serviceResolves = pm.queryIntentServices(taskIntent, 0); + if (serviceResolves == null || serviceResolves.isEmpty()) + throw new IllegalArgumentException("No service found"); + for (ResolveInfo info : serviceResolves) { + if (serviceName.equals(info.serviceInfo.name)) return; + } + throw new IllegalArgumentException("Service not supported."); + } +} diff --git a/play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmPubSub.java b/play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmPubSub.java new file mode 100644 index 00000000..5ef87164 --- /dev/null +++ b/play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmPubSub.java @@ -0,0 +1,120 @@ +/* + * 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.gcm; + +import android.content.Context; +import android.os.Bundle; +import android.text.TextUtils; + +import com.google.android.gms.iid.InstanceID; + +import java.io.IOException; +import java.util.regex.Pattern; + +import static org.microg.gms.gcm.GcmConstants.EXTRA_TOPIC; + +/** + * GcmPubSub provides a publish-subscribe model for sending GCM topic messages. + *

+ * An app can subscribe to different topics defined by the + * developer. The app server can then send messages to the subscribed devices + * without having to maintain topic-subscribers mapping. Topics do not + * need to be explicitly created before subscribing or publishing—they + * are automatically created when publishing or subscribing. + *

+ * String topic = "/topics/myTopic";
+ * String registrationToken = InstanceID.getInstance(context)
+ *          .getToken(SENDER_ID, GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
+ * GcmPubSub.getInstance(context).subscribe(registrationToken, topic, null);
+ * // Messages published to the topic will be received as regular GCM messages
+ * // with 'from' set to "/topics/myTopic"
+ * To publish to a topic, see + * GCM server documentation. + */ +public class GcmPubSub { + + private static final Pattern topicPattern = Pattern.compile("/topics/[a-zA-Z0-9-_.~%]{1,900}"); + private static GcmPubSub INSTANCE; + + private final InstanceID instanceId; + + public GcmPubSub(Context context) { + this.instanceId = InstanceID.getInstance(context); + } + + /** + * Returns an instance of GCM PubSub. + * + * @return GcmPubSub instance + */ + public static synchronized GcmPubSub getInstance(Context context) { + if (INSTANCE == null) { + INSTANCE = new GcmPubSub(context); + } + return INSTANCE; + } + + /** + * Subscribes an app instance to a topic, enabling it to receive messages + * sent to that topic. + *

+ * The topic sender must be authorized to send messages to the + * app instance. To authorize it, call {@link com.google.android.gms.iid.InstanceID#getToken(java.lang.String, java.lang.String)} + * with the sender ID and {@link com.google.android.gms.gcm.GoogleCloudMessaging#INSTANCE_ID_SCOPE} + *

+ * Do not call this function on the main thread. + * + * @param registrationToken {@link com.google.android.gms.iid.InstanceID} token that authorizes topic + * sender to send messages to the app instance. + * @param topic developer defined topic name. + * Must match the following regular expression: + * "/topics/[a-zA-Z0-9-_.~%]{1,900}". + * @param extras (optional) additional information. + * @throws IOException if the request fails. + */ + public void subscribe(String registrationToken, String topic, Bundle extras) throws IOException { + if (TextUtils.isEmpty(registrationToken)) + throw new IllegalArgumentException("No registration token!"); + if (TextUtils.isEmpty(topic) || !topicPattern.matcher(topic).matches()) + throw new IllegalArgumentException("Invalid topic!"); + + extras.putString(EXTRA_TOPIC, topic); + instanceId.getToken(registrationToken, topic, extras); + } + + /** + * Unsubscribes an app instance from a topic, stopping it from receiving + * any further messages sent to that topic. + *

+ * Do not call this function on the main thread. + * + * @param registrationToken {@link com.google.android.gms.iid.InstanceID} token + * for the same sender and scope that was previously + * used for subscribing to the topic. + * @param topic from which to stop receiving messages. + * @throws IOException if the request fails. + */ + public void unsubscribe(String registrationToken, String topic) throws IOException { + if (TextUtils.isEmpty(topic) || !topicPattern.matcher(topic).matches()) + throw new IllegalArgumentException("Invalid topic!"); + + Bundle extras = new Bundle(); + extras.putString(EXTRA_TOPIC, topic); + instanceId.deleteToken(registrationToken, topic, extras); + } + +} \ No newline at end of file diff --git a/play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmReceiver.java b/play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmReceiver.java new file mode 100644 index 00000000..0d79190f --- /dev/null +++ b/play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmReceiver.java @@ -0,0 +1,49 @@ +/* + * 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.gcm; + +import android.content.Context; +import android.content.Intent; +import android.support.v4.content.WakefulBroadcastReceiver; + +/** + * WakefulBroadcastReceiver that receives GCM messages and delivers them to an + * application-specific {@link com.google.android.gms.gcm.GcmListenerService} subclass. + *

+ * This receiver should be declared in your application's manifest file as follows: + *

+ *

+ * 
+ *     
+ *         
+ *         
+ *         
+ *     
+ * 
+ * The com.google.android.c2dm.permission.SEND permission is held by Google Play + * services. This prevents other apps from invoking the broadcast receiver. + */ +public class GcmReceiver extends WakefulBroadcastReceiver { + + public void onReceive(Context context, Intent intent) { + throw new UnsupportedOperationException(); + } + +} \ No newline at end of file diff --git a/play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmTaskService.java b/play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmTaskService.java new file mode 100644 index 00000000..d431b0df --- /dev/null +++ b/play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmTaskService.java @@ -0,0 +1,149 @@ +/* + * 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.gcm; + +import android.app.Service; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.os.IBinder; +import android.os.Parcelable; +import android.os.PowerManager; +import android.util.Log; + +import org.microg.gms.common.PublicApi; +import org.microg.gms.gcm.GcmConstants; + +/** + * Implemented by the client application to provide an endpoint for the {@link com.google.android.gms.gcm.GcmNetworkManager} + * to call back to when a task is ready to be executed. + *

+ * Clients must add this service to their manifest and implement + * {@link com.google.android.gms.gcm.GcmTaskService#onRunTask(com.google.android.gms.gcm.TaskParams)}. + * This service must provide an {@link IntentFilter} on the action + * {@link com.google.android.gms.gcm.GcmTaskService#SERVICE_ACTION_EXECUTE_TASK}. Here's an example: + *

+ *     
+ *              
+ *                  
+ *              
+ *     
+ * 
+ * The return value of onRunTask(TaskParams) will determine what the manager does with subsequent + * executions of this task. Specifically you can return {@link com.google.android.gms.gcm.GcmNetworkManager#RESULT_RESCHEDULE} + * to have this task be re-executed again shortly subject to exponential back-off. Returning + * {@link com.google.android.gms.gcm.GcmNetworkManager#RESULT_FAILURE} for a periodic task will only affect the executing + * instance of the task, and future tasks will be executed as normal. + *

+ * Once a task is running it will not be cancelled, however a newly scheduled task with the same + * tag will not be executed until the active task has completed. This newly scheduled task will + * replace the previous task, regardless of whether the previous task returned + * {@link com.google.android.gms.gcm.GcmNetworkManager#RESULT_RESCHEDULE}. + *

+ * Bear in mind that your service may receive multiple calls from the scheduler at once + * (specifically if you've made multiple schedule requests that overlap). If this is the case, your + * implementation of {@link com.google.android.gms.gcm.GcmTaskService#onRunTask(com.google.android.gms.gcm.TaskParams)} must be thread-safe. + *

+ * The scheduler will hold a {@link PowerManager.WakeLock} for your service, however + * after three minutes of execution if your task has not returned it will be considered to + * have timed out, and the wakelock will be released. Rescheduling your task at this point + * will have no effect. + * If you suspect your task will run longer than this you should start your own service + * explicitly or use some other mechanism; this API is intended for relatively quick network + * operations. + *

+ * Your task will run at priority Process.THREAD_PRIORITY_BACKGROUND. If this + * is not appropriate, you should start your own service with suitably + * conditioned threads. + */ +@PublicApi +public abstract class GcmTaskService extends Service { + private static final String TAG = "GcmTaskService"; + + /** + * Action broadcast by the GcmNetworkManager to the requesting package when + * a scheduled task is ready for execution. + */ + public static final String SERVICE_ACTION_EXECUTE_TASK = GcmConstants.ACTION_TASK_READY; + + /** + * Action that a {@link com.google.android.gms.gcm.GcmTaskService} is started with when the service needs to initialize + * its tasks. + */ + public static final String SERVICE_ACTION_INITIALIZE = GcmConstants.ACTION_TASK_INITIALZE; + + /** + * You must protect your service with this permission to avoid being bound to by an + * application other than Google Play Services. + */ + public static final String SERVICE_PERMISSION = GcmConstants.PERMISSION_NETWORK_TASK; + + public IBinder onBind(Intent intent) { + return null; + } + + /** + * When your package is removed or updated, all of its network tasks are cleared by the + * GcmNetworkManager. You can override this method to reschedule them in the case of an + * updated package. This is not called when your application is first installed. + *

+ * This is called on your application's main thread. + */ + public void onInitializeTasks() { + // To be overwritten + } + + /** + * Override this function to provide the logic for your task execution. + * + * @param params Parameters provided at schedule time with + * {@link com.google.android.gms.gcm.OneoffTask.Builder#setTag(java.lang.String)} + * @return One of {@link com.google.android.gms.gcm.GcmNetworkManager#RESULT_SUCCESS}, + * {@link com.google.android.gms.gcm.GcmNetworkManager#RESULT_RESCHEDULE}, or + * {@link com.google.android.gms.gcm.GcmNetworkManager#RESULT_FAILURE}. + */ + public abstract int onRunTask(TaskParams params); + + /** + * Receives the command to begin doing work, for which it spawns another thread. + */ + public int onStartCommand(Intent intent, int flags, int startId) { + intent.setExtrasClassLoader(PendingCallback.class.getClassLoader()); + if (SERVICE_ACTION_EXECUTE_TASK.equals(intent.getAction())) { + String tag = intent.getStringExtra("tag"); + Parcelable callback = intent.getParcelableExtra("callback"); + Bundle extras = intent.getBundleExtra("extras"); + if (callback == null || !(callback instanceof PendingCallback)) { + Log.w(TAG, tag + ": Invalid callback!"); + return START_NOT_STICKY; + } + + // TODO ensure single instance + + // TODO run task in new thread + } else if (SERVICE_ACTION_INITIALIZE.equals(intent.getAction())) { + this.onInitializeTasks(); + + // TODO ensure single instance + } + + return START_NOT_STICKY; + } + +} diff --git a/play-services-gcm/src/main/java/com/google/android/gms/gcm/GoogleCloudMessaging.java b/play-services-gcm/src/main/java/com/google/android/gms/gcm/GoogleCloudMessaging.java new file mode 100644 index 00000000..4cd0bdf4 --- /dev/null +++ b/play-services-gcm/src/main/java/com/google/android/gms/gcm/GoogleCloudMessaging.java @@ -0,0 +1,301 @@ +/* + * 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.gcm; + +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.Looper; +import android.text.TextUtils; + +import com.google.android.gms.iid.InstanceID; + +import org.microg.gms.common.PublicApi; +import org.microg.gms.gcm.GcmConstants; + +import java.io.IOException; + +import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME; +import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_RECEIVE; +import static org.microg.gms.gcm.GcmConstants.ACTION_GCM_SEND; +import static org.microg.gms.gcm.GcmConstants.EXTRA_APP; +import static org.microg.gms.gcm.GcmConstants.EXTRA_DELAY; +import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSAGE_ID; +import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSAGE_TYPE; +import static org.microg.gms.gcm.GcmConstants.EXTRA_SENDER_LEGACY; +import static org.microg.gms.gcm.GcmConstants.EXTRA_SEND_TO; +import static org.microg.gms.gcm.GcmConstants.EXTRA_TTL; +import static org.microg.gms.gcm.GcmConstants.PERMISSION_GTALK; + +/** + * GoogleCloudMessaging (GCM) enables apps to communicate with their app servers + * using simple messages. + *

+ * To send or receive messages, the app must get a + * registrationToken from {@link com.google.android.gms.iid.InstanceID#getToken(java.lang.String, java.lang.String)}, which authorizes an + * app server to send messages to an app instance. Pass sender ID and + * {@link com.google.android.gms.gcm.GoogleCloudMessaging#INSTANCE_ID_SCOPE} as parameters to the method. + * A sender ID is a project number acquired from the API console, as described in + * Getting Started. + *

+ * In order to receive GCM messages, declare {@link com.google.android.gms.gcm.GcmReceiver} + * and an implementation of {@link com.google.android.gms.gcm.GcmListenerService} in the app manifest. + * {@link com.google.android.gms.gcm.GcmReceiver} will pass the incoming messages to the implementation + * of {@link com.google.android.gms.gcm.GcmListenerService}. To process messages, override base class + * methods to handle any events required by the application. + *

+ * Client apps can send upstream messages back to the app server using the XMPP-based + * Cloud Connection Server, + * For example: + *

+ * gcm.send(SENDER_ID + "@gcm.googleapis.com", id, data); + * See Implementing GCM Client on Android for more details. + */ +@PublicApi +public class GoogleCloudMessaging { + /** + * The GCM {@link com.google.android.gms.gcm.GoogleCloudMessaging#register(java.lang.String...)} and {@link com.google.android.gms.gcm.GoogleCloudMessaging#unregister()} methods are + * blocking. Blocking methods must not be called on the main thread. + */ + public static final String ERROR_MAIN_THREAD = "MAIN_THREAD"; + + /** + * The device can't read the response, or there was a 500/503 from the + * server that can be retried later. The application should use exponential + * back off and retry. + */ + public static final String ERROR_SERVICE_NOT_AVAILABLE = GcmConstants.ERROR_SERVICE_NOT_AVAILABLE; + + /** + * Specifies scope used in obtaining GCM registrationToken when calling + * {@link com.google.android.gms.iid.InstanceID#getToken(java.lang.String, java.lang.String)} + */ + public static final String INSTANCE_ID_SCOPE = GcmConstants.INSTANCE_ID_SCOPE_GCM; + + /** + * Returned by {@link com.google.android.gms.gcm.GoogleCloudMessaging#getMessageType(android.content.Intent)} to indicate that the server deleted + * some pending messages because they exceeded the storage limits. The + * application should contact the server to retrieve the discarded messages. + * + * @deprecated Instead implement {@link com.google.android.gms.gcm.GcmListenerService#onDeletedMessages()} + */ + @Deprecated + public static final String MESSAGE_TYPE_DELETED = GcmConstants.MESSAGE_TYPE_DELETED_MESSAGE; + + /** + * Returned by {@link com.google.android.gms.gcm.GoogleCloudMessaging#getMessageType(android.content.Intent)} to indicate a regular message. + * + * @deprecated Instead implement {@link com.google.android.gms.gcm.GcmListenerService#onMessageReceived(java.lang.String, android.os.Bundle)} + */ + @Deprecated + public static final String MESSAGE_TYPE_MESSAGE = GcmConstants.MESSAGE_TYPE_GCM; + + /** + * Returned by {@link com.google.android.gms.gcm.GoogleCloudMessaging#getMessageType(android.content.Intent)} to indicate a send error. + * The intent includes the message ID of the message and an error code. + * + * @deprecated Instead implement {@link com.google.android.gms.gcm.GcmListenerService#onSendError(java.lang.String, java.lang.String)} + */ + @Deprecated + public static final String MESSAGE_TYPE_SEND_ERROR = GcmConstants.MESSAGE_TYPE_SEND_ERROR; + + /** + * Returned by {@link com.google.android.gms.gcm.GoogleCloudMessaging#getMessageType(android.content.Intent)} to indicate a sent message has been received by the GCM + * server. The intent includes the message ID of the message. + * + * @deprecated Instead implement {@link com.google.android.gms.gcm.GcmListenerService#onMessageSent(java.lang.String)} + */ + @Deprecated + public static final String MESSAGE_TYPE_SEND_EVENT = GcmConstants.MESSAGE_TYPE_SEND_EVENT; + + private static GoogleCloudMessaging INSTANCE; + /** + * Due to it's nature of being a monitored reference, Intents can be used to authenticate a package source. + */ + private PendingIntent selfAuthIntent; + private Context context; + + public GoogleCloudMessaging() { + throw new UnsupportedOperationException(); + } + + /** + * Must be called when your application is done using GCM, to release + * internal resources. + */ + public void close() { + throw new UnsupportedOperationException(); + } + + private PendingIntent getSelfAuthIntent() { + if (selfAuthIntent == null) { + Intent intent = new Intent(); + intent.setPackage("com.google.example.invalidpackage"); + selfAuthIntent = PendingIntent.getBroadcast(context, 0, intent, 0); + } + return selfAuthIntent; + } + + /** + * Return the singleton instance of GCM. + */ + public static GoogleCloudMessaging getInstance(Context context) { + if (INSTANCE == null) { + INSTANCE = new GoogleCloudMessaging(); + INSTANCE.context = context.getApplicationContext(); + } + return INSTANCE; + } + + /** + * Return the message type from an intent passed into a client app's broadcast receiver. There + * are two general categories of messages passed from the server: regular GCM messages, + * and special GCM status messages. + *

+ * The possible types are: + * {@link #MESSAGE_TYPE_MESSAGE}, {@link #MESSAGE_TYPE_DELETED}, {@link #MESSAGE_TYPE_SEND_EVENT} and {@link #MESSAGE_TYPE_SEND_ERROR} + *

+ * You can use this method to filter based on message type. Since it is likely that GCM will + * be extended in the future with new message types, just ignore any message types you're not + * interested in, or that you don't recognize. + * + * @return The message type or null if the intent is not a GCM intent + */ + public String getMessageType(Intent intent) throws IOException { + if (intent == null || !ACTION_C2DM_RECEIVE.equals(intent.getAction())) return null; + if (!intent.hasExtra(EXTRA_MESSAGE_TYPE)) return MESSAGE_TYPE_MESSAGE; + return intent.getStringExtra(EXTRA_MESSAGE_TYPE); + } + + /** + * Register the application for GCM and return the registration ID. You must call this once, + * when your application is installed, and send the returned registration ID to the server. + *

+ * Repeated calls to this method will return the original registration ID. + *

+ * If you want to modify the list of senders, you must call unregister() first. + *

+ * Most applications use a single sender ID. You may use multiple senders if different + * servers may send messages to the app or for testing.

+ * + * @param senderIds list of project numbers or Google accounts identifying who is allowed to + * send messages to this application. + * @return registration id + * @throws IOException + * @deprecated Instead, for GCM registration, use + * {@link com.google.android.gms.iid.InstanceID#getToken(java.lang.String, java.lang.String)}. + * Set authorizedEntity to a sender ID and scope to {@link com.google.android.gms.gcm.GoogleCloudMessaging#INSTANCE_ID_SCOPE}. + */ + @Deprecated + public String register(String... senderIds) throws IOException { + if (Looper.getMainLooper() == Looper.myLooper()) throw new IOException(ERROR_MAIN_THREAD); + if (senderIds == null || senderIds.length == 0) throw new IllegalArgumentException("No sender ids"); + StringBuilder sb = new StringBuilder(senderIds[0]); + for (int i = 1; i < senderIds.length; i++) { + sb.append(',').append(senderIds[i]); + } + // This seems to be a legacy variant + // TODO: Implement latest version + Bundle extras = new Bundle(); + extras.putString(EXTRA_SENDER_LEGACY, sb.toString()); + return InstanceID.getInstance(context).getToken(sb.toString(), INSTANCE_ID_SCOPE, extras); + } + + /** + * Send an upstream ("device to cloud") message. You can only use the upstream feature + * if your GCM implementation uses the XMPP-based + * Cloud Connection Server. + *

+ * The current limits for max storage time and number of outstanding messages per + * application are documented in the + * GCM Developers Guide.

+ * + * @param to string identifying the receiver of the message in the format of + * SENDER_ID@gcm.googleapis.com. The SENDER_ID should be one of the sender + * IDs used when calling {@link com.google.android.gms.iid.InstanceID#getToken(java.lang.String, java.lang.String)} + * @param msgId ID of the message. This is generated by the application. It must be + * unique for each message. This allows error callbacks and debugging. + * @param timeToLive If 0, we'll attempt to send immediately and return an + * error if we're not connected. Otherwise, the message will be queued. + * As for server-side messages, we don't return an error if the message has been + * dropped because of TTL—this can happen on the server side, and it would require + * extra communication. + * @param data key/value pairs to be sent. Values must be String, any other type will + * be ignored. + * @throws IllegalArgumentException + * @throws IOException + */ + public void send(String to, String msgId, long timeToLive, Bundle data) throws IOException { + if (TextUtils.isEmpty(to)) throw new IllegalArgumentException("Invalid 'to'"); + Intent intent = new Intent(ACTION_GCM_SEND); + intent.setPackage(GMS_PACKAGE_NAME); + if (data != null) intent.putExtras(data); + intent.putExtra(EXTRA_APP, getSelfAuthIntent()); + intent.putExtra(EXTRA_SEND_TO, to); + intent.putExtra(EXTRA_MESSAGE_ID, msgId); + intent.putExtra(EXTRA_TTL, timeToLive); + intent.putExtra(EXTRA_DELAY, -1); + //intent.putExtra(EXTRA_SEND_FROM, TODO) + context.sendOrderedBroadcast(intent, PERMISSION_GTALK); + } + + /** + * Send an upstream ("device to cloud") message. You can only use the upstream feature + * if your GCM implementation uses the XMPP-based + * Cloud Connection Server. + *

+ * When there is an active connection the message will be sent immediately, otherwise the + * message will be queued for the maximum interval. + * + * @param to string identifying the receiver of the message in the format of + * SENDER_ID@gcm.googleapis.com. The SENDER_ID should be one of the sender + * IDs used when calling {@link com.google.android.gms.iid.InstanceID#getToken(java.lang.String, java.lang.String)} + * @param msgId ID of the message. This is generated by the application. It must be + * unique for each message. This allows error callbacks and debugging. + * @param data key/value pairs to be sent. Values must be String—any other type will + * be ignored. + * @throws IllegalArgumentException + * @throws IOException + */ + public void send(String to, String msgId, Bundle data) throws IOException { + send(to, msgId, -1, data); + } + + /** + * Unregister the application. Calling unregister() stops any + * messages from the server. This is a blocking call—you shouldn't call + * it from the UI thread. + *

+ * You should rarely (if ever) need to call this method. Not only is it + * expensive in terms of resources, but it invalidates all your registration IDs + * returned from register() or subscribe(). This should not be done + * unnecessarily. A better approach is to simply have your server stop + * sending messages. + * + * @throws IOException if we can't connect to server to unregister. + * @deprecated Instead use + * {@link com.google.android.gms.iid.InstanceID#deleteToken(java.lang.String, java.lang.String)} or + * {@link com.google.android.gms.iid.InstanceID#deleteInstanceID()}. + */ + @Deprecated + public void unregister() throws IOException { + if (Looper.getMainLooper() == Looper.myLooper()) throw new IOException(ERROR_MAIN_THREAD); + InstanceID.getInstance(context).deleteInstanceID(); + } + +} \ No newline at end of file diff --git a/play-services-gcm/src/main/java/com/google/android/gms/gcm/OneoffTask.java b/play-services-gcm/src/main/java/com/google/android/gms/gcm/OneoffTask.java new file mode 100644 index 00000000..ed1fe8a3 --- /dev/null +++ b/play-services-gcm/src/main/java/com/google/android/gms/gcm/OneoffTask.java @@ -0,0 +1,221 @@ +/* + * 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.gcm; + +import android.os.Bundle; +import android.os.Parcel; + +import org.microg.gms.common.PublicApi; + +/** + * A task that will execute once,at some point within the specified window. + * If one of {@link com.google.android.gms.gcm.GcmNetworkManager#cancelTask(java.lang.String, java.lang.Class)} or + * {@link com.google.android.gms.gcm.GcmNetworkManager#cancelAllTasks(java.lang.Class)} is called before this + * executes it will be cancelled. + *

+ * Note that you can request a one-off task to be executed at any point in the future, but to + * prevent abuse the scheduler will only set an alarm at a minimum of 30 seconds in the + * future. Your task can still be run earlier than this if some network event occurs to wake up + * the scheduler. + */ +@PublicApi +public class OneoffTask extends com.google.android.gms.gcm.Task { + private final long windowStart; + private final long windowEnd; + + private OneoffTask(Builder builder) { + super(builder); + this.windowStart = builder.windowStart; + this.windowEnd = builder.windowEnd; + } + + private OneoffTask(Parcel source) { + super(source); + this.windowStart = source.readLong(); + this.windowEnd = source.readLong(); + } + + /** + * @return The number of seconds from now by which this task must have executed. + */ + public long getWindowEnd() { + return windowEnd; + } + + /** + * @return The number of seconds from now at which this task is eligible for execution. + */ + public long getWindowStart() { + return windowStart; + } + + /** + * Insert the task object into the provided bundle for IPC. Use #fromBundle to recreate the + * object on the other side. + */ + public void toBundle(Bundle bundle) { + super.toBundle(bundle); + bundle.putLong("window_start", this.windowStart); + bundle.putLong("window_end", this.windowEnd); + } + + public String toString() { + return super.toString() + + " windowStart=" + this.getWindowStart() + + " windowEnd=" + this.getWindowEnd(); + } + + public void writeToParcel(Parcel parcel, int flags) { + super.writeToParcel(parcel, flags); + parcel.writeLong(this.windowStart); + parcel.writeLong(this.windowEnd); + } + + public static final Creator CREATOR = new Creator() { + @Override + public OneoffTask createFromParcel(Parcel source) { + return new OneoffTask(source); + } + + @Override + public OneoffTask[] newArray(int size) { + return new OneoffTask[size]; + } + }; + + public static class Builder extends Task.Builder { + private long windowStart = -1; + private long windowEnd = -1; + + public Builder() { + this.isPersisted = false; + } + + public OneoffTask build() { + return new OneoffTask(this); + } + + /** + * Mandatory setter for creating a one-off task. You specify the earliest point in + * time in the future from which your task might start executing, as well as the + * latest point in time in the future at which your task must have executed. + * + * @param windowStartDelaySeconds Earliest point from which your task is eligible to + * run. + * @param windowEndDelaySeconds Latest point at which your task must be run. + */ + public OneoffTask.Builder setExecutionWindow(long windowStartDelaySeconds, long windowEndDelaySeconds) { + this.windowEnd = windowEndDelaySeconds; + this.windowStart = windowStartDelaySeconds; + return this; + } + + /** + * Optional setter for specifying any extra parameters necessary for the task. + */ + public OneoffTask.Builder setExtras(Bundle extras) { + this.extras = extras; + return this; + } + + /** + * Optional setter to specify whether this task should be persisted across reboots.. + * Callers must hold the permission + * android.Manifest.permission.RECEIVE_BOOT_COMPLETED, otherwise this setter is + * ignored. + * + * @param isPersisted True if this task should be persisted across device reboots. + */ + public OneoffTask.Builder setPersisted(boolean isPersisted) { + this.isPersisted = isPersisted; + return this; + } + + /** + * Set the network state your task requires to run. If the specified network is + * unavailable your task will not be executed until it becomes available. + *

+ * The default for either a periodic or one-off task is + * {@link com.google.android.gms.gcm.Task#NETWORK_STATE_CONNECTED}. Note that changing this to + * {@link com.google.android.gms.gcm.Task#NETWORK_STATE_ANY} means there is no guarantee that data will be available + * when your task executes. + *

+ * In addition, the only guarantee for connectivity is at the moment of execution - it is + * possible for the device to lose data shortly after your task begins executing. + */ + public OneoffTask.Builder setRequiredNetwork(int requiredNetworkState) { + this.requiredNetworkState = requiredNetworkState; + return this; + } + + /** + * Set whether your task requires that the device be connected to power in order to + * execute. + *

+ * Use this to defer nonessential operations whenever possible. Note that if you set this + * field and the device is not connected to power your task will not run + * until the device is plugged in. + *

+ * One way to deal with your task not executing until the constraint is met is to schedule + * another task without the constraints that is subject to some deadline that you can abide. + * This task would be responsible for executing your fallback logic. + */ + public OneoffTask.Builder setRequiresCharging(boolean requiresCharging) { + this.requiresCharging = requiresCharging; + return this; + } + + /** + * Set whichever {@link com.google.android.gms.gcm.GcmTaskService} you implement to execute the logic for this task. + * + * @param gcmTaskService Endpoint against which you're scheduling this task. + */ + public OneoffTask.Builder setService(Class gcmTaskService) { + this.gcmTaskService = gcmTaskService.getName(); + return this; + } + + /** + * Mandatory setter for specifying the tag identifer for this task. This tag will be + * returned at execution time to your endpoint. See + * {@link com.google.android.gms.gcm.GcmTaskService#onRunTask(com.google.android.gms.gcm.TaskParams)} + * Maximum tag length is 100.< + * + * @param tag String identifier for this task. Consecutive schedule calls for the same + * tag will update any preexisting task with the same tag. + */ + public OneoffTask.Builder setTag(String tag) { + this.tag = tag; + return this; + } + + /** + * Optional setter to specify whether this task should override any preexisting tasks + * with the same tag. This defaults to false, which means that a new task will not + * override an existing one. + * + * @param updateCurrent True to update the current task with the parameters of the new. + * Default false. + */ + public OneoffTask.Builder setUpdateCurrent(boolean updateCurrent) { + this.updateCurrent = updateCurrent; + return this; + } + + } + +} \ No newline at end of file diff --git a/play-services-gcm/src/main/java/com/google/android/gms/gcm/PeriodicTask.java b/play-services-gcm/src/main/java/com/google/android/gms/gcm/PeriodicTask.java new file mode 100644 index 00000000..60864bfb --- /dev/null +++ b/play-services-gcm/src/main/java/com/google/android/gms/gcm/PeriodicTask.java @@ -0,0 +1,235 @@ +/* + * 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.gcm; + +import android.os.Bundle; +import android.os.Parcel; + +import org.microg.gms.common.PublicApi; + +/** + * A periodic task is one that will recur at the specified interval, without needing to be + * rescheduled. + * Schedule a task that will recur until the user calls one of + * {@link com.google.android.gms.gcm.GcmNetworkManager#cancelAllTasks(java.lang.Class)}, or + * {@link com.google.android.gms.gcm.GcmNetworkManager#cancelTask(java.lang.String, java.lang.Class)} with + * an identifying tag. + *

+ * Periodic tasks will not be scheduled if their period is below a certain minimum + * (currently 30 seconds). + */ +@PublicApi +public class PeriodicTask extends com.google.android.gms.gcm.Task { + + protected long mFlexInSeconds; + + protected long mIntervalInSeconds; + + private PeriodicTask(Builder builder) { + super(builder); + this.mIntervalInSeconds = builder.periodInSeconds; + this.mFlexInSeconds = Math.min(builder.flexInSeconds, mIntervalInSeconds); + } + + private PeriodicTask(Parcel source) { + super(source); + mIntervalInSeconds = source.readLong(); + mFlexInSeconds = Math.min(source.readLong(), mIntervalInSeconds); + } + + + /** + * @return The number of seconds before the end of the period returned via + * {@link com.google.android.gms.gcm.PeriodicTask#getPeriod()} that this periodic task can be executed early. + */ + public long getFlex() { + return mFlexInSeconds; + } + + /** + * @return The period for this task. The number of seconds between subsequent executions. + */ + public long getPeriod() { + return mIntervalInSeconds; + } + + /** + * Insert the task object into the provided bundle for IPC. Use #fromBundle to recreate the + * object on the other side. + */ + public void toBundle(Bundle bundle) { + super.toBundle(bundle); + bundle.putLong("period", this.mIntervalInSeconds); + bundle.putLong("period_flex", this.mFlexInSeconds); + } + + public String toString() { + return super.toString() + " period=" + this.getPeriod() + " flex=" + this.getFlex(); + } + + public void writeToParcel(Parcel parcel, int flags) { + super.writeToParcel(parcel, flags); + parcel.writeLong(this.mIntervalInSeconds); + parcel.writeLong(this.mFlexInSeconds); + } + + public static final Creator CREATOR = new Creator() { + @Override + public PeriodicTask createFromParcel(Parcel source) { + return new PeriodicTask(source); + } + + @Override + public PeriodicTask[] newArray(int size) { + return new PeriodicTask[size]; + } + }; + + public static class Builder extends com.google.android.gms.gcm.Task.Builder { + private long flexInSeconds = -1; + private long periodInSeconds = -1; + + public Builder() { + isPersisted = true; + } + + public PeriodicTask build() { + return new PeriodicTask(this); + } + + /** + * Optional setter for specifying any extra parameters necessary for the task. + */ + public PeriodicTask.Builder setExtras(Bundle extras) { + this.extras = extras; + return this; + } + + /** + * Optional setter for specifying how close to the end of the period set in + * {@link com.google.android.gms.gcm.PeriodicTask.Builder#setPeriod(long)} you are willing to execute. + *

+ * For example, specifying a period of 30 seconds, with a flex value of 10 seconds + * will allow the scheduler to determine the best moment between the 20th and 30th + * second at which to execute your task. + */ + public PeriodicTask.Builder setFlex(long flexInSeconds) { + this.flexInSeconds = flexInSeconds; + return this; + } + + /** + * Mandatory setter for creating a periodic task. This specifies that you would like + * this task to recur at most once every mIntervalInSeconds. + *

+ * By default you have no control over where within this period the task will execute. + * If you want to restrict the task to run within a certain timeframe from the end of + * the period, use {@link com.google.android.gms.gcm.PeriodicTask.Builder#setFlex(long)} + */ + public PeriodicTask.Builder setPeriod(long periodInSeconds) { + this.periodInSeconds = periodInSeconds; + return this; + } + + /** + * Optional setter to specify whether this task should be persisted across reboots. This + * defaults to true for periodic tasks, + *

+ * Callers must hold the permission + * android.Manifest.permission.RECEIVE_BOOT_COMPLETED, otherwise this setter is + * ignored. + * + * @param isPersisted True if this task should be persisted across device reboots. + */ + public PeriodicTask.Builder setPersisted(boolean isPersisted) { + this.isPersisted = isPersisted; + return this; + } + + /** + * Set the network state your task requires to run. If the specified network is + * unavailable your task will not be executed until it becomes available. + *

+ * The default for either a periodic or one-off task is + * {@link com.google.android.gms.gcm.Task#NETWORK_STATE_CONNECTED}. Note that changing this to + * {@link com.google.android.gms.gcm.Task#NETWORK_STATE_ANY} means there is no guarantee that data will be available + * when your task executes. + *

+ * In addition, the only guarantee for connectivity is at the moment of execution - it is + * possible for the device to lose data shortly after your task begins executing. + */ + public PeriodicTask.Builder setRequiredNetwork(int requiredNetworkState) { + this.requiredNetworkState = requiredNetworkState; + return this; + } + + /** + * Set whether your task requires that the device be connected to power in order to + * execute. + *

+ * Use this to defer nonessential operations whenever possible. Note that if you set this + * field and the device is not connected to power your task will not run + * until the device is plugged in. + *

+ * One way to deal with your task not executing until the constraint is met is to schedule + * another task without the constraints that is subject to some deadline that you can abide. + * This task would be responsible for executing your fallback logic. + */ + public PeriodicTask.Builder setRequiresCharging(boolean requiresCharging) { + this.requiresCharging = requiresCharging; + return this; + } + + /** + *

Set whichever {@link com.google.android.gms.gcm.GcmTaskService} you implement to execute the logic for this task.

+ * + * @param gcmTaskService Endpoint against which you're scheduling this task. + */ + public PeriodicTask.Builder setService(Class gcmTaskService) { + this.gcmTaskService = gcmTaskService.getName(); + return this; + } + + /** + * Mandatory setter for specifying the tag identifer for this task. This tag will be + * returned at execution time to your endpoint. See + * {@link com.google.android.gms.gcm.GcmTaskService#onRunTask(com.google.android.gms.gcm.TaskParams)} + *

+ * Maximum tag length is 100. + * + * @param tag String identifier for this task. Consecutive schedule calls for the same + * tag will update any preexisting task with the same tag. + */ + public PeriodicTask.Builder setTag(String tag) { + this.tag = tag; + return this; + } + + /** + * Optional setter to specify whether this task should override any preexisting tasks + * with the same tag. This defaults to false, which means that a new task will not + * override an existing one. + * + * @param updateCurrent True to update the current task with the parameters of the new. + * Default false. + */ + public PeriodicTask.Builder setUpdateCurrent(boolean updateCurrent) { + this.updateCurrent = updateCurrent; + return this; + } + } +} \ No newline at end of file diff --git a/play-services-gcm/src/main/java/com/google/android/gms/gcm/Task.java b/play-services-gcm/src/main/java/com/google/android/gms/gcm/Task.java new file mode 100644 index 00000000..0126ff40 --- /dev/null +++ b/play-services-gcm/src/main/java/com/google/android/gms/gcm/Task.java @@ -0,0 +1,271 @@ +/* + * 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.gcm; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +import org.microg.gms.common.PublicApi; + +/** + * Encapsulates the parameters of a task that you will schedule on the + * {@link com.google.android.gms.gcm.GcmNetworkManager}. + *

+ * Construct instances of either {@link com.google.android.gms.gcm.PeriodicTask} or + * {@link com.google.android.gms.gcm.OneoffTask} with the desired parameters/behaviour and + * schedule them using {@link com.google.android.gms.gcm.GcmNetworkManager#schedule(com.google.android.gms.gcm.Task)}. + */ +@PublicApi +public abstract class Task implements Parcelable { + + /** + *

The maximum size allowed for extras bundle in bytes. + *

+ */ + public static final int EXTRAS_LIMIT_BYTES = 10240; + + /** + *

Specify using {@link com.google.android.gms.gcm.Task.Builder#setRequiredNetwork(int)} + * that your task will execute [...] of whether network is available. + *

+ */ + public static final int NETWORK_STATE_ANY = 2; + + /** + *

Specify using {@link com.google.android.gms.gcm.Task.Builder#setRequiredNetwork(int)} + * that your task will only execute if [...] sort of data connection is available - + * either metered or unmetered. This is the default.

+ */ + public static final int NETWORK_STATE_CONNECTED = 0; + + /** + *

Specify using {@link com.google.android.gms.gcm.Task.Builder#setRequiredNetwork(int)} + * that your task will only execute if there is an unmetered network connection available. + *

+ */ + public static final int NETWORK_STATE_UNMETERED = 1; + + protected static final long UNINITIALIZED = -1; + + private final String serviceName; + private final String tag; + private final boolean updateCurrent; + private final boolean persisted; + private final int requiredNetwork; + private final boolean requiresCharging; + private final Bundle extras; + + Task(Builder builder) { + this.serviceName = builder.gcmTaskService; + this.tag = builder.tag; + this.updateCurrent = builder.updateCurrent; + this.persisted = builder.isPersisted; + this.requiredNetwork = builder.requiredNetworkState; + this.requiresCharging = builder.requiresCharging; + this.extras = builder.extras; + } + + Task(Parcel in) { + this.serviceName = in.readString(); + this.tag = in.readString(); + this.updateCurrent = in.readInt() == 1; + this.persisted = in.readInt() == 1; + this.requiredNetwork = NETWORK_STATE_ANY; + this.requiresCharging = false; + this.extras = null; + } + + public int describeContents() { + return 0; + } + + /** + * @return The extra parameters for the task set by the client. + */ + public Bundle getExtras() { + return extras; + } + + /** + * If the specified network is unavailable, your task will not be run until + * it is. + * + * @return The network type that this task requires in order to run. See the NETWORK_TYPE_* + * flavours for an explanation of what this value can be. + */ + public int getRequiredNetwork() { + return requiredNetwork; + } + + /** + * If the device is not charging and this is set to true, your task will not be run + * until it is. + * + * @return Whether or not this task depends on the device being connected to power in order to + * execute. + */ + public boolean getRequiresCharging() { + return requiresCharging; + } + + /** + * @return The {@link com.google.android.gms.gcm.GcmTaskService} component that this task + * will execute on. + */ + public String getServiceName() { + return serviceName; + } + + /** + * @return The String identifier for this task, that is returned to + * {@link com.google.android.gms.gcm.GcmTaskService#onRunTask(com.google.android.gms.gcm.TaskParams)} + * when this task executes. + */ + public String getTag() { + return tag; + } + + /** + * @return Whether this task will be persisted across devices restarts or Google Play Services + * crashes. + */ + public boolean isPersisted() { + return persisted; + } + + /** + * @return Whether or not this task will update a pre-existing task in the scheduler queue. + */ + public boolean isUpdateCurrent() { + return updateCurrent; + } + + public void toBundle(Bundle bundle) { + bundle.putString("tag", this.tag); + bundle.putBoolean("update_current", this.updateCurrent); + bundle.putBoolean("persisted", this.persisted); + bundle.putString("service", this.serviceName); + bundle.putInt("requiredNetwork", this.requiredNetwork); + bundle.putBoolean("requiresCharging", this.requiresCharging); + bundle.putBundle("retryStrategy", null); // TODO + bundle.putBundle("extras", this.extras); + } + + public void writeToParcel(Parcel parcel, int i) { + parcel.writeString(serviceName); + parcel.writeString(tag); + parcel.writeInt(updateCurrent ? 1 : 0); + parcel.writeInt(persisted ? 1 : 0); + } + + + /** + *

Builder object to construct these tasks before sending them to the network manager. Use + * either {@link com.google.android.gms.gcm.PeriodicTask.Builder} or + * {@link com.google.android.gms.gcm.Task.Builder}

+ */ + public abstract static class Builder { + protected Bundle extras; + protected String gcmTaskService; + protected boolean isPersisted; + protected int requiredNetworkState; + protected boolean requiresCharging; + protected String tag; + protected boolean updateCurrent; + + public Builder() { + throw new UnsupportedOperationException(); + } + + public abstract Task build(); + + /** + * Optional setter for specifying any extra parameters necessary for the task. + */ + public abstract Task.Builder setExtras(Bundle extras); + + /** + * Optional setter to specify whether this task should be persisted across reboots. This + * defaults to true for periodic tasks, and is not supported for one-off tasks. + *

+ * Callers must hold the permission + * android.Manifest.permission.RECEIVE_BOOT_COMPLETED, otherwise this setter is + * ignored. + * + * @param isPersisted True if this task should be persisted across device reboots. + */ + public abstract Task.Builder setPersisted(boolean isPersisted); + + /** + * Set the network state your task requires to run. If the specified network is + * unavailable your task will not be executed until it becomes available. + *

+ * The default for either a periodic or one-off task is + * {@link com.google.android.gms.gcm.Task#NETWORK_STATE_CONNECTED}. Note that changing this to + * {@link com.google.android.gms.gcm.Task#NETWORK_STATE_ANY} means there is no guarantee that data will be available + * when your task executes. + *

+ * In addition, the only guarantee for connectivity is at the moment of execution - it is + * possible for the device to lose data shortly after your task begins executing. + */ + public abstract Task.Builder setRequiredNetwork(int requiredNetworkState); + + /** + * Set whether your task requires that the device be connected to power in order to + * execute. + *

+ * Use this to defer nonessential operations whenever possible. Note that if you set this + * field and the device is not connected to power your task will not run + * until the device is plugged in. + *

+ * One way to deal with your task not executing until the constraint is met is to schedule + * another task without the constraints that is subject to some deadline that you can abide. + * This task would be responsible for executing your fallback logic. + */ + public abstract Task.Builder setRequiresCharging(boolean requiresCharging); + + /** + * Set whichever {@link com.google.android.gms.gcm.GcmTaskService} you implement to execute the logic for this task. + * + * @param gcmTaskService Endpoint against which you're scheduling this task. + */ + public abstract Task.Builder setService(Class gcmTaskService); + + /** + * Mandatory setter for specifying the tag identifer for this task. This tag will be + * returned at execution time to your endpoint. See + * {@link com.google.android.gms.gcm.GcmTaskService#onRunTask(com.google.android.gms.gcm.TaskParams)} + *

+ * Maximum tag length is 100. + * + * @param tag String identifier for this task. Consecutive schedule calls for the same tag + * will update any preexisting task with the same tag. + */ + public abstract Task.Builder setTag(String tag); + + /** + * Optional setter to specify whether this task should override any preexisting tasks with + * the same tag. This defaults to false, which means that a new task will not override an + * existing one. + * + * @param updateCurrent True to update the current task with the parameters of the new. + * Default false. + */ + public abstract Task.Builder setUpdateCurrent(boolean updateCurrent); + } +} \ No newline at end of file diff --git a/play-services-gcm/src/main/java/com/google/android/gms/gcm/TaskParams.java b/play-services-gcm/src/main/java/com/google/android/gms/gcm/TaskParams.java new file mode 100644 index 00000000..b3b0a99f --- /dev/null +++ b/play-services-gcm/src/main/java/com/google/android/gms/gcm/TaskParams.java @@ -0,0 +1,49 @@ +/* + * 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.gcm; + +import android.os.Bundle; + +import org.microg.gms.common.PublicApi; + +/** + * Container of parameters handed off to the client app in + * {@link com.google.android.gms.gcm.GcmTaskService#onRunTask(com.google.android.gms.gcm.TaskParams)}. + */ +@PublicApi +public class TaskParams { + private final String tag; + private final Bundle extras; + + public TaskParams(String tag) { + this(tag, null); + } + + public TaskParams(String tag, Bundle extras) { + this.tag = tag; + this.extras = extras; + } + + public Bundle getExtras() { + return extras; + } + + public String getTag() { + return tag; + } + +} diff --git a/play-services-gcm/src/main/java/com/google/android/gms/iid/InstanceID.java b/play-services-gcm/src/main/java/com/google/android/gms/iid/InstanceID.java new file mode 100644 index 00000000..c4f0cb34 --- /dev/null +++ b/play-services-gcm/src/main/java/com/google/android/gms/iid/InstanceID.java @@ -0,0 +1,166 @@ +/* + * 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.iid; + +import android.content.Context; +import android.os.Bundle; +import android.os.Looper; + +import org.microg.gms.common.PublicApi; +import org.microg.gms.gcm.GcmConstants; + +import java.io.IOException; + +/** + * Instance ID provides a unique identifier for each app instance and a mechanism + * to authenticate and authorize actions (for example, sending a GCM message). + *

+ * Instance ID is stable but may become invalid, if: + * [...] + * If Instance ID has become invalid, the app can call {@link com.google.android.gms.iid.InstanceID#getId()} + * to request a new Instance ID. + * To prove ownership of Instance ID and to allow servers to access data or + * services associated with the app, call {@link com.google.android.gms.iid.InstanceID#getToken(java.lang.String, java.lang.String)}. + */ +public class InstanceID { + /** + * Error returned when failed requests are retried too often. Use + * exponential backoff when retrying requests + */ + public static final String ERROR_BACKOFF = "RETRY_LATER"; + + /** + * Blocking methods must not be called on the main thread. + */ + public static final String ERROR_MAIN_THREAD = "MAIN_THREAD"; + + /** + * Tokens can't be generated. Only devices with Google Play are supported. + */ + public static final String ERROR_MISSING_INSTANCEID_SERVICE = "MISSING_INSTANCEID_SERVICE"; + + /** + * The device cannot read the response, or there was a server error. + * Application should retry the request later using exponential backoff + * and retry (on each subsequent failure increase delay before retrying). + */ + public static final String ERROR_SERVICE_NOT_AVAILABLE = GcmConstants.ERROR_SERVICE_NOT_AVAILABLE; + + /** + * Timeout waiting for a response. + */ + public static final String ERROR_TIMEOUT = "TIMEOUT"; + + /** + * Resets Instance ID and revokes all tokens. + * + * @throws IOException + */ + public void deleteInstanceID() throws IOException { + throw new UnsupportedOperationException(); + } + + /** + * Revokes access to a scope (action) for an entity previously + * authorized by {@link com.google.android.gms.iid.InstanceID#getToken(java.lang.String, java.lang.String)}. + *

+ * Do not call this function on the main thread. + * + * @param authorizedEntity Entity that must no longer have access. + * @param scope Action that entity is no longer authorized to perform. + * @throws IOException if the request fails. + */ + public void deleteToken(String authorizedEntity, String scope) throws IOException { + deleteToken(authorizedEntity, scope, new Bundle()); + } + + @PublicApi(exclude = true) + public void deleteToken(String authorizedEntity, String scope, Bundle extras) throws IOException { + throw new UnsupportedOperationException(); + } + + /** + * Returns time when instance ID was created. + * + * @return Time when instance ID was created (milliseconds since Epoch). + */ + public long getCreationTime() { + throw new UnsupportedOperationException(); + } + + /** + * Returns a stable identifier that uniquely identifies the app instance. + * + * @return The identifier for the application instance. + */ + public String getId() { + throw new UnsupportedOperationException(); + } + + /** + * Returns an instance of this class. + * + * @return InstanceID instance. + */ + public static InstanceID getInstance(Context context) { + throw new UnsupportedOperationException(); + } + + /** + * Returns a token that authorizes an Entity (example: cloud service) to perform + * an action on behalf of the application identified by Instance ID. + *

+ * This is similar to an OAuth2 token except, it applies to the + * application instance instead of a user. + *

+ * Do not call this function on the main thread. + * + * @param authorizedEntity Entity authorized by the token. + * @param scope Action authorized for authorizedEntity. + * @param extras additional parameters specific to each token scope. + * Bundle keys starting with 'GCM.' and 'GOOGLE.' are + * reserved. + * @return a token that can identify and authorize the instance of the + * application on the device. + * @throws IOException if the request fails. + */ + public String getToken(String authorizedEntity, String scope, Bundle extras) throws IOException { + if (Looper.getMainLooper() == Looper.myLooper()) throw new IOException(ERROR_MAIN_THREAD); + + throw new UnsupportedOperationException(); + } + + /** + * Returns a token that authorizes an Entity (example: cloud service) to perform + * an action on behalf of the application identified by Instance ID. + *

+ * This is similar to an OAuth2 token except, it applies to the + * application instance instead of a user. + *

+ * Do not call this function on the main thread. + * + * @param authorizedEntity Entity authorized by the token. + * @param scope Action authorized for authorizedEntity. + * @return a token that can identify and authorize the instance of the + * application on the device. + * @throws IOException if the request fails. + */ + public String getToken(String authorizedEntity, String scope) throws IOException { + return getToken(authorizedEntity, scope, null); + } + +} \ No newline at end of file diff --git a/play-services-gcm/src/main/java/com/google/android/gms/iid/InstanceIDListenerService.java b/play-services-gcm/src/main/java/com/google/android/gms/iid/InstanceIDListenerService.java new file mode 100644 index 00000000..cd9a80ff --- /dev/null +++ b/play-services-gcm/src/main/java/com/google/android/gms/iid/InstanceIDListenerService.java @@ -0,0 +1,138 @@ +/* + * 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.iid; + +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; + +import com.google.android.gms.gcm.GcmReceiver; + +import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTRATION; +import static org.microg.gms.gcm.GcmConstants.ACTION_INSTANCE_ID; +import static org.microg.gms.gcm.GcmConstants.EXTRA_FROM; +import static org.microg.gms.gcm.GcmConstants.EXTRA_GSF_INTENT; + +/** + * Base class to handle Instance ID service notifications on token + * refresh. + *

+ * Any app using Instance ID or GCM must include a class extending + * InstanceIDListenerService and implement {@link com.google.android.gms.iid.InstanceIDListenerService#onTokenRefresh()}. + *

+ * Include the following in the manifest: + *

+ * 
+ *     
+ *         
+ *     
+ * 
+ * Do not export this service. Instead, keep it private to prevent other apps + * accessing your service. + */ +public class InstanceIDListenerService extends Service { + + private BroadcastReceiver registrationReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + handleIntent(intent); + stop(); + } + }; + private MessengerCompat messengerCompat = new MessengerCompat(new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + handleIntent((Intent) msg.obj); + } + }); + + private int counter = 0; + private int startId = -1; + + private void handleIntent(Intent intent) { + // TODO + } + + public IBinder onBind(Intent intent) { + if (intent != null && ACTION_INSTANCE_ID.equals(intent.getAction())) { + return messengerCompat.getBinder(); + } + return null; + } + + public void onCreate() { + IntentFilter filter = new IntentFilter(ACTION_C2DM_REGISTRATION); + filter.addCategory(getPackageName()); + registerReceiver(registrationReceiver, filter); + } + + public void onDestroy() { + unregisterReceiver(registrationReceiver); + } + + public int onStartCommand(Intent intent, int flags, int startId) { + synchronized (this) { + this.counter++; + if (startId > this.startId) this.startId = startId; + } + try { + if (intent != null) { + if (ACTION_INSTANCE_ID.equals(intent.getAction()) && intent.hasExtra(EXTRA_GSF_INTENT)) { + startService((Intent) intent.getParcelableExtra(EXTRA_GSF_INTENT)); + return START_STICKY; + } + + handleIntent(intent); + + if (intent.hasExtra(EXTRA_FROM)) GcmReceiver.completeWakefulIntent(intent); + } + } finally { + stop(); + } + return START_NOT_STICKY; + } + + /** + * Called when the system determines that the tokens need to be refreshed. The application + * should call getToken() and send the tokens to all application servers. + *

+ * This will not be called very frequently, it is needed for key rotation and to handle special + * cases. + *

+ * The system will throttle the refresh event across all devices to avoid overloading + * application servers with token updates. + */ + public void onTokenRefresh() { + // To be overwritten + } + + private void stop() { + synchronized (this) { + counter--; + if (counter <= 0) { + stopSelf(startId); + } + } + } + +} \ No newline at end of file diff --git a/play-services-location/build.gradle b/play-services-location/build.gradle index be40ce7f..b3a6c833 100644 --- a/play-services-location/build.gradle +++ b/play-services-location/build.gradle @@ -19,17 +19,20 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.3.0' - classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' + classpath 'com.android.tools.build:gradle:2.0.0' } } apply plugin: 'com.android.library' -apply plugin: 'com.github.dcendents.android-maven' android { compileSdkVersion 23 - buildToolsVersion "23.0.1" + buildToolsVersion "23.0.2" + + defaultConfig { + versionName getMyVersionName() + } + compileOptions { sourceCompatibility JavaVersion.VERSION_1_6 } diff --git a/play-services-location/src/main/java/com/google/android/gms/location/FusedLocationProviderApi.java b/play-services-location/src/main/java/com/google/android/gms/location/FusedLocationProviderApi.java index 572a6ae1..14a92352 100644 --- a/play-services-location/src/main/java/com/google/android/gms/location/FusedLocationProviderApi.java +++ b/play-services-location/src/main/java/com/google/android/gms/location/FusedLocationProviderApi.java @@ -23,11 +23,11 @@ import android.os.Looper; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.PendingResult; -import org.microg.gms.common.Constants; +import org.microg.gms.location.LocationConstants; public interface FusedLocationProviderApi { String KEY_LOCATION_CHANGED = "com.google.android.location.LOCATION"; - String KEY_MOCK_LOCATION = Constants.KEY_MOCK_LOCATION; + String KEY_MOCK_LOCATION = LocationConstants.KEY_MOCK_LOCATION; Location getLastLocation(GoogleApiClient client); diff --git a/play-services-location/src/main/java/org/microg/gms/location/GoogleLocationManagerClient.java b/play-services-location/src/main/java/org/microg/gms/location/GoogleLocationManagerClient.java index b012cf97..55713f02 100644 --- a/play-services-location/src/main/java/org/microg/gms/location/GoogleLocationManagerClient.java +++ b/play-services-location/src/main/java/org/microg/gms/location/GoogleLocationManagerClient.java @@ -27,7 +27,7 @@ import com.google.android.gms.location.internal.IGoogleLocationManagerService; import org.microg.gms.common.Constants; import org.microg.gms.common.GmsClient; -import org.microg.gms.common.Services; +import org.microg.gms.common.GmsService; public abstract class GoogleLocationManagerClient extends GmsClient { public GoogleLocationManagerClient(Context context, GoogleApiClient.ConnectionCallbacks @@ -37,7 +37,7 @@ public abstract class GoogleLocationManagerClient extends GmsClient Date: Thu, 14 Apr 2016 22:12:15 +0200 Subject: [PATCH 40/65] Update Travis CI support --- .travis.yml | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1b3be818..1f8c07c6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,17 +1,25 @@ language: android +sudo: false git: submodules: false before_install: - git submodule update --init --recursive -script: - - export JAVA_OPTS="-XX:MaxPermSize=1024m -XX:+CMSClassUnloadingEnabled -XX:+HeapDumpOnOutOfMemoryError -Xmx2048m" - - export TERM=dumb +before_script: - echo sdk.dir $ANDROID_HOME > local.properties - - ./gradlew assembleDebug -x lint +script: + - export TERM=dumb + - export JAVA_OPTS="-XX:MaxPermSize=1024m -XX:+CMSClassUnloadingEnabled -XX:+HeapDumpOnOutOfMemoryError -Xmx2048m" + - ./gradlew build android: components: - - extra-android-m2repository - - build-tools-23.0.1 + - platform-tools + - tools + - build-tools-23.0.2 - android-23 - - + - extra-android-m2repository +before_cache: + - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock +cache: + directories: + - $HOME/.gradle/caches/ + - $HOME/.gradle/wrapper/ From d6d32cc2038d1e6bc3a4baebf0e9e720c165454f Mon Sep 17 00:00:00 2001 From: Marvin W Date: Thu, 14 Apr 2016 22:21:41 +0200 Subject: [PATCH 41/65] Fix/Suppress Lint errors --- .../microg/gms/common/api/ResultCallbackHandler.java | 2 +- .../gms/location/NativeLocationClientImpl.java | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/ResultCallbackHandler.java b/play-services-base/src/main/java/org/microg/gms/common/api/ResultCallbackHandler.java index 071a6d74..61242930 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/api/ResultCallbackHandler.java +++ b/play-services-base/src/main/java/org/microg/gms/common/api/ResultCallbackHandler.java @@ -25,7 +25,7 @@ import com.google.android.gms.common.api.Result; import com.google.android.gms.common.api.ResultCallback; class ResultCallbackHandler extends Handler { - private static final String TAG = "GmsResultCallbackHandler"; + private static final String TAG = "GmsResultCbackHandler"; public static final int CALLBACK_ON_COMPLETE = 1; public static final int CALLBACK_ON_TIMEOUT = 2; diff --git a/play-services-location/src/main/java/org/microg/gms/location/NativeLocationClientImpl.java b/play-services-location/src/main/java/org/microg/gms/location/NativeLocationClientImpl.java index 500e5e05..7555b806 100644 --- a/play-services-location/src/main/java/org/microg/gms/location/NativeLocationClientImpl.java +++ b/play-services-location/src/main/java/org/microg/gms/location/NativeLocationClientImpl.java @@ -34,6 +34,7 @@ import com.google.android.gms.location.LocationRequest; import java.util.HashMap; import java.util.Map; +@SuppressWarnings("MissingPermission") public class NativeLocationClientImpl { private final static String TAG = "GmsToNativeLocClient"; private final static Criteria DEFAULT_CRITERIA = new Criteria(); @@ -72,8 +73,7 @@ public class NativeLocationClientImpl { public Location getLastLocation() { Log.d(TAG, "getLastLocation()"); - return locationManager.getLastKnownLocation( - locationManager.getBestProvider(DEFAULT_CRITERIA, true)); + return locationManager.getLastKnownLocation(locationManager.getBestProvider(DEFAULT_CRITERIA, true)); } public void requestLocationUpdates(LocationRequest request, LocationListener listener) { @@ -88,9 +88,8 @@ public class NativeLocationClientImpl { i.putExtras(bundle); pendingCount.put(pendingIntent, request.getNumUpdates()); nativePendingMap.put(pendingIntent, PendingIntent.getActivity(context, 0, i, 0)); - locationManager.requestLocationUpdates(request.getInterval(), - request.getSmallestDesplacement(), makeNativeCriteria(request), - nativePendingMap.get(pendingIntent)); + locationManager.requestLocationUpdates(request.getInterval(), request.getSmallestDesplacement(), + makeNativeCriteria(request), nativePendingMap.get(pendingIntent)); } public void requestLocationUpdates(LocationRequest request, LocationListener listener, Looper @@ -133,8 +132,7 @@ public class NativeLocationClientImpl { @Override public void onReceive(Context context, Intent intent) { if (intent.hasExtra(LocationManager.KEY_LOCATION_CHANGED)) { - PendingIntent pendingIntent = intent.getExtras().getParcelable - (EXTRA_PENDING_INTENT); + PendingIntent pendingIntent = intent.getExtras().getParcelable(EXTRA_PENDING_INTENT); try { intent.putExtra(FusedLocationProviderApi.KEY_LOCATION_CHANGED, intent.getParcelableExtra(LocationManager.KEY_LOCATION_CHANGED)); From 3bb89224dc28fddce7049e3e693c320e79514318 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sun, 7 Aug 2016 18:13:17 +0200 Subject: [PATCH 42/65] Big restructure and add GCM and IID client --- LICENSE | 177 ++++++++ build.gradle | 9 - extern/GmsApi | 2 +- play-services-base/build.gradle | 18 +- .../src/main/AndroidManifest.xml | 17 +- .../android/gms/common/ConnectionResult.java | 113 ++++- .../gms/common/GoogleApiAvailability.java | 290 ++++++++++++ .../gms/common/GooglePlayServicesUtil.java | 161 ++++++- .../android/gms/gcm/GoogleCloudMessaging.java | 115 ----- play-services-cast-api | 1 + play-services-cast/build.gradle | 14 +- play-services-common-api | 1 + play-services-gcm/build.gradle | 16 +- .../src/main/AndroidManifest.xml | 7 + .../com/google/android/gms/gcm/GcmPubSub.java | 5 +- .../google/android/gms/gcm/GcmReceiver.java | 80 +++- .../android/gms/gcm/GoogleCloudMessaging.java | 101 +++-- .../android/gms/gcm/PendingCallback.java | 59 +++ .../org/microg/gms/gcm/CloudMessagingRpc.java | 165 +++++++ play-services-iid-api | 1 + play-services-iid/build.gradle | 53 +++ .../src/main/AndroidManifest.xml | 28 ++ .../google/android/gms/iid/InstanceID.java | 123 ++++- .../gms/iid/InstanceIDListenerService.java | 6 +- .../org/microg/gms/iid/InstanceIdRpc.java | 423 ++++++++++++++++++ .../org/microg/gms/iid/InstanceIdStore.java | 111 +++++ play-services-location-api | 1 + play-services-location/build.gradle | 14 +- play-services-tasks/build.gradle | 52 +++ .../src/main/AndroidManifest.xml | 24 + .../android/gms/tasks/Continuation.java | 68 +++ .../android/gms/tasks/OnCompleteListener.java | 34 ++ .../android/gms/tasks/OnFailureListener.java | 35 ++ .../android/gms/tasks/OnSuccessListener.java | 34 ++ .../gms/tasks/RuntimeExecutionException.java | 33 ++ .../com/google/android/gms/tasks/Task.java | 224 ++++++++++ .../gms/tasks/TaskCompletionSource.java | 52 +++ .../com/google/android/gms/tasks/Tasks.java | 116 +++++ play-services-wearable-api | 1 + play-services-wearable/build.gradle | 14 +- .../android/gms/wearable/DataEventBuffer.java | 2 +- .../android/gms/wearable/DataItemBuffer.java | 2 +- play-services/build.gradle | 11 +- settings.gradle | 7 + 44 files changed, 2604 insertions(+), 216 deletions(-) create mode 100644 LICENSE create mode 100644 play-services-base/src/main/java/com/google/android/gms/common/GoogleApiAvailability.java delete mode 100644 play-services-base/src/main/java/com/google/android/gms/gcm/GoogleCloudMessaging.java create mode 120000 play-services-cast-api create mode 120000 play-services-common-api create mode 100644 play-services-gcm/src/main/java/com/google/android/gms/gcm/PendingCallback.java create mode 100644 play-services-gcm/src/main/java/org/microg/gms/gcm/CloudMessagingRpc.java create mode 120000 play-services-iid-api create mode 100644 play-services-iid/build.gradle create mode 100644 play-services-iid/src/main/AndroidManifest.xml rename {play-services-gcm => play-services-iid}/src/main/java/com/google/android/gms/iid/InstanceID.java (56%) rename {play-services-gcm => play-services-iid}/src/main/java/com/google/android/gms/iid/InstanceIDListenerService.java (95%) create mode 100644 play-services-iid/src/main/java/org/microg/gms/iid/InstanceIdRpc.java create mode 100644 play-services-iid/src/main/java/org/microg/gms/iid/InstanceIdStore.java create mode 120000 play-services-location-api create mode 100644 play-services-tasks/build.gradle create mode 100644 play-services-tasks/src/main/AndroidManifest.xml create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/Continuation.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/OnCompleteListener.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/OnFailureListener.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/OnSuccessListener.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/RuntimeExecutionException.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/Task.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/TaskCompletionSource.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/Tasks.java create mode 120000 play-services-wearable-api diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..f433b1a5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/build.gradle b/build.gradle index c6c27343..504fe91d 100644 --- a/build.gradle +++ b/build.gradle @@ -15,16 +15,7 @@ */ // Top-level build file where you can add configuration options common to all sub-projects/modules. -String getMyVersionName() { - def stdout = new ByteArrayOutputStream() - if (rootProject.file("gradlew").exists()) - exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout } - else // automatic build system, don't tag dirty - exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout } - return stdout.toString().trim().substring(1) -} subprojects { group = 'org.microg' - version = getMyVersionName() } diff --git a/extern/GmsApi b/extern/GmsApi index 6aa11065..be6af2ee 160000 --- a/extern/GmsApi +++ b/extern/GmsApi @@ -1 +1 @@ -Subproject commit 6aa110657beec0b3e6c26d1030943e0b97683335 +Subproject commit be6af2eeb76ba4bcc1700285f2ba87f217ab6ac2 diff --git a/play-services-base/build.gradle b/play-services-base/build.gradle index 52ff3bd9..bfb54b6f 100644 --- a/play-services-base/build.gradle +++ b/play-services-base/build.gradle @@ -19,15 +19,24 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.0.0' + classpath 'com.android.tools.build:gradle:2.1.2' } } apply plugin: 'com.android.library' +String getMyVersionName() { + def stdout = new ByteArrayOutputStream() + if (rootProject.file("gradlew").exists()) + exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout } + else // automatic build system, don't tag dirty + exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout } + return stdout.toString().trim().substring(1) +} + android { compileSdkVersion 23 - buildToolsVersion "23.0.2" + buildToolsVersion "23.0.3" defaultConfig { versionName getMyVersionName() @@ -39,6 +48,7 @@ android { } dependencies { - compile 'com.android.support:support-v4:23.3.0' - compile project(':play-services-api') + compile 'com.android.support:support-v4:23.4.0' + compile project(':play-services-common-api') + compile project(':play-services-tasks') } diff --git a/play-services-base/src/main/AndroidManifest.xml b/play-services-base/src/main/AndroidManifest.xml index 8b7acc02..2aa07523 100644 --- a/play-services-base/src/main/AndroidManifest.xml +++ b/play-services-base/src/main/AndroidManifest.xml @@ -15,10 +15,19 @@ ~ limitations under the License. --> - + - + - + + + + + diff --git a/play-services-base/src/main/java/com/google/android/gms/common/ConnectionResult.java b/play-services-base/src/main/java/com/google/android/gms/common/ConnectionResult.java index dbd87044..f6f75c1a 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/ConnectionResult.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/ConnectionResult.java @@ -20,9 +20,12 @@ 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}. @@ -128,8 +131,18 @@ public class ConnectionResult { @Deprecated public static final int DRIVE_EXTERNAL_STORAGE_REQUIRED = 1500; - private final PendingIntent pendingIntent; 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. @@ -138,8 +151,89 @@ public class ConnectionResult { * @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); + } } /** @@ -151,6 +245,15 @@ public class ConnectionResult { 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 @@ -162,6 +265,11 @@ public class ConnectionResult { 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. @@ -196,8 +304,7 @@ public class ConnectionResult { public void startResolutionForResult(Activity activity, int requestCode) throws IntentSender.SendIntentException { if (hasResolution()) { - activity.startIntentSenderForResult(pendingIntent.getIntentSender(), requestCode, null, - 0, 0, 0); + activity.startIntentSenderForResult(pendingIntent.getIntentSender(), requestCode, null, 0, 0, 0); } } } diff --git a/play-services-base/src/main/java/com/google/android/gms/common/GoogleApiAvailability.java b/play-services-base/src/main/java/com/google/android/gms/common/GoogleApiAvailability.java new file mode 100644 index 00000000..b7129e18 --- /dev/null +++ b/play-services-base/src/main/java/com/google/android/gms/common/GoogleApiAvailability.java @@ -0,0 +1,290 @@ +/* + * 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; + +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.support.v4.app.FragmentActivity; +import android.util.Log; + +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; + +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; + +@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.MAX_REFERENCE_VERSION; + + 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; + } + + /** + * 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 + } + } +} diff --git a/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesUtil.java b/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesUtil.java index faaa6adc..dd67e42f 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesUtil.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesUtil.java @@ -23,6 +23,7 @@ import android.app.PendingIntent; import android.content.Context; import android.content.DialogInterface; import android.content.pm.PackageManager; +import android.content.res.Resources; import android.util.Log; import org.microg.gms.common.Constants; @@ -35,69 +36,199 @@ import org.microg.gms.common.PublicApi; *

* TODO: methods :) */ +@PublicApi public class GooglePlayServicesUtil { private static final String TAG = "GooglePlayServicesUtil"; public static final String GMS_ERROR_DIALOG = "GooglePlayServicesErrorDialog"; + + /** + * Package name for Google Play services. + */ + @Deprecated 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). + */ + @Deprecated public static final int GOOGLE_PLAY_SERVICES_VERSION_CODE = Constants.MAX_REFERENCE_VERSION; + + /** + * Package name for Google Play Store. + */ public static final String GOOGLE_PLAY_STORE_PACKAGE = "com.android.vending"; + /** + * 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 errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call. + * If errorCode is {@link ConnectionResult#SUCCESS} then null is returned. + * @param activity parent activity for creating the dialog, also used for identifying + * language to display dialog in. + * @param requestCode The requestCode given when calling startActivityForResult. + */ + @Deprecated public static Dialog getErrorDialog(int errorCode, Activity activity, int requestCode) { - return null; // TODO + return getErrorDialog(errorCode, activity, requestCode, null); } - public static Dialog getErrorDialog(int errorCode, Activity activity, int requestCode, - DialogInterface.OnCancelListener cancelListener) { - return null; // TODO + /** + * 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 errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call. + * If errorCode is {@link ConnectionResult#SUCCESS} then null is returned. + * @param activity parent activity for creating the dialog, also used for identifying + * language to display dialog in. + * @param requestCode The requestCode given when calling startActivityForResult. + * @param cancelListener The {@link DialogInterface.OnCancelListener} to invoke if the dialog + * is canceled. + */ + @Deprecated + public static Dialog getErrorDialog(int errorCode, Activity activity, int requestCode, DialogInterface.OnCancelListener cancelListener) { + return GoogleApiAvailability.getInstance().getErrorDialog(activity, errorCode, requestCode, cancelListener); } + /** + * Returns a PendingIntent to address the provided errorCode. It will direct them to one of the + * following places 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 errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call. + * If errorCode is {@link ConnectionResult#SUCCESS} then null is returned. + * @param activity parent context for creating the PendingIntent. + * @param requestCode The requestCode given when calling startActivityForResult. + */ + @Deprecated public static PendingIntent getErrorPendingIntent(int errorCode, Activity activity, - int requestCode) { + int requestCode) { return null; // TODO } + /** + * Returns a human-readable string of the error code returned from {@link #isGooglePlayServicesAvailable(Context)}. + */ + @Deprecated public static String getErrorString(int errorCode) { return null; // TODO } + /** + * Returns the open source software license information for the Google Play services + * application, or null if Google Play services is not available on this device. + */ + @Deprecated public static String getOpenSourceSoftwareLicenseInfo(Context context) { return null; // TODO } + /** + * This gets the Context object of the Buddy APK. This loads the Buddy APK code from the Buddy + * APK into memory. This returned context can be used to create classes and obtain resources + * defined in the Buddy APK. + * + * @return The Context object of the Buddy APK or null if the Buddy APK is not installed on the device. + */ public static Context getRemoteContext(Context context) { return null; // TODO } + /** + * This gets the Resources object of the Buddy APK. + * + * @return The Resources object of the Buddy APK or null if the Buddy APK is not installed on the device. + */ + public static Resources getRemoteResources(Context context) { + return null; // TODO + } + + /** + * 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_VERSION_UPDATE_REQUIRED, + * SERVICE_DISABLED, SERVICE_INVALID + */ + @Deprecated public static 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 ConnectionResult.SUCCESS; } + @Deprecated public static boolean isGoogleSignedUid(PackageManager packageManager, int uid) { return false; // TODO } + /** + * Determines whether an error is user-recoverable. If true, proceed by calling + * {@link #getErrorDialog(int, Activity, int)} and showing the dialog. + * + * @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)}, or + * returned to your application via {@link com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener#onConnectionFailed(ConnectionResult)} + * @return true if the error is recoverable with {@link #getErrorDialog(int, Activity, int)} + */ + @Deprecated public static boolean isUserRecoverableError(int errorCode) { return false; // TODO } - public static boolean showErrorDialogFragment(int errorCode, Activity activity, - int requestCode) { + /** + * Display a DialogFragment for an error code returned by {@link #isGooglePlayServicesAvailable(Context)}. + * + * @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call. + * If errorCode is {@link ConnectionResult#SUCCESS} then null is returned. + * @param activity parent activity for creating the dialog, also used for identifying + * language to display dialog in. + * @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 android.support.v4.app.FragmentActivity}. + */ + @Deprecated + public static boolean showErrorDialogFragment(int errorCode, Activity activity, int requestCode) { + return showErrorDialogFragment(errorCode, activity, requestCode, null); + } + + @Deprecated + public static boolean showErrorDialogFragment(int errorCode, Activity activity, Fragment fragment, int requestCode, DialogInterface.OnCancelListener cancelListener) { return false; // TODO } - public static boolean showErrorDialogFragment(int errorCode, Activity activity, - Fragment fragment, int requestCode, DialogInterface.OnCancelListener cancelListener) { - return false; // TODO - } - - public static boolean showErrorDialogFragment(int errorCode, Activity activity, int requestCode, - DialogInterface.OnCancelListener cancelListener) { - return false; // TODO + /** + * @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call. + * If errorCode is {@link ConnectionResult#SUCCESS} then null is returned. + * @param activity parent activity for creating the dialog, also used for identifying + * language to display dialog in. + * @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 android.support.v4.app.FragmentActivity}. + */ + @Deprecated + public static boolean showErrorDialogFragment(int errorCode, Activity activity, int requestCode, DialogInterface.OnCancelListener cancelListener) { + return showErrorDialogFragment(errorCode, activity, null, requestCode, cancelListener); } + /** + * Displays a notification relevant to the provided error code. This method is similar to + * {@link #getErrorDialog(int, android.app.Activity, int)}, but is provided for background + * tasks that cannot or shouldn't 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}. + */ + @Deprecated public static void showErrorNotification(int errorCode, Context context) { // TODO } diff --git a/play-services-base/src/main/java/com/google/android/gms/gcm/GoogleCloudMessaging.java b/play-services-base/src/main/java/com/google/android/gms/gcm/GoogleCloudMessaging.java deleted file mode 100644 index b867ae7a..00000000 --- a/play-services-base/src/main/java/com/google/android/gms/gcm/GoogleCloudMessaging.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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.gcm; - -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; - -import java.io.IOException; - -public class GoogleCloudMessaging { - - /** - * The GCM {@link #register(String...)} and {@link #unregister()} methods are blocking. You - * should not run them in the main thread or in broadcast receivers. - */ - public static final String ERROR_MAIN_THREAD = "MAIN_THREAD"; - - /** - * The device can't read the response, or there was a 500/503 from the server that can be - * retried later. The application should use exponential back off and retry. - */ - public static final String ERROR_SERVICE_NOT_AVAILABLE = "SERVICE_NOT_AVAILABLE"; - - /** - * Returned by {@link #getMessageType(Intent)} to indicate that the server deleted some - * pending messages because they were collapsible. - */ - public static final String MESSAGE_TYPE_DELETED = "deleted_messages"; - - /** - * Returned by {@link #getMessageType(Intent)} to indicate a regular message. - */ - public static final String MESSAGE_TYPE_MESSAGE = "gcm"; - - /** - * Returned by {@link #getMessageType(Intent)} to indicate a send error. The intent includes - * the message ID of the message and an error code. - */ - public static final String MESSAGE_TYPE_SEND_ERROR = "send_error"; - - /** - * Return the singleton instance of GCM. - */ - public static synchronized GoogleCloudMessaging getInstance(Context context) { - return null; - } - - /** - * Must be called when your application is done using GCM, to release internal resources. - */ - public void close() { - - } - - /** - * Return the message type from an intent passed into a client app's broadcast receiver. - * There are two general categories of messages passed from the server: regular GCM messages, - * and special GCM status messages. The possible types are: - *

    - *
  • {@link #MESSAGE_TYPE_MESSAGE}—regular message from your server.
  • - *
  • {@link #MESSAGE_TYPE_DELETED}—special status message indicating that some messages have been collapsed by GCM.
  • - *
  • {@link #MESSAGE_TYPE_SEND_ERROR}—special status message indicating that there were errors sending one of the messages.
  • - *
- * You can use this method to filter based on message type. Since it is likely that GCM will - * be extended in the future with new message types, just ignore any message types you're not - * interested in, or that you don't recognize. - * - * @param intent - * @return - */ - public String getMessageType(Intent intent) { - return null; - } - - public String register(String... senderIds) { - return null; - } - - public void send(String to, String msgId, long timeToLive, Bundle data) { - - } - - public void send(String to, String msgId, Bundle data) { - - } - - /** - * Unregister the application. Calling unregister() stops any messages from the server. - * This is a blocking call—you shouldn't call it from the UI thread. - * You should rarely (if ever) need to call this method. Not only is it expensive in terms of - * resources, but it invalidates your registration ID, which you should never change - * unnecessarily. A better approach is to simply have your server stop sending messages. - * Only use unregister if you want to change your sender ID. - * - * @throws IOException if we can't connect to server to unregister. - */ - public void unregister() throws IOException { - - } -} diff --git a/play-services-cast-api b/play-services-cast-api new file mode 120000 index 00000000..36406dfb --- /dev/null +++ b/play-services-cast-api @@ -0,0 +1 @@ +extern/GmsApi/play-services-cast-api/ \ No newline at end of file diff --git a/play-services-cast/build.gradle b/play-services-cast/build.gradle index f900225d..a407e8b6 100644 --- a/play-services-cast/build.gradle +++ b/play-services-cast/build.gradle @@ -19,15 +19,24 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.0.0' + classpath 'com.android.tools.build:gradle:2.1.2' } } apply plugin: 'com.android.library' +String getMyVersionName() { + def stdout = new ByteArrayOutputStream() + if (rootProject.file("gradlew").exists()) + exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout } + else // automatic build system, don't tag dirty + exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout } + return stdout.toString().trim().substring(1) +} + android { compileSdkVersion 23 - buildToolsVersion "23.0.2" + buildToolsVersion "23.0.3" defaultConfig { versionName getMyVersionName() @@ -40,4 +49,5 @@ android { dependencies { compile project(':play-services-base') + compile project(':play-services-cast-api') } diff --git a/play-services-common-api b/play-services-common-api new file mode 120000 index 00000000..c85558d0 --- /dev/null +++ b/play-services-common-api @@ -0,0 +1 @@ +extern/GmsApi/play-services-common-api/ \ No newline at end of file diff --git a/play-services-gcm/build.gradle b/play-services-gcm/build.gradle index b3a6c833..c9b0c102 100644 --- a/play-services-gcm/build.gradle +++ b/play-services-gcm/build.gradle @@ -19,15 +19,24 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.0.0' + classpath 'com.android.tools.build:gradle:2.1.2' } } apply plugin: 'com.android.library' +String getMyVersionName() { + def stdout = new ByteArrayOutputStream() + if (rootProject.file("gradlew").exists()) + exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout } + else // automatic build system, don't tag dirty + exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout } + return stdout.toString().trim().substring(1) +} + android { compileSdkVersion 23 - buildToolsVersion "23.0.2" + buildToolsVersion "23.0.3" defaultConfig { versionName getMyVersionName() @@ -39,5 +48,6 @@ android { } dependencies { - compile project(':play-services-base') + compile project(':play-services-iid') + // compile project(':play-services-measurement') } \ No newline at end of file diff --git a/play-services-gcm/src/main/AndroidManifest.xml b/play-services-gcm/src/main/AndroidManifest.xml index baaa3ad3..16f7b82f 100644 --- a/play-services-gcm/src/main/AndroidManifest.xml +++ b/play-services-gcm/src/main/AndroidManifest.xml @@ -20,5 +20,12 @@ + + + + + + + diff --git a/play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmPubSub.java b/play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmPubSub.java index 5ef87164..134aa710 100644 --- a/play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmPubSub.java +++ b/play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmPubSub.java @@ -90,8 +90,9 @@ public class GcmPubSub { if (TextUtils.isEmpty(registrationToken)) throw new IllegalArgumentException("No registration token!"); if (TextUtils.isEmpty(topic) || !topicPattern.matcher(topic).matches()) - throw new IllegalArgumentException("Invalid topic!"); + throw new IllegalArgumentException("Invalid topic: " + topic); + if (extras == null) extras = new Bundle(); extras.putString(EXTRA_TOPIC, topic); instanceId.getToken(registrationToken, topic, extras); } @@ -110,7 +111,7 @@ public class GcmPubSub { */ public void unsubscribe(String registrationToken, String topic) throws IOException { if (TextUtils.isEmpty(topic) || !topicPattern.matcher(topic).matches()) - throw new IllegalArgumentException("Invalid topic!"); + throw new IllegalArgumentException("Invalid topic: " + topic); Bundle extras = new Bundle(); extras.putString(EXTRA_TOPIC, topic); diff --git a/play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmReceiver.java b/play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmReceiver.java index 0d79190f..a00d6a0f 100644 --- a/play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmReceiver.java +++ b/play-services-gcm/src/main/java/com/google/android/gms/gcm/GcmReceiver.java @@ -16,9 +16,25 @@ package com.google.android.gms.gcm; +import android.Manifest; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.os.Build; import android.support.v4.content.WakefulBroadcastReceiver; +import android.util.Base64; +import android.util.Log; + +import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTRATION; +import static org.microg.gms.gcm.GcmConstants.ACTION_INSTANCE_ID; +import static org.microg.gms.gcm.GcmConstants.EXTRA_FROM; +import static org.microg.gms.gcm.GcmConstants.EXTRA_RAWDATA; +import static org.microg.gms.gcm.GcmConstants.EXTRA_RAWDATA_BASE64; +import static org.microg.gms.gcm.GcmConstants.GCMID_INSTANCE_ID; +import static org.microg.gms.gcm.GcmConstants.GCMID_REFRESH; /** * WakefulBroadcastReceiver that receives GCM messages and delivers them to an @@ -41,9 +57,71 @@ import android.support.v4.content.WakefulBroadcastReceiver; * services. This prevents other apps from invoking the broadcast receiver. */ public class GcmReceiver extends WakefulBroadcastReceiver { + private static final String TAG = "GcmReceiver"; public void onReceive(Context context, Intent intent) { - throw new UnsupportedOperationException(); + sanitizeIntent(context, intent); + enforceIntentClassName(context, intent); + sendIntent(context, intent); + if (getResultCode() == 0) setResultCodeIfOrdered(-1); } + private void sanitizeIntent(Context context, Intent intent) { + intent.setComponent(null); + intent.setPackage(context.getPackageName()); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + intent.removeCategory(context.getPackageName()); + } + String from = intent.getStringExtra(EXTRA_FROM); + if (ACTION_C2DM_REGISTRATION.equals(intent.getAction()) || GCMID_INSTANCE_ID.equals(from) || GCMID_REFRESH.equals(from)) { + intent.setAction(ACTION_INSTANCE_ID); + } + String base64encoded = intent.getStringExtra(EXTRA_RAWDATA_BASE64); + if (base64encoded != null) { + intent.putExtra(EXTRA_RAWDATA, Base64.decode(base64encoded, Base64.DEFAULT)); + intent.removeExtra(EXTRA_RAWDATA_BASE64); + } + } + + private void enforceIntentClassName(Context context, Intent intent) { + ResolveInfo resolveInfo = context.getPackageManager().resolveService(intent, 0); + if (resolveInfo == null || resolveInfo.serviceInfo == null) { + Log.e(TAG, "Failed to resolve target intent service, skipping classname enforcement"); + return; + } + ServiceInfo serviceInfo = resolveInfo.serviceInfo; + if (!context.getPackageName().equals(serviceInfo.packageName) || serviceInfo.name == null) { + Log.e(TAG, "Error resolving target intent service, skipping classname enforcement. Resolved service was: " + serviceInfo.packageName + "/" + serviceInfo.name); + return; + } + intent.setClassName(context, serviceInfo.name.startsWith(".") ? (context.getPackageName() + serviceInfo.name) : serviceInfo.name); + } + + private void sendIntent(Context context, Intent intent) { + setResultCodeIfOrdered(500); + try { + ComponentName startedComponent; + if (context.checkCallingOrSelfPermission(Manifest.permission.WAKE_LOCK) == PackageManager.PERMISSION_GRANTED) { + startedComponent = startWakefulService(context, intent); + } else { + Log.d(TAG, "Missing wake lock permission, service start may be delayed"); + startedComponent = context.startService(intent); + } + if (startedComponent == null) { + Log.e(TAG, "Error while delivering the message: ServiceIntent not found."); + setResultCodeIfOrdered(404); + } else { + setResultCodeIfOrdered(-1); + } + } catch (SecurityException e) { + Log.e(TAG, "Error while delivering the message to the serviceIntent", e); + setResultCodeIfOrdered(401); + } + } + + private void setResultCodeIfOrdered(int code) { + if (isOrderedBroadcast()) { + setResultCode(code); + } + } } \ No newline at end of file diff --git a/play-services-gcm/src/main/java/com/google/android/gms/gcm/GoogleCloudMessaging.java b/play-services-gcm/src/main/java/com/google/android/gms/gcm/GoogleCloudMessaging.java index 4cd0bdf4..18b06ad6 100644 --- a/play-services-gcm/src/main/java/com/google/android/gms/gcm/GoogleCloudMessaging.java +++ b/play-services-gcm/src/main/java/com/google/android/gms/gcm/GoogleCloudMessaging.java @@ -16,7 +16,6 @@ package com.google.android.gms.gcm; -import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.os.Bundle; @@ -26,21 +25,23 @@ import android.text.TextUtils; import com.google.android.gms.iid.InstanceID; import org.microg.gms.common.PublicApi; +import org.microg.gms.gcm.CloudMessagingRpc; import org.microg.gms.gcm.GcmConstants; import java.io.IOException; import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME; import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_RECEIVE; -import static org.microg.gms.gcm.GcmConstants.ACTION_GCM_SEND; -import static org.microg.gms.gcm.GcmConstants.EXTRA_APP; import static org.microg.gms.gcm.GcmConstants.EXTRA_DELAY; +import static org.microg.gms.gcm.GcmConstants.EXTRA_ERROR; import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSAGE_ID; import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSAGE_TYPE; +import static org.microg.gms.gcm.GcmConstants.EXTRA_REGISTRATION_ID; +import static org.microg.gms.gcm.GcmConstants.EXTRA_SENDER; import static org.microg.gms.gcm.GcmConstants.EXTRA_SENDER_LEGACY; +import static org.microg.gms.gcm.GcmConstants.EXTRA_SEND_FROM; import static org.microg.gms.gcm.GcmConstants.EXTRA_SEND_TO; import static org.microg.gms.gcm.GcmConstants.EXTRA_TTL; -import static org.microg.gms.gcm.GcmConstants.PERMISSION_GTALK; /** * GoogleCloudMessaging (GCM) enables apps to communicate with their app servers @@ -123,43 +124,33 @@ public class GoogleCloudMessaging { @Deprecated public static final String MESSAGE_TYPE_SEND_EVENT = GcmConstants.MESSAGE_TYPE_SEND_EVENT; - private static GoogleCloudMessaging INSTANCE; - /** - * Due to it's nature of being a monitored reference, Intents can be used to authenticate a package source. - */ - private PendingIntent selfAuthIntent; + private static GoogleCloudMessaging instance; + + private CloudMessagingRpc rpc; private Context context; public GoogleCloudMessaging() { - throw new UnsupportedOperationException(); } /** * Must be called when your application is done using GCM, to release * internal resources. */ - public void close() { - throw new UnsupportedOperationException(); - } - - private PendingIntent getSelfAuthIntent() { - if (selfAuthIntent == null) { - Intent intent = new Intent(); - intent.setPackage("com.google.example.invalidpackage"); - selfAuthIntent = PendingIntent.getBroadcast(context, 0, intent, 0); - } - return selfAuthIntent; + public synchronized void close() { + instance = null; + rpc.close(); } /** * Return the singleton instance of GCM. */ public static GoogleCloudMessaging getInstance(Context context) { - if (INSTANCE == null) { - INSTANCE = new GoogleCloudMessaging(); - INSTANCE.context = context.getApplicationContext(); + if (instance == null) { + instance = new GoogleCloudMessaging(); + instance.context = context.getApplicationContext(); + instance.rpc = new CloudMessagingRpc(instance.context); } - return INSTANCE; + return instance; } /** @@ -204,16 +195,23 @@ public class GoogleCloudMessaging { @Deprecated public String register(String... senderIds) throws IOException { if (Looper.getMainLooper() == Looper.myLooper()) throw new IOException(ERROR_MAIN_THREAD); + if (senderIds == null || senderIds.length == 0) throw new IllegalArgumentException("No sender ids"); StringBuilder sb = new StringBuilder(senderIds[0]); for (int i = 1; i < senderIds.length; i++) { sb.append(',').append(senderIds[i]); } - // This seems to be a legacy variant - // TODO: Implement latest version - Bundle extras = new Bundle(); - extras.putString(EXTRA_SENDER_LEGACY, sb.toString()); - return InstanceID.getInstance(context).getToken(sb.toString(), INSTANCE_ID_SCOPE, extras); + String sender = sb.toString(); + + if (isLegacyFallback()) { + Bundle extras = new Bundle(); + extras.putString(EXTRA_SENDER_LEGACY, sender); + return InstanceID.getInstance(context).getToken(sb.toString(), INSTANCE_ID_SCOPE, extras); + } else { + Bundle extras = new Bundle(); + extras.putString(EXTRA_SENDER, sender); + return rpc.handleRegisterMessageResult(rpc.sendRegisterMessageBlocking(extras)); + } } /** @@ -242,16 +240,27 @@ public class GoogleCloudMessaging { */ public void send(String to, String msgId, long timeToLive, Bundle data) throws IOException { if (TextUtils.isEmpty(to)) throw new IllegalArgumentException("Invalid 'to'"); - Intent intent = new Intent(ACTION_GCM_SEND); - intent.setPackage(GMS_PACKAGE_NAME); - if (data != null) intent.putExtras(data); - intent.putExtra(EXTRA_APP, getSelfAuthIntent()); - intent.putExtra(EXTRA_SEND_TO, to); - intent.putExtra(EXTRA_MESSAGE_ID, msgId); - intent.putExtra(EXTRA_TTL, timeToLive); - intent.putExtra(EXTRA_DELAY, -1); - //intent.putExtra(EXTRA_SEND_FROM, TODO) - context.sendOrderedBroadcast(intent, PERMISSION_GTALK); + + if (isLegacyFallback()) { + Bundle extras = new Bundle(); + for (String key : data.keySet()) { + Object o = extras.get(key); + if (o instanceof String) { + extras.putString("gcm." + key, (String) o); + } + } + extras.putString(EXTRA_SEND_TO, to); + extras.putString(EXTRA_MESSAGE_ID, msgId); + InstanceID.getInstance(context).requestToken("GCM", "upstream", extras); + } else { + Bundle extras = data != null ? new Bundle(data) : new Bundle(); + extras.putString(EXTRA_SEND_TO, to); + extras.putString(EXTRA_SEND_FROM, getFrom(to)); + extras.putString(EXTRA_MESSAGE_ID, msgId); + extras.putLong(EXTRA_TTL, timeToLive); + extras.putInt(EXTRA_DELAY, -1); + rpc.sendGcmMessage(extras); + } } /** @@ -298,4 +307,16 @@ public class GoogleCloudMessaging { InstanceID.getInstance(context).deleteInstanceID(); } + private boolean isLegacyFallback() { + String gcmPackageName = CloudMessagingRpc.getGcmPackageName(context); + return gcmPackageName != null && gcmPackageName.endsWith(".gsf"); + } + + private String getFrom(String to) { + int i = to.indexOf('@'); + if (i > 0) { + to = to.substring(0, i); + } + return InstanceID.getInstance(context).getStore().get("", to, INSTANCE_ID_SCOPE); + } } \ No newline at end of file diff --git a/play-services-gcm/src/main/java/com/google/android/gms/gcm/PendingCallback.java b/play-services-gcm/src/main/java/com/google/android/gms/gcm/PendingCallback.java new file mode 100644 index 00000000..6eed3e3d --- /dev/null +++ b/play-services-gcm/src/main/java/com/google/android/gms/gcm/PendingCallback.java @@ -0,0 +1,59 @@ +/* + * 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.gcm; + +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; + +public class PendingCallback implements Parcelable { + private final IBinder binder; + + public PendingCallback(IBinder binder) { + this.binder = binder; + } + + private PendingCallback(Parcel in) { + this.binder = in.readStrongBinder(); + } + + public IBinder getBinder() { + return binder; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeStrongBinder(binder); + } + + public static final Creator CREATOR = new Creator() { + @Override + public PendingCallback createFromParcel(Parcel source) { + return new PendingCallback(source); + } + + @Override + public PendingCallback[] newArray(int size) { + return new PendingCallback[size]; + } + }; +} diff --git a/play-services-gcm/src/main/java/org/microg/gms/gcm/CloudMessagingRpc.java b/play-services-gcm/src/main/java/org/microg/gms/gcm/CloudMessagingRpc.java new file mode 100644 index 00000000..0c833fe9 --- /dev/null +++ b/play-services-gcm/src/main/java/org/microg/gms/gcm/CloudMessagingRpc.java @@ -0,0 +1,165 @@ +/* + * 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.gcm; + +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.util.Log; + +import com.google.android.gms.iid.InstanceID; + +import java.io.IOException; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static com.google.android.gms.gcm.GoogleCloudMessaging.ERROR_SERVICE_NOT_AVAILABLE; +import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME; +import static org.microg.gms.common.Constants.GSF_PACKAGE_NAME; +import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTER; +import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTRATION; +import static org.microg.gms.gcm.GcmConstants.ACTION_GCM_SEND; +import static org.microg.gms.gcm.GcmConstants.EXTRA_APP; +import static org.microg.gms.gcm.GcmConstants.EXTRA_ERROR; +import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSAGE_ID; +import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSENGER; +import static org.microg.gms.gcm.GcmConstants.EXTRA_REGISTRATION_ID; +import static org.microg.gms.gcm.GcmConstants.EXTRA_UNREGISTERED; +import static org.microg.gms.gcm.GcmConstants.PERMISSION_GTALK; +import static org.microg.gms.gcm.GcmConstants.PERMISSION_RECEIVE; + +public class CloudMessagingRpc { + private static final AtomicInteger messageIdCounter = new AtomicInteger(1); + private static String gcmPackageName; + + private final BlockingQueue messengerResponseQueue = new LinkedBlockingQueue(); + private final Messenger messenger = new Messenger(new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + if (msg == null || !(msg.obj instanceof Intent)) { + // Invalid message -> drop + return; + } + Intent intent = (Intent) msg.obj; + if (ACTION_C2DM_REGISTRATION.equals(intent.getAction())) { + messengerResponseQueue.add(intent); + } + } + }); + + /** + * Due to it's nature of being a monitored reference, Intents can be used to authenticate a package source. + */ + private PendingIntent selfAuthIntent; + private Context context; + + public CloudMessagingRpc(Context context) { + this.context = context; + } + + public static String getGcmPackageName(Context context) { + if (gcmPackageName != null) { + return gcmPackageName; + } + PackageManager packageManager = context.getPackageManager(); + for (ResolveInfo resolveInfo : packageManager.queryIntentServices(new Intent(ACTION_C2DM_REGISTER), 0)) { + if (packageManager.checkPermission(PERMISSION_RECEIVE, resolveInfo.serviceInfo.packageName) == PERMISSION_GRANTED) { + return gcmPackageName = resolveInfo.serviceInfo.packageName; + } + } + try { + ApplicationInfo appInfo = packageManager.getApplicationInfo(GMS_PACKAGE_NAME, 0); + return gcmPackageName = appInfo.packageName; + } catch (PackageManager.NameNotFoundException ignored) { + } + try { + ApplicationInfo appInfo = packageManager.getApplicationInfo(GSF_PACKAGE_NAME, 0); + return gcmPackageName = appInfo.packageName; + } catch (PackageManager.NameNotFoundException ex3) { + return null; + } + } + + public void close() { + // Cancel the authentication + if (selfAuthIntent != null) { + selfAuthIntent.cancel(); + selfAuthIntent = null; + } + } + + private PendingIntent getSelfAuthIntent() { + if (selfAuthIntent == null) { + Intent intent = new Intent(); + intent.setPackage("com.google.example.invalidpackage"); + selfAuthIntent = PendingIntent.getBroadcast(context, 0, intent, 0); + } + return selfAuthIntent; + } + + public Intent sendRegisterMessageBlocking(Bundle extras) throws IOException { + sendRegisterMessage(extras); + Intent resultIntent; + try { + resultIntent = messengerResponseQueue.poll(30, TimeUnit.SECONDS); + } catch (InterruptedException e) { + throw new IOException(e.getMessage()); + } + if (resultIntent == null) { + throw new IOException(ERROR_SERVICE_NOT_AVAILABLE); + } + return resultIntent; + } + + private void sendRegisterMessage(Bundle extras) { + Intent intent = new Intent(ACTION_C2DM_REGISTER); + intent.setPackage(getGcmPackageName(context)); + extras.putString(EXTRA_MESSAGE_ID, "google.rpc" + messageIdCounter.getAndIncrement()); + intent.putExtras(extras); + intent.putExtra(EXTRA_MESSENGER, messenger); + intent.putExtra(EXTRA_APP, getSelfAuthIntent()); + context.startService(intent); + } + + public void sendGcmMessage(Bundle extras) { + Intent intent = new Intent(ACTION_GCM_SEND); + intent.setPackage(GMS_PACKAGE_NAME); + intent.putExtras(extras); + intent.putExtra(EXTRA_APP, getSelfAuthIntent()); + context.sendOrderedBroadcast(intent, PERMISSION_GTALK); + } + + public String handleRegisterMessageResult(Intent resultIntent) throws IOException { + if (resultIntent == null) throw new IOException(InstanceID.ERROR_SERVICE_NOT_AVAILABLE); + String result = resultIntent.getStringExtra(EXTRA_REGISTRATION_ID); + if (result == null) result = resultIntent.getStringExtra(EXTRA_UNREGISTERED); + if (result != null) return result; + result = resultIntent.getStringExtra(EXTRA_ERROR); + throw new IOException(result != null ? result : InstanceID.ERROR_SERVICE_NOT_AVAILABLE); + } +} diff --git a/play-services-iid-api b/play-services-iid-api new file mode 120000 index 00000000..304442a6 --- /dev/null +++ b/play-services-iid-api @@ -0,0 +1 @@ +extern/GmsApi/play-services-iid-api \ No newline at end of file diff --git a/play-services-iid/build.gradle b/play-services-iid/build.gradle new file mode 100644 index 00000000..d3d6661c --- /dev/null +++ b/play-services-iid/build.gradle @@ -0,0 +1,53 @@ +/* + * 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. + */ + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.1.2' + } +} + +apply plugin: 'com.android.library' + +String getMyVersionName() { + def stdout = new ByteArrayOutputStream() + if (rootProject.file("gradlew").exists()) + exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout } + else // automatic build system, don't tag dirty + exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout } + return stdout.toString().trim().substring(1) +} + +android { + compileSdkVersion 23 + buildToolsVersion "23.0.3" + + defaultConfig { + versionName getMyVersionName() + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_6 + } +} + +dependencies { + compile project(':play-services-base') + compile project(':play-services-iid-api') +} diff --git a/play-services-iid/src/main/AndroidManifest.xml b/play-services-iid/src/main/AndroidManifest.xml new file mode 100644 index 00000000..1f5f60cd --- /dev/null +++ b/play-services-iid/src/main/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + diff --git a/play-services-gcm/src/main/java/com/google/android/gms/iid/InstanceID.java b/play-services-iid/src/main/java/com/google/android/gms/iid/InstanceID.java similarity index 56% rename from play-services-gcm/src/main/java/com/google/android/gms/iid/InstanceID.java rename to play-services-iid/src/main/java/com/google/android/gms/iid/InstanceID.java index c4f0cb34..1202e201 100644 --- a/play-services-gcm/src/main/java/com/google/android/gms/iid/InstanceID.java +++ b/play-services-iid/src/main/java/com/google/android/gms/iid/InstanceID.java @@ -19,23 +19,46 @@ package com.google.android.gms.iid; import android.content.Context; import android.os.Bundle; import android.os.Looper; +import android.text.TextUtils; +import android.util.Base64; +import android.util.Log; import org.microg.gms.common.PublicApi; import org.microg.gms.gcm.GcmConstants; +import org.microg.gms.iid.InstanceIdRpc; +import org.microg.gms.iid.InstanceIdStore; import java.io.IOException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.Map; + +import static org.microg.gms.gcm.GcmConstants.EXTRA_DELETE; +import static org.microg.gms.gcm.GcmConstants.EXTRA_SCOPE; +import static org.microg.gms.gcm.GcmConstants.EXTRA_SENDER; +import static org.microg.gms.gcm.GcmConstants.EXTRA_SUBSCIPTION; +import static org.microg.gms.gcm.GcmConstants.EXTRA_SUBTYPE; /** * Instance ID provides a unique identifier for each app instance and a mechanism * to authenticate and authorize actions (for example, sending a GCM message). *

* Instance ID is stable but may become invalid, if: - * [...] + *

    + *
  • App deletes Instance ID
  • + *
  • Device is factory reset
  • + *
  • User uninstalls the app
  • + *
  • User clears app data
  • + *
* If Instance ID has become invalid, the app can call {@link com.google.android.gms.iid.InstanceID#getId()} * to request a new Instance ID. * To prove ownership of Instance ID and to allow servers to access data or * services associated with the app, call {@link com.google.android.gms.iid.InstanceID#getToken(java.lang.String, java.lang.String)}. */ +@PublicApi public class InstanceID { /** * Error returned when failed requests are retried too often. Use @@ -65,13 +88,31 @@ public class InstanceID { */ public static final String ERROR_TIMEOUT = "TIMEOUT"; + private static final int RSA_KEY_SIZE = 2048; + private static final String TAG = "InstanceID"; + + private static InstanceIdStore storeInstance; + private static InstanceIdRpc rpc; + private static Map instances = new HashMap(); + + private final String subtype; + private KeyPair keyPair; + private long creationTime; + + private InstanceID(String subtype) { + this.subtype = subtype == null ? "" : subtype; + } + /** * Resets Instance ID and revokes all tokens. * * @throws IOException */ public void deleteInstanceID() throws IOException { - throw new UnsupportedOperationException(); + deleteToken("*", "*"); + creationTime = 0; + storeInstance.delete(subtype + "|"); + keyPair = null; } /** @@ -85,12 +126,25 @@ public class InstanceID { * @throws IOException if the request fails. */ public void deleteToken(String authorizedEntity, String scope) throws IOException { - deleteToken(authorizedEntity, scope, new Bundle()); + deleteToken(authorizedEntity, scope, null); } @PublicApi(exclude = true) public void deleteToken(String authorizedEntity, String scope, Bundle extras) throws IOException { - throw new UnsupportedOperationException(); + if (Looper.getMainLooper() == Looper.myLooper()) throw new IOException(ERROR_MAIN_THREAD); + + storeInstance.delete(subtype, authorizedEntity, scope); + + if (extras == null) extras = new Bundle(); + extras.putString(EXTRA_SENDER, authorizedEntity); + extras.putString(EXTRA_SUBSCIPTION, authorizedEntity); + extras.putString(EXTRA_DELETE, "1"); + extras.putString("X-" + EXTRA_DELETE, "1"); + extras.putString(EXTRA_SUBTYPE, TextUtils.isEmpty(subtype) ? authorizedEntity : subtype); + extras.putString("X-" + EXTRA_SUBTYPE, TextUtils.isEmpty(subtype) ? authorizedEntity : subtype); + if (scope != null) extras.putString(EXTRA_SCOPE, scope); + + rpc.handleRegisterMessageResult(rpc.sendRegisterMessageBlocking(extras, getKeyPair())); } /** @@ -99,7 +153,13 @@ public class InstanceID { * @return Time when instance ID was created (milliseconds since Epoch). */ public long getCreationTime() { - throw new UnsupportedOperationException(); + if (creationTime == 0) { + String s = storeInstance.get(subtype, "cre"); + if (s != null) { + creationTime = Long.parseLong(s); + } + } + return creationTime; } /** @@ -108,7 +168,7 @@ public class InstanceID { * @return The identifier for the application instance. */ public String getId() { - throw new UnsupportedOperationException(); + return sha1KeyPair(getKeyPair()); } /** @@ -117,7 +177,17 @@ public class InstanceID { * @return InstanceID instance. */ public static InstanceID getInstance(Context context) { - throw new UnsupportedOperationException(); + String subtype = ""; + if (storeInstance == null) { + storeInstance = new InstanceIdStore(context.getApplicationContext()); + rpc = new InstanceIdRpc(context.getApplicationContext()); + } + InstanceID instance = instances.get(subtype); + if (instance == null) { + instance = new InstanceID(subtype); + instances.put(subtype, instance); + } + return instance; } /** @@ -163,4 +233,43 @@ public class InstanceID { return getToken(authorizedEntity, scope, null); } + @PublicApi(exclude = true) + public InstanceIdStore getStore() { + return storeInstance; + } + + @PublicApi(exclude = true) + public String requestToken(String authorizedEntity, String scope, Bundle extras) { + throw new UnsupportedOperationException(); + } + + private synchronized KeyPair getKeyPair() { + if (keyPair == null) { + keyPair = storeInstance.getKeyPair(subtype); + if (keyPair == null) { + try { + KeyPairGenerator rsaGenerator = KeyPairGenerator.getInstance("RSA"); + rsaGenerator.initialize(RSA_KEY_SIZE); + keyPair = rsaGenerator.generateKeyPair(); + creationTime = System.currentTimeMillis(); + storeInstance.put(subtype, keyPair, creationTime); + } catch (NoSuchAlgorithmException e) { + Log.w(TAG, e); + } + } + } + return keyPair; + } + + @PublicApi(exclude = true) + public static String sha1KeyPair(KeyPair keyPair) { + try { + byte[] digest = MessageDigest.getInstance("SHA1").digest(keyPair.getPublic().getEncoded()); + digest[0] = (byte) (112 + (0xF & digest[0]) & 0xFF); + return Base64.encodeToString(digest, 0, 8, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING); + } catch (NoSuchAlgorithmException e) { + Log.w(TAG, e); + return null; + } + } } \ No newline at end of file diff --git a/play-services-gcm/src/main/java/com/google/android/gms/iid/InstanceIDListenerService.java b/play-services-iid/src/main/java/com/google/android/gms/iid/InstanceIDListenerService.java similarity index 95% rename from play-services-gcm/src/main/java/com/google/android/gms/iid/InstanceIDListenerService.java rename to play-services-iid/src/main/java/com/google/android/gms/iid/InstanceIDListenerService.java index cd9a80ff..350972f2 100644 --- a/play-services-gcm/src/main/java/com/google/android/gms/iid/InstanceIDListenerService.java +++ b/play-services-iid/src/main/java/com/google/android/gms/iid/InstanceIDListenerService.java @@ -25,8 +25,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; - -import com.google.android.gms.gcm.GcmReceiver; +import android.support.v4.content.WakefulBroadcastReceiver; import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTRATION; import static org.microg.gms.gcm.GcmConstants.ACTION_INSTANCE_ID; @@ -104,7 +103,8 @@ public class InstanceIDListenerService extends Service { handleIntent(intent); - if (intent.hasExtra(EXTRA_FROM)) GcmReceiver.completeWakefulIntent(intent); + if (intent.hasExtra(EXTRA_FROM)) + WakefulBroadcastReceiver.completeWakefulIntent(intent); } } finally { stop(); diff --git a/play-services-iid/src/main/java/org/microg/gms/iid/InstanceIdRpc.java b/play-services-iid/src/main/java/org/microg/gms/iid/InstanceIdRpc.java new file mode 100644 index 00000000..aa8caba6 --- /dev/null +++ b/play-services-iid/src/main/java/org/microg/gms/iid/InstanceIdRpc.java @@ -0,0 +1,423 @@ +/* + * 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.iid; + +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.Build; +import android.os.Bundle; +import android.os.ConditionVariable; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.Parcelable; +import android.os.RemoteException; +import android.os.SystemClock; +import android.text.TextUtils; +import android.util.Base64; +import android.util.Log; + +import com.google.android.gms.iid.InstanceID; +import com.google.android.gms.iid.MessengerCompat; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.Signature; +import java.security.interfaces.RSAPrivateKey; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; + +import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static com.google.android.gms.iid.InstanceID.ERROR_BACKOFF; +import static com.google.android.gms.iid.InstanceID.ERROR_MISSING_INSTANCEID_SERVICE; +import static com.google.android.gms.iid.InstanceID.ERROR_SERVICE_NOT_AVAILABLE; +import static com.google.android.gms.iid.InstanceID.ERROR_TIMEOUT; +import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME; +import static org.microg.gms.common.Constants.GSF_PACKAGE_NAME; +import static org.microg.gms.common.Constants.MAX_REFERENCE_VERSION; +import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTER; +import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTRATION; +import static org.microg.gms.gcm.GcmConstants.ACTION_INSTANCE_ID; +import static org.microg.gms.gcm.GcmConstants.EXTRA_APP; +import static org.microg.gms.gcm.GcmConstants.EXTRA_APP_ID; +import static org.microg.gms.gcm.GcmConstants.EXTRA_APP_VERSION_CODE; +import static org.microg.gms.gcm.GcmConstants.EXTRA_APP_VERSION_NAME; +import static org.microg.gms.gcm.GcmConstants.EXTRA_CLIENT_VERSION; +import static org.microg.gms.gcm.GcmConstants.EXTRA_ERROR; +import static org.microg.gms.gcm.GcmConstants.EXTRA_GMS_VERSION; +import static org.microg.gms.gcm.GcmConstants.EXTRA_GSF_INTENT; +import static org.microg.gms.gcm.GcmConstants.EXTRA_IS_MESSENGER2; +import static org.microg.gms.gcm.GcmConstants.EXTRA_KID; +import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSENGER; +import static org.microg.gms.gcm.GcmConstants.EXTRA_OS_VERSION; +import static org.microg.gms.gcm.GcmConstants.EXTRA_PUBLIC_KEY; +import static org.microg.gms.gcm.GcmConstants.EXTRA_REGISTRATION_ID; +import static org.microg.gms.gcm.GcmConstants.EXTRA_SIGNATURE; +import static org.microg.gms.gcm.GcmConstants.EXTRA_UNREGISTERED; +import static org.microg.gms.gcm.GcmConstants.EXTRA_USE_GSF; +import static org.microg.gms.gcm.GcmConstants.PERMISSION_RECEIVE; + +public class InstanceIdRpc { + private static final String TAG = "InstanceID/Rpc"; + + private static final int BLOCKING_WAIT_TIME = 30000; + + private static String iidPackageName; + private static int lastRequestId; + private static int retryCount; + private static Map blockingResponses = new HashMap(); + + private long nextAttempt; + private int interval; + private Context context; + private PendingIntent selfAuthToken; + private Messenger messenger; + private Messenger myMessenger; + private MessengerCompat messengerCompat; + + public InstanceIdRpc(Context context) { + this.context = context; + } + + public static String getIidPackageName(Context context) { + if (iidPackageName != null) { + return iidPackageName; + } + PackageManager packageManager = context.getPackageManager(); + for (ResolveInfo resolveInfo : packageManager.queryIntentServices(new Intent(ACTION_C2DM_REGISTER), 0)) { + if (packageManager.checkPermission(PERMISSION_RECEIVE, resolveInfo.serviceInfo.packageName) == PERMISSION_GRANTED) { + return iidPackageName = resolveInfo.serviceInfo.packageName; + } + } + try { + ApplicationInfo appInfo = packageManager.getApplicationInfo(GMS_PACKAGE_NAME, 0); + return iidPackageName = appInfo.packageName; + } catch (PackageManager.NameNotFoundException ignored) { + } + try { + ApplicationInfo appInfo = packageManager.getApplicationInfo(GSF_PACKAGE_NAME, 0); + return iidPackageName = appInfo.packageName; + } catch (PackageManager.NameNotFoundException ex3) { + Log.w(TAG, "Both Google Play Services and legacy GSF package are missing"); + return null; + } + } + + private static int getGmsVersionCode(final Context context) { + final PackageManager packageManager = context.getPackageManager(); + try { + return packageManager.getPackageInfo(getIidPackageName(context), 0).versionCode; + } catch (PackageManager.NameNotFoundException ex) { + return -1; + } + } + + private static int getSelfVersionCode(final Context context) { + try { + return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode; + } catch (PackageManager.NameNotFoundException neverHappens) { + return 0; + } + } + + private static String getSelfVersionName(final Context context) { + try { + return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName; + } catch (PackageManager.NameNotFoundException neverHappens) { + return null; + } + } + + void initialize() { + if (myMessenger != null) return; + getIidPackageName(context); + myMessenger = new Messenger(new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + if (msg == null) { + return; + } + if (msg.obj instanceof Intent) { + Intent intent = (Intent) msg.obj; + intent.setExtrasClassLoader(MessengerCompat.class.getClassLoader()); + if (intent.hasExtra(EXTRA_MESSENGER)) { + Parcelable messengerCandidate = intent.getParcelableExtra(EXTRA_MESSENGER); + if (messengerCandidate instanceof MessengerCompat) { + messengerCompat = (MessengerCompat) messengerCandidate; + } else if (messengerCandidate instanceof Messenger) { + messenger = (Messenger) messengerCandidate; + } + } + handleResponseInternal(intent); + } else { + Log.w(TAG, "Dropping invalid message"); + } + } + }); + } + + public void handleResponseInternal(Intent resultIntent) { + if (resultIntent == null) return; + if (!ACTION_C2DM_REGISTRATION.equals(resultIntent.getAction()) && !ACTION_INSTANCE_ID.equals(resultIntent.getAction())) + return; + String result = resultIntent.getStringExtra(EXTRA_REGISTRATION_ID); + if (result == null) result = resultIntent.getStringExtra(EXTRA_UNREGISTERED); + if (result == null) { + handleError(resultIntent); + return; + } + retryCount = 0; + nextAttempt = 0; + interval = 0; + + String requestId = null; + if (result.startsWith("|")) { + // parse structured response + String[] split = result.split("\\|"); + if (!"ID".equals(split[1])) { + Log.w(TAG, "Unexpected structured response " + result); + } + requestId = split[2]; + if (split.length > 4) { + if ("SYNC".equals(split[3])) { + // TODO: sync + } else if("RST".equals(split[3])) { + // TODO: rst + resultIntent.removeExtra(EXTRA_REGISTRATION_ID); + return; + } + } + result = split[split.length-1]; + if (result.startsWith(":")) + result = result.substring(1); + resultIntent.putExtra(EXTRA_REGISTRATION_ID, result); + } + setResponse(requestId, resultIntent); + } + + private void handleError(Intent resultIntent) { + String error = resultIntent.getStringExtra("error"); + if (error == null) return; + String requestId = null; + if (error.startsWith("|")) { + // parse structured error message + String[] split = error.split("\\|"); + if (!"ID".equals(split[1])) { + Log.w(TAG, "Unexpected structured response " + error); + } + if (split.length > 2) { + requestId = split[2]; + error = split[3]; + if (error.startsWith(":")) + error = error.substring(1); + } else { + error = "UNKNOWN"; + } + resultIntent.putExtra("error", error); + } + setResponse(requestId, resultIntent); + long retryAfter = resultIntent.getLongExtra("Retry-After", 0); + if (retryAfter > 0) { + interval = (int) (retryAfter * 1000); + nextAttempt = SystemClock.elapsedRealtime() + interval; + Log.d(TAG, "Server requested retry delay: " + interval); + } else if (ERROR_SERVICE_NOT_AVAILABLE.equals(error) || "AUTHENTICATION_FAILED".equals(error) + && GSF_PACKAGE_NAME.equals(getIidPackageName(context))) { + retryCount++; + if (retryCount < 3) return; + if (retryCount == 3) interval = 1000 + new Random().nextInt(1000); + interval = interval * 2; + nextAttempt = SystemClock.elapsedRealtime() + interval; + Log.d(TAG, "Setting retry delay to " + interval); + } + } + + private synchronized PendingIntent getSelfAuthToken() { + if (selfAuthToken == null) { + Intent intent = new Intent(); + intent.setPackage("com.google.example.invalidpackage"); + selfAuthToken = PendingIntent.getBroadcast(context, 0, intent, 0); + } + return selfAuthToken; + } + + private static synchronized String getRequestId() { + return Integer.toString(lastRequestId++); + } + + private void sendRegisterMessage(Bundle data, KeyPair keyPair, String requestId) throws IOException { + long elapsedRealtime = SystemClock.elapsedRealtime(); + if (nextAttempt != 0 && elapsedRealtime <= nextAttempt) { + Log.w(TAG, "Had to wait for " + interval + ", that's still " + (nextAttempt - elapsedRealtime)); + throw new IOException(ERROR_BACKOFF); + } + initialize(); + if (iidPackageName == null) { + throw new IOException(ERROR_MISSING_INSTANCEID_SERVICE); + } + Intent intent = new Intent(ACTION_C2DM_REGISTER); + intent.setPackage(iidPackageName); + data.putString(EXTRA_GMS_VERSION, Integer.toString(getGmsVersionCode(context))); + data.putString(EXTRA_OS_VERSION, Integer.toString(Build.VERSION.SDK_INT)); + data.putString(EXTRA_APP_VERSION_CODE, Integer.toString(getSelfVersionCode(context))); + data.putString(EXTRA_APP_VERSION_NAME, getSelfVersionName(context)); + data.putString(EXTRA_CLIENT_VERSION, "iid-" + MAX_REFERENCE_VERSION); + data.putString(EXTRA_APP_ID, InstanceID.sha1KeyPair(keyPair)); + String pub = base64encode(keyPair.getPublic().getEncoded()); + data.putString(EXTRA_PUBLIC_KEY, pub); + data.putString(EXTRA_SIGNATURE, sign(keyPair, context.getPackageName(), pub)); + intent.putExtras(data); + intent.putExtra(EXTRA_APP, getSelfAuthToken()); + sendRequest(intent, requestId); + } + + private static String sign(KeyPair keyPair, String... payload) { + byte[] bytes; + try { + bytes = TextUtils.join("\n", payload).getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + Log.e(TAG, "Unable to encode", e); + return null; + } + PrivateKey privateKey = keyPair.getPrivate(); + try { + Signature signature = Signature.getInstance(privateKey instanceof RSAPrivateKey ? "SHA256withRSA" : "SHA256withECDSA"); + signature.initSign(privateKey); + signature.update(bytes); + return base64encode(signature.sign()); + } catch (GeneralSecurityException e) { + Log.e(TAG, "Unable to sign", e); + return null; + } + } + + private static String base64encode(byte[] bytes) { + return Base64.encodeToString(bytes, Base64.URL_SAFE + Base64.NO_PADDING + Base64.NO_WRAP); + } + + private void sendRequest(Intent intent, String requestId) { + intent.putExtra(EXTRA_KID, "|ID|" + requestId + "|"); + intent.putExtra("X-" + EXTRA_KID, "|ID|" + requestId + "|"); + Log.d(TAG, "Sending " + intent.getExtras()); + if (messenger != null) { + intent.putExtra(EXTRA_MESSENGER, myMessenger); + Message msg = Message.obtain(); + msg.obj = intent; + try { + messenger.send(msg); + return; + } catch (RemoteException e) { + Log.d(TAG, "Messenger failed, falling back to service"); + } + } + + boolean useGsf = iidPackageName.endsWith(".gsf"); + if (intent.hasExtra(EXTRA_USE_GSF)) + useGsf = "1".equals(intent.getStringExtra(EXTRA_USE_GSF)); + + if (useGsf) { + Intent holder = new Intent(ACTION_INSTANCE_ID); + holder.setPackage(context.getPackageName()); + holder.putExtra(EXTRA_GSF_INTENT, intent); + context.startService(holder); + } else { + intent.putExtra(EXTRA_MESSENGER, myMessenger); + intent.putExtra(EXTRA_IS_MESSENGER2, "1"); + if (messengerCompat != null) { + Message msg = Message.obtain(); + msg.obj = intent; + try { + messengerCompat.send(msg); + return; + } catch (RemoteException e) { + Log.d(TAG, "Messenger failed, falling back to service"); + } + } + context.startService(intent); + } + } + + public Intent sendRegisterMessageBlocking(Bundle data, KeyPair keyPair) throws IOException { + Intent intent = sendRegisterMessageBlockingInternal(data, keyPair); + if (intent != null && intent.hasExtra(EXTRA_MESSENGER)) { + // Now with a messenger + intent = sendRegisterMessageBlockingInternal(data, keyPair); + } + return intent; + } + + private Intent sendRegisterMessageBlockingInternal(Bundle data, KeyPair keyPair) throws IOException { + ConditionVariable cv = new ConditionVariable(); + String requestId = getRequestId(); + synchronized (InstanceIdRpc.class) { + blockingResponses.put(requestId, cv); + } + + sendRegisterMessage(data, keyPair, requestId); + + cv.block(BLOCKING_WAIT_TIME); + synchronized (InstanceIdRpc.class) { + Object res = blockingResponses.remove(requestId); + if (res instanceof Intent) { + return (Intent) res; + } else if (res instanceof String) { + throw new IOException((String) res); + } + Log.w(TAG, "No response " + res); + throw new IOException(ERROR_TIMEOUT); + } + } + + public String handleRegisterMessageResult(Intent resultIntent) throws IOException { + if (resultIntent == null) throw new IOException(ERROR_SERVICE_NOT_AVAILABLE); + String result = resultIntent.getStringExtra(EXTRA_REGISTRATION_ID); + if (result == null) result = resultIntent.getStringExtra(EXTRA_UNREGISTERED); + if (result != null) return result; + result = resultIntent.getStringExtra(EXTRA_ERROR); + throw new IOException(result != null ? result : ERROR_SERVICE_NOT_AVAILABLE); + } + + private void setResponse(String requestId, Object response) { + if (requestId == null) { + for (String r : blockingResponses.keySet()) { + setResponse(r, response); + } + } + Object old = blockingResponses.get(requestId); + blockingResponses.put(requestId, response); + if (old instanceof ConditionVariable) { + ((ConditionVariable) old).open(); + } else if (old instanceof Messenger) { + Message msg = Message.obtain(); + msg.obj = response; + try { + ((Messenger) old).send(msg); + } catch (RemoteException e) { + Log.w(TAG, "Failed to send response", e); + } + } + } +} diff --git a/play-services-iid/src/main/java/org/microg/gms/iid/InstanceIdStore.java b/play-services-iid/src/main/java/org/microg/gms/iid/InstanceIdStore.java new file mode 100644 index 00000000..c597b6c8 --- /dev/null +++ b/play-services-iid/src/main/java/org/microg/gms/iid/InstanceIdStore.java @@ -0,0 +1,111 @@ +/* + * 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.iid; + +import android.content.Context; +import android.content.SharedPreferences; +import android.util.Base64; +import android.util.Log; + +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +public class InstanceIdStore { + private static final String TAG = "InstanceID/Store"; + private static final String PREF_NAME = "com.google.android.gms.appid"; + + private Context context; + private SharedPreferences sharedPreferences; + + public InstanceIdStore(Context context) { + this.context = context; + this.sharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); + } + + public synchronized String get(String key) { + return sharedPreferences.getString(key, null); + } + + public String get(String subtype, String key) { + return get(subtype + "|S|" + key); + } + + public String get(String subtype, String authorizedEntity, String scope) { + return get(subtype + "|T|" + authorizedEntity + "|" + scope); + } + + public KeyPair getKeyPair(String subtype) { + String pub = get(subtype, "|P|"); + String priv = get(subtype, "|K|"); + if (pub == null || priv == null) { + return null; + } + try { + byte[] pubKey = Base64.decode(pub, Base64.URL_SAFE); + byte[] privKey = Base64.decode(priv, Base64.URL_SAFE); + KeyFactory rsaFactory = KeyFactory.getInstance("RSA"); + return new KeyPair(rsaFactory.generatePublic(new X509EncodedKeySpec(pubKey)), rsaFactory.generatePrivate(new PKCS8EncodedKeySpec(privKey))); + } catch (Exception e) { + Log.w(TAG, "Invalid key stored " + e); + return null; + } + } + + public synchronized void put(String key, String value) { + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putString(key, value); + editor.apply(); + } + + public void put(String subtype, String key, String value) { + put(subtype + "|S|" + key, value); + } + + public void put(String subtype, String authorizedEntity, String scope, String value) { + put(subtype + "|T|" + authorizedEntity + "|" + scope, value); + } + + public synchronized void put(String subtype, KeyPair keyPair, long timestamp) { + put(subtype, "|P|", Base64.encodeToString(keyPair.getPublic().getEncoded(), Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING)); + put(subtype, "|K|", Base64.encodeToString(keyPair.getPrivate().getEncoded(), Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING)); + put(subtype, "cre", Long.toString(timestamp)); + } + + public synchronized void delete() { + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.clear(); + editor.apply(); + } + + public synchronized void delete(String prefix) { + SharedPreferences.Editor editor = sharedPreferences.edit(); + for (String key : sharedPreferences.getAll().keySet()) { + if (key.startsWith(prefix)) { + editor.remove(key); + } + } + editor.apply(); + } + + public synchronized void delete(String subtype, String authorizedEntity, String scope) { + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.remove(subtype + "|T|" + authorizedEntity + "|" + scope); + editor.apply(); + } +} diff --git a/play-services-location-api b/play-services-location-api new file mode 120000 index 00000000..990e4285 --- /dev/null +++ b/play-services-location-api @@ -0,0 +1 @@ +extern/GmsApi/play-services-location-api/ \ No newline at end of file diff --git a/play-services-location/build.gradle b/play-services-location/build.gradle index b3a6c833..4a211cf9 100644 --- a/play-services-location/build.gradle +++ b/play-services-location/build.gradle @@ -19,15 +19,24 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.0.0' + classpath 'com.android.tools.build:gradle:2.1.2' } } apply plugin: 'com.android.library' +String getMyVersionName() { + def stdout = new ByteArrayOutputStream() + if (rootProject.file("gradlew").exists()) + exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout } + else // automatic build system, don't tag dirty + exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout } + return stdout.toString().trim().substring(1) +} + android { compileSdkVersion 23 - buildToolsVersion "23.0.2" + buildToolsVersion "23.0.3" defaultConfig { versionName getMyVersionName() @@ -40,4 +49,5 @@ android { dependencies { compile project(':play-services-base') + compile project(':play-services-location-api') } \ No newline at end of file diff --git a/play-services-tasks/build.gradle b/play-services-tasks/build.gradle new file mode 100644 index 00000000..cc390af7 --- /dev/null +++ b/play-services-tasks/build.gradle @@ -0,0 +1,52 @@ +/* + * 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. + */ + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.1.2' + } +} + +apply plugin: 'com.android.library' + +String getMyVersionName() { + def stdout = new ByteArrayOutputStream() + if (rootProject.file("gradlew").exists()) + exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout } + else // automatic build system, don't tag dirty + exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout } + return stdout.toString().trim().substring(1) +} + +android { + compileSdkVersion 23 + buildToolsVersion "23.0.3" + + defaultConfig { + versionName getMyVersionName() + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_6 + } +} + +dependencies { + compile project(':play-services-common-api') +} diff --git a/play-services-tasks/src/main/AndroidManifest.xml b/play-services-tasks/src/main/AndroidManifest.xml new file mode 100644 index 00000000..351619a2 --- /dev/null +++ b/play-services-tasks/src/main/AndroidManifest.xml @@ -0,0 +1,24 @@ + + + + + + + + + diff --git a/play-services-tasks/src/main/java/com/google/android/gms/tasks/Continuation.java b/play-services-tasks/src/main/java/com/google/android/gms/tasks/Continuation.java new file mode 100644 index 00000000..5630929f --- /dev/null +++ b/play-services-tasks/src/main/java/com/google/android/gms/tasks/Continuation.java @@ -0,0 +1,68 @@ +/* + * 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.tasks; + +import org.microg.gms.common.PublicApi; + +/** + * A function that is called to continue execution after completion of a {@link Task}. + * + * @see Task#continueWith(Continuation) + * @see Task#continueWithTask(Continuation) + */ +@PublicApi +public interface Continuation { + /** + * Returns the result of applying this Continuation to {@code task}. + *

+ * To propagate failure from the completed Task call {@link Task#getResult()} and allow the + * {@link RuntimeExecutionException} to propagate. The RuntimeExecutionException will be + * unwrapped such that the Task returned by {@link Task#continueWith(Continuation)} or + * {@link Task#continueWithTask(Continuation)} fails with the original exception. + *

+ * To suppress specific failures call {@link Task#getResult(Class)} and catch the exception + * types of interest: + *

task.continueWith(new Continuation() {
+     *     @Override
+     *     public String then(Task task) {
+     *         try {
+     *             return task.getResult(IOException.class);
+     *         } catch (FileNotFoundException e) {
+     *             return "Not found";
+     *         } catch (IOException e) {
+     *             return "Read failed";
+     *         }
+     *     }
+     * }
+ *

+ * To suppress all failures guard any calls to {@link Task#getResult()} with {@link Task#isSuccessful()}: + *

task.continueWith(new Continuation() {
+     *     @Override
+     *     public String then(Task task) {
+     *         if (task.isSuccessful()) {
+     *             return task.getResult();
+     *         } else {
+     *             return DEFAULT_VALUE;
+     *         }
+     *     }
+     * }
+ * + * @param task the completed Task. Never null + * @throws Exception if the result couldn't be produced + */ + TContinuationResult then(Task task); +} diff --git a/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnCompleteListener.java b/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnCompleteListener.java new file mode 100644 index 00000000..7c6c870f --- /dev/null +++ b/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnCompleteListener.java @@ -0,0 +1,34 @@ +/* + * 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.tasks; + +import org.microg.gms.common.PublicApi; + +/** + * Listener called when a {@link Task} completes. + * + * @see Task#addOnCompleteListener(OnCompleteListener) + */ +@PublicApi +public interface OnCompleteListener { + /** + * Called when the Task completes. + * + * @param task the completed Task. Never null + */ + void onComplete(Task task); +} diff --git a/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnFailureListener.java b/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnFailureListener.java new file mode 100644 index 00000000..13cc7334 --- /dev/null +++ b/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnFailureListener.java @@ -0,0 +1,35 @@ +/* + * 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.tasks; + +import org.microg.gms.common.PublicApi; + +/** + * Listener called when a {@link Task} fails with an exception. + * + * @see Task#addOnFailureListener(OnFailureListener) + */ +@PublicApi +public interface OnFailureListener { + + /** + * Called when the Task fails with an exception. + * + * @param e the exception that caused the Task to fail. Never null + */ + void onFailure(Exception e); +} diff --git a/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnSuccessListener.java b/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnSuccessListener.java new file mode 100644 index 00000000..37407ec9 --- /dev/null +++ b/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnSuccessListener.java @@ -0,0 +1,34 @@ +/* + * 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.tasks; + +import org.microg.gms.common.PublicApi; + +/** + * Listener called when a {@link Task} completes successfully. + * + * @see Task#addOnSuccessListener(OnSuccessListener) + */ +@PublicApi +public interface OnSuccessListener { + /** + * Called when the {@link Task} completes successfully. + * + * @param result the result of the Task + */ + void onSuccess(TResult result); +} diff --git a/play-services-tasks/src/main/java/com/google/android/gms/tasks/RuntimeExecutionException.java b/play-services-tasks/src/main/java/com/google/android/gms/tasks/RuntimeExecutionException.java new file mode 100644 index 00000000..10c0337d --- /dev/null +++ b/play-services-tasks/src/main/java/com/google/android/gms/tasks/RuntimeExecutionException.java @@ -0,0 +1,33 @@ +/* + * 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.tasks; + +import org.microg.gms.common.PublicApi; + +import java.util.concurrent.ExecutionException; + +/** + * Runtime version of {@link ExecutionException}. + * + * @see Task#getResult(Class) + */ +@PublicApi +public class RuntimeExecutionException extends RuntimeException { + public RuntimeExecutionException(Throwable cause) { + super(cause); + } +} diff --git a/play-services-tasks/src/main/java/com/google/android/gms/tasks/Task.java b/play-services-tasks/src/main/java/com/google/android/gms/tasks/Task.java new file mode 100644 index 00000000..4f9ce4dd --- /dev/null +++ b/play-services-tasks/src/main/java/com/google/android/gms/tasks/Task.java @@ -0,0 +1,224 @@ +/* + * 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.tasks; + +import android.app.Activity; + +import org.microg.gms.common.PublicApi; + +import java.util.concurrent.Executor; + +/** + * Represents an asynchronous operation. + */ +@PublicApi +public abstract class Task { + + public Task() { + } + + /** + * Adds a listener that is called when the Task completes. + *

+ * The listener will be called on main application thread. If the Task is already complete, a + * call to the listener will be immediately scheduled. If multiple listeners are added, they + * will be called in the order in which they were added. + * + * @return this Task + */ + public Task addOnCompleteListener(OnCompleteListener listener) { + throw new UnsupportedOperationException("addOnCompleteListener is not implemented"); + } + + /** + * Adds an Activity-scoped listener that is called when the Task completes. + *

+ * The listener will be called on main application thread. If the Task is already complete, a + * call to the listener will be immediately scheduled. If multiple listeners are added, they + * will be called in the order in which they were added. + *

+ * The listener will be automatically removed during {@link Activity#onStop()}. + * + * @return this Task + */ + public Task addOnCompleteListener(Activity activity, OnCompleteListener listener) { + throw new UnsupportedOperationException("addOnCompleteListener is not implemented"); + } + + /** + * Adds a listener that is called when the Task completes. + *

+ * If the Task is already complete, a call to the listener will be immediately scheduled. If + * multiple listeners are added, they will be called in the order in which they were added. + * + * @param executor the executor to use to call the listener + * @return this Task + */ + public Task addOnCompleteListener(Executor executor, OnCompleteListener listener) { + throw new UnsupportedOperationException("addOnCompleteListener is not implemented"); + } + + /** + * Adds an Activity-scoped listener that is called if the Task fails. + *

+ * The listener will be called on main application thread. If the Task has already failed, a + * call to the listener will be immediately scheduled. If multiple listeners are added, they + * will be called in the order in which they were added. + *

+ * The listener will be automatically removed during {@link Activity#onStop()}. + * + * @return this Task + */ + public abstract Task addOnFailureListener(Activity activity, OnFailureListener listener); + + /** + * Adds an Activity-scoped listener that is called if the Task fails. + *

+ * The listener will be called on main application thread. If the Task has already failed, a + * call to the listener will be immediately scheduled. If multiple listeners are added, they + * will be called in the order in which they were added. + * + * @return this Task + */ + public abstract Task addOnFailureListener(OnFailureListener listener); + + /** + * Adds a listener that is called if the Task fails. + *

+ * If the Task has already failed, a call to the listener will be immediately scheduled. If + * multiple listeners are added, they will be called in the order in which they were added. + * + * @param executor the executor to use to call the listener + * @return this Task + */ + public abstract Task addOnFailureListener(Executor executor, OnFailureListener listener); + + + /** + * Adds a listener that is called if the Task completes successfully. + *

+ * If multiple listeners are added, they will be called in the order in which they were added. If + * the Task has already completed successfully, a call to the listener will be immediately scheduled. + * + * @param executor the executor to use to call the listener + * @return this Task + */ + public abstract Task addOnSuccessListener(Executor executor, OnSuccessListener listener); + + /** + * Adds a listener that is called if the Task completes successfully. + *

+ * The listener will be called on the main application thread. If the Task has already + * completed successfully, a call to the listener will be immediately scheduled. If multiple + * listeners are added, they will be called in the order in which they were added. + * + * @return this Task + */ + public abstract Task addOnSuccessListener(OnSuccessListener listener); + + /** + * Adds an Activity-scoped listener that is called if the Task completes successfully. + *

+ * The listener will be called on the main application thread. If the Task has already + * completed successfully, a call to the listener will be immediately scheduled. If multiple + * listeners are added, they will be called in the order in which they were added. + *

+ * The listener will be automatically removed during {@link Activity#onStop()}. + * + * @return this Task + */ + public abstract Task addOnSuccessListener(Activity activity, OnSuccessListener listener); + + + /** + * Returns a new Task that will be completed with the result of applying the specified + * Continuation to this Task. + *

+ * The Continuation will be called on the main application thread. + * + * @see Continuation#then(Task) + */ + public Task continueWith(Continuation continuation) { + throw new UnsupportedOperationException("continueWith is not implemented"); + } + + /** + * Returns a new Task that will be completed with the result of applying the specified Continuation to this Task. + * + * @param executor the executor to use to call the Continuation + * @see Continuation#then(Task) + */ + public Task continueWith(Executor executor, Continuation continuation) { + throw new UnsupportedOperationException("continueWith is not implemented"); + } + + /** + * Returns a new Task that will be completed with the result of applying the specified + * Continuation to this Task. + *

+ * The Continuation will be called on the main application thread. + * + * @see Continuation#then(Task) + */ + public Task continueWithTask(Continuation> continuation) { + throw new UnsupportedOperationException("continueWithTask is not implemented"); + } + + /** + * Returns a new Task that will be completed with the result of applying the specified Continuation to this Task. + * + * @param executor the executor to use to call the Continuation + * @see Continuation#then(Task) + */ + public Task continueWithTask(Executor executor, Continuation> var2) { + throw new UnsupportedOperationException("continueWithTask is not implemented"); + } + + /** + * Returns the exception that caused the Task to fail. Returns {@code null} if the Task is not + * yet complete, or completed successfully. + */ + public abstract Exception getException(); + + /** + * Gets the result of the Task, if it has already completed. + * + * @throws IllegalStateException if the Task is not yet complete + * @throws RuntimeExecutionException if the Task failed with an exception + */ + public abstract TResult getResult(); + + /** + * Gets the result of the Task, if it has already completed. + * + * @throws IllegalStateException if the Task is not yet complete + * @throws X if the Task failed with an exception of type X + * @throws RuntimeExecutionException if the Task failed with an exception that was not of type X + */ + public abstract TResult getResult(Class exceptionType) throws X; + + /** + * Returns {@code true} if the Task is complete; {@code false} otherwise. + */ + public abstract boolean isComplete(); + + /** + * Returns {@code true} if the Task has completed successfully; {@code false} otherwise. + */ + public abstract boolean isSuccessful(); + +} diff --git a/play-services-tasks/src/main/java/com/google/android/gms/tasks/TaskCompletionSource.java b/play-services-tasks/src/main/java/com/google/android/gms/tasks/TaskCompletionSource.java new file mode 100644 index 00000000..1371e224 --- /dev/null +++ b/play-services-tasks/src/main/java/com/google/android/gms/tasks/TaskCompletionSource.java @@ -0,0 +1,52 @@ +/* + * 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.tasks; + +import org.microg.gms.common.PublicApi; + +/** + * Provides the ability to create an incomplete {@link Task} and later complete it by either + * calling {@link #setResult(TResult)} or {@link #setException(Exception)}. + */ +@PublicApi +public class TaskCompletionSource { + public TaskCompletionSource() { + } + + /** + * Returns the Task. + */ + public Task getTask() { + return null; + } + + /** + * Completes the Task with the specified exception. + * @throws IllegalStateException if the Task is already complete + */ + public void setException(Exception e) { + + } + + /** + * Completes the Task with the specified result. + * @throws IllegalStateException if the Task is already complete + */ + public void setResult(TResult result) { + + } +} diff --git a/play-services-tasks/src/main/java/com/google/android/gms/tasks/Tasks.java b/play-services-tasks/src/main/java/com/google/android/gms/tasks/Tasks.java new file mode 100644 index 00000000..c02ed055 --- /dev/null +++ b/play-services-tasks/src/main/java/com/google/android/gms/tasks/Tasks.java @@ -0,0 +1,116 @@ +/* + * 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.tasks; + +import org.microg.gms.common.PublicApi; + +import java.util.Arrays; +import java.util.Collection; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * {@link Task} utility methods. + */ +@PublicApi +public final class Tasks { + + /** + * Blocks until the specified Task is complete. + * + * @return the Task's result + * @throws ExecutionException if the Task fails + * @throws InterruptedException if an interrupt occurs while waiting for the Task to complete + * @throws TimeoutException if the specified timeout is reached before the Task completes + */ + public static TResult await(Task task, long timeout, TimeUnit unit) { + // TODO + return null; + } + + /** + * Blocks until the specified Task is complete. + * + * @return the Task's result + * @throws ExecutionException if the Task fails + * @throws InterruptedException if an interrupt occurs while waiting for the Task to complete + */ + public static TResult await(Task task) { + // TODO + return null; + } + + /** + * Returns a Task that will be completed with the result of the specified Callable. + *

+ * The Callable will be called on the main application thread. + */ + public static Task call(Callable callable) { + // TODO + return null; + } + + /** + * Returns a Task that will be completed with the result of the specified Callable. + * + * @param executor the Executor to use to call the Callable + */ + public static Task call(Executor executor, Callable callable) { + // TODO + return null; + } + + /** + * Returns a completed Task with the specified exception. + */ + public static Task forException(Exception e) { + // TODO + return null; + } + + /** + * Returns a completed Task with the specified result. + */ + public static Task forResult(TResult result) { + // TODO + return null; + } + + /** + * Returns a Task that completes successfully when all of the specified Tasks complete + * successfully. Does not accept nulls. + * + * @throws NullPointerException if any of the provided Tasks are null + */ + public static Task whenAll(Collection> tasks) { + // TODO + return null; + } + + /** + * Returns a Task that completes successfully when all of the specified Tasks complete + * successfully. Does not accept nulls. + * + * @throws NullPointerException if any of the provided Tasks are null + */ + public static Task whenAll(Task... tasks) { + return whenAll(Arrays.asList(tasks)); + } +} diff --git a/play-services-wearable-api b/play-services-wearable-api new file mode 120000 index 00000000..b8f16ab8 --- /dev/null +++ b/play-services-wearable-api @@ -0,0 +1 @@ +extern/GmsApi/play-services-wearable-api/ \ No newline at end of file diff --git a/play-services-wearable/build.gradle b/play-services-wearable/build.gradle index f900225d..94ece58f 100644 --- a/play-services-wearable/build.gradle +++ b/play-services-wearable/build.gradle @@ -19,15 +19,24 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.0.0' + classpath 'com.android.tools.build:gradle:2.1.2' } } apply plugin: 'com.android.library' +String getMyVersionName() { + def stdout = new ByteArrayOutputStream() + if (rootProject.file("gradlew").exists()) + exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout } + else // automatic build system, don't tag dirty + exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout } + return stdout.toString().trim().substring(1) +} + android { compileSdkVersion 23 - buildToolsVersion "23.0.2" + buildToolsVersion "23.0.3" defaultConfig { versionName getMyVersionName() @@ -40,4 +49,5 @@ android { dependencies { compile project(':play-services-base') + compile project(':play-services-wearable-api') } diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataEventBuffer.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataEventBuffer.java index b5cc0952..cdeb6bc3 100644 --- a/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataEventBuffer.java +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataEventBuffer.java @@ -33,7 +33,7 @@ public class DataEventBuffer extends DataBuffer implements Result { @PublicApi(exclude = true) public DataEventBuffer(DataHolder dataHolder) { super(dataHolder); - status = new Status(dataHolder.statusCode); + status = new Status(dataHolder.getStatusCode()); } @Override diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataItemBuffer.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataItemBuffer.java index 56b71acf..9859f56d 100644 --- a/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataItemBuffer.java +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataItemBuffer.java @@ -30,7 +30,7 @@ public class DataItemBuffer extends DataBuffer implements Result { @PublicApi(exclude = true) public DataItemBuffer(DataHolder dataHolder) { super(dataHolder); - status = new Status(dataHolder.statusCode); + status = new Status(dataHolder.getStatusCode()); } @Override diff --git a/play-services/build.gradle b/play-services/build.gradle index f8844af4..48cde72e 100644 --- a/play-services/build.gradle +++ b/play-services/build.gradle @@ -19,12 +19,21 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.0.0' + classpath 'com.android.tools.build:gradle:2.1.0' } } apply plugin: 'com.android.library' +String getMyVersionName() { + def stdout = new ByteArrayOutputStream() + if (rootProject.file("gradlew").exists()) + exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout } + else // automatic build system, don't tag dirty + exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout } + return stdout.toString().trim().substring(1) +} + android { compileSdkVersion 23 buildToolsVersion "23.0.2" diff --git a/settings.gradle b/settings.gradle index 2b54450b..870c2121 100644 --- a/settings.gradle +++ b/settings.gradle @@ -16,12 +16,19 @@ include ':safe-parcel' +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' include ':play-services-api' include ':play-services-base' include ':play-services-cast' include ':play-services-gcm' +include ':play-services-iid' include ':play-services-location' +include ':play-services-tasks' include ':play-services-wearable' include ':play-services' From 25d0e3be421b61f661a1d6ee540bbf7f512caa45 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sun, 7 Aug 2016 18:19:36 +0200 Subject: [PATCH 43/65] Fix travis --- .travis.yml | 2 +- play-services/build.gradle | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1f8c07c6..8f34e4b8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ android: components: - platform-tools - tools - - build-tools-23.0.2 + - build-tools-23.0.3 - android-23 - extra-android-m2repository before_cache: diff --git a/play-services/build.gradle b/play-services/build.gradle index 48cde72e..9c4a6711 100644 --- a/play-services/build.gradle +++ b/play-services/build.gradle @@ -19,7 +19,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.1.0' + classpath 'com.android.tools.build:gradle:2.1.2' } } @@ -36,7 +36,7 @@ String getMyVersionName() { android { compileSdkVersion 23 - buildToolsVersion "23.0.2" + buildToolsVersion "23.0.3" defaultConfig { versionName getMyVersionName() From 29d8b8fbb5a3c906652fa045630fbcf4d21229e6 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sun, 14 Aug 2016 10:29:03 +0200 Subject: [PATCH 44/65] Update lib for API changes, add support for wearable listening and sending messages --- build.gradle | 3 + extern/GmsApi | 2 +- play-services-base/build.gradle | 3 +- .../src/main/AndroidManifest.xml | 2 +- .../android/gms/common/ConnectionResult.java | 310 --------- .../gms/common/GooglePlayServicesClient.java | 4 +- .../google/android/gms/common/api/Api.java | 84 --- .../gms/common/api/GoogleApiClient.java | 494 ------------- .../android/gms/common/api/PendingResult.java | 59 -- .../android/gms/common/api/Releasable.java | 25 - .../gms/common/api/ResultCallback.java | 33 - .../java/org/microg/gms/common/GmsClient.java | 33 +- .../org/microg/gms/common/GmsConnector.java | 29 +- .../org/microg/gms/common/api/ApiBuilder.java | 30 - .../microg/gms/common/api/ApiConnection.java | 27 - .../gms/common/api/GoogleApiClientImpl.java | 223 ------ play-services-basement | 1 + .../src/main/AndroidManifest.xml | 8 +- play-services-common-api | 1 - .../FusedLocationProviderApiImpl.java | 30 +- .../location/GoogleLocationManagerClient.java | 7 +- play-services-tasks/build.gradle | 2 +- play-services-wearable/build.gradle | 7 + .../android/gms/wearable/CapabilityApi.java | 193 +++++ .../gms/wearable/ChannelIOException.java | 54 ++ .../google/android/gms/wearable/DataMap.java | 114 ++- .../android/gms/wearable/MessageApi.java | 88 --- .../google/android/gms/wearable/Wearable.java | 8 + .../gms/wearable/WearableListenerService.java | 216 +++++- .../gms/wearable/BaseWearableCallbacks.java | 117 ++++ .../org/microg/gms/wearable/DataApiImpl.java | 18 +- .../microg/gms/wearable/MessageApiImpl.java | 26 +- .../org/microg/gms/wearable/NodeApiImpl.java | 8 +- .../gms/wearable/WearableApiBuilder.java | 4 +- .../gms/wearable/WearableClientImpl.java | 53 ++ .../wearable/databundle/DataBundleUtil.java | 658 ++++++++++++++++++ .../gms/wearable/databundle/DataBundle.java | 64 ++ .../wearable/databundle/DataBundleEntry.java | 80 +++ .../databundle/DataBundleTypedValue.java | 80 +++ .../wearable/databundle/DataBundleValue.java | 259 +++++++ .../src/main/protos-repo/databundle.proto | 33 + settings.gradle | 2 +- 42 files changed, 2033 insertions(+), 1459 deletions(-) delete mode 100644 play-services-base/src/main/java/com/google/android/gms/common/ConnectionResult.java delete mode 100644 play-services-base/src/main/java/com/google/android/gms/common/api/Api.java delete mode 100644 play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java delete mode 100644 play-services-base/src/main/java/com/google/android/gms/common/api/PendingResult.java delete mode 100644 play-services-base/src/main/java/com/google/android/gms/common/api/Releasable.java delete mode 100644 play-services-base/src/main/java/com/google/android/gms/common/api/ResultCallback.java delete mode 100644 play-services-base/src/main/java/org/microg/gms/common/api/ApiBuilder.java delete mode 100644 play-services-base/src/main/java/org/microg/gms/common/api/ApiConnection.java delete mode 100644 play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java create mode 120000 play-services-basement delete mode 120000 play-services-common-api create mode 100644 play-services-wearable/src/main/java/com/google/android/gms/wearable/CapabilityApi.java create mode 100644 play-services-wearable/src/main/java/com/google/android/gms/wearable/ChannelIOException.java delete mode 100644 play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageApi.java create mode 100644 play-services-wearable/src/main/java/org/microg/gms/wearable/BaseWearableCallbacks.java create mode 100644 play-services-wearable/src/main/java/org/microg/gms/wearable/WearableClientImpl.java create mode 100644 play-services-wearable/src/main/java/org/microg/gms/wearable/databundle/DataBundleUtil.java create mode 100644 play-services-wearable/src/main/protos-java/org/microg/gms/wearable/databundle/DataBundle.java create mode 100644 play-services-wearable/src/main/protos-java/org/microg/gms/wearable/databundle/DataBundleEntry.java create mode 100644 play-services-wearable/src/main/protos-java/org/microg/gms/wearable/databundle/DataBundleTypedValue.java create mode 100644 play-services-wearable/src/main/protos-java/org/microg/gms/wearable/databundle/DataBundleValue.java create mode 100644 play-services-wearable/src/main/protos-repo/databundle.proto diff --git a/build.gradle b/build.gradle index 504fe91d..7fbd598a 100644 --- a/build.gradle +++ b/build.gradle @@ -18,4 +18,7 @@ subprojects { group = 'org.microg' + repositories { + jcenter() + } } diff --git a/extern/GmsApi b/extern/GmsApi index be6af2ee..b9cb95d3 160000 --- a/extern/GmsApi +++ b/extern/GmsApi @@ -1 +1 @@ -Subproject commit be6af2eeb76ba4bcc1700285f2ba87f217ab6ac2 +Subproject commit b9cb95d39bdb4bbac6dd0d2b0405e4d5a23717c2 diff --git a/play-services-base/build.gradle b/play-services-base/build.gradle index bfb54b6f..a2d4f921 100644 --- a/play-services-base/build.gradle +++ b/play-services-base/build.gradle @@ -48,7 +48,6 @@ android { } dependencies { - compile 'com.android.support:support-v4:23.4.0' - compile project(':play-services-common-api') + compile project(':play-services-basement') compile project(':play-services-tasks') } diff --git a/play-services-base/src/main/AndroidManifest.xml b/play-services-base/src/main/AndroidManifest.xml index 2aa07523..bb6c2140 100644 --- a/play-services-base/src/main/AndroidManifest.xml +++ b/play-services-base/src/main/AndroidManifest.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - diff --git a/play-services-base/src/main/java/com/google/android/gms/common/ConnectionResult.java b/play-services-base/src/main/java/com/google/android/gms/common/ConnectionResult.java deleted file mode 100644 index f6f75c1a..00000000 --- a/play-services-base/src/main/java/com/google/android/gms/common/ConnectionResult.java +++ /dev/null @@ -1,310 +0,0 @@ -/* - * 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-base/src/main/java/com/google/android/gms/common/GooglePlayServicesClient.java b/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesClient.java index b9cece5c..a330a257 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesClient.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesClient.java @@ -41,13 +41,13 @@ public interface GooglePlayServicesClient { void unregisterConnectionFailedListener(OnConnectionFailedListener listener); @Deprecated - public interface OnConnectionFailedListener { + interface OnConnectionFailedListener { void onConnectionFailed(ConnectionResult result); } @Deprecated - public interface ConnectionCallbacks { + interface ConnectionCallbacks { void onConnected(Bundle connectionHint); diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/Api.java b/play-services-base/src/main/java/com/google/android/gms/common/api/Api.java deleted file mode 100644 index 1b0f03f2..00000000 --- a/play-services-base/src/main/java/com/google/android/gms/common/api/Api.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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-base/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java b/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java deleted file mode 100644 index 7140ca96..00000000 --- a/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java +++ /dev/null @@ -1,494 +0,0 @@ -/* - * 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 - */ - public 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 - */ - public 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. - */ - public 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. - */ - public 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() - */ - public 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. - */ - public 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. - */ - public 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) - */ - public 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() - */ - public 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. - */ - public 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) - */ - public 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. - */ - public 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. - */ - public void unregisterConnectionFailedListener(OnConnectionFailedListener listener); - - /** - * Builder to configure a {@link GoogleApiClient}. - */ - @PublicApi - public 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 - public 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 - public 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-base/src/main/java/com/google/android/gms/common/api/PendingResult.java b/play-services-base/src/main/java/com/google/android/gms/common/api/PendingResult.java deleted file mode 100644 index 28943f18..00000000 --- a/play-services-base/src/main/java/com/google/android/gms/common/api/PendingResult.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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. - *

- *

    - *
  • via blocking calls to {@link #await()}, or {@link #await(long, TimeUnit)}, or
  • - *
  • via a callback by passing in an object implementing interface {@link ResultCallback} to - * {@link #setResultCallback(ResultCallback)}.
  • - *
- * 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-base/src/main/java/com/google/android/gms/common/api/Releasable.java b/play-services-base/src/main/java/com/google/android/gms/common/api/Releasable.java deleted file mode 100644 index 6856634a..00000000 --- a/play-services-base/src/main/java/com/google/android/gms/common/api/Releasable.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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; - -/** - * Represents a resource, or a holder of resources, which may be released once they are no longer - * needed. - */ -public interface Releasable { - public void release(); -} diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/ResultCallback.java b/play-services-base/src/main/java/com/google/android/gms/common/api/ResultCallback.java deleted file mode 100644 index 4695dbcb..00000000 --- a/play-services-base/src/main/java/com/google/android/gms/common/api/ResultCallback.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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-base/src/main/java/org/microg/gms/common/GmsClient.java b/play-services-base/src/main/java/org/microg/gms/common/GmsClient.java index 2d88a881..dfbff345 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/GmsClient.java +++ b/play-services-base/src/main/java/org/microg/gms/common/GmsClient.java @@ -16,6 +16,7 @@ package org.microg.gms.common; +import android.accounts.Account; import android.content.ComponentName; import android.content.Context; import android.content.ServiceConnection; @@ -27,6 +28,7 @@ import android.util.Log; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.internal.GetServiceRequest; import com.google.android.gms.common.internal.IGmsCallbacks; import com.google.android.gms.common.internal.IGmsServiceBroker; @@ -41,18 +43,31 @@ public abstract class GmsClient implements ApiConnection { protected ConnectionState state = ConnectionState.NOT_CONNECTED; private ServiceConnection serviceConnection; private I serviceInterface; + private String actionString; + + protected int serviceId = -1; + protected Account account = null; + protected Bundle extras = new Bundle(); public GmsClient(Context context, GoogleApiClient.ConnectionCallbacks callbacks, - GoogleApiClient.OnConnectionFailedListener connectionFailedListener) { + GoogleApiClient.OnConnectionFailedListener connectionFailedListener, String actionString) { this.context = context; this.callbacks = callbacks; this.connectionFailedListener = connectionFailedListener; + this.actionString = actionString; } - protected abstract String getActionString(); - - protected abstract void onConnectedToBroker(IGmsServiceBroker broker, GmsCallbacks callbacks) - throws RemoteException; + protected void onConnectedToBroker(IGmsServiceBroker broker, GmsCallbacks callbacks) throws RemoteException { + if (serviceId == -1) { + throw new IllegalStateException("Service ID not set in constructor and onConnectedToBroker not implemented"); + } + GetServiceRequest request = new GetServiceRequest(serviceId); + request.extras = new Bundle(); + request.packageName = context.getPackageName(); + request.account = account; + request.extras = extras; + broker.getService(callbacks, request); + } protected abstract I interfaceFromBinder(IBinder binder); @@ -64,12 +79,10 @@ public abstract class GmsClient implements ApiConnection { } state = ConnectionState.CONNECTING; if (serviceConnection != null) { - MultiConnectionKeeper.getInstance(context) - .unbind(getActionString(), serviceConnection); + MultiConnectionKeeper.getInstance(context).unbind(actionString, serviceConnection); } serviceConnection = new GmsServiceConnection(); - if (!MultiConnectionKeeper.getInstance(context).bind(getActionString(), - serviceConnection)) { + if (!MultiConnectionKeeper.getInstance(context).bind(actionString, serviceConnection)) { state = ConnectionState.ERROR; handleConnectionFailed(); } @@ -90,7 +103,7 @@ public abstract class GmsClient implements ApiConnection { } serviceInterface = null; if (serviceConnection != null) { - MultiConnectionKeeper.getInstance(context).unbind(getActionString(), serviceConnection); + MultiConnectionKeeper.getInstance(context).unbind(actionString, serviceConnection); serviceConnection = null; } state = ConnectionState.NOT_CONNECTED; diff --git a/play-services-base/src/main/java/org/microg/gms/common/GmsConnector.java b/play-services-base/src/main/java/org/microg/gms/common/GmsConnector.java index efb66edd..8d6dbdad 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/GmsConnector.java +++ b/play-services-base/src/main/java/org/microg/gms/common/GmsConnector.java @@ -23,25 +23,29 @@ import android.util.Log; 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.Result; import org.microg.gms.common.api.AbstractPendingResult; import org.microg.gms.common.api.ApiConnection; import org.microg.gms.common.api.GoogleApiClientImpl; -public class GmsConnector { +public class GmsConnector { private static final String TAG = "GmsConnector"; private final GoogleApiClientImpl apiClient; - private final Api api; + private final Api api; private final Callback callback; - public GmsConnector(GoogleApiClient apiClient, Api api, Callback callback) { + public GmsConnector(GoogleApiClient apiClient, Api api, Callback callback) { this.apiClient = (GoogleApiClientImpl) apiClient; this.api = api; this.callback = callback; } + public static PendingResult call(GoogleApiClient client, Api api, GmsConnector.Callback callback) { + return new GmsConnector(client, api, callback).connect(); + } public AbstractPendingResult connect() { Log.d(TAG, "connect()"); @@ -54,8 +58,12 @@ public class GmsConnector { - public R onClientAvailable(C client) throws RemoteException; + public interface Callback { + void onClientAvailable(C client, ResultProvider resultProvider) throws RemoteException; + + interface ResultProvider { + void onResultAvailable(R result); + } } private class Handler extends android.os.Handler { @@ -66,10 +74,15 @@ public class GmsConnector result = (AbstractPendingResult) msg.obj; + final AbstractPendingResult result = (AbstractPendingResult) msg.obj; try { - C connection = (C)apiClient.getApiConnection(api); - result.deliverResult(callback.onClientAvailable(connection)); + C connection = (C) apiClient.getApiConnection(api); + callback.onClientAvailable(connection, new GmsConnector.Callback.ResultProvider() { + @Override + public void onResultAvailable(R realResult) { + result.deliverResult(realResult); + } + }); } catch (RemoteException ignored) { } diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/ApiBuilder.java b/play-services-base/src/main/java/org/microg/gms/common/api/ApiBuilder.java deleted file mode 100644 index b20899df..00000000 --- a/play-services-base/src/main/java/org/microg/gms/common/api/ApiBuilder.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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; - -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-base/src/main/java/org/microg/gms/common/api/ApiConnection.java b/play-services-base/src/main/java/org/microg/gms/common/api/ApiConnection.java deleted file mode 100644 index 019ea5d8..00000000 --- a/play-services-base/src/main/java/org/microg/gms/common/api/ApiConnection.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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-base/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java b/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java deleted file mode 100644 index b47982ca..00000000 --- a/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * 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; - -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-basement b/play-services-basement new file mode 120000 index 00000000..aac14e46 --- /dev/null +++ b/play-services-basement @@ -0,0 +1 @@ +extern/GmsApi/play-services-basement \ No newline at end of file diff --git a/play-services-cast/src/main/AndroidManifest.xml b/play-services-cast/src/main/AndroidManifest.xml index 2031102a..0921b431 100644 --- a/play-services-cast/src/main/AndroidManifest.xml +++ b/play-services-cast/src/main/AndroidManifest.xml @@ -15,10 +15,10 @@ ~ limitations under the License. --> - + - + - + diff --git a/play-services-common-api b/play-services-common-api deleted file mode 120000 index c85558d0..00000000 --- a/play-services-common-api +++ /dev/null @@ -1 +0,0 @@ -extern/GmsApi/play-services-common-api/ \ No newline at end of file diff --git a/play-services-location/src/main/java/org/microg/gms/location/FusedLocationProviderApiImpl.java b/play-services-location/src/main/java/org/microg/gms/location/FusedLocationProviderApiImpl.java index a9d6bf12..5ce97ba8 100644 --- a/play-services-location/src/main/java/org/microg/gms/location/FusedLocationProviderApiImpl.java +++ b/play-services-location/src/main/java/org/microg/gms/location/FusedLocationProviderApiImpl.java @@ -22,7 +22,6 @@ import android.os.Looper; import android.os.RemoteException; import android.util.Log; -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.Result; @@ -33,6 +32,7 @@ import com.google.android.gms.location.LocationRequest; import com.google.android.gms.location.LocationServices; import org.microg.gms.common.GmsConnector; +import org.microg.gms.common.api.ApiConnection; public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { private static final String TAG = "GmsFusedApiImpl"; @@ -50,7 +50,7 @@ public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { @Override public PendingResult requestLocationUpdates(GoogleApiClient client, - final LocationRequest request, final LocationListener listener) { + final LocationRequest request, final LocationListener listener) { return callVoid(client, new Runnable() { @Override public void run(LocationClientImpl client) throws RemoteException { @@ -61,8 +61,8 @@ public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { @Override public PendingResult requestLocationUpdates(GoogleApiClient client, - final LocationRequest request, final LocationListener listener, - final Looper looper) { + final LocationRequest request, final LocationListener listener, + final Looper looper) { return callVoid(client, new Runnable() { @Override public void run(LocationClientImpl client) throws RemoteException { @@ -73,7 +73,7 @@ public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { @Override public PendingResult requestLocationUpdates(GoogleApiClient client, - final LocationRequest request, final PendingIntent callbackIntent) { + final LocationRequest request, final PendingIntent callbackIntent) { return callVoid(client, new Runnable() { @Override public void run(LocationClientImpl client) throws RemoteException { @@ -84,7 +84,7 @@ public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { @Override public PendingResult removeLocationUpdates(GoogleApiClient client, - final LocationListener listener) { + final LocationListener listener) { return callVoid(client, new Runnable() { @Override public void run(LocationClientImpl client) throws RemoteException { @@ -95,7 +95,7 @@ public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { @Override public PendingResult removeLocationUpdates(GoogleApiClient client, - final PendingIntent callbackIntent) { + final PendingIntent callbackIntent) { return callVoid(client, new Runnable() { @Override public void run(LocationClientImpl client) throws RemoteException { @@ -125,15 +125,13 @@ public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { } private PendingResult callVoid(GoogleApiClient client, final Runnable runnable) { - return new GmsConnector(client, LocationServices.API, - new GmsConnector.Callback() { - @Override - public Result onClientAvailable(LocationClientImpl client) throws - RemoteException { - runnable.run(client); - return Status.SUCCESS; - } - }).connect(); + return GmsConnector.call(client, LocationServices.API, new GmsConnector.Callback() { + @Override + public void onClientAvailable(LocationClientImpl client, ResultProvider resultProvider) throws RemoteException { + runnable.run(client); + resultProvider.onResultAvailable(Status.SUCCESS); + } + }); } private interface Runnable { diff --git a/play-services-location/src/main/java/org/microg/gms/location/GoogleLocationManagerClient.java b/play-services-location/src/main/java/org/microg/gms/location/GoogleLocationManagerClient.java index 55713f02..2a15d015 100644 --- a/play-services-location/src/main/java/org/microg/gms/location/GoogleLocationManagerClient.java +++ b/play-services-location/src/main/java/org/microg/gms/location/GoogleLocationManagerClient.java @@ -32,12 +32,7 @@ import org.microg.gms.common.GmsService; public abstract class GoogleLocationManagerClient extends GmsClient { public GoogleLocationManagerClient(Context context, GoogleApiClient.ConnectionCallbacks callbacks, GoogleApiClient.OnConnectionFailedListener connectionFailedListener) { - super(context, callbacks, connectionFailedListener); - } - - @Override - protected String getActionString() { - return GmsService.LOCATION_MANAGER.ACTION; + super(context, callbacks, connectionFailedListener, GmsService.LOCATION_MANAGER.ACTION); } @Override diff --git a/play-services-tasks/build.gradle b/play-services-tasks/build.gradle index cc390af7..d80b1829 100644 --- a/play-services-tasks/build.gradle +++ b/play-services-tasks/build.gradle @@ -48,5 +48,5 @@ android { } dependencies { - compile project(':play-services-common-api') + compile project(':play-services-basement') } diff --git a/play-services-wearable/build.gradle b/play-services-wearable/build.gradle index 94ece58f..d3a8d0ec 100644 --- a/play-services-wearable/build.gradle +++ b/play-services-wearable/build.gradle @@ -42,6 +42,12 @@ android { versionName getMyVersionName() } + sourceSets { + main { + java.srcDirs += 'src/main/protos-java' + } + } + compileOptions { sourceCompatibility JavaVersion.VERSION_1_6 } @@ -50,4 +56,5 @@ android { dependencies { compile project(':play-services-base') compile project(':play-services-wearable-api') + compile 'com.squareup.wire:wire-runtime:1.6.1' } diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/CapabilityApi.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/CapabilityApi.java new file mode 100644 index 00000000..6f43bc7a --- /dev/null +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/CapabilityApi.java @@ -0,0 +1,193 @@ +/* + * 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 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 org.microg.gms.common.PublicApi; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Map; + +/** + * Exposes an API to learn about capabilities provided by nodes on the Wear network. + *

+ * Capabilities are local to an application. + */ +@PublicApi +public interface CapabilityApi { + /** + * Capability changed action for use in manifest-based listener filters. + *

+ * Capability events do not support filtering by host, but can be filtered by path. + * + * @see WearableListenerService + */ + String ACTION_CAPABILITY_CHANGED = "com.google.android.gms.wearable.CAPABILITY_CHANGED"; + + /** + * Filter type for {@link #getCapability(GoogleApiClient, String, int)}, {@link #getAllCapabilities(GoogleApiClient, int)}: + * If this filter is set then the full set of nodes that declare the given capability will be + * included in the capability's CapabilityInfo. + */ + int FILTER_ALL = 0; + + /** + * Filter type for {@link #addListener(GoogleApiClient, CapabilityListener, Uri, int)}, if this + * filter is set, the given URI will be taken as a literal path, and the operation will apply + * to the matching capability only. + */ + int FILTER_LITERAL = 0; + + /** + * Filter type for {@link #addListener(GoogleApiClient, CapabilityListener, Uri, int)}, if this + * filter is set, the given URI will be taken as a path prefix, and the operation will apply + * to all matching capabilities. + */ + int FILTER_PREFIX = 1; + + /** + * Filter type for {@link #getCapability(GoogleApiClient, String, int)}, {@link #getAllCapabilities(GoogleApiClient, int): + * If this filter is set then only reachable nodes that declare the given capability will be + * included in the capability's CapabilityInfo. + */ + int FILTER_REACHABLE = 1; + + /** + * Registers a listener to be notified of a specific capability being added to or removed from + * the Wear network. Calls to this method should be balanced with {@link #removeCapabilityListener(GoogleApiClient, CapabilityListener, String)} + * 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}. + */ + PendingResult addCapabilityListener(GoogleApiClient client, CapabilityListener listener, String capability); + + /** + * Registers a listener to be notified of capabilities being added to or removed from the Wear + * network. Calls to this method should be balanced with {@link #removeListener(GoogleApiClient, CapabilityListener)} + * to avoid leaking resources. + *

+ * {@code uri} and {@code filterType} can be used to filter the capability changes sent to the + * listener. For example, if {@code uri} and {@code filterType} create a prefix filter, then + * only capabilities matching that prefix will be notified. The {@code uri} follows the rules + * of the {@code } element of {@code }. The path is ignored if a URI host + * is not specified. To match capabilities by name or name prefix, the host must be {@code *}. i.e: + *

+ *

wear://* /
+ * 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 WearableListenerService. + */ + PendingResult addListener(GoogleApiClient client, CapabilityListener listener, Uri uri, @CapabilityFilterType int filterType); + + /** + * Announces that a capability has become available on the local node. + */ + PendingResult addLocalCapability(GoogleApiClient client, String capability); + + /** + * Returns information about all capabilities, including the nodes that declare those + * capabilities. The filter parameter controls whether all nodes are returned, {@link #FILTER_ALL}, + * or only those that are currently reachable by this node, {@link #FILTER_REACHABLE}. + *

+ * The local node will never be returned in the set of nodes. + */ + PendingResult getAllCapabilities(GoogleApiClient client, @NodeFilterType int nodeFilter); + + /** + * Returns information about a capabilities, including the nodes that declare this capability. + * The filter parameter controls whether all nodes are returned, {@link #FILTER_ALL}, or only + * those that are currently reachable by this node, {@link #FILTER_REACHABLE}. + *

+ * The local node will never be returned in the set of nodes. + */ + PendingResult getCapability(GoogleApiClient client, String capability, @NodeFilterType int nodeFilter); + + /** + * Removes a listener which was previously added through {@link #addCapabilityListener(GoogleApiClient, CapabilityListener, String)}. + * The listener is only removed from listening for the capability provided and will continue to + * receive messages for any other capabilities it was previously registered for that have not + * also been removed. + */ + PendingResult removeCapabilityListener(GoogleApiClient client, CapabilityListener listener, String capability); + + /** + * Removes a listener which was previously added through {@link #addListener(GoogleApiClient, CapabilityListener, Uri, int)}. + * The listener is only removed from listening for the capability provided and will continue to + * receive messages for any other capabilities it was previously registered for that have not + * also been removed. + */ + PendingResult removeListener(GoogleApiClient client, CapabilityListener listener); + + /** + * Announces that a capability is no longer available on the local node. Note: this will not + * remove any capabilities announced in the Manifest for an app. + */ + PendingResult removeLocalCapability(GoogleApiClient client, String capability); + + /** + * Result returned from {@link #addLocalCapability(GoogleApiClient, String)} + */ + interface AddLocalCapabilityResult extends Result { + } + + @Retention(RetentionPolicy.SOURCE) + @interface CapabilityFilterType { + } + + /** + * Listener for changes in the reachable nodes providing a capability. + */ + interface CapabilityListener { + void onCapabilityChanged(CapabilityInfo capabilityInfo); + } + + /** + * Result returned from {@link #getAllCapabilities(GoogleApiClient, int)} + */ + interface GetAllCapabilitiesResult extends Result { + Map getAllCapabilities(); + } + + /** + * Result returned from {@link #getCapability(GoogleApiClient, String, int)} + */ + interface GetCapabilityResult extends Result { + CapabilityInfo getCapability(); + } + + @Retention(RetentionPolicy.SOURCE) + @interface NodeFilterType { + } + + /** + * Result returned from {@link #removeLocalCapability(GoogleApiClient, String)} + */ + interface RemoveLocalCapabilityResult extends Result { + } +} diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/ChannelIOException.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/ChannelIOException.java new file mode 100644 index 00000000..46b99879 --- /dev/null +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/ChannelIOException.java @@ -0,0 +1,54 @@ +/* + * 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.wearable.ChannelApi.ChannelListener; + +import java.io.IOException; + +/** + * A subclass of {@link IOException} which can be thrown from the streams returned by + * {@link Channel#getInputStream(GoogleApiClient)} and {@link Channel#getOutputStream(GoogleApiClient)}. + */ +public class ChannelIOException extends IOException { + + private int closeReason; + private int appSpecificErrorCode; + + public ChannelIOException(String message, int closeReason, int appSpecificErrorCode) { + super(message); + this.closeReason = closeReason; + this.appSpecificErrorCode = appSpecificErrorCode; + } + + /** + * Returns the app-specific error code passed to {@link Channel#close(GoogleApiClient, int)} if + * that's the reason for the stream closing, or {@code 0} otherwise. + */ + public int getAppSpecificErrorCode() { + return appSpecificErrorCode; + } + + /** + * Returns one of {@link ChannelListener#CLOSE_REASON_NORMAL}, {@link ChannelListener#CLOSE_REASON_DISCONNECTED}, + * {@link ChannelListener#CLOSE_REASON_REMOTE_CLOSE}, or {@link ChannelListener#CLOSE_REASON_LOCAL_CLOSE}, + * to indicate the reason for the stream closing. + */ + public int getCloseReason() { + return closeReason; + } +} diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataMap.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataMap.java index 0cbb2c63..4a24d29c 100644 --- a/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataMap.java +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/DataMap.java @@ -19,12 +19,29 @@ package com.google.android.gms.wearable; import android.os.Bundle; import org.microg.gms.common.PublicApi; +import org.microg.gms.wearable.databundle.DataBundleUtil; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; +import static org.microg.gms.wearable.databundle.DataBundleUtil.ASSET_TYPE_CODE; +import static org.microg.gms.wearable.databundle.DataBundleUtil.BOOLEAN_TYPE_CODE; +import static org.microg.gms.wearable.databundle.DataBundleUtil.BYTE_ARRAY_TYPE_CODE; +import static org.microg.gms.wearable.databundle.DataBundleUtil.BYTE_TYPE_CODE; +import static org.microg.gms.wearable.databundle.DataBundleUtil.DATAMAP_TYPE_CODE; +import static org.microg.gms.wearable.databundle.DataBundleUtil.DOUBLE_TYPE_CODE; +import static org.microg.gms.wearable.databundle.DataBundleUtil.FLOAT_ARRAY_TYPE_CODE; +import static org.microg.gms.wearable.databundle.DataBundleUtil.FLOAT_TYPE_CODE; +import static org.microg.gms.wearable.databundle.DataBundleUtil.INTEGER_TYPE_CODE; +import static org.microg.gms.wearable.databundle.DataBundleUtil.LIST_TYPE_CODE; +import static org.microg.gms.wearable.databundle.DataBundleUtil.LONG_ARRAY_TYPE_CODE; +import static org.microg.gms.wearable.databundle.DataBundleUtil.LONG_TYPE_CODE; +import static org.microg.gms.wearable.databundle.DataBundleUtil.STRING_ARRAY_TYPE_CODE; +import static org.microg.gms.wearable.databundle.DataBundleUtil.STRING_TYPE_CODE; + /** * A map of data supported by {@link PutDataMapRequest} and {@link DataMapItem}s. DataMap may * convert to and from Bundles, but will drop any types not explicitly supported by DataMap in the @@ -74,6 +91,40 @@ public class DataMap { return o instanceof DataMap && data.equals(((DataMap) o).data); } + public StoredType getType(String key) { + return types.get(key); + } + + @PublicApi(exclude = true) + public enum StoredType { + Asset(ASSET_TYPE_CODE), Boolean(BOOLEAN_TYPE_CODE), Byte(BYTE_TYPE_CODE), + ByteArray(BYTE_ARRAY_TYPE_CODE), DataMap(DATAMAP_TYPE_CODE), DataMapArrayList(DataMap), + Double(DOUBLE_TYPE_CODE), Float(FLOAT_TYPE_CODE), FloatArray(FLOAT_ARRAY_TYPE_CODE), + Integer(INTEGER_TYPE_CODE), IntegerArrayList(Integer), Long(LONG_TYPE_CODE), + LongArray(LONG_ARRAY_TYPE_CODE), String(STRING_TYPE_CODE), + StringArray(STRING_ARRAY_TYPE_CODE), StringArrayList(String); + + private int typeCode; + private StoredType listType; + + StoredType(int typeCode) { + this.typeCode = typeCode; + } + + StoredType(StoredType listType) { + this.typeCode = LIST_TYPE_CODE; + this.listType = listType; + } + + public int getTypeCode() { + return typeCode; + } + + public StoredType getListType() { + return listType; + } + } + /** * @return a DataMap from a Bundle. The input Bundle is expected to contain only elements * supported by DataMap. Any elements in the Bundle not supported by DataMap will be dropped. @@ -82,7 +133,46 @@ public class DataMap { DataMap res = new DataMap(); if (bundle != null) { for (String key : bundle.keySet()) { - res.data.put(key, bundle.get(key)); + Object val = bundle.get(key); + if (val instanceof Asset) { + res.putAsset(key, (Asset) val); + } else if (val instanceof Boolean) { + res.putBoolean(key, (Boolean) val); + } else if (val instanceof Byte) { + res.putByte(key, (Byte) val); + } else if (val instanceof byte[]) { + res.putByteArray(key, (byte[]) val); + } else if (val instanceof Bundle) { + res.putDataMap(key, DataMap.fromBundle((Bundle) val)); + } else if (val instanceof Double) { + res.putDouble(key, (Double) val); + } else if (val instanceof Float) { + res.putFloat(key, (Float) val); + } else if (val instanceof float[]) { + res.putFloatArray(key, (float[]) val); + } else if (val instanceof Integer) { + res.putInt(key, (Integer) val); + } else if (val instanceof Long) { + res.putLong(key, (Long) val); + } else if (val instanceof long[]) { + res.putLongArray(key, (long[]) val); + } else if (val instanceof String) { + res.putString(key, (String) val); + } else if (val instanceof String[]) { + res.putStringArray(key, (String[]) val); + } else if (val instanceof ArrayList) { + if (((ArrayList) val).isEmpty() || ((ArrayList) val).get(0) instanceof String) { + res.putStringArrayList(key, (ArrayList) val); + } else if (((ArrayList) val).get(0) instanceof Bundle) { + ArrayList dataMaps = new ArrayList(); + for (Bundle b : ((ArrayList) val)) { + dataMaps.add(DataMap.fromBundle(b)); + } + res.putDataMapArrayList(key, dataMaps); + } else if (((ArrayList) val).get(0) instanceof Integer) { + res.putIntegerArrayList(key, (ArrayList) val); + } + } } } return res; @@ -92,7 +182,7 @@ public class DataMap { * @return a DataMap from a byte[]. */ public static DataMap fromByteArray(byte[] bytes) { - return null; // TODO + return DataBundleUtil.readDataMap(bytes, Collections.emptyList()); } /** @@ -213,6 +303,11 @@ public class DataMap { } } + public void putAsset(String key, Asset value) { + data.put(key, value); + types.put(key, StoredType.Asset); + } + public void putBoolean(String key, boolean value) { data.put(key, value); types.put(key, StoredType.Boolean); @@ -302,7 +397,7 @@ public class DataMap { for (String key : data.keySet()) { switch (types.get(key)) { case Asset: - bundle.putParcelable(key, (Asset)data.get(key)); + bundle.putParcelable(key, (Asset) data.get(key)); break; case Boolean: bundle.putBoolean(key, (Boolean) data.get(key)); @@ -317,7 +412,11 @@ public class DataMap { bundle.putBundle(key, ((DataMap) data.get(key)).toBundle()); break; case DataMapArrayList: - // TODO + ArrayList bundles = new ArrayList(); + for (DataMap dataMap : ((ArrayList) data.get(key))) { + bundles.add(dataMap.toBundle()); + } + bundle.putParcelableArrayList(key, bundles); break; case Double: bundle.putDouble(key, (Double) data.get(key)); @@ -355,15 +454,10 @@ public class DataMap { } public byte[] toByteArray() { - return null; // TODO + return DataBundleUtil.createBytes(this); } public String toString() { return "DataMap{size=" + size() + "}"; } - - private enum StoredType { - Asset, Boolean, Byte, ByteArray, DataMap, DataMapArrayList, Double, Float, FloatArray, - Integer, IntegerArrayList, Long, LongArray, String, StringArray, StringArrayList - } } diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageApi.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageApi.java deleted file mode 100644 index e2016f73..00000000 --- a/play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageApi.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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.wearable; - -import com.google.android.gms.common.api.GoogleApiClient; -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 org.microg.gms.common.PublicApi; - -@PublicApi -public interface MessageApi { - - /** - * A value returned by {@link SendMessageResult#getRequestId()} when - * {@link #sendMessage(GoogleApiClient, String, String, byte[])} fails. - */ - int UNKNOWN_REQUEST_ID = -1; - - /** - * Registers a listener to be notified of received messages. Calls to this method should - * balanced with {@link #removeListener(GoogleApiClient, MessageListener)} to avoid leaking - * resources. - *

- * Callers wishing to be notified of events in the background should use {@link WearableListenerService}. - */ - PendingResult addListener(GoogleApiClient client, MessageListener listener); - - /** - * Removes a message listener which was previously added through - * {@link #addListener(GoogleApiClient, MessageListener)}. - */ - PendingResult removeListener(GoogleApiClient client, MessageListener listener); - - /** - * Sends {@code byte[]} data to the specified node. - * - * @param nodeId identifier for a particular node on the Android Wear network. Valid targets - * may be obtained through {@link NodeApi#getConnectedNodes(GoogleApiClient)} - * or from the host in {@link DataItem#getUri()}. - * @param path identifier used to specify a particular endpoint at the receiving node - * @param data small array of information to pass to the target node. Generally not larger - * than 100k - */ - PendingResult sendMessage(GoogleApiClient client, String nodeId, - String path, byte[] data); - - /** - * Used with {@link MessageApi#addListener(GoogleApiClient, MessageListener)} to receive - * message events. - *

- * Callers wishing to be notified of events in the background should use - * {@link WearableListenerService}. - */ - interface MessageListener { - /** - * Notification that a message has been received. - */ - void onMessageReceived(MessageEvent messageEvent); - } - - /** - * Contains the request id assigned to the message. On failure, the id will be - * {@link MessageApi#UNKNOWN_REQUEST_ID} and the status will be unsuccessful. - */ - interface SendMessageResult extends Result { - /** - * @return an ID used to identify the sent message. If {@link #getStatus()} is not - * successful, this value will be {@link MessageApi#UNKNOWN_REQUEST_ID}. - */ - int getRequestId(); - } -} diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/Wearable.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/Wearable.java index 420652a3..1a505b50 100644 --- a/play-services-wearable/src/main/java/com/google/android/gms/wearable/Wearable.java +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/Wearable.java @@ -19,6 +19,7 @@ package com.google.android.gms.wearable; import com.google.android.gms.common.api.Api; import com.google.android.gms.common.api.GoogleApiClient; +import org.microg.gms.common.PublicApi; import org.microg.gms.wearable.DataApiImpl; import org.microg.gms.wearable.MessageApiImpl; import org.microg.gms.wearable.NodeApiImpl; @@ -27,6 +28,7 @@ import org.microg.gms.wearable.WearableApiBuilder; /** * An API for the Android Wear platform. */ +@PublicApi public class Wearable { /** * Token to pass to {@link GoogleApiClient.Builder#addApi(Api)} to enable the Wearable features. @@ -38,6 +40,12 @@ public class Wearable { public static final NodeApi NodeApi = new NodeApiImpl(); public static class WearableOptions implements Api.ApiOptions.Optional { + /** + * Special option for microG to allow implementation of a FOSS first party Android Wear app + */ + @PublicApi(exclude = true) + public boolean firstPartyMode = false; + public static class Builder { public WearableOptions build() { return new WearableOptions(); diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/WearableListenerService.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/WearableListenerService.java index 5817c5d3..b3e6c8ff 100644 --- a/play-services-wearable/src/main/java/com/google/android/gms/wearable/WearableListenerService.java +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/WearableListenerService.java @@ -18,41 +18,251 @@ package com.google.android.gms.wearable; import android.app.Service; import android.content.Intent; +import android.os.Binder; +import android.os.Handler; +import android.os.HandlerThread; import android.os.IBinder; +import android.os.Looper; +import android.os.RemoteException; +import android.util.Log; + +import com.google.android.gms.common.data.DataHolder; +import com.google.android.gms.wearable.internal.AmsEntityUpdateParcelable; +import com.google.android.gms.wearable.internal.AncsNotificationParcelable; +import com.google.android.gms.wearable.internal.CapabilityInfoParcelable; +import com.google.android.gms.wearable.internal.ChannelEventParcelable; +import com.google.android.gms.wearable.internal.IWearableListener; +import com.google.android.gms.wearable.internal.MessageEventParcelable; +import com.google.android.gms.wearable.internal.NodeParcelable; + +import org.microg.gms.common.PublicApi; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME; + +@PublicApi +public abstract class WearableListenerService extends Service implements CapabilityApi.CapabilityListener, ChannelApi.ChannelListener, DataApi.DataListener, MessageApi.MessageListener, NodeApi.NodeListener { + private static final String BIND_LISTENER_INTENT_ACTION = "com.google.android.gms.wearable.BIND_LISTENER"; + private static final String TAG = "GmsWearListenerSvc"; + + private HandlerThread handlerThread; + private IWearableListener listener; + private ServiceHandler serviceHandler; + private Object lock = new Object(); + private boolean disconnected = false; -public abstract class WearableListenerService extends Service implements DataApi.DataListener, MessageApi.MessageListener, NodeApi.NodeListener { @Override public IBinder onBind(Intent intent) { + if (BIND_LISTENER_INTENT_ACTION.equals(intent.getAction())) { + return listener.asBinder(); + } return null; } + @Override + public void onCapabilityChanged(CapabilityInfo capabilityInfo) { + } + + public void onConnectedNodes(List connectedNodes) { + } + + @Override + public void onChannelClosed(Channel channel, int closeReason, int appSpecificErrorCode) { + } + + @Override + public void onChannelOpened(Channel channel) { + } + @Override public void onCreate() { super.onCreate(); + handlerThread = new HandlerThread("WearableListenerService"); + handlerThread.start(); + serviceHandler = new ServiceHandler(handlerThread.getLooper()); + listener = new Listener(); } @Override public void onDataChanged(DataEventBuffer dataEvents) { - } @Override public void onDestroy() { + synchronized (lock) { + if (serviceHandler == null) { + throw new IllegalStateException("serviceHandler not set, did you override onCreate() but forget to call super.onCreate()?"); + } + serviceHandler.getLooper().quit(); + } super.onDestroy(); } + @PublicApi(exclude = true) + public void onEntityUpdate(AmsEntityUpdate entityUpdate) { + } + + @Override + public void onInputClosed(Channel channel, @ChannelApi.CloseReason int closeReason, int appSpecificErrorCode) { + } + @Override public void onMessageReceived(MessageEvent messageEvent) { + } + @PublicApi(exclude = true) + public void onNotificationReceived(AncsNotification notification) { + } + + @Override + public void onOutputClosed(Channel channel, @ChannelApi.CloseReason int closeReason, int appSpecificErrorCode) { } @Override public void onPeerConnected(Node peer) { - } @Override public void onPeerDisconnected(Node peer) { + } + private class Listener extends IWearableListener.Stub { + private int knownGoodUid = -1; + + private boolean post(Runnable runnable) { + int callingUid = Binder.getCallingUid(); + if (callingUid != knownGoodUid) { + // TODO: Verify Gms is calling + String[] packagesForUid = getPackageManager().getPackagesForUid(callingUid); + if (packagesForUid != null) { + if (Arrays.asList(packagesForUid).contains(GMS_PACKAGE_NAME)) { + knownGoodUid = callingUid; + } else { + throw new SecurityException("Caller is not Services Core"); + } + } + } + synchronized (lock) { + if (disconnected) { + return false; + } + serviceHandler.post(runnable); + return true; + } + } + + @Override + public void onDataChanged(final DataHolder data) throws RemoteException { + post(new Runnable() { + @Override + public void run() { + WearableListenerService.this.onDataChanged(new DataEventBuffer(data)); + } + }); + } + + @Override + public void onMessageReceived(final MessageEventParcelable messageEvent) throws RemoteException { + post(new Runnable() { + @Override + public void run() { + WearableListenerService.this.onMessageReceived(messageEvent); + } + }); + } + + @Override + public void onPeerConnected(final NodeParcelable node) throws RemoteException { + post(new Runnable() { + @Override + public void run() { + WearableListenerService.this.onPeerConnected(node); + } + }); + } + + @Override + public void onPeerDisconnected(final NodeParcelable node) throws RemoteException { + post(new Runnable() { + @Override + public void run() { + WearableListenerService.this.onPeerDisconnected(node); + } + }); + } + + @Override + public void onConnectedNodes(final List nodes) throws RemoteException { + post(new Runnable() { + @Override + public void run() { + WearableListenerService.this.onConnectedNodes(new ArrayList(nodes)); + } + }); + } + + @Override + public void onConnectedCapabilityChanged(final CapabilityInfoParcelable capabilityInfo) throws RemoteException { + post(new Runnable() { + @Override + public void run() { + WearableListenerService.this.onCapabilityChanged(capabilityInfo); + } + }); + } + + @Override + public void onNotificationReceived(final AncsNotificationParcelable notification) throws RemoteException { + post(new Runnable() { + @Override + public void run() { + WearableListenerService.this.onNotificationReceived(notification); + } + }); + } + + @Override + public void onEntityUpdate(final AmsEntityUpdateParcelable update) throws RemoteException { + post(new Runnable() { + @Override + public void run() { + WearableListenerService.this.onEntityUpdate(update); + } + }); + } + + @Override + public void onChannelEvent(final ChannelEventParcelable channelEvent) throws RemoteException { + post(new Runnable() { + @Override + public void run() { + switch (channelEvent.eventType) { + case 1: + WearableListenerService.this.onChannelOpened(channelEvent.channel); + break; + case 2: + WearableListenerService.this.onChannelClosed(channelEvent.channel, channelEvent.closeReason, channelEvent.appSpecificErrorCode); + break; + case 3: + WearableListenerService.this.onInputClosed(channelEvent.channel, channelEvent.closeReason, channelEvent.appSpecificErrorCode); + break; + case 4: + WearableListenerService.this.onOutputClosed(channelEvent.channel, channelEvent.closeReason, channelEvent.appSpecificErrorCode); + break; + default: + Log.w(TAG, "Unknown ChannelEvent.eventType"); + } + } + }); + } + } + + private class ServiceHandler extends Handler { + public ServiceHandler(Looper looper) { + super(looper); + } } } diff --git a/play-services-wearable/src/main/java/org/microg/gms/wearable/BaseWearableCallbacks.java b/play-services-wearable/src/main/java/org/microg/gms/wearable/BaseWearableCallbacks.java new file mode 100644 index 00000000..5d58d576 --- /dev/null +++ b/play-services-wearable/src/main/java/org/microg/gms/wearable/BaseWearableCallbacks.java @@ -0,0 +1,117 @@ +/* + * 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.wearable; + +import android.os.RemoteException; +import android.util.Log; + +import com.google.android.gms.common.api.Status; +import com.google.android.gms.common.data.DataHolder; +import com.google.android.gms.wearable.internal.DeleteDataItemsResponse; +import com.google.android.gms.wearable.internal.GetCloudSyncSettingResponse; +import com.google.android.gms.wearable.internal.GetConfigResponse; +import com.google.android.gms.wearable.internal.GetConfigsResponse; +import com.google.android.gms.wearable.internal.GetConnectedNodesResponse; +import com.google.android.gms.wearable.internal.GetDataItemResponse; +import com.google.android.gms.wearable.internal.GetFdForAssetResponse; +import com.google.android.gms.wearable.internal.GetLocalNodeResponse; +import com.google.android.gms.wearable.internal.IWearableCallbacks; +import com.google.android.gms.wearable.internal.PutDataResponse; +import com.google.android.gms.wearable.internal.SendMessageResponse; +import com.google.android.gms.wearable.internal.StorageInfoResponse; + +public class BaseWearableCallbacks extends IWearableCallbacks.Stub { + private static final String TAG = "GmsWearBaseCallback"; + + @Override + public void onGetConfigResponse(GetConfigResponse response) throws RemoteException { + Log.d(TAG, "unimplemented Method: onGetConfigResponse"); + + } + + @Override + public void onPutDataResponse(PutDataResponse response) throws RemoteException { + Log.d(TAG, "unimplemented Method: onPutDataResponse"); + + } + + @Override + public void onGetDataItemResponse(GetDataItemResponse response) throws RemoteException { + Log.d(TAG, "unimplemented Method: onGetDataItemResponse"); + + } + + @Override + public void onDataItemChanged(DataHolder dataHolder) throws RemoteException { + Log.d(TAG, "unimplemented Method: onDataItemChanged"); + + } + + @Override + public void onDeleteDataItemsResponse(DeleteDataItemsResponse response) throws RemoteException { + Log.d(TAG, "unimplemented Method: onDeleteDataItemsResponse"); + + } + + @Override + public void onSendMessageResponse(SendMessageResponse response) throws RemoteException { + Log.d(TAG, "unimplemented Method: onSendMessageResponse"); + + } + + @Override + public void onGetFdForAssetResponse(GetFdForAssetResponse response) throws RemoteException { + Log.d(TAG, "unimplemented Method: onGetFdForAssetResponse"); + + } + + @Override + public void onGetLocalNodeResponse(GetLocalNodeResponse response) throws RemoteException { + Log.d(TAG, "unimplemented Method: onGetLocalNodeResponse"); + + } + + @Override + public void onGetConnectedNodesResponse(GetConnectedNodesResponse response) throws RemoteException { + Log.d(TAG, "unimplemented Method: onGetConnectedNodesResponse"); + + } + + @Override + public void onStatus(Status status) throws RemoteException { + Log.d(TAG, "unimplemented Method: onStatus"); + + } + + @Override + public void onStorageInfoResponse(StorageInfoResponse response) throws RemoteException { + Log.d(TAG, "unimplemented Method: onStorageInfoResponse"); + + } + + @Override + public void onGetConfigsResponse(GetConfigsResponse response) throws RemoteException { + Log.d(TAG, "unimplemented Method: onGetConfigsResponse"); + + } + + @Override + public void onGetCloudSyncSettingResponse(GetCloudSyncSettingResponse response) throws RemoteException { + Log.d(TAG, "unimplemented Method: onGetCloudSyncSettingResponse"); + + } +} diff --git a/play-services-wearable/src/main/java/org/microg/gms/wearable/DataApiImpl.java b/play-services-wearable/src/main/java/org/microg/gms/wearable/DataApiImpl.java index f7a09865..7da5f5f7 100644 --- a/play-services-wearable/src/main/java/org/microg/gms/wearable/DataApiImpl.java +++ b/play-services-wearable/src/main/java/org/microg/gms/wearable/DataApiImpl.java @@ -30,46 +30,46 @@ import com.google.android.gms.wearable.internal.PutDataRequest; public class DataApiImpl implements DataApi { @Override public PendingResult addListener(GoogleApiClient client, DataListener listener) { - return null; + throw new UnsupportedOperationException(); } @Override public PendingResult deleteDataItems(GoogleApiClient client, Uri uri) { - return null; + throw new UnsupportedOperationException(); } @Override public PendingResult getDataItem(GoogleApiClient client, Uri uri) { - return null; + throw new UnsupportedOperationException(); } @Override public PendingResult getDataItems(GoogleApiClient client) { - return null; + throw new UnsupportedOperationException(); } @Override public PendingResult getDataItems(GoogleApiClient client, Uri uri) { - return null; + throw new UnsupportedOperationException(); } @Override public PendingResult getFdForAsset(GoogleApiClient client, DataItemAsset asset) { - return null; + throw new UnsupportedOperationException(); } @Override public PendingResult getFdForAsset(GoogleApiClient client, Asset asset) { - return null; + throw new UnsupportedOperationException(); } @Override public PendingResult putDataItem(GoogleApiClient client, PutDataRequest request) { - return null; + throw new UnsupportedOperationException(); } @Override public PendingResult removeListener(GoogleApiClient client, DataListener listener) { - return null; + throw new UnsupportedOperationException(); } } diff --git a/play-services-wearable/src/main/java/org/microg/gms/wearable/MessageApiImpl.java b/play-services-wearable/src/main/java/org/microg/gms/wearable/MessageApiImpl.java index 3312ad45..2bb784dc 100644 --- a/play-services-wearable/src/main/java/org/microg/gms/wearable/MessageApiImpl.java +++ b/play-services-wearable/src/main/java/org/microg/gms/wearable/MessageApiImpl.java @@ -16,24 +16,42 @@ package org.microg.gms.wearable; +import android.os.RemoteException; +import android.util.Log; + 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 com.google.android.gms.wearable.MessageApi; +import com.google.android.gms.wearable.Wearable; +import com.google.android.gms.wearable.internal.SendMessageResponse; + +import org.microg.gms.common.GmsConnector; +import org.microg.gms.common.api.ApiConnection; public class MessageApiImpl implements MessageApi { @Override public PendingResult addListener(GoogleApiClient client, MessageListener listener) { - return null; + throw new UnsupportedOperationException(); } @Override public PendingResult removeListener(GoogleApiClient client, MessageListener listener) { - return null; + throw new UnsupportedOperationException(); } @Override - public PendingResult sendMessage(GoogleApiClient client, String nodeId, String path, byte[] data) { - return null; + public PendingResult sendMessage(GoogleApiClient client, final String nodeId, final String path, final byte[] data) { + return GmsConnector.call(client, Wearable.API, new GmsConnector.Callback() { + @Override + public void onClientAvailable(WearableClientImpl client, final ResultProvider resultProvider) throws RemoteException { + client.getServiceInterface().sendMessage(new BaseWearableCallbacks() { + @Override + public void onSendMessageResponse(SendMessageResponse response) throws RemoteException { + resultProvider.onResultAvailable(response); + } + }, nodeId, path, data); + } + }); } } diff --git a/play-services-wearable/src/main/java/org/microg/gms/wearable/NodeApiImpl.java b/play-services-wearable/src/main/java/org/microg/gms/wearable/NodeApiImpl.java index 4cf51a46..d1a86b8a 100644 --- a/play-services-wearable/src/main/java/org/microg/gms/wearable/NodeApiImpl.java +++ b/play-services-wearable/src/main/java/org/microg/gms/wearable/NodeApiImpl.java @@ -24,21 +24,21 @@ import com.google.android.gms.wearable.NodeApi; public class NodeApiImpl implements NodeApi { @Override public PendingResult addListener(GoogleApiClient client, NodeListener listener) { - return null; + throw new UnsupportedOperationException(); } @Override public PendingResult getConnectedNodes(GoogleApiClient client) { - return null; + throw new UnsupportedOperationException(); } @Override public PendingResult getLocalNode(GoogleApiClient client) { - return null; + throw new UnsupportedOperationException(); } @Override public PendingResult removeListener(GoogleApiClient client, NodeListener listener) { - return null; + throw new UnsupportedOperationException(); } } diff --git a/play-services-wearable/src/main/java/org/microg/gms/wearable/WearableApiBuilder.java b/play-services-wearable/src/main/java/org/microg/gms/wearable/WearableApiBuilder.java index 1d103230..768509d9 100644 --- a/play-services-wearable/src/main/java/org/microg/gms/wearable/WearableApiBuilder.java +++ b/play-services-wearable/src/main/java/org/microg/gms/wearable/WearableApiBuilder.java @@ -18,7 +18,6 @@ package org.microg.gms.wearable; import android.content.Context; import android.os.Looper; -import android.util.Log; import com.google.android.gms.common.api.AccountInfo; import com.google.android.gms.common.api.GoogleApiClient; @@ -34,7 +33,6 @@ public class WearableApiBuilder implements ApiBuilder public ApiConnection build(Context context, Looper looper, Wearable.WearableOptions options, AccountInfo accountInfo, GoogleApiClient.ConnectionCallbacks callbacks, GoogleApiClient.OnConnectionFailedListener connectionFailedListener) { - Log.d(TAG, "Wearables not supported"); - return null; + return new WearableClientImpl(context, options, callbacks, connectionFailedListener); } } diff --git a/play-services-wearable/src/main/java/org/microg/gms/wearable/WearableClientImpl.java b/play-services-wearable/src/main/java/org/microg/gms/wearable/WearableClientImpl.java new file mode 100644 index 00000000..310590b2 --- /dev/null +++ b/play-services-wearable/src/main/java/org/microg/gms/wearable/WearableClientImpl.java @@ -0,0 +1,53 @@ +/* + * 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.wearable; + +import android.content.Context; +import android.os.IBinder; +import android.util.Log; + +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.wearable.Wearable; +import com.google.android.gms.wearable.internal.IWearableService; + +import org.microg.gms.common.GmsClient; +import org.microg.gms.common.GmsService; +import org.microg.gms.common.api.GoogleApiClientImpl; + +public class WearableClientImpl extends GmsClient { + private static final String TAG = "GmsWearClient"; + + public WearableClientImpl(Context context, Wearable.WearableOptions options, GoogleApiClient.ConnectionCallbacks callbacks, GoogleApiClient.OnConnectionFailedListener connectionFailedListener) { + super(context, callbacks, connectionFailedListener, GmsService.WEARABLE.ACTION); + serviceId = GmsService.WEARABLE.SERVICE_ID; + if (options != null && options.firstPartyMode) + extras.putBoolean("firstPartyMode", true); + Log.d(TAG, ""); + } + + @Override + protected IWearableService interfaceFromBinder(IBinder binder) { + return IWearableService.Stub.asInterface(binder); + } + + public static WearableClientImpl get(GoogleApiClient apiClient) { + if (apiClient instanceof GoogleApiClientImpl) { + return (WearableClientImpl) ((GoogleApiClientImpl) apiClient).getApiConnection(Wearable.API); + } + return null; + } +} diff --git a/play-services-wearable/src/main/java/org/microg/gms/wearable/databundle/DataBundleUtil.java b/play-services-wearable/src/main/java/org/microg/gms/wearable/databundle/DataBundleUtil.java new file mode 100644 index 00000000..69a71964 --- /dev/null +++ b/play-services-wearable/src/main/java/org/microg/gms/wearable/databundle/DataBundleUtil.java @@ -0,0 +1,658 @@ +/* + * 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.wearable.databundle; + +import android.util.SparseArray; + +import com.google.android.gms.wearable.Asset; +import com.google.android.gms.wearable.DataMap; +import com.squareup.wire.Wire; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import okio.ByteString; + +public class DataBundleUtil { + + public static DataMap readDataMap(byte[] bytes, List assets) { + try { + return readDataMap(new Wire().parseFrom(bytes, DataBundle.class), assets); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static DataMap readDataMap(DataBundle dataBundle, List assets) { + return readDataMap(dataBundle.entries, assets); + } + + public static DataMap readDataMap(List entries, List assets) { + DataMap dataMap = new DataMap(); + for (DataBundleEntry entry : entries) { + readAndStore(dataMap, entry.key, entry.typedValue, assets); + } + return dataMap; + } + + public static byte[] createBytes(DataMap dataMap) { + AssetAnnotatedDataBundle dataBundle = createDataBundle(dataMap); + if (!dataBundle.getAssets().isEmpty()) { + throw new UnsupportedOperationException(); + } + return dataBundle.getData(); + } + + public static AssetAnnotatedDataBundle createDataBundle(DataMap dataMap) { + AssetAnnotatedDataBundle dataBundle = new AssetAnnotatedDataBundle(); + dataBundle.assets = new ArrayList(); + dataBundle.dataBundle = new DataBundle(createEntryList(dataMap, dataBundle.assets)); + return dataBundle; + } + + private static List createEntryList(DataMap dataMap, List assets) { + List entries = new ArrayList(); + for (String key : dataMap.keySet()) { + entries.add(getTypeHelper(dataMap.getType(key)).loadAndCreateEntry(dataMap, key, assets)); + } + return entries; + } + + private static void readAndStore(DataMap dataMap, String key, DataBundleTypedValue value, List assets) { + if (value.type == null) return; + getTypeHelper(value.type).readAndStore(dataMap, key, value, assets); + } + + + private static SparseArray typeHelperByCode; + + private static void rememberTypeReader(TypeHelper typeHelper) { + typeHelperByCode.put(typeHelper.type, typeHelper); + } + + static TypeHelper getTypeHelper(int type) { + if (typeHelperByCode.get(type) != null) { + return typeHelperByCode.get(type); + } else { + throw new IllegalArgumentException(); + } + } + + static TypeHelper getTypeHelper(DataMap.StoredType type) { + return getTypeHelper(type.getTypeCode()); + } + + static TypeHelper getListInnerTypeHelper(DataMap.StoredType type) { + return getTypeHelper(type.getListType()); + } + + public static final int BYTE_ARRAY_TYPE_CODE = 1; + static TypeHelper BYTEARRAY = new TypeHelper(BYTE_ARRAY_TYPE_CODE) { + @Override + byte[] read(DataBundleValue value, List assets) { + return value.byteArray.toByteArray(); + } + + @Override + DataBundleValue create(byte[] value, List assets) { + return new DataBundleValue.Builder().byteArray(ByteString.of(value)).build(); + } + + @Override + void store(DataMap dataMap, String key, byte[] value) { + dataMap.putByteArray(key, value); + } + + @Override + byte[] load(DataMap dataMap, String key) { + return dataMap.getByteArray(key); + } + }; + + public static final int STRING_TYPE_CODE = 2; + static TypeHelper STRING = new TypeHelper(STRING_TYPE_CODE) { + @Override + String read(DataBundleValue value, List assets) { + return value.stringVal; + } + + @Override + DataBundleValue create(String value, List assets) { + return new DataBundleValue.Builder().stringVal(value).build(); + } + + @Override + void store(DataMap dataMap, String key, String value) { + dataMap.putString(key, value); + } + + @Override + void storeList(DataMap dataMap, String key, ArrayList valueList) { + dataMap.putStringArrayList(key, valueList); + } + + @Override + String load(DataMap dataMap, String key) { + return dataMap.getString(key); + } + + @Override + AnnotatedArrayList loadList(DataMap dataMap, String key) { + AnnotatedArrayList list = new AnnotatedArrayList(this); + list.addAll(dataMap.getStringArrayList(key)); + return list; + } + }; + + public static final int DOUBLE_TYPE_CODE = 3; + static TypeHelper DOUBLE = new TypeHelper(DOUBLE_TYPE_CODE) { + @Override + Double read(DataBundleValue value, List assets) { + return value.doubleVal; + } + + @Override + DataBundleValue create(Double value, List assets) { + return new DataBundleValue.Builder().doubleVal(value).build(); + } + + @Override + void store(DataMap dataMap, String key, Double value) { + dataMap.putDouble(key, value); + } + + @Override + Double load(DataMap dataMap, String key) { + return dataMap.getDouble(key); + } + }; + + public static final int FLOAT_TYPE_CODE = 4; + static TypeHelper FLOAT = new TypeHelper(FLOAT_TYPE_CODE) { + @Override + Float read(DataBundleValue value, List assets) { + return value.floatVal; + } + + @Override + DataBundleValue create(Float value, List assets) { + return new DataBundleValue.Builder().floatVal(value).build(); + } + + @Override + void store(DataMap dataMap, String key, Float value) { + dataMap.putFloat(key, value); + } + + @Override + Float load(DataMap dataMap, String key) { + return dataMap.getFloat(key); + } + }; + + public static final int LONG_TYPE_CODE = 5; + static TypeHelper LONG = new TypeHelper(LONG_TYPE_CODE) { + @Override + Long read(DataBundleValue value, List assets) { + return value.longVal; + } + + @Override + DataBundleValue create(Long value, List assets) { + return new DataBundleValue.Builder().longVal(value).build(); + } + + @Override + void store(DataMap dataMap, String key, Long value) { + dataMap.putLong(key, value); + } + + @Override + Long load(DataMap dataMap, String key) { + return dataMap.getLong(key); + } + }; + + public static final int INTEGER_TYPE_CODE = 6; + static TypeHelper INTEGER = new TypeHelper(INTEGER_TYPE_CODE) { + @Override + Integer read(DataBundleValue value, List assets) { + return value.intVal; + } + + @Override + DataBundleValue create(Integer value, List assets) { + return new DataBundleValue.Builder().intVal(value).build(); + } + + @Override + void store(DataMap dataMap, String key, Integer value) { + dataMap.putInt(key, value); + } + + @Override + void storeList(DataMap dataMap, String key, ArrayList valueList) { + dataMap.putIntegerArrayList(key, valueList); + } + + @Override + Integer load(DataMap dataMap, String key) { + return dataMap.getInt(key); + } + + @Override + AnnotatedArrayList loadList(DataMap dataMap, String key) { + AnnotatedArrayList list = new AnnotatedArrayList(this); + list.addAll(dataMap.getIntegerArrayList(key)); + return list; + } + }; + + public static final int BYTE_TYPE_CODE = 7; + static TypeHelper BYTE = new TypeHelper(BYTE_TYPE_CODE) { + @Override + Byte read(DataBundleValue value, List assets) { + return (byte) (int) value.byteVal; + } + + @Override + DataBundleValue create(Byte value, List assets) { + return new DataBundleValue.Builder().byteVal((int) (byte) value).build(); + } + + @Override + void store(DataMap dataMap, String key, Byte value) { + dataMap.putByte(key, value); + } + + @Override + Byte load(DataMap dataMap, String key) { + return dataMap.getByte(key); + } + }; + + public static final int BOOLEAN_TYPE_CODE = 8; + static TypeHelper BOOLEAN = new TypeHelper(BOOLEAN_TYPE_CODE) { + @Override + Boolean read(DataBundleValue value, List assets) { + return value.booleanVal; + } + + @Override + DataBundleValue create(Boolean value, List assets) { + return new DataBundleValue.Builder().booleanVal(value).build(); + } + + @Override + void store(DataMap dataMap, String key, Boolean value) { + dataMap.putBoolean(key, value); + } + + @Override + Boolean load(DataMap dataMap, String key) { + return dataMap.getBoolean(key); + } + }; + + public static final int DATAMAP_TYPE_CODE = 9; + static TypeHelper DATAMAP = new TypeHelper(DATAMAP_TYPE_CODE) { + @Override + DataMap read(DataBundleValue value, List assets) { + return readDataMap(value.map, assets); + } + + @Override + DataBundleValue create(DataMap value, List assets) { + return new DataBundleValue.Builder().map(createEntryList(value, assets)).build(); + } + + @Override + void store(DataMap dataMap, String key, DataMap value) { + dataMap.putDataMap(key, value); + } + + @Override + void storeList(DataMap dataMap, String key, ArrayList valueList) { + dataMap.putDataMapArrayList(key, valueList); + } + + @Override + DataMap load(DataMap dataMap, String key) { + return dataMap.getDataMap(key); + } + + @Override + AnnotatedArrayList loadList(DataMap dataMap, String key) { + AnnotatedArrayList list = new AnnotatedArrayList(this); + list.addAll(dataMap.getDataMapArrayList(key)); + return list; + } + }; + + public static final int LIST_TYPE_CODE = 10; + static TypeHelper> ARRAYLIST = new TypeHelper>(LIST_TYPE_CODE) { + @Override + AnnotatedArrayList read(DataBundleValue value, List assets) { + TypeHelper innerTypeHelper = NULL; + for (DataBundleTypedValue typedValue : value.list) { + if (innerTypeHelper == NULL) { + innerTypeHelper = getTypeHelper(typedValue.type); + } else if (typedValue.type != innerTypeHelper.type && typedValue.type != NULL.type) { + throw new IllegalArgumentException("List has elements of different types: " + innerTypeHelper.type + " and " + typedValue.type); + } + } + return innerTypeHelper.readList(value.list, assets); + } + + @Override + DataBundleValue create(AnnotatedArrayList value, List assets) { + return new DataBundleValue.Builder().list(value.createList(assets)).build(); + } + + @Override + void store(DataMap dataMap, String key, AnnotatedArrayList value) { + value.store(dataMap, key); + } + + @Override + AnnotatedArrayList load(DataMap dataMap, String key) { + return getListInnerTypeHelper(dataMap.getType(key)).loadList(dataMap, key); + } + }; + + public static final int STRING_ARRAY_TYPE_CODE = 11; + static TypeHelper STRINGARRAY = new TypeHelper(STRING_ARRAY_TYPE_CODE) { + @Override + String[] read(DataBundleValue value, List assets) { + return value.stringArray.toArray(new String[value.stringArray.size()]); + } + + @Override + DataBundleValue create(String[] value, List assets) { + return new DataBundleValue.Builder().stringArray(Arrays.asList(value)).build(); + } + + @Override + void store(DataMap dataMap, String key, String[] value) { + dataMap.putStringArray(key, value); + } + + @Override + String[] load(DataMap dataMap, String key) { + return dataMap.getStringArray(key); + } + }; + + public static final int LONG_ARRAY_TYPE_CODE = 12; + static TypeHelper LONGARRAY = new TypeHelper(LONG_ARRAY_TYPE_CODE) { + @Override + long[] read(DataBundleValue value, List assets) { + long[] longArr = new long[value.longArray.size()]; + for (int i = 0; i < value.longArray.size(); i++) { + longArr[i] = value.longArray.get(i); + } + return longArr; + } + + @Override + DataBundleValue create(long[] value, List assets) { + List longList = new ArrayList(value.length); + for (long l : value) { + longList.add(l); + } + return new DataBundleValue.Builder().longArray(longList).build(); + } + + @Override + void store(DataMap dataMap, String key, long[] value) { + dataMap.putLongArray(key, value); + } + + @Override + long[] load(DataMap dataMap, String key) { + return dataMap.getLongArray(key); + } + }; + + public static final int ASSET_TYPE_CODE = 13; + static TypeHelper ASSET = new TypeHelper(ASSET_TYPE_CODE) { + @Override + Asset read(DataBundleValue value, List assets) { + return assets.get(value.assetIndex); + } + + @Override + DataBundleValue create(Asset value, List assets) { + int index; + if (assets.contains(value)) { + index = assets.indexOf(value); + } else { + index = assets.size(); + assets.add(value); + } + return new DataBundleValue.Builder().assetIndex(index).build(); + } + + @Override + void store(DataMap dataMap, String key, Asset value) { + dataMap.putAsset(key, value); + } + + @Override + Asset load(DataMap dataMap, String key) { + return dataMap.getAsset(key); + } + }; + + public static final int NULL_TYPE_CODE = 14; + static TypeHelper NULL = new TypeHelper(NULL_TYPE_CODE) { + @Override + String read(DataBundleValue value, List assets) { + return null; + } + + @Override + DataBundleValue create(String value, List assets) { + return new DataBundleValue.Builder().build(); + } + + @Override + void store(DataMap dataMap, String key, String value) { + dataMap.putString(key, value); + } + + @Override + void storeList(DataMap dataMap, String key, ArrayList valueList) { + dataMap.putStringArrayList(key, valueList); + } + + @Override + String load(DataMap dataMap, String key) { + return null; + } + + @Override + AnnotatedArrayList loadList(DataMap dataMap, String key) { + AnnotatedArrayList list = new AnnotatedArrayList(this); + list.addAll(dataMap.getStringArrayList(key)); + return list; + } + }; + + public static final int FLOAT_ARRAY_TYPE_CODE = 15; + static TypeHelper FLOATARRAY = new TypeHelper(FLOAT_ARRAY_TYPE_CODE) { + @Override + float[] read(DataBundleValue value, List assets) { + float[] floatArr = new float[value.floatArray.size()]; + for (int i = 0; i < value.floatArray.size(); i++) { + floatArr[i] = value.floatArray.get(i); + } + return floatArr; + } + + @Override + DataBundleValue create(float[] value, List assets) { + List floatList = new ArrayList(value.length); + for (float f : value) { + floatList.add(f); + } + return new DataBundleValue.Builder().floatArray(floatList).build(); + } + + @Override + void store(DataMap dataMap, String key, float[] value) { + dataMap.putFloatArray(key, value); + } + + @Override + float[] load(DataMap dataMap, String key) { + return dataMap.getFloatArray(key); + } + }; + + static { + typeHelperByCode = new SparseArray(); + rememberTypeReader(BYTEARRAY); + rememberTypeReader(STRING); + rememberTypeReader(DOUBLE); + rememberTypeReader(FLOAT); + rememberTypeReader(LONG); + rememberTypeReader(INTEGER); + rememberTypeReader(BYTE); + rememberTypeReader(BOOLEAN); + rememberTypeReader(DATAMAP); + rememberTypeReader(ARRAYLIST); + rememberTypeReader(STRINGARRAY); + rememberTypeReader(LONGARRAY); + rememberTypeReader(ASSET); + rememberTypeReader(NULL); + rememberTypeReader(FLOATARRAY); + } + + static class AssetAnnotatedDataBundle { + private DataBundle dataBundle; + private List assets; + + public List getAssets() { + return assets; + } + + public byte[] getData() { + return dataBundle.toByteArray(); + } + } + + static class AnnotatedArrayList extends ArrayList { + private TypeHelper innerType; + + public AnnotatedArrayList(TypeHelper innerType) { + this.innerType = innerType; + } + + void store(DataMap dataMap, String key) { + innerType.storeList(dataMap, key, this); + } + + public List createList(List assets) { + return innerType.createList(this, assets); + } + } + + static abstract class TypeHelper { + private int type; + + public TypeHelper(int type) { + this.type = type; + } + + abstract T read(DataBundleValue value, List assets); + + abstract DataBundleValue create(T value, List assets); + + T read(DataBundleTypedValue value, List assets) { + if (value.type == NULL_TYPE_CODE) { + return null; + } else if (value.type == type) { + return read(value.value, assets); + } else { + throw new IllegalArgumentException(); + } + } + + abstract void store(DataMap dataMap, String key, T value); + + void storeList(DataMap dataMap, String key, ArrayList valueList) { + throw new UnsupportedOperationException(); + } + + abstract T load(DataMap dataMap, String key); + + AnnotatedArrayList loadList(DataMap dataMap, String key) { + throw new UnsupportedOperationException(); + } + + void readAndStore(DataMap dataMap, String key, DataBundleValue value, List assets) { + store(dataMap, key, read(value, assets)); + } + + void readAndStore(DataMap dataMap, String key, DataBundleTypedValue value, List assets) { + store(dataMap, key, read(value, assets)); + } + + void readAndStore(DataMap dataMap, DataBundleEntry entry, List assets) { + readAndStore(dataMap, entry.key, entry.typedValue, assets); + } + + AnnotatedArrayList readList(List values, List assets) { + AnnotatedArrayList list = new AnnotatedArrayList(this); + for (DataBundleTypedValue value : values) { + list.add(read(value, assets)); + } + return list; + } + + List createList(AnnotatedArrayList value, List assets) { + List list = new ArrayList(); + for (T val : value) { + list.add(createTyped(val, assets)); + } + return list; + } + + void readAndStore(DataMap dataMap, String key, List values, List assets) { + storeList(dataMap, key, readList(values, assets)); + } + + DataBundleTypedValue createTyped(T value, List assets) { + return new DataBundleTypedValue(type, create(value, assets)); + } + + DataBundleValue loadAndCreate(DataMap dataMap, String key, List assets) { + return create(load(dataMap, key), assets); + } + + DataBundleTypedValue loadAndCreateTyped(DataMap dataMap, String key, List assets) { + return createTyped(load(dataMap, key), assets); + } + + DataBundleEntry loadAndCreateEntry(DataMap dataMap, String key, List assets) { + return new DataBundleEntry(key, loadAndCreateTyped(dataMap, key, assets)); + } + } +} diff --git a/play-services-wearable/src/main/protos-java/org/microg/gms/wearable/databundle/DataBundle.java b/play-services-wearable/src/main/protos-java/org/microg/gms/wearable/databundle/DataBundle.java new file mode 100644 index 00000000..ba388652 --- /dev/null +++ b/play-services-wearable/src/main/protos-java/org/microg/gms/wearable/databundle/DataBundle.java @@ -0,0 +1,64 @@ +// Code generated by Wire protocol buffer compiler, do not edit. +// Source file: protos-repo/databundle.proto +package org.microg.gms.wearable.databundle; + +import com.squareup.wire.Message; +import com.squareup.wire.ProtoField; +import java.util.Collections; +import java.util.List; + +import static com.squareup.wire.Message.Label.REPEATED; + +public final class DataBundle extends Message { + + public static final List DEFAULT_ENTRIES = Collections.emptyList(); + + @ProtoField(tag = 1, label = REPEATED, messageType = DataBundleEntry.class) + public final List entries; + + public DataBundle(List entries) { + this.entries = immutableCopyOf(entries); + } + + private DataBundle(Builder builder) { + this(builder.entries); + setBuilder(builder); + } + + @Override + public boolean equals(Object other) { + if (other == this) return true; + if (!(other instanceof DataBundle)) return false; + return equals(entries, ((DataBundle) other).entries); + } + + @Override + public int hashCode() { + int result = hashCode; + return result != 0 ? result : (hashCode = entries != null ? entries.hashCode() : 1); + } + + public static final class Builder extends Message.Builder { + + public List entries; + + public Builder() { + } + + public Builder(DataBundle message) { + super(message); + if (message == null) return; + this.entries = copyOf(message.entries); + } + + public Builder entries(List entries) { + this.entries = checkForNulls(entries); + return this; + } + + @Override + public DataBundle build() { + return new DataBundle(this); + } + } +} diff --git a/play-services-wearable/src/main/protos-java/org/microg/gms/wearable/databundle/DataBundleEntry.java b/play-services-wearable/src/main/protos-java/org/microg/gms/wearable/databundle/DataBundleEntry.java new file mode 100644 index 00000000..a5f52d38 --- /dev/null +++ b/play-services-wearable/src/main/protos-java/org/microg/gms/wearable/databundle/DataBundleEntry.java @@ -0,0 +1,80 @@ +// Code generated by Wire protocol buffer compiler, do not edit. +// Source file: protos-repo/databundle.proto +package org.microg.gms.wearable.databundle; + +import com.squareup.wire.Message; +import com.squareup.wire.ProtoField; + +import static com.squareup.wire.Message.Datatype.STRING; + +public final class DataBundleEntry extends Message { + + public static final String DEFAULT_KEY = ""; + + @ProtoField(tag = 1, type = STRING) + public final String key; + + @ProtoField(tag = 2) + public final DataBundleTypedValue typedValue; + + public DataBundleEntry(String key, DataBundleTypedValue typedValue) { + this.key = key; + this.typedValue = typedValue; + } + + private DataBundleEntry(Builder builder) { + this(builder.key, builder.typedValue); + setBuilder(builder); + } + + @Override + public boolean equals(Object other) { + if (other == this) return true; + if (!(other instanceof DataBundleEntry)) return false; + DataBundleEntry o = (DataBundleEntry) other; + return equals(key, o.key) + && equals(typedValue, o.typedValue); + } + + @Override + public int hashCode() { + int result = hashCode; + if (result == 0) { + result = key != null ? key.hashCode() : 0; + result = result * 37 + (typedValue != null ? typedValue.hashCode() : 0); + hashCode = result; + } + return result; + } + + public static final class Builder extends Message.Builder { + + public String key; + public DataBundleTypedValue typedValue; + + public Builder() { + } + + public Builder(DataBundleEntry message) { + super(message); + if (message == null) return; + this.key = message.key; + this.typedValue = message.typedValue; + } + + public Builder key(String key) { + this.key = key; + return this; + } + + public Builder typedValue(DataBundleTypedValue typedValue) { + this.typedValue = typedValue; + return this; + } + + @Override + public DataBundleEntry build() { + return new DataBundleEntry(this); + } + } +} diff --git a/play-services-wearable/src/main/protos-java/org/microg/gms/wearable/databundle/DataBundleTypedValue.java b/play-services-wearable/src/main/protos-java/org/microg/gms/wearable/databundle/DataBundleTypedValue.java new file mode 100644 index 00000000..9a7d08f6 --- /dev/null +++ b/play-services-wearable/src/main/protos-java/org/microg/gms/wearable/databundle/DataBundleTypedValue.java @@ -0,0 +1,80 @@ +// Code generated by Wire protocol buffer compiler, do not edit. +// Source file: protos-repo/databundle.proto +package org.microg.gms.wearable.databundle; + +import com.squareup.wire.Message; +import com.squareup.wire.ProtoField; + +import static com.squareup.wire.Message.Datatype.INT32; + +public final class DataBundleTypedValue extends Message { + + public static final Integer DEFAULT_TYPE = 0; + + @ProtoField(tag = 1, type = INT32) + public final Integer type; + + @ProtoField(tag = 2) + public final DataBundleValue value; + + public DataBundleTypedValue(Integer type, DataBundleValue value) { + this.type = type; + this.value = value; + } + + private DataBundleTypedValue(Builder builder) { + this(builder.type, builder.value); + setBuilder(builder); + } + + @Override + public boolean equals(Object other) { + if (other == this) return true; + if (!(other instanceof DataBundleTypedValue)) return false; + DataBundleTypedValue o = (DataBundleTypedValue) other; + return equals(type, o.type) + && equals(value, o.value); + } + + @Override + public int hashCode() { + int result = hashCode; + if (result == 0) { + result = type != null ? type.hashCode() : 0; + result = result * 37 + (value != null ? value.hashCode() : 0); + hashCode = result; + } + return result; + } + + public static final class Builder extends Message.Builder { + + public Integer type; + public DataBundleValue value; + + public Builder() { + } + + public Builder(DataBundleTypedValue message) { + super(message); + if (message == null) return; + this.type = message.type; + this.value = message.value; + } + + public Builder type(Integer type) { + this.type = type; + return this; + } + + public Builder value(DataBundleValue value) { + this.value = value; + return this; + } + + @Override + public DataBundleTypedValue build() { + return new DataBundleTypedValue(this); + } + } +} diff --git a/play-services-wearable/src/main/protos-java/org/microg/gms/wearable/databundle/DataBundleValue.java b/play-services-wearable/src/main/protos-java/org/microg/gms/wearable/databundle/DataBundleValue.java new file mode 100644 index 00000000..4b9a120a --- /dev/null +++ b/play-services-wearable/src/main/protos-java/org/microg/gms/wearable/databundle/DataBundleValue.java @@ -0,0 +1,259 @@ +// Code generated by Wire protocol buffer compiler, do not edit. +// Source file: protos-repo/databundle.proto +package org.microg.gms.wearable.databundle; + +import com.squareup.wire.Message; +import com.squareup.wire.ProtoField; +import java.util.Collections; +import java.util.List; +import okio.ByteString; + +import static com.squareup.wire.Message.Datatype.BOOL; +import static com.squareup.wire.Message.Datatype.BYTES; +import static com.squareup.wire.Message.Datatype.DOUBLE; +import static com.squareup.wire.Message.Datatype.FLOAT; +import static com.squareup.wire.Message.Datatype.INT32; +import static com.squareup.wire.Message.Datatype.INT64; +import static com.squareup.wire.Message.Datatype.STRING; +import static com.squareup.wire.Message.Label.REPEATED; + +public final class DataBundleValue extends Message { + + public static final ByteString DEFAULT_BYTEARRAY = ByteString.EMPTY; + public static final String DEFAULT_STRINGVAL = ""; + public static final Double DEFAULT_DOUBLEVAL = 0D; + public static final Float DEFAULT_FLOATVAL = 0F; + public static final Long DEFAULT_LONGVAL = 0L; + public static final Integer DEFAULT_INTVAL = 0; + public static final Integer DEFAULT_BYTEVAL = 0; + public static final Boolean DEFAULT_BOOLEANVAL = false; + public static final List DEFAULT_MAP = Collections.emptyList(); + public static final List DEFAULT_LIST = Collections.emptyList(); + public static final List DEFAULT_STRINGARRAY = Collections.emptyList(); + public static final List DEFAULT_LONGARRAY = Collections.emptyList(); + public static final Integer DEFAULT_ASSETINDEX = 0; + public static final List DEFAULT_FLOATARRAY = Collections.emptyList(); + + @ProtoField(tag = 1, type = BYTES) + public final ByteString byteArray; + + @ProtoField(tag = 2, type = STRING) + public final String stringVal; + + @ProtoField(tag = 3, type = DOUBLE) + public final Double doubleVal; + + @ProtoField(tag = 4, type = FLOAT) + public final Float floatVal; + + @ProtoField(tag = 5, type = INT64) + public final Long longVal; + + @ProtoField(tag = 6, type = INT32) + public final Integer intVal; + + @ProtoField(tag = 7, type = INT32) + public final Integer byteVal; + + @ProtoField(tag = 8, type = BOOL) + public final Boolean booleanVal; + + @ProtoField(tag = 9, label = REPEATED, messageType = DataBundleEntry.class) + public final List map; + + @ProtoField(tag = 10, label = REPEATED, messageType = DataBundleTypedValue.class) + public final List list; + + @ProtoField(tag = 11, type = STRING, label = REPEATED) + public final List stringArray; + + @ProtoField(tag = 12, type = INT64, label = REPEATED) + public final List longArray; + + @ProtoField(tag = 13, type = INT32) + public final Integer assetIndex; + + @ProtoField(tag = 14, type = FLOAT, label = REPEATED) + public final List floatArray; + + public DataBundleValue(ByteString byteArray, String stringVal, Double doubleVal, Float floatVal, Long longVal, Integer intVal, Integer byteVal, Boolean booleanVal, List map, List list, List stringArray, List longArray, Integer assetIndex, List floatArray) { + this.byteArray = byteArray; + this.stringVal = stringVal; + this.doubleVal = doubleVal; + this.floatVal = floatVal; + this.longVal = longVal; + this.intVal = intVal; + this.byteVal = byteVal; + this.booleanVal = booleanVal; + this.map = immutableCopyOf(map); + this.list = immutableCopyOf(list); + this.stringArray = immutableCopyOf(stringArray); + this.longArray = immutableCopyOf(longArray); + this.assetIndex = assetIndex; + this.floatArray = immutableCopyOf(floatArray); + } + + private DataBundleValue(Builder builder) { + this(builder.byteArray, builder.stringVal, builder.doubleVal, builder.floatVal, builder.longVal, builder.intVal, builder.byteVal, builder.booleanVal, builder.map, builder.list, builder.stringArray, builder.longArray, builder.assetIndex, builder.floatArray); + setBuilder(builder); + } + + @Override + public boolean equals(Object other) { + if (other == this) return true; + if (!(other instanceof DataBundleValue)) return false; + DataBundleValue o = (DataBundleValue) other; + return equals(byteArray, o.byteArray) + && equals(stringVal, o.stringVal) + && equals(doubleVal, o.doubleVal) + && equals(floatVal, o.floatVal) + && equals(longVal, o.longVal) + && equals(intVal, o.intVal) + && equals(byteVal, o.byteVal) + && equals(booleanVal, o.booleanVal) + && equals(map, o.map) + && equals(list, o.list) + && equals(stringArray, o.stringArray) + && equals(longArray, o.longArray) + && equals(assetIndex, o.assetIndex) + && equals(floatArray, o.floatArray); + } + + @Override + public int hashCode() { + int result = hashCode; + if (result == 0) { + result = byteArray != null ? byteArray.hashCode() : 0; + result = result * 37 + (stringVal != null ? stringVal.hashCode() : 0); + result = result * 37 + (doubleVal != null ? doubleVal.hashCode() : 0); + result = result * 37 + (floatVal != null ? floatVal.hashCode() : 0); + result = result * 37 + (longVal != null ? longVal.hashCode() : 0); + result = result * 37 + (intVal != null ? intVal.hashCode() : 0); + result = result * 37 + (byteVal != null ? byteVal.hashCode() : 0); + result = result * 37 + (booleanVal != null ? booleanVal.hashCode() : 0); + result = result * 37 + (map != null ? map.hashCode() : 1); + result = result * 37 + (list != null ? list.hashCode() : 1); + result = result * 37 + (stringArray != null ? stringArray.hashCode() : 1); + result = result * 37 + (longArray != null ? longArray.hashCode() : 1); + result = result * 37 + (assetIndex != null ? assetIndex.hashCode() : 0); + result = result * 37 + (floatArray != null ? floatArray.hashCode() : 1); + hashCode = result; + } + return result; + } + + public static final class Builder extends Message.Builder { + + public ByteString byteArray; + public String stringVal; + public Double doubleVal; + public Float floatVal; + public Long longVal; + public Integer intVal; + public Integer byteVal; + public Boolean booleanVal; + public List map; + public List list; + public List stringArray; + public List longArray; + public Integer assetIndex; + public List floatArray; + + public Builder() { + } + + public Builder(DataBundleValue message) { + super(message); + if (message == null) return; + this.byteArray = message.byteArray; + this.stringVal = message.stringVal; + this.doubleVal = message.doubleVal; + this.floatVal = message.floatVal; + this.longVal = message.longVal; + this.intVal = message.intVal; + this.byteVal = message.byteVal; + this.booleanVal = message.booleanVal; + this.map = copyOf(message.map); + this.list = copyOf(message.list); + this.stringArray = copyOf(message.stringArray); + this.longArray = copyOf(message.longArray); + this.assetIndex = message.assetIndex; + this.floatArray = copyOf(message.floatArray); + } + + public Builder byteArray(ByteString byteArray) { + this.byteArray = byteArray; + return this; + } + + public Builder stringVal(String stringVal) { + this.stringVal = stringVal; + return this; + } + + public Builder doubleVal(Double doubleVal) { + this.doubleVal = doubleVal; + return this; + } + + public Builder floatVal(Float floatVal) { + this.floatVal = floatVal; + return this; + } + + public Builder longVal(Long longVal) { + this.longVal = longVal; + return this; + } + + public Builder intVal(Integer intVal) { + this.intVal = intVal; + return this; + } + + public Builder byteVal(Integer byteVal) { + this.byteVal = byteVal; + return this; + } + + public Builder booleanVal(Boolean booleanVal) { + this.booleanVal = booleanVal; + return this; + } + + public Builder map(List map) { + this.map = checkForNulls(map); + return this; + } + + public Builder list(List list) { + this.list = checkForNulls(list); + return this; + } + + public Builder stringArray(List stringArray) { + this.stringArray = checkForNulls(stringArray); + return this; + } + + public Builder longArray(List longArray) { + this.longArray = checkForNulls(longArray); + return this; + } + + public Builder assetIndex(Integer assetIndex) { + this.assetIndex = assetIndex; + return this; + } + + public Builder floatArray(List floatArray) { + this.floatArray = checkForNulls(floatArray); + return this; + } + + @Override + public DataBundleValue build() { + return new DataBundleValue(this); + } + } +} diff --git a/play-services-wearable/src/main/protos-repo/databundle.proto b/play-services-wearable/src/main/protos-repo/databundle.proto new file mode 100644 index 00000000..bc8cb317 --- /dev/null +++ b/play-services-wearable/src/main/protos-repo/databundle.proto @@ -0,0 +1,33 @@ +option java_package = "org.microg.gms.wearable.databundle"; +option java_outer_classname = "DataBundleProto"; + +message DataBundle { + repeated DataBundleEntry entries = 1; +} + +message DataBundleEntry { + optional string key = 1; + optional DataBundleTypedValue typedValue = 2; +} + +message DataBundleTypedValue { + optional int32 type = 1; + optional DataBundleValue value = 2; +} + +message DataBundleValue { + optional bytes byteArray = 1; + optional string stringVal = 2; + optional double doubleVal = 3; + optional float floatVal = 4; + optional int64 longVal = 5; + optional int32 intVal = 6; + optional int32 byteVal = 7; + optional bool booleanVal = 8; + repeated DataBundleEntry map = 9; + repeated DataBundleTypedValue list = 10; + repeated string stringArray = 11; + repeated int64 longArray = 12; + optional int32 assetIndex = 13; + repeated float floatArray = 14; +} diff --git a/settings.gradle b/settings.gradle index 870c2121..13740781 100644 --- a/settings.gradle +++ b/settings.gradle @@ -16,8 +16,8 @@ include ':safe-parcel' +include ':play-services-basement' 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' From 310127db7905a15af2589a161169292275ead064 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sun, 14 Aug 2016 10:41:13 +0200 Subject: [PATCH 45/65] Disable 'InvalidPackage' lint --- play-services-wearable/build.gradle | 4 ++++ play-services/build.gradle | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/play-services-wearable/build.gradle b/play-services-wearable/build.gradle index d3a8d0ec..d5ad389a 100644 --- a/play-services-wearable/build.gradle +++ b/play-services-wearable/build.gradle @@ -51,6 +51,10 @@ android { compileOptions { sourceCompatibility JavaVersion.VERSION_1_6 } + + lintOptions { + disable 'InvalidPackage' + } } dependencies { diff --git a/play-services/build.gradle b/play-services/build.gradle index 9c4a6711..323f81c6 100644 --- a/play-services/build.gradle +++ b/play-services/build.gradle @@ -45,6 +45,10 @@ android { compileOptions { sourceCompatibility JavaVersion.VERSION_1_6 } + + lintOptions { + disable 'InvalidPackage' + } } dependencies { From c450e2d8adc859972cde572d908193fe6ba73a5b Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sat, 27 Aug 2016 12:46:12 +0200 Subject: [PATCH 46/65] Update Gradle and Sublib --- build.gradle | 19 +++++++++++++++++-- extern/GmsApi | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- play-services-base/build.gradle | 13 ++----------- play-services-cast/build.gradle | 13 ++----------- play-services-gcm/build.gradle | 13 ++----------- play-services-iid/build.gradle | 13 ++----------- play-services-location/build.gradle | 13 ++----------- play-services-tasks/build.gradle | 13 ++----------- play-services-wearable/build.gradle | 13 ++----------- play-services/build.gradle | 13 ++----------- 11 files changed, 35 insertions(+), 92 deletions(-) diff --git a/build.gradle b/build.gradle index 7fbd598a..635996ac 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 microG Project Team + * 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. @@ -14,7 +14,22 @@ * limitations under the License. */ -// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.1.3' + classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' + } +} + +allprojects { + apply plugin: 'idea' + ext.androidBuildVersionTools = "23.0.3" +} + +def androidCompileSdk() { return 23 } subprojects { group = 'org.microg' diff --git a/extern/GmsApi b/extern/GmsApi index b9cb95d3..c6f5d258 160000 --- a/extern/GmsApi +++ b/extern/GmsApi @@ -1 +1 @@ -Subproject commit b9cb95d39bdb4bbac6dd0d2b0405e4d5a23717c2 +Subproject commit c6f5d25850d86a78119c0175678c81178b0500e1 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 90babf29..2b30f04c 100755 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -19,4 +19,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.12-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/play-services-base/build.gradle b/play-services-base/build.gradle index a2d4f921..83a0d2c3 100644 --- a/play-services-base/build.gradle +++ b/play-services-base/build.gradle @@ -14,15 +14,6 @@ * limitations under the License. */ -buildscript { - repositories { - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:2.1.2' - } -} - apply plugin: 'com.android.library' String getMyVersionName() { @@ -35,8 +26,8 @@ String getMyVersionName() { } android { - compileSdkVersion 23 - buildToolsVersion "23.0.3" + compileSdkVersion androidCompileSdk() + buildToolsVersion "$androidBuildVersionTools" defaultConfig { versionName getMyVersionName() diff --git a/play-services-cast/build.gradle b/play-services-cast/build.gradle index a407e8b6..88b18fc3 100644 --- a/play-services-cast/build.gradle +++ b/play-services-cast/build.gradle @@ -14,15 +14,6 @@ * limitations under the License. */ -buildscript { - repositories { - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:2.1.2' - } -} - apply plugin: 'com.android.library' String getMyVersionName() { @@ -35,8 +26,8 @@ String getMyVersionName() { } android { - compileSdkVersion 23 - buildToolsVersion "23.0.3" + compileSdkVersion androidCompileSdk() + buildToolsVersion "$androidBuildVersionTools" defaultConfig { versionName getMyVersionName() diff --git a/play-services-gcm/build.gradle b/play-services-gcm/build.gradle index c9b0c102..64aaf2db 100644 --- a/play-services-gcm/build.gradle +++ b/play-services-gcm/build.gradle @@ -14,15 +14,6 @@ * limitations under the License. */ -buildscript { - repositories { - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:2.1.2' - } -} - apply plugin: 'com.android.library' String getMyVersionName() { @@ -35,8 +26,8 @@ String getMyVersionName() { } android { - compileSdkVersion 23 - buildToolsVersion "23.0.3" + compileSdkVersion androidCompileSdk() + buildToolsVersion "$androidBuildVersionTools" defaultConfig { versionName getMyVersionName() diff --git a/play-services-iid/build.gradle b/play-services-iid/build.gradle index d3d6661c..4975b16e 100644 --- a/play-services-iid/build.gradle +++ b/play-services-iid/build.gradle @@ -14,15 +14,6 @@ * limitations under the License. */ -buildscript { - repositories { - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:2.1.2' - } -} - apply plugin: 'com.android.library' String getMyVersionName() { @@ -35,8 +26,8 @@ String getMyVersionName() { } android { - compileSdkVersion 23 - buildToolsVersion "23.0.3" + compileSdkVersion androidCompileSdk() + buildToolsVersion "$androidBuildVersionTools" defaultConfig { versionName getMyVersionName() diff --git a/play-services-location/build.gradle b/play-services-location/build.gradle index 4a211cf9..1ba77d1a 100644 --- a/play-services-location/build.gradle +++ b/play-services-location/build.gradle @@ -14,15 +14,6 @@ * limitations under the License. */ -buildscript { - repositories { - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:2.1.2' - } -} - apply plugin: 'com.android.library' String getMyVersionName() { @@ -35,8 +26,8 @@ String getMyVersionName() { } android { - compileSdkVersion 23 - buildToolsVersion "23.0.3" + compileSdkVersion androidCompileSdk() + buildToolsVersion "$androidBuildVersionTools" defaultConfig { versionName getMyVersionName() diff --git a/play-services-tasks/build.gradle b/play-services-tasks/build.gradle index d80b1829..0dc4acc2 100644 --- a/play-services-tasks/build.gradle +++ b/play-services-tasks/build.gradle @@ -14,15 +14,6 @@ * limitations under the License. */ -buildscript { - repositories { - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:2.1.2' - } -} - apply plugin: 'com.android.library' String getMyVersionName() { @@ -35,8 +26,8 @@ String getMyVersionName() { } android { - compileSdkVersion 23 - buildToolsVersion "23.0.3" + compileSdkVersion androidCompileSdk() + buildToolsVersion "$androidBuildVersionTools" defaultConfig { versionName getMyVersionName() diff --git a/play-services-wearable/build.gradle b/play-services-wearable/build.gradle index d5ad389a..249b5b1b 100644 --- a/play-services-wearable/build.gradle +++ b/play-services-wearable/build.gradle @@ -14,15 +14,6 @@ * limitations under the License. */ -buildscript { - repositories { - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:2.1.2' - } -} - apply plugin: 'com.android.library' String getMyVersionName() { @@ -35,8 +26,8 @@ String getMyVersionName() { } android { - compileSdkVersion 23 - buildToolsVersion "23.0.3" + compileSdkVersion androidCompileSdk() + buildToolsVersion "$androidBuildVersionTools" defaultConfig { versionName getMyVersionName() diff --git a/play-services/build.gradle b/play-services/build.gradle index 323f81c6..c1a7d6dd 100644 --- a/play-services/build.gradle +++ b/play-services/build.gradle @@ -14,15 +14,6 @@ * limitations under the License. */ -buildscript { - repositories { - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:2.1.2' - } -} - apply plugin: 'com.android.library' String getMyVersionName() { @@ -35,8 +26,8 @@ String getMyVersionName() { } android { - compileSdkVersion 23 - buildToolsVersion "23.0.3" + compileSdkVersion androidCompileSdk() + buildToolsVersion "$androidBuildVersionTools" defaultConfig { versionName getMyVersionName() From 3a219305d801f79c5385583e8ae8a9e9febc0160 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sat, 24 Sep 2016 21:00:24 +0200 Subject: [PATCH 47/65] Update build tools, sdk version and api lib --- .travis.yml | 14 ++++++-------- build.gradle | 6 +++--- extern/GmsApi | 2 +- safe-parcel | 1 - 4 files changed, 10 insertions(+), 13 deletions(-) delete mode 120000 safe-parcel diff --git a/.travis.yml b/.travis.yml index 8f34e4b8..c57a40b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,21 +1,19 @@ language: android -sudo: false git: submodules: false before_install: - git submodule update --init --recursive -before_script: - - echo sdk.dir $ANDROID_HOME > local.properties script: + - export JAVA_OPTS="-XX:+CMSClassUnloadingEnabled -XX:+HeapDumpOnOutOfMemoryError -Xmx2048m" - export TERM=dumb - - export JAVA_OPTS="-XX:MaxPermSize=1024m -XX:+CMSClassUnloadingEnabled -XX:+HeapDumpOnOutOfMemoryError -Xmx2048m" - - ./gradlew build + - echo sdk.dir $ANDROID_HOME > local.properties + - jdk_switcher use oraclejdk8 + - ./gradlew assemble android: components: - - platform-tools - tools - - build-tools-23.0.3 - - android-23 + - build-tools-24.0.2 + - android-24 - extra-android-m2repository before_cache: - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock diff --git a/build.gradle b/build.gradle index 635996ac..27dfeb02 100644 --- a/build.gradle +++ b/build.gradle @@ -19,17 +19,17 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.1.3' + classpath 'com.android.tools.build:gradle:2.2.0' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' } } allprojects { apply plugin: 'idea' - ext.androidBuildVersionTools = "23.0.3" + ext.androidBuildVersionTools = "24.0.2" } -def androidCompileSdk() { return 23 } +def androidCompileSdk() { return 24 } subprojects { group = 'org.microg' diff --git a/extern/GmsApi b/extern/GmsApi index c6f5d258..acec513b 160000 --- a/extern/GmsApi +++ b/extern/GmsApi @@ -1 +1 @@ -Subproject commit c6f5d25850d86a78119c0175678c81178b0500e1 +Subproject commit acec513b19aaba856722fc22c9adf3f5e0ea3ac7 diff --git a/safe-parcel b/safe-parcel deleted file mode 120000 index 5c222c0b..00000000 --- a/safe-parcel +++ /dev/null @@ -1 +0,0 @@ -extern/GmsApi/safe-parcel \ No newline at end of file From 2e3a13eaa5684a1caefcbf7d55ff80bb0d37ed12 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sun, 6 Nov 2016 16:13:15 +0100 Subject: [PATCH 48/65] Prepare for maven publishing, adopt api changes --- README.md | 36 +- build.gradle | 2 +- extern/GmsApi | 2 +- play-services-base/gradle.properties | 34 ++ .../android/gms/common/api/AccountInfo.java | 23 + .../google/android/gms/common/api/Api.java | 84 +++ .../gms/common/api/GoogleApiClient.java | 494 ++++++++++++++++++ .../android/gms/common/api/PendingResult.java | 59 +++ .../org/microg/gms/common/GmsConnector.java | 2 + .../api/AbstractPlayServicesClient.java | 4 +- .../org/microg/gms/common/api/ApiBuilder.java | 30 ++ .../microg/gms/common/api/ApiConnection.java | 27 + .../gms/common/api/GoogleApiClientImpl.java | 242 +++++++++ play-services-cast/gradle.properties | 34 ++ play-services-gcm/gradle.properties | 34 ++ play-services-iid/gradle.properties | 34 ++ play-services-location/gradle.properties | 34 ++ play-services-tasks/gradle.properties | 34 ++ play-services-wearable/gradle.properties | 34 ++ .../google/android/gms/wearable/Channel.java | 94 ++++ .../android/gms/wearable/ChannelApi.java | 185 +++++++ .../android/gms/wearable/MessageApi.java | 88 ++++ .../gms/wearable/WearableListenerService.java | 9 +- .../org/microg/gms/wearable/ChannelImpl.java | 107 ++++ .../microg/gms/wearable/MessageApiImpl.java | 22 +- .../wearable/databundle/DataBundleUtil.java | 14 +- play-services/gradle.properties | 34 ++ 27 files changed, 1746 insertions(+), 50 deletions(-) create mode 100644 play-services-base/gradle.properties create mode 100644 play-services-base/src/main/java/com/google/android/gms/common/api/AccountInfo.java create mode 100644 play-services-base/src/main/java/com/google/android/gms/common/api/Api.java create mode 100644 play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java create mode 100644 play-services-base/src/main/java/com/google/android/gms/common/api/PendingResult.java create mode 100644 play-services-base/src/main/java/org/microg/gms/common/api/ApiBuilder.java create mode 100644 play-services-base/src/main/java/org/microg/gms/common/api/ApiConnection.java create mode 100644 play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java create mode 100644 play-services-cast/gradle.properties create mode 100644 play-services-gcm/gradle.properties create mode 100644 play-services-iid/gradle.properties create mode 100644 play-services-location/gradle.properties create mode 100644 play-services-tasks/gradle.properties create mode 100644 play-services-wearable/gradle.properties create mode 100644 play-services-wearable/src/main/java/com/google/android/gms/wearable/Channel.java create mode 100644 play-services-wearable/src/main/java/com/google/android/gms/wearable/ChannelApi.java create mode 100644 play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageApi.java create mode 100644 play-services-wearable/src/main/java/org/microg/gms/wearable/ChannelImpl.java create mode 100644 play-services/gradle.properties diff --git a/README.md b/README.md index 1154cef8..b5adc0ea 100644 --- a/README.md +++ b/README.md @@ -9,38 +9,10 @@ WIP This is still work in progress, and most applications will not build. However feel free to try it out and create issues for missing method calls (please include an application to test it). -Example: DashClock ------------------- -[DashClock](https://code.google.com/p/dashclock/) is an open source clock widget with enhanced features. -However it uses play services as location backend and thus requires proprietary libraries to compile it. +Developer Notes +--------------- -However, it is possible to build DashClock using GmsLib, supporting all it's location features, with or without play services installed. -To do this, download and build GmsLib plus its submodules and install it to the local gradle repository: +Replace all compile includes to com.google.android.gms with org.microg master-SNAPSHOT includes - $ git clone https://github.com/microg/android_external_GmsLib.git GmsLib - $ cd GmsLib - $ git submodule update --init --recursive - $ gradle install + sed -i 's/compile [\'"]com.google.android.gms:\([^:]*\):[^\']*[\'"]/compile \'org.microg:\1:master-SNAPSHOT\'/g' build.gradle -Then update the main/build.gradle to point to non-google gms in local maven: - - repositories { - + maven { url "${System.env.HOME}/.m2/repository" } // This can be mavenLocal() since Gradle 2.0 - mavenCentral() - flatDir { - dirs '../local_aars' - } - } - - dependencies { - compile 'com.android.support:support-v13:22.0.0' - - compile 'com.google.android.gms:play-services:4.0.30' - + compile 'org.microg.gms:play-services:1.0-SNAPSHOT' - //compile 'com.mobeta.android.dslv:drag-sort-listview:0.6.1-SNAPSHOT-AAR' - compile 'com.mobeta.android.dslv:drag-sort-listview:0.6.1-SNAPSHOT-AAR@aar' - compile project(':api') - } - -Afterwards you can compile dashclock the usual way: - - $ gradle :main:assembleDebug diff --git a/build.gradle b/build.gradle index 27dfeb02..bb43d8b6 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.2.0' + classpath 'com.android.tools.build:gradle:2.2.2' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' } } diff --git a/extern/GmsApi b/extern/GmsApi index acec513b..9ff42ae7 160000 --- a/extern/GmsApi +++ b/extern/GmsApi @@ -1 +1 @@ -Subproject commit acec513b19aaba856722fc22c9adf3f5e0ea3ac7 +Subproject commit 9ff42ae73a02ea971dc557f657b612113dfa6e24 diff --git a/play-services-base/gradle.properties b/play-services-base/gradle.properties new file mode 100644 index 00000000..b31a23e5 --- /dev/null +++ b/play-services-base/gradle.properties @@ -0,0 +1,34 @@ +# +# 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. +# + +POM_NAME=Play Services Library Base +POM_DESCRIPTION=Base classes used by all Play Services Library modules + +POM_PACKAGING=aar + +POM_URL=https://github.com/microg/android_external_GmsLib + +POM_SCM_URL=https://github.com/microg/android_external_GmsLib +POM_SCM_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git +POM_SCM_DEV_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git + +POM_LICENCE_NAME=The Apache Software License, Version 2.0 +POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt +POM_LICENCE_DIST=repo + +POM_DEVELOPER_ID=mar-v-in +POM_DEVELOPER_NAME=Marvin W + diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/AccountInfo.java b/play-services-base/src/main/java/com/google/android/gms/common/api/AccountInfo.java new file mode 100644 index 00000000..109be301 --- /dev/null +++ b/play-services-base/src/main/java/com/google/android/gms/common/api/AccountInfo.java @@ -0,0 +1,23 @@ +/* + * 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.safeparcel.AutoSafeParcelable; + +public class AccountInfo extends AutoSafeParcelable { + public static final Creator CREATOR = new AutoCreator(AccountInfo.class); +} diff --git a/play-services-base/src/main/java/com/google/android/gms/common/api/Api.java b/play-services-base/src/main/java/com/google/android/gms/common/api/Api.java new file mode 100644 index 00000000..1b0f03f2 --- /dev/null +++ b/play-services-base/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-base/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java b/play-services-base/src/main/java/com/google/android/gms/common/api/GoogleApiClient.java new file mode 100644 index 00000000..87b05d6b --- /dev/null +++ b/play-services-base/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-base/src/main/java/com/google/android/gms/common/api/PendingResult.java b/play-services-base/src/main/java/com/google/android/gms/common/api/PendingResult.java new file mode 100644 index 00000000..28943f18 --- /dev/null +++ b/play-services-base/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. + *

+ *

    + *
  • via blocking calls to {@link #await()}, or {@link #await(long, TimeUnit)}, or
  • + *
  • via a callback by passing in an object implementing interface {@link ResultCallback} to + * {@link #setResultCallback(ResultCallback)}.
  • + *
+ * 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-base/src/main/java/org/microg/gms/common/GmsConnector.java b/play-services-base/src/main/java/org/microg/gms/common/GmsConnector.java index 8d6dbdad..078d8f88 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/GmsConnector.java +++ b/play-services-base/src/main/java/org/microg/gms/common/GmsConnector.java @@ -49,6 +49,7 @@ public class GmsConnector { public AbstractPendingResult connect() { Log.d(TAG, "connect()"); + apiClient.incrementUsageCounter(); apiClient.getApiConnection(api); Looper looper = apiClient.getLooper(); final AbstractPendingResult result = new AbstractPendingResult(looper); @@ -81,6 +82,7 @@ public class GmsConnector { @Override public void onResultAvailable(R realResult) { result.deliverResult(realResult); + apiClient.decrementUsageCounter(); } }); } catch (RemoteException ignored) { diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/AbstractPlayServicesClient.java b/play-services-base/src/main/java/org/microg/gms/common/api/AbstractPlayServicesClient.java index 32d817da..5b8eb16a 100644 --- a/play-services-base/src/main/java/org/microg/gms/common/api/AbstractPlayServicesClient.java +++ b/play-services-base/src/main/java/org/microg/gms/common/api/AbstractPlayServicesClient.java @@ -25,7 +25,7 @@ import org.microg.gms.common.ForwardConnectionCallbacks; import org.microg.gms.common.ForwardConnectionFailedListener; public class AbstractPlayServicesClient implements GooglePlayServicesClient { - private static final String TAG = "GmsPlatServicesClient"; + private static final String TAG = "GmsPlayServicesClient"; protected final GoogleApiClient googleApiClient; @@ -46,7 +46,7 @@ public class AbstractPlayServicesClient implements GooglePlayServicesClient { @Override public void disconnect() { Log.d(TAG, "disconnect()"); - googleApiClient.disconnect(); + //TODO googleApiClient.disconnect(); } @Override diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/ApiBuilder.java b/play-services-base/src/main/java/org/microg/gms/common/api/ApiBuilder.java new file mode 100644 index 00000000..6c12551e --- /dev/null +++ b/play-services-base/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-base/src/main/java/org/microg/gms/common/api/ApiConnection.java b/play-services-base/src/main/java/org/microg/gms/common/api/ApiConnection.java new file mode 100644 index 00000000..019ea5d8 --- /dev/null +++ b/play-services-base/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-base/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java b/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java new file mode 100644 index 00000000..0757a8c0 --- /dev/null +++ b/play-services-base/src/main/java/org/microg/gms/common/api/GoogleApiClientImpl.java @@ -0,0 +1,242 @@ +/* + * 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); + } + } + }; + private int usageCounter = 0; + private boolean shouldDisconnect = false; + + 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 synchronized void incrementUsageCounter() { + usageCounter++; + } + + public synchronized void decrementUsageCounter() { + usageCounter--; + if (shouldDisconnect) disconnect(); + } + + 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()) { + if (shouldDisconnect) { + shouldDisconnect = false; + return; + } + 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() { + if (usageCounter > 0) { + shouldDisconnect = true; + } else { + 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-cast/gradle.properties b/play-services-cast/gradle.properties new file mode 100644 index 00000000..937fcc06 --- /dev/null +++ b/play-services-cast/gradle.properties @@ -0,0 +1,34 @@ +# +# 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. +# + +POM_NAME=Play Services Cast Library +POM_DESCRIPTION=The Play Services Library module to access the Cast API + +POM_PACKAGING=aar + +POM_URL=https://github.com/microg/android_external_GmsLib + +POM_SCM_URL=https://github.com/microg/android_external_GmsLib +POM_SCM_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git +POM_SCM_DEV_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git + +POM_LICENCE_NAME=The Apache Software License, Version 2.0 +POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt +POM_LICENCE_DIST=repo + +POM_DEVELOPER_ID=mar-v-in +POM_DEVELOPER_NAME=Marvin W + diff --git a/play-services-gcm/gradle.properties b/play-services-gcm/gradle.properties new file mode 100644 index 00000000..2265b126 --- /dev/null +++ b/play-services-gcm/gradle.properties @@ -0,0 +1,34 @@ +# +# 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. +# + +POM_NAME=Play Services GCM Library +POM_DESCRIPTION=The Play Services Library module to access Google Cloud Messaging + +POM_PACKAGING=aar + +POM_URL=https://github.com/microg/android_external_GmsLib + +POM_SCM_URL=https://github.com/microg/android_external_GmsLib +POM_SCM_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git +POM_SCM_DEV_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git + +POM_LICENCE_NAME=The Apache Software License, Version 2.0 +POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt +POM_LICENCE_DIST=repo + +POM_DEVELOPER_ID=mar-v-in +POM_DEVELOPER_NAME=Marvin W + diff --git a/play-services-iid/gradle.properties b/play-services-iid/gradle.properties new file mode 100644 index 00000000..2e179864 --- /dev/null +++ b/play-services-iid/gradle.properties @@ -0,0 +1,34 @@ +# +# 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. +# + +POM_NAME=Play Services IID Library +POM_DESCRIPTION=The Play Services Library module to access the InstanceID API + +POM_PACKAGING=aar + +POM_URL=https://github.com/microg/android_external_GmsLib + +POM_SCM_URL=https://github.com/microg/android_external_GmsLib +POM_SCM_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git +POM_SCM_DEV_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git + +POM_LICENCE_NAME=The Apache Software License, Version 2.0 +POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt +POM_LICENCE_DIST=repo + +POM_DEVELOPER_ID=mar-v-in +POM_DEVELOPER_NAME=Marvin W + diff --git a/play-services-location/gradle.properties b/play-services-location/gradle.properties new file mode 100644 index 00000000..bfc9b4b2 --- /dev/null +++ b/play-services-location/gradle.properties @@ -0,0 +1,34 @@ +# +# 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. +# + +POM_NAME=Play Services Cast Library +POM_DESCRIPTION=The Play Services Library module to access Google Location Services + +POM_PACKAGING=aar + +POM_URL=https://github.com/microg/android_external_GmsLib + +POM_SCM_URL=https://github.com/microg/android_external_GmsLib +POM_SCM_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git +POM_SCM_DEV_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git + +POM_LICENCE_NAME=The Apache Software License, Version 2.0 +POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt +POM_LICENCE_DIST=repo + +POM_DEVELOPER_ID=mar-v-in +POM_DEVELOPER_NAME=Marvin W + diff --git a/play-services-tasks/gradle.properties b/play-services-tasks/gradle.properties new file mode 100644 index 00000000..328b6fdd --- /dev/null +++ b/play-services-tasks/gradle.properties @@ -0,0 +1,34 @@ +# +# 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. +# + +POM_NAME=Play Services Library Tasks +POM_DESCRIPTION=Classes used by some Play Services Library modules to abstract tasks + +POM_PACKAGING=aar + +POM_URL=https://github.com/microg/android_external_GmsLib + +POM_SCM_URL=https://github.com/microg/android_external_GmsLib +POM_SCM_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git +POM_SCM_DEV_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git + +POM_LICENCE_NAME=The Apache Software License, Version 2.0 +POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt +POM_LICENCE_DIST=repo + +POM_DEVELOPER_ID=mar-v-in +POM_DEVELOPER_NAME=Marvin W + diff --git a/play-services-wearable/gradle.properties b/play-services-wearable/gradle.properties new file mode 100644 index 00000000..036604f2 --- /dev/null +++ b/play-services-wearable/gradle.properties @@ -0,0 +1,34 @@ +# +# 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. +# + +POM_NAME=Play Services Wearable Library +POM_DESCRIPTION=The Play Services Library module to access the Wearable API + +POM_PACKAGING=aar + +POM_URL=https://github.com/microg/android_external_GmsLib + +POM_SCM_URL=https://github.com/microg/android_external_GmsLib +POM_SCM_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git +POM_SCM_DEV_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git + +POM_LICENCE_NAME=The Apache Software License, Version 2.0 +POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt +POM_LICENCE_DIST=repo + +POM_DEVELOPER_ID=mar-v-in +POM_DEVELOPER_NAME=Marvin W + diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/Channel.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/Channel.java new file mode 100644 index 00000000..7c74ca5f --- /dev/null +++ b/play-services-wearable/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/src/main/java/com/google/android/gms/wearable/ChannelApi.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/ChannelApi.java new file mode 100644 index 00000000..66d6ae71 --- /dev/null +++ b/play-services-wearable/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: + *

    + *
  • {@link ChannelListener#CLOSE_REASON_DISCONNECTED}
  • + *
  • {@link ChannelListener#CLOSE_REASON_NORMAL}
  • + *
  • {@link ChannelListener#CLOSE_REASON_LOCAL_CLOSE}
  • + *
  • {@link ChannelListener#CLOSE_REASON_REMOTE_CLOSE}
  • + *
+ */ + @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/src/main/java/com/google/android/gms/wearable/MessageApi.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageApi.java new file mode 100644 index 00000000..e2016f73 --- /dev/null +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/MessageApi.java @@ -0,0 +1,88 @@ +/* + * 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.wearable; + +import com.google.android.gms.common.api.GoogleApiClient; +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 org.microg.gms.common.PublicApi; + +@PublicApi +public interface MessageApi { + + /** + * A value returned by {@link SendMessageResult#getRequestId()} when + * {@link #sendMessage(GoogleApiClient, String, String, byte[])} fails. + */ + int UNKNOWN_REQUEST_ID = -1; + + /** + * Registers a listener to be notified of received messages. Calls to this method should + * balanced with {@link #removeListener(GoogleApiClient, MessageListener)} to avoid leaking + * resources. + *

+ * Callers wishing to be notified of events in the background should use {@link WearableListenerService}. + */ + PendingResult addListener(GoogleApiClient client, MessageListener listener); + + /** + * Removes a message listener which was previously added through + * {@link #addListener(GoogleApiClient, MessageListener)}. + */ + PendingResult removeListener(GoogleApiClient client, MessageListener listener); + + /** + * Sends {@code byte[]} data to the specified node. + * + * @param nodeId identifier for a particular node on the Android Wear network. Valid targets + * may be obtained through {@link NodeApi#getConnectedNodes(GoogleApiClient)} + * or from the host in {@link DataItem#getUri()}. + * @param path identifier used to specify a particular endpoint at the receiving node + * @param data small array of information to pass to the target node. Generally not larger + * than 100k + */ + PendingResult sendMessage(GoogleApiClient client, String nodeId, + String path, byte[] data); + + /** + * Used with {@link MessageApi#addListener(GoogleApiClient, MessageListener)} to receive + * message events. + *

+ * Callers wishing to be notified of events in the background should use + * {@link WearableListenerService}. + */ + interface MessageListener { + /** + * Notification that a message has been received. + */ + void onMessageReceived(MessageEvent messageEvent); + } + + /** + * Contains the request id assigned to the message. On failure, the id will be + * {@link MessageApi#UNKNOWN_REQUEST_ID} and the status will be unsuccessful. + */ + interface SendMessageResult extends Result { + /** + * @return an ID used to identify the sent message. If {@link #getStatus()} is not + * successful, this value will be {@link MessageApi#UNKNOWN_REQUEST_ID}. + */ + int getRequestId(); + } +} diff --git a/play-services-wearable/src/main/java/com/google/android/gms/wearable/WearableListenerService.java b/play-services-wearable/src/main/java/com/google/android/gms/wearable/WearableListenerService.java index b3e6c8ff..3ce99b5a 100644 --- a/play-services-wearable/src/main/java/com/google/android/gms/wearable/WearableListenerService.java +++ b/play-services-wearable/src/main/java/com/google/android/gms/wearable/WearableListenerService.java @@ -36,6 +36,7 @@ import com.google.android.gms.wearable.internal.MessageEventParcelable; import com.google.android.gms.wearable.internal.NodeParcelable; import org.microg.gms.common.PublicApi; +import org.microg.gms.wearable.ChannelImpl; import java.util.ArrayList; import java.util.Arrays; @@ -241,16 +242,16 @@ public abstract class WearableListenerService extends Service implements Capabil public void run() { switch (channelEvent.eventType) { case 1: - WearableListenerService.this.onChannelOpened(channelEvent.channel); + WearableListenerService.this.onChannelOpened(new ChannelImpl(channelEvent.channel)); break; case 2: - WearableListenerService.this.onChannelClosed(channelEvent.channel, channelEvent.closeReason, channelEvent.appSpecificErrorCode); + WearableListenerService.this.onChannelClosed(new ChannelImpl(channelEvent.channel), channelEvent.closeReason, channelEvent.appSpecificErrorCode); break; case 3: - WearableListenerService.this.onInputClosed(channelEvent.channel, channelEvent.closeReason, channelEvent.appSpecificErrorCode); + WearableListenerService.this.onInputClosed(new ChannelImpl(channelEvent.channel), channelEvent.closeReason, channelEvent.appSpecificErrorCode); break; case 4: - WearableListenerService.this.onOutputClosed(channelEvent.channel, channelEvent.closeReason, channelEvent.appSpecificErrorCode); + WearableListenerService.this.onOutputClosed(new ChannelImpl(channelEvent.channel), channelEvent.closeReason, channelEvent.appSpecificErrorCode); break; default: Log.w(TAG, "Unknown ChannelEvent.eventType"); diff --git a/play-services-wearable/src/main/java/org/microg/gms/wearable/ChannelImpl.java b/play-services-wearable/src/main/java/org/microg/gms/wearable/ChannelImpl.java new file mode 100644 index 00000000..e1284c6c --- /dev/null +++ b/play-services-wearable/src/main/java/org/microg/gms/wearable/ChannelImpl.java @@ -0,0 +1,107 @@ +/* + * 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.wearable; + +import android.net.Uri; +import android.util.Log; + +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 com.google.android.gms.wearable.Channel; +import com.google.android.gms.wearable.ChannelApi; +import com.google.android.gms.wearable.internal.ChannelParcelable; + +public class ChannelImpl extends ChannelParcelable implements Channel { + private static final String TAG = "GmsWearChannelImpl"; + + public ChannelImpl(String token, String nodeId, String path) { + super(token, nodeId, path); + } + + public ChannelImpl(ChannelParcelable wrapped) { + this(wrapped.token, wrapped.nodeId, wrapped.path); + } + + + @Override + public PendingResult addListener(GoogleApiClient client, ChannelApi.ChannelListener listener) { + Log.d(TAG, "unimplemented Method: addListener"); + return null; + } + + @Override + public PendingResult close(GoogleApiClient client, int errorCode) { + Log.d(TAG, "unimplemented Method: close"); + return null; + } + + @Override + public PendingResult close(GoogleApiClient client) { + Log.d(TAG, "unimplemented Method: close"); + return null; + } + + @Override + public PendingResult getInputStream(GoogleApiClient client) { + Log.d(TAG, "unimplemented Method: getInputStream"); + return null; + } + + @Override + public PendingResult getOutputStream(GoogleApiClient client) { + Log.d(TAG, "unimplemented Method: getOutputStream"); + return null; + } + + public String getNodeId() { + return nodeId; + } + + @Override + public String getPath() { + return path; + } + + public String getToken() { + return token; + } + + @Override + public PendingResult receiveFile(GoogleApiClient client, Uri uri, boolean append) { + Log.d(TAG, "unimplemented Method: receiveFile"); + return null; + } + + @Override + public PendingResult removeListener(GoogleApiClient client, ChannelApi.ChannelListener listener) { + Log.d(TAG, "unimplemented Method: removeListener"); + return null; + } + + @Override + public PendingResult sendFile(GoogleApiClient client, Uri uri) { + Log.d(TAG, "unimplemented Method: sendFile"); + return null; + } + + @Override + public PendingResult sendFile(GoogleApiClient client, Uri uri, long startOffset, long length) { + Log.d(TAG, "unimplemented Method: sendFile"); + return null; + } +} diff --git a/play-services-wearable/src/main/java/org/microg/gms/wearable/MessageApiImpl.java b/play-services-wearable/src/main/java/org/microg/gms/wearable/MessageApiImpl.java index 2bb784dc..552a486b 100644 --- a/play-services-wearable/src/main/java/org/microg/gms/wearable/MessageApiImpl.java +++ b/play-services-wearable/src/main/java/org/microg/gms/wearable/MessageApiImpl.java @@ -17,7 +17,6 @@ package org.microg.gms.wearable; import android.os.RemoteException; -import android.util.Log; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.PendingResult; @@ -27,7 +26,6 @@ import com.google.android.gms.wearable.Wearable; import com.google.android.gms.wearable.internal.SendMessageResponse; import org.microg.gms.common.GmsConnector; -import org.microg.gms.common.api.ApiConnection; public class MessageApiImpl implements MessageApi { @Override @@ -48,10 +46,28 @@ public class MessageApiImpl implements MessageApi { client.getServiceInterface().sendMessage(new BaseWearableCallbacks() { @Override public void onSendMessageResponse(SendMessageResponse response) throws RemoteException { - resultProvider.onResultAvailable(response); + resultProvider.onResultAvailable(new SendMessageResultImpl(response)); } }, nodeId, path, data); } }); } + + public static class SendMessageResultImpl implements SendMessageResult { + private SendMessageResponse response; + + public SendMessageResultImpl(SendMessageResponse response) { + this.response = response; + } + + @Override + public int getRequestId() { + return response.requestId; + } + + @Override + public Status getStatus() { + return new Status(response.statusCode); + } + } } diff --git a/play-services-wearable/src/main/java/org/microg/gms/wearable/databundle/DataBundleUtil.java b/play-services-wearable/src/main/java/org/microg/gms/wearable/databundle/DataBundleUtil.java index 69a71964..cfc41cd2 100644 --- a/play-services-wearable/src/main/java/org/microg/gms/wearable/databundle/DataBundleUtil.java +++ b/play-services-wearable/src/main/java/org/microg/gms/wearable/databundle/DataBundleUtil.java @@ -139,7 +139,7 @@ public class DataBundleUtil { @Override void store(DataMap dataMap, String key, String value) { - dataMap.putString(key, value); + if (value != null) dataMap.putString(key, value); } @Override @@ -174,7 +174,7 @@ public class DataBundleUtil { @Override void store(DataMap dataMap, String key, Double value) { - dataMap.putDouble(key, value); + if (value != null) dataMap.putDouble(key, value); } @Override @@ -197,7 +197,7 @@ public class DataBundleUtil { @Override void store(DataMap dataMap, String key, Float value) { - dataMap.putFloat(key, value); + if (value != null) dataMap.putFloat(key, value); } @Override @@ -220,7 +220,7 @@ public class DataBundleUtil { @Override void store(DataMap dataMap, String key, Long value) { - dataMap.putLong(key, value); + if (value != null) dataMap.putLong(key, value); } @Override @@ -243,7 +243,7 @@ public class DataBundleUtil { @Override void store(DataMap dataMap, String key, Integer value) { - dataMap.putInt(key, value); + if (value != null) dataMap.putInt(key, value); } @Override @@ -278,7 +278,7 @@ public class DataBundleUtil { @Override void store(DataMap dataMap, String key, Byte value) { - dataMap.putByte(key, value); + if (value != null) dataMap.putByte(key, value); } @Override @@ -301,7 +301,7 @@ public class DataBundleUtil { @Override void store(DataMap dataMap, String key, Boolean value) { - dataMap.putBoolean(key, value); + if (value != null) dataMap.putBoolean(key, value); } @Override diff --git a/play-services/gradle.properties b/play-services/gradle.properties new file mode 100644 index 00000000..b8e4412a --- /dev/null +++ b/play-services/gradle.properties @@ -0,0 +1,34 @@ +# +# 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. +# + +POM_NAME=Play Services Library +POM_DESCRIPTION=The whole package of Play Services libraries. Use play-services-* artifacts if you only need certain parts + +POM_PACKAGING=aar + +POM_URL=https://github.com/microg/android_external_GmsLib + +POM_SCM_URL=https://github.com/microg/android_external_GmsLib +POM_SCM_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git +POM_SCM_DEV_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git + +POM_LICENCE_NAME=The Apache Software License, Version 2.0 +POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt +POM_LICENCE_DIST=repo + +POM_DEVELOPER_ID=mar-v-in +POM_DEVELOPER_NAME=Marvin W + From 4bffa5799a9ce406a268941e26fbb4a637d42446 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Mon, 7 Nov 2016 14:55:49 +0100 Subject: [PATCH 49/65] Fix missing api reference --- play-services-cast-framework-api | 1 + settings.gradle | 1 + 2 files changed, 2 insertions(+) create mode 120000 play-services-cast-framework-api diff --git a/play-services-cast-framework-api b/play-services-cast-framework-api new file mode 120000 index 00000000..e7676f17 --- /dev/null +++ b/play-services-cast-framework-api @@ -0,0 +1 @@ +extern/GmsApi/play-services-cast-framework-api/ \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 13740781..a095f186 100644 --- a/settings.gradle +++ b/settings.gradle @@ -18,6 +18,7 @@ include ':safe-parcel' include ':play-services-basement' include ':play-services-cast-api' +include ':play-services-cast-framework-api' include ':play-services-iid-api' include ':play-services-location-api' include ':play-services-wearable-api' From a0300494f6cf65334c1d2f969571ef6069579bca Mon Sep 17 00:00:00 2001 From: Marvin W Date: Mon, 14 Nov 2016 20:26:09 +0100 Subject: [PATCH 50/65] Update build tools, set sdk versions through gradle --- .travis.yml | 2 +- build.gradle | 6 +++++- extern/GmsApi | 2 +- play-services-base/build.gradle | 2 ++ play-services-base/src/main/AndroidManifest.xml | 2 -- play-services-cast/build.gradle | 2 ++ play-services-cast/src/main/AndroidManifest.xml | 8 +------- play-services-gcm/build.gradle | 2 ++ play-services-gcm/src/main/AndroidManifest.xml | 14 +++++--------- play-services-iid/build.gradle | 2 ++ play-services-iid/src/main/AndroidManifest.xml | 12 ++++-------- play-services-location/build.gradle | 2 ++ .../src/main/AndroidManifest.xml | 7 +++---- play-services-tasks/build.gradle | 2 ++ play-services-tasks/src/main/AndroidManifest.xml | 8 +------- play-services-wearable/build.gradle | 2 ++ .../src/main/AndroidManifest.xml | 8 +------- play-services/build.gradle | 2 ++ 18 files changed, 38 insertions(+), 47 deletions(-) diff --git a/.travis.yml b/.travis.yml index c57a40b2..612507ec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ script: android: components: - tools - - build-tools-24.0.2 + - build-tools-24.0.3 - android-24 - extra-android-m2repository before_cache: diff --git a/build.gradle b/build.gradle index bb43d8b6..be127829 100644 --- a/build.gradle +++ b/build.gradle @@ -26,11 +26,15 @@ buildscript { allprojects { apply plugin: 'idea' - ext.androidBuildVersionTools = "24.0.2" + ext.androidBuildVersionTools = "24.0.3" } def androidCompileSdk() { return 24 } +def androidTargetSdk() { return 24 } + +def androidMinSdk() { return 9 } + subprojects { group = 'org.microg' repositories { diff --git a/extern/GmsApi b/extern/GmsApi index 9ff42ae7..0b4f43c6 160000 --- a/extern/GmsApi +++ b/extern/GmsApi @@ -1 +1 @@ -Subproject commit 9ff42ae73a02ea971dc557f657b612113dfa6e24 +Subproject commit 0b4f43c6a6a091dbdeb0ec544d533373a83ea319 diff --git a/play-services-base/build.gradle b/play-services-base/build.gradle index 83a0d2c3..69288170 100644 --- a/play-services-base/build.gradle +++ b/play-services-base/build.gradle @@ -31,6 +31,8 @@ android { defaultConfig { versionName getMyVersionName() + minSdkVersion androidMinSdk() + targetSdkVersion androidTargetSdk() } compileOptions { diff --git a/play-services-base/src/main/AndroidManifest.xml b/play-services-base/src/main/AndroidManifest.xml index bb6c2140..5046e2a9 100644 --- a/play-services-base/src/main/AndroidManifest.xml +++ b/play-services-base/src/main/AndroidManifest.xml @@ -18,8 +18,6 @@ - - diff --git a/play-services-cast/build.gradle b/play-services-cast/build.gradle index 88b18fc3..0ff2309a 100644 --- a/play-services-cast/build.gradle +++ b/play-services-cast/build.gradle @@ -31,6 +31,8 @@ android { defaultConfig { versionName getMyVersionName() + minSdkVersion androidMinSdk() + targetSdkVersion androidTargetSdk() } compileOptions { diff --git a/play-services-cast/src/main/AndroidManifest.xml b/play-services-cast/src/main/AndroidManifest.xml index 0921b431..7c61e449 100644 --- a/play-services-cast/src/main/AndroidManifest.xml +++ b/play-services-cast/src/main/AndroidManifest.xml @@ -15,10 +15,4 @@ ~ limitations under the License. --> - - - - - - + diff --git a/play-services-gcm/build.gradle b/play-services-gcm/build.gradle index 64aaf2db..ca11d55f 100644 --- a/play-services-gcm/build.gradle +++ b/play-services-gcm/build.gradle @@ -31,6 +31,8 @@ android { defaultConfig { versionName getMyVersionName() + minSdkVersion androidMinSdk() + targetSdkVersion androidTargetSdk() } compileOptions { diff --git a/play-services-gcm/src/main/AndroidManifest.xml b/play-services-gcm/src/main/AndroidManifest.xml index 16f7b82f..95a2f021 100644 --- a/play-services-gcm/src/main/AndroidManifest.xml +++ b/play-services-gcm/src/main/AndroidManifest.xml @@ -15,17 +15,13 @@ ~ limitations under the License. --> - - - + - - + + - - - + diff --git a/play-services-iid/build.gradle b/play-services-iid/build.gradle index 4975b16e..4aaf7e81 100644 --- a/play-services-iid/build.gradle +++ b/play-services-iid/build.gradle @@ -31,6 +31,8 @@ android { defaultConfig { versionName getMyVersionName() + minSdkVersion androidMinSdk() + targetSdkVersion androidTargetSdk() } compileOptions { diff --git a/play-services-iid/src/main/AndroidManifest.xml b/play-services-iid/src/main/AndroidManifest.xml index 1f5f60cd..5f895c7e 100644 --- a/play-services-iid/src/main/AndroidManifest.xml +++ b/play-services-iid/src/main/AndroidManifest.xml @@ -15,14 +15,10 @@ ~ limitations under the License. --> - - - + - - - - + + diff --git a/play-services-location/build.gradle b/play-services-location/build.gradle index 1ba77d1a..d1ffc59a 100644 --- a/play-services-location/build.gradle +++ b/play-services-location/build.gradle @@ -31,6 +31,8 @@ android { defaultConfig { versionName getMyVersionName() + minSdkVersion androidMinSdk() + targetSdkVersion androidTargetSdk() } compileOptions { diff --git a/play-services-location/src/main/AndroidManifest.xml b/play-services-location/src/main/AndroidManifest.xml index 79a62e35..23d4419f 100644 --- a/play-services-location/src/main/AndroidManifest.xml +++ b/play-services-location/src/main/AndroidManifest.xml @@ -15,11 +15,10 @@ ~ limitations under the License. --> - + - - + diff --git a/play-services-tasks/build.gradle b/play-services-tasks/build.gradle index 0dc4acc2..133f6c46 100644 --- a/play-services-tasks/build.gradle +++ b/play-services-tasks/build.gradle @@ -31,6 +31,8 @@ android { defaultConfig { versionName getMyVersionName() + minSdkVersion androidMinSdk() + targetSdkVersion androidTargetSdk() } compileOptions { diff --git a/play-services-tasks/src/main/AndroidManifest.xml b/play-services-tasks/src/main/AndroidManifest.xml index 351619a2..e92fbfcd 100644 --- a/play-services-tasks/src/main/AndroidManifest.xml +++ b/play-services-tasks/src/main/AndroidManifest.xml @@ -15,10 +15,4 @@ ~ limitations under the License. --> - - - - - - + diff --git a/play-services-wearable/build.gradle b/play-services-wearable/build.gradle index 249b5b1b..a65bd2b7 100644 --- a/play-services-wearable/build.gradle +++ b/play-services-wearable/build.gradle @@ -31,6 +31,8 @@ android { defaultConfig { versionName getMyVersionName() + minSdkVersion androidMinSdk() + targetSdkVersion androidTargetSdk() } sourceSets { diff --git a/play-services-wearable/src/main/AndroidManifest.xml b/play-services-wearable/src/main/AndroidManifest.xml index bb22a334..b9ced1d6 100644 --- a/play-services-wearable/src/main/AndroidManifest.xml +++ b/play-services-wearable/src/main/AndroidManifest.xml @@ -15,10 +15,4 @@ ~ limitations under the License. --> - - - - - - + diff --git a/play-services/build.gradle b/play-services/build.gradle index c1a7d6dd..1ff4eba7 100644 --- a/play-services/build.gradle +++ b/play-services/build.gradle @@ -31,6 +31,8 @@ android { defaultConfig { versionName getMyVersionName() + minSdkVersion androidMinSdk() + targetSdkVersion androidTargetSdk() } compileOptions { From 3eac7fc77f572f3d85ee61e17d1b4e25301f9aad Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sat, 11 Feb 2017 00:36:14 +0100 Subject: [PATCH 51/65] Add ActivityRecognition client code --- extern/GmsApi | 2 +- .../gms/location/ActivityRecognition.java | 40 ++++++ .../gms/location/ActivityRecognitionApi.java | 115 ++++++++++++++++++ .../location/FusedLocationProviderApi.java | 18 +-- .../gms/location/LocationStatusCodes.java | 26 ++++ .../ActivityRecognitionApiBuilder.java | 34 ++++++ .../location/ActivityRecognitionApiImpl.java | 67 ++++++++++ .../ActivityRecognitionClientImpl.java | 37 ++++++ .../FusedLocationProviderApiImpl.java | 20 +-- 9 files changed, 340 insertions(+), 19 deletions(-) create mode 100644 play-services-location/src/main/java/com/google/android/gms/location/ActivityRecognition.java create mode 100644 play-services-location/src/main/java/com/google/android/gms/location/ActivityRecognitionApi.java create mode 100644 play-services-location/src/main/java/com/google/android/gms/location/LocationStatusCodes.java create mode 100644 play-services-location/src/main/java/org/microg/gms/location/ActivityRecognitionApiBuilder.java create mode 100644 play-services-location/src/main/java/org/microg/gms/location/ActivityRecognitionApiImpl.java create mode 100644 play-services-location/src/main/java/org/microg/gms/location/ActivityRecognitionClientImpl.java diff --git a/extern/GmsApi b/extern/GmsApi index 0b4f43c6..c61a147d 160000 --- a/extern/GmsApi +++ b/extern/GmsApi @@ -1 +1 @@ -Subproject commit 0b4f43c6a6a091dbdeb0ec544d533373a83ea319 +Subproject commit c61a147de29456270bce1203c9f6700268b068c5 diff --git a/play-services-location/src/main/java/com/google/android/gms/location/ActivityRecognition.java b/play-services-location/src/main/java/com/google/android/gms/location/ActivityRecognition.java new file mode 100644 index 00000000..ef9d76c3 --- /dev/null +++ b/play-services-location/src/main/java/com/google/android/gms/location/ActivityRecognition.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 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.location; + +import com.google.android.gms.common.api.Api; +import com.google.android.gms.common.api.GoogleApiClient.Builder; + +import org.microg.gms.location.ActivityRecognitionApiBuilder; +import org.microg.gms.location.ActivityRecognitionApiImpl; + +/** + * The main entry point for activity recognition integration. + */ +public class ActivityRecognition { + public static final String CLIENT_NAME = "activity_recognition"; + + /** + * Token to pass to {@link Builder#addApi(Api)} to enable ContextServices. + */ + public static final Api API = new Api(new ActivityRecognitionApiBuilder()); + + /** + * Entry point to the activity recognition APIs. + */ + public static final ActivityRecognitionApi ActivityRecognitionApi = new ActivityRecognitionApiImpl(); +} diff --git a/play-services-location/src/main/java/com/google/android/gms/location/ActivityRecognitionApi.java b/play-services-location/src/main/java/com/google/android/gms/location/ActivityRecognitionApi.java new file mode 100644 index 00000000..08bc50b9 --- /dev/null +++ b/play-services-location/src/main/java/com/google/android/gms/location/ActivityRecognitionApi.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 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.location; + +import android.app.PendingIntent; +import android.os.Bundle; + +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks; +import com.google.android.gms.common.api.PendingResult; +import com.google.android.gms.common.api.Status; + +/** + * The main entry point for interacting with activity recognition. + *

+ * The methods must be used in conjunction with a GoogleApiClient. E.g. + *

+ *  new GoogleApiClient.Builder(context)
+ *          .addApi(ActivityRecognition.API)
+ *          .addConnectionCallbacks(this)
+ *          .addOnConnectionFailedListener(this)
+ *          .build()
+ * 
+ */ +public interface ActivityRecognitionApi { + /** + * Removes all activity updates for the specified PendingIntent. + *

+ * Calling this function requires the com.google.android.gms.permission.ACTIVITY_RECOGNITION + * permission. + * + * @param client An existing GoogleApiClient. It must be connected at the time of this + * call, which is normally achieved by calling {@link GoogleApiClient#connect()} + * and waiting for {@link ConnectionCallbacks#onConnected(Bundle)} to be + * called. + * @param callbackIntent the PendingIntent that was used in {@code #requestActivityUpdates(GoogleApiClient, long, PendingIntent)} + * or is equal as defined by {@link Object#equals(Object)}. + * @return a PendingResult for the call, check {@link Status#isSuccess()} to determine if it + * was successful. + */ + PendingResult removeActivityUpdates(GoogleApiClient client, PendingIntent callbackIntent); + + /** + * Register for activity recognition updates. + *

+ * The activities are detected by periodically waking up the device and reading short bursts of + * sensor data. It only makes use of low power sensors in order to keep the power usage to a + * minimum. For example, it can detect if the user is currently on foot, in a car, on a bicycle + * or still. See {@link DetectedActivity} for more details. + *

+ * The activity detection update interval can be controlled with the detectionIntervalMillis + * parameter. Larger values will result in fewer activity detections while improving battery + * life. Smaller values will result in more frequent activity detections but will consume more + * power since the device must be woken up more frequently. {@code Long.MAX_VALUE} means it only + * monitors the results requested by other clients without consuming additional power. + *

+ * Activities may be received more frequently than the detectionIntervalMillis parameter if + * another application has also requested activity updates at a faster rate. It may also receive + * updates faster when the activity detection service receives a signal that the current + * activity may change, such as if the device has been still for a long period of time and is + * then unplugged from a phone charger. + *

+ * Activities may arrive several seconds after the requested detectionIntervalMillis if the + * activity detection service requires more samples to make a more accurate prediction. + *

+ * To conserve battery, activity reporting may stop when the device is 'STILL' for an extended + * period of time. It will resume once the device moves again. This only happens on devices that + * support the Sensor.TYPE_SIGNIFICANT_MOTION hardware. + *

+ * Beginning in API 21, activities may be received less frequently than the + * detectionIntervalMillis parameter if the device is in power save mode and the screen is off. + *

+ * A common use case is that an application wants to monitor activities in the background and + * perform an action when a specific activity is detected. To do this without needing a service + * that is always on in the background consuming resources, detected activities are delivered + * via an intent. The application specifies a PendingIntent callback (typically an + * IntentService) which will be called with an intent when activities are detected. The intent + * recipient can extract the {@link ActivityRecognitionResult} using {@link ActivityRecognitionResult#extractResult(android.content.Intent)}. + * See the documentation of {@link PendingIntent} for more details. + *

+ * Any requests previously registered with {@link #requestActivityUpdates(GoogleApiClient, long, PendingIntent)} + * that have the same PendingIntent (as defined by {@link Object#equals(Object)}) will be + * replaced by this request. + *

+ * Calling this function requires the com.google.android.gms.permission.ACTIVITY_RECOGNITION + * permission. + * + * @param client An existing GoogleApiClient. It must be connected at the time + * of this call, which is normally achieved by calling {@link GoogleApiClient#connect()} + * and waiting for {@link ConnectionCallbacks#onConnected(Bundle)} + * to be called. + * @param detectionIntervalMillis the desired time between activity detections. Larger values + * will result in fewer activity detections while improving + * battery life. A value of 0 will result in activity detections + * at the fastest possible rate. + * @param callbackIntent a PendingIntent to be sent for each activity detection. + * @return a PendingResult for the call, check {@link Status#isSuccess()} to determine if it + * was successful. + */ + PendingResult requestActivityUpdates(GoogleApiClient client, long detectionIntervalMillis, PendingIntent callbackIntent); +} diff --git a/play-services-location/src/main/java/com/google/android/gms/location/FusedLocationProviderApi.java b/play-services-location/src/main/java/com/google/android/gms/location/FusedLocationProviderApi.java index 14a92352..0e2fb00e 100644 --- a/play-services-location/src/main/java/com/google/android/gms/location/FusedLocationProviderApi.java +++ b/play-services-location/src/main/java/com/google/android/gms/location/FusedLocationProviderApi.java @@ -22,30 +22,32 @@ import android.os.Looper; 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 org.microg.gms.location.LocationConstants; public interface FusedLocationProviderApi { + @Deprecated String KEY_LOCATION_CHANGED = "com.google.android.location.LOCATION"; String KEY_MOCK_LOCATION = LocationConstants.KEY_MOCK_LOCATION; Location getLastLocation(GoogleApiClient client); - PendingResult requestLocationUpdates(GoogleApiClient client, LocationRequest request, - LocationListener listener); + PendingResult requestLocationUpdates(GoogleApiClient client, LocationRequest request, + LocationListener listener); - PendingResult requestLocationUpdates(GoogleApiClient client, LocationRequest request, + PendingResult requestLocationUpdates(GoogleApiClient client, LocationRequest request, LocationListener listener, Looper looper); - PendingResult requestLocationUpdates(GoogleApiClient client, LocationRequest request, + PendingResult requestLocationUpdates(GoogleApiClient client, LocationRequest request, PendingIntent callbackIntent); - PendingResult removeLocationUpdates(GoogleApiClient client, LocationListener listener); + PendingResult removeLocationUpdates(GoogleApiClient client, LocationListener listener); - PendingResult removeLocationUpdates(GoogleApiClient client, + PendingResult removeLocationUpdates(GoogleApiClient client, PendingIntent callbackIntent); - PendingResult setMockMode(GoogleApiClient client, boolean isMockMode); + PendingResult setMockMode(GoogleApiClient client, boolean isMockMode); - PendingResult setMockLocation(GoogleApiClient client, Location mockLocation); + PendingResult setMockLocation(GoogleApiClient client, Location mockLocation); } diff --git a/play-services-location/src/main/java/com/google/android/gms/location/LocationStatusCodes.java b/play-services-location/src/main/java/com/google/android/gms/location/LocationStatusCodes.java new file mode 100644 index 00000000..4e3f7d54 --- /dev/null +++ b/play-services-location/src/main/java/com/google/android/gms/location/LocationStatusCodes.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 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.location; + +@Deprecated +public class LocationStatusCodes { + public static final int ERROR = 1; + public static final int GEOFENCE_NOT_AVAILABLE = 1000; + public static final int GEOFENCE_TOO_MANY_GEOFENCES = 1001; + public static final int GEOFENCE_TOO_MANY_PENDING_INTENTS = 1002; + public static final int SUCCESS = 0; +} diff --git a/play-services-location/src/main/java/org/microg/gms/location/ActivityRecognitionApiBuilder.java b/play-services-location/src/main/java/org/microg/gms/location/ActivityRecognitionApiBuilder.java new file mode 100644 index 00000000..f36ed0c0 --- /dev/null +++ b/play-services-location/src/main/java/org/microg/gms/location/ActivityRecognitionApiBuilder.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 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 org.microg.gms.location; + +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; + +import org.microg.gms.common.api.ApiBuilder; +import org.microg.gms.common.api.ApiConnection; + +public class ActivityRecognitionApiBuilder implements ApiBuilder { + @Override + public ApiConnection build(Context context, Looper looper, Api.ApiOptions.NoOptions options, AccountInfo accountInfo, GoogleApiClient.ConnectionCallbacks callbacks, GoogleApiClient.OnConnectionFailedListener connectionFailedListener) { + return new ActivityRecognitionClientImpl(context, callbacks, connectionFailedListener); + } +} diff --git a/play-services-location/src/main/java/org/microg/gms/location/ActivityRecognitionApiImpl.java b/play-services-location/src/main/java/org/microg/gms/location/ActivityRecognitionApiImpl.java new file mode 100644 index 00000000..5c09eefd --- /dev/null +++ b/play-services-location/src/main/java/org/microg/gms/location/ActivityRecognitionApiImpl.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 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 org.microg.gms.location; + +import android.app.PendingIntent; +import android.os.RemoteException; +import android.util.Log; + +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 com.google.android.gms.location.ActivityRecognition; +import com.google.android.gms.location.ActivityRecognitionApi; + +import org.microg.gms.common.GmsConnector; + +public class ActivityRecognitionApiImpl implements ActivityRecognitionApi { + private static final String TAG = "GmsActivityApiImpl"; + + @Override + public PendingResult removeActivityUpdates(GoogleApiClient client, final PendingIntent callbackIntent) { + return callVoid(client, new Runnable() { + @Override + public void run(ActivityRecognitionClientImpl client) throws RemoteException { + client.removeActivityUpdates(callbackIntent); + } + }); + } + + @Override + public PendingResult requestActivityUpdates(GoogleApiClient client, final long detectionIntervalMillis, final PendingIntent callbackIntent) { + return callVoid(client, new Runnable() { + @Override + public void run(ActivityRecognitionClientImpl client) throws RemoteException { + client.requestActivityUpdates(detectionIntervalMillis, callbackIntent); + } + }); + } + + private PendingResult callVoid(GoogleApiClient client, final Runnable runnable) { + return GmsConnector.call(client, ActivityRecognition.API, new GmsConnector.Callback() { + @Override + public void onClientAvailable(ActivityRecognitionClientImpl client, ResultProvider resultProvider) throws RemoteException { + runnable.run(client); + resultProvider.onResultAvailable(Status.SUCCESS); + } + }); + } + + private interface Runnable { + void run(ActivityRecognitionClientImpl client) throws RemoteException; + } +} diff --git a/play-services-location/src/main/java/org/microg/gms/location/ActivityRecognitionClientImpl.java b/play-services-location/src/main/java/org/microg/gms/location/ActivityRecognitionClientImpl.java new file mode 100644 index 00000000..cf065fc9 --- /dev/null +++ b/play-services-location/src/main/java/org/microg/gms/location/ActivityRecognitionClientImpl.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 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 org.microg.gms.location; + +import android.app.PendingIntent; +import android.content.Context; +import android.os.RemoteException; + +import com.google.android.gms.common.api.GoogleApiClient; + +public class ActivityRecognitionClientImpl extends GoogleLocationManagerClient { + public ActivityRecognitionClientImpl(Context context, GoogleApiClient.ConnectionCallbacks callbacks, GoogleApiClient.OnConnectionFailedListener connectionFailedListener) { + super(context, callbacks, connectionFailedListener); + } + + public void requestActivityUpdates(long detectionIntervalMillis, PendingIntent callbackIntent) throws RemoteException { + getServiceInterface().requestActivityUpdates(detectionIntervalMillis, true, callbackIntent); + } + + public void removeActivityUpdates(PendingIntent callbackIntent) throws RemoteException { + getServiceInterface().removeActivityUpdates(callbackIntent); + } +} diff --git a/play-services-location/src/main/java/org/microg/gms/location/FusedLocationProviderApiImpl.java b/play-services-location/src/main/java/org/microg/gms/location/FusedLocationProviderApiImpl.java index 5ce97ba8..4da06a0a 100644 --- a/play-services-location/src/main/java/org/microg/gms/location/FusedLocationProviderApiImpl.java +++ b/play-services-location/src/main/java/org/microg/gms/location/FusedLocationProviderApiImpl.java @@ -49,7 +49,7 @@ public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { } @Override - public PendingResult requestLocationUpdates(GoogleApiClient client, + public PendingResult requestLocationUpdates(GoogleApiClient client, final LocationRequest request, final LocationListener listener) { return callVoid(client, new Runnable() { @Override @@ -60,7 +60,7 @@ public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { } @Override - public PendingResult requestLocationUpdates(GoogleApiClient client, + public PendingResult requestLocationUpdates(GoogleApiClient client, final LocationRequest request, final LocationListener listener, final Looper looper) { return callVoid(client, new Runnable() { @@ -72,7 +72,7 @@ public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { } @Override - public PendingResult requestLocationUpdates(GoogleApiClient client, + public PendingResult requestLocationUpdates(GoogleApiClient client, final LocationRequest request, final PendingIntent callbackIntent) { return callVoid(client, new Runnable() { @Override @@ -83,7 +83,7 @@ public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { } @Override - public PendingResult removeLocationUpdates(GoogleApiClient client, + public PendingResult removeLocationUpdates(GoogleApiClient client, final LocationListener listener) { return callVoid(client, new Runnable() { @Override @@ -94,7 +94,7 @@ public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { } @Override - public PendingResult removeLocationUpdates(GoogleApiClient client, + public PendingResult removeLocationUpdates(GoogleApiClient client, final PendingIntent callbackIntent) { return callVoid(client, new Runnable() { @Override @@ -105,7 +105,7 @@ public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { } @Override - public PendingResult setMockMode(GoogleApiClient client, final boolean isMockMode) { + public PendingResult setMockMode(GoogleApiClient client, final boolean isMockMode) { return callVoid(client, new Runnable() { @Override public void run(LocationClientImpl client) throws RemoteException { @@ -115,7 +115,7 @@ public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { } @Override - public PendingResult setMockLocation(GoogleApiClient client, final Location mockLocation) { + public PendingResult setMockLocation(GoogleApiClient client, final Location mockLocation) { return callVoid(client, new Runnable() { @Override public void run(LocationClientImpl client) throws RemoteException { @@ -124,10 +124,10 @@ public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { }); } - private PendingResult callVoid(GoogleApiClient client, final Runnable runnable) { - return GmsConnector.call(client, LocationServices.API, new GmsConnector.Callback() { + private PendingResult callVoid(GoogleApiClient client, final Runnable runnable) { + return GmsConnector.call(client, LocationServices.API, new GmsConnector.Callback() { @Override - public void onClientAvailable(LocationClientImpl client, ResultProvider resultProvider) throws RemoteException { + public void onClientAvailable(LocationClientImpl client, ResultProvider resultProvider) throws RemoteException { runnable.run(client); resultProvider.onResultAvailable(Status.SUCCESS); } From aec1ad93b8b419ba3ea59bde24aae45832227720 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sat, 11 Feb 2017 02:14:16 +0100 Subject: [PATCH 52/65] Add Geofencing API client code --- extern/GmsApi | 2 +- .../android/gms/location/GeofencingApi.java | 28 ++++++ .../FusedLocationProviderApiImpl.java | 1 - .../gms/location/GeofencingApiImpl.java | 94 +++++++++++++++++++ .../gms/location/LocationClientImpl.java | 40 +++++++- .../location/NativeLocationClientImpl.java | 59 +++++++++++- 6 files changed, 218 insertions(+), 6 deletions(-) diff --git a/extern/GmsApi b/extern/GmsApi index c61a147d..3f1fa813 160000 --- a/extern/GmsApi +++ b/extern/GmsApi @@ -1 +1 @@ -Subproject commit c61a147de29456270bce1203c9f6700268b068c5 +Subproject commit 3f1fa81390d67053d3aa50b0bc3537953396bcf6 diff --git a/play-services-location/src/main/java/com/google/android/gms/location/GeofencingApi.java b/play-services-location/src/main/java/com/google/android/gms/location/GeofencingApi.java index b2b581bf..0268aeb2 100644 --- a/play-services-location/src/main/java/com/google/android/gms/location/GeofencingApi.java +++ b/play-services-location/src/main/java/com/google/android/gms/location/GeofencingApi.java @@ -16,5 +16,33 @@ package com.google.android.gms.location; +import android.app.PendingIntent; + +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.List; + +/** + * The main entry point for interacting with the geofencing APIs. + *

+ * The methods must be used in conjunction with a GoogleApiClient. E.g. + *

+ *  new GoogleApiClient.Builder(context)
+ *          .addApi(LocationServices.API)
+ *          .addConnectionCallbacks(this)
+ *          .addOnConnectionFailedListener(this)
+ *          .build()
+ * 
+ */ public interface GeofencingApi { + PendingResult addGeofences(GoogleApiClient client, GeofencingRequest geofencingRequest, PendingIntent pendingIntent); + + @Deprecated + PendingResult addGeofences(GoogleApiClient client, List geofences, PendingIntent pendingIntent); + + PendingResult removeGeofences(GoogleApiClient client, List geofenceRequestIds); + + PendingResult removeGeofences(GoogleApiClient client, PendingIntent pendingIntent); } diff --git a/play-services-location/src/main/java/org/microg/gms/location/FusedLocationProviderApiImpl.java b/play-services-location/src/main/java/org/microg/gms/location/FusedLocationProviderApiImpl.java index 4da06a0a..84584ac0 100644 --- a/play-services-location/src/main/java/org/microg/gms/location/FusedLocationProviderApiImpl.java +++ b/play-services-location/src/main/java/org/microg/gms/location/FusedLocationProviderApiImpl.java @@ -137,5 +137,4 @@ public class FusedLocationProviderApiImpl implements FusedLocationProviderApi { private interface Runnable { void run(LocationClientImpl client) throws RemoteException; } - } diff --git a/play-services-location/src/main/java/org/microg/gms/location/GeofencingApiImpl.java b/play-services-location/src/main/java/org/microg/gms/location/GeofencingApiImpl.java index 7b6512b0..e0b09c98 100644 --- a/play-services-location/src/main/java/org/microg/gms/location/GeofencingApiImpl.java +++ b/play-services-location/src/main/java/org/microg/gms/location/GeofencingApiImpl.java @@ -16,7 +16,101 @@ package org.microg.gms.location; +import android.app.PendingIntent; +import android.os.RemoteException; +import android.support.annotation.NonNull; +import android.util.Log; + +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 com.google.android.gms.location.Geofence; import com.google.android.gms.location.GeofencingApi; +import com.google.android.gms.location.GeofencingRequest; +import com.google.android.gms.location.LocationServices; +import com.google.android.gms.location.internal.IGeofencerCallbacks; +import com.google.android.gms.location.internal.ParcelableGeofence; + +import org.microg.gms.common.GmsConnector; + +import java.util.ArrayList; +import java.util.List; public class GeofencingApiImpl implements GeofencingApi { + @Override + public PendingResult addGeofences(GoogleApiClient client, final GeofencingRequest geofencingRequest, final PendingIntent pendingIntent) { + return callGeofencer(client, new Runnable() { + @Override + public void run(LocationClientImpl client, IGeofencerCallbacks callbacks) throws RemoteException { + client.addGeofences(geofencingRequest, pendingIntent, callbacks); + } + }); + } + + @Override + public PendingResult addGeofences(GoogleApiClient client, final List geofences, final PendingIntent pendingIntent) { + final List geofenceList = new ArrayList(); + for (Geofence geofence : geofences) { + if (geofence instanceof ParcelableGeofence) geofenceList.add((ParcelableGeofence) geofence); + } + return callGeofencer(client, new Runnable() { + @Override + public void run(LocationClientImpl client, IGeofencerCallbacks callbacks) throws RemoteException { + client.addGeofences(geofenceList, pendingIntent, callbacks); + } + }); + } + + @Override + public PendingResult removeGeofences(GoogleApiClient client, final List geofenceRequestIds) { + return callGeofencer(client, new Runnable() { + @Override + public void run(LocationClientImpl client, IGeofencerCallbacks callbacks) throws RemoteException { + client.removeGeofences(geofenceRequestIds, callbacks); + } + }); + } + + @Override + public PendingResult removeGeofences(GoogleApiClient client, final PendingIntent pendingIntent) { + return callGeofencer(client, new Runnable() { + @Override + public void run(LocationClientImpl client, IGeofencerCallbacks callbacks) throws RemoteException { + client.removeGeofences(pendingIntent, callbacks); + } + }); + } + + @NonNull + private IGeofencerCallbacks.Stub createGeofencerCallbacks(final GmsConnector.Callback.ResultProvider resultProvider) { + return new IGeofencerCallbacks.Stub(){ + @Override + public void onAddGeofenceResult(int statusCode, String[] requestIds) throws RemoteException { + resultProvider.onResultAvailable(new Status(statusCode)); + } + + @Override + public void onRemoveGeofencesByRequestIdsResult(int statusCode, String[] requestIds) throws RemoteException { + resultProvider.onResultAvailable(new Status(statusCode)); + } + + @Override + public void onRemoveGeofencesByPendingIntentResult(int statusCode, PendingIntent pendingIntent) throws RemoteException { + resultProvider.onResultAvailable(new Status(statusCode)); + } + }; + } + + private PendingResult callGeofencer(GoogleApiClient client, final Runnable runnable) { + return GmsConnector.call(client, LocationServices.API, new GmsConnector.Callback() { + @Override + public void onClientAvailable(LocationClientImpl client, ResultProvider resultProvider) throws RemoteException { + runnable.run(client, createGeofencerCallbacks(resultProvider)); + } + }); + } + + private interface Runnable { + void run(LocationClientImpl client, IGeofencerCallbacks callbacks) throws RemoteException; + } } diff --git a/play-services-location/src/main/java/org/microg/gms/location/LocationClientImpl.java b/play-services-location/src/main/java/org/microg/gms/location/LocationClientImpl.java index e3593198..1d5f5b6c 100644 --- a/play-services-location/src/main/java/org/microg/gms/location/LocationClientImpl.java +++ b/play-services-location/src/main/java/org/microg/gms/location/LocationClientImpl.java @@ -25,14 +25,18 @@ import android.os.RemoteException; import android.util.Log; import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.location.GeofencingRequest; import com.google.android.gms.location.ILocationListener; import com.google.android.gms.location.LocationListener; import com.google.android.gms.location.LocationRequest; import com.google.android.gms.location.LocationServices; +import com.google.android.gms.location.internal.IGeofencerCallbacks; +import com.google.android.gms.location.internal.ParcelableGeofence; import org.microg.gms.common.api.GoogleApiClientImpl; import java.util.HashMap; +import java.util.List; import java.util.Map; public class LocationClientImpl extends GoogleLocationManagerClient { @@ -42,7 +46,7 @@ public class LocationClientImpl extends GoogleLocationManagerClient { public LocationClientImpl(Context context, GoogleApiClient.ConnectionCallbacks callbacks, - GoogleApiClient.OnConnectionFailedListener connectionFailedListener) { + GoogleApiClient.OnConnectionFailedListener connectionFailedListener) { super(context, callbacks, connectionFailedListener); Log.d(TAG, ""); } @@ -55,6 +59,38 @@ public class LocationClientImpl extends GoogleLocationManagerClient { return null; } + public void addGeofences(GeofencingRequest request, PendingIntent pendingIntent, IGeofencerCallbacks callbacks) throws RemoteException { + if (nativeLocation != null) { + nativeLocation.addGeofences(request, pendingIntent, callbacks); + } else { + getServiceInterface().addGeofences(request, pendingIntent, callbacks); + } + } + + public void addGeofences(List request, PendingIntent pendingIntent, IGeofencerCallbacks callbacks) throws RemoteException { + if (nativeLocation != null) { + nativeLocation.addGeofences(request, pendingIntent, callbacks); + } else { + getServiceInterface().addGeofencesList(request, pendingIntent, callbacks, getContext().getPackageName()); + } + } + + public void removeGeofences(List geofenceRequestIds, IGeofencerCallbacks callbacks) throws RemoteException { + if (nativeLocation != null) { + nativeLocation.removeGeofences(geofenceRequestIds, callbacks); + } else { + getServiceInterface().removeGeofencesById(geofenceRequestIds.toArray(new String[geofenceRequestIds.size()]), callbacks, getContext().getPackageName()); + } + } + + public void removeGeofences(PendingIntent pendingIntent, IGeofencerCallbacks callbacks) throws RemoteException { + if (nativeLocation != null) { + nativeLocation.removeGeofences(pendingIntent, callbacks); + } else { + getServiceInterface().removeGeofencesByIntent(pendingIntent, callbacks, getContext().getPackageName()); + } + } + public Location getLastLocation() throws RemoteException { Log.d(TAG, "getLastLocation()"); if (nativeLocation != null) { @@ -92,7 +128,7 @@ public class LocationClientImpl extends GoogleLocationManagerClient { } public void requestLocationUpdates(LocationRequest request, LocationListener listener, - Looper looper) throws RemoteException { + Looper looper) throws RemoteException { if (nativeLocation != null) { nativeLocation.requestLocationUpdates(request, listener, looper); } diff --git a/play-services-location/src/main/java/org/microg/gms/location/NativeLocationClientImpl.java b/play-services-location/src/main/java/org/microg/gms/location/NativeLocationClientImpl.java index 7555b806..158c8cc2 100644 --- a/play-services-location/src/main/java/org/microg/gms/location/NativeLocationClientImpl.java +++ b/play-services-location/src/main/java/org/microg/gms/location/NativeLocationClientImpl.java @@ -25,15 +25,29 @@ import android.location.Location; import android.location.LocationManager; import android.os.Bundle; import android.os.Looper; +import android.os.RemoteException; +import android.os.SystemClock; import android.util.Log; +import com.google.android.gms.common.api.CommonStatusCodes; import com.google.android.gms.location.FusedLocationProviderApi; +import com.google.android.gms.location.Geofence; +import com.google.android.gms.location.GeofenceStatusCodes; +import com.google.android.gms.location.GeofencingEvent; +import com.google.android.gms.location.GeofencingRequest; import com.google.android.gms.location.LocationListener; import com.google.android.gms.location.LocationRequest; +import com.google.android.gms.location.internal.IGeofencerCallbacks; +import com.google.android.gms.location.internal.ParcelableGeofence; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; +import static android.location.LocationManager.KEY_LOCATION_CHANGED; +import static android.location.LocationManager.KEY_PROXIMITY_ENTERING; + @SuppressWarnings("MissingPermission") public class NativeLocationClientImpl { private final static String TAG = "GmsToNativeLocClient"; @@ -71,6 +85,39 @@ public class NativeLocationClientImpl { return criteria; } + public void addGeofences(GeofencingRequest geofencingRequest, PendingIntent pendingIntent, IGeofencerCallbacks callbacks) throws RemoteException { + Log.d(TAG, "addGeofences(GeofencingRequest)"); + callbacks.onAddGeofenceResult(GeofenceStatusCodes.GEOFENCE_NOT_AVAILABLE, new String[0]); + } + + public void addGeofences(List geofences, PendingIntent pendingIntent, IGeofencerCallbacks callbacks) throws RemoteException { + Log.d(TAG, "addGeofences(List)"); + Intent i = new Intent(context, NativePendingIntentForwarder.class); + Bundle bundle = new Bundle(); + bundle.putParcelable(EXTRA_PENDING_INTENT, pendingIntent); + i.putExtras(bundle); + nativePendingMap.put(pendingIntent, PendingIntent.getActivity(context, 0, i, 0)); + List requestIds = new ArrayList(); + for (ParcelableGeofence geofence : geofences) { + locationManager.addProximityAlert(geofence.latitude, geofence.longitude, geofence.radius, + geofence.expirationTime - SystemClock.elapsedRealtime(), nativePendingMap.get(pendingIntent)); + requestIds.add(geofence.getRequestId()); + } + callbacks.onAddGeofenceResult(CommonStatusCodes.SUCCESS, requestIds.toArray(new String[requestIds.size()])); + } + + public void removeGeofences(List requestIds, IGeofencerCallbacks callbacks) throws RemoteException { + Log.d(TAG, "removeGeofences(List)"); + callbacks.onRemoveGeofencesByRequestIdsResult(GeofenceStatusCodes.ERROR, requestIds.toArray(new String[requestIds.size()])); + } + + public void removeGeofences(PendingIntent pendingIntent, IGeofencerCallbacks callbacks) throws RemoteException { + Log.d(TAG, "removeGeofences(PendingIntent)"); + locationManager.removeProximityAlert(nativePendingMap.get(pendingIntent)); + nativePendingMap.remove(pendingIntent); + callbacks.onRemoveGeofencesByPendingIntentResult(CommonStatusCodes.SUCCESS, pendingIntent); + } + public Location getLastLocation() { Log.d(TAG, "getLastLocation()"); return locationManager.getLastKnownLocation(locationManager.getBestProvider(DEFAULT_CRITERIA, true)); @@ -131,11 +178,19 @@ public class NativeLocationClientImpl { @Override public void onReceive(Context context, Intent intent) { - if (intent.hasExtra(LocationManager.KEY_LOCATION_CHANGED)) { + if (intent.hasExtra(KEY_PROXIMITY_ENTERING)) { + PendingIntent pendingIntent = intent.getExtras().getParcelable(EXTRA_PENDING_INTENT); + try { + intent.putExtra(GeofencingEvent.EXTRA_TRANSITION, intent.getBooleanExtra(KEY_PROXIMITY_ENTERING, false) ? Geofence.GEOFENCE_TRANSITION_ENTER : Geofence.GEOFENCE_TRANSITION_EXIT); + pendingIntent.send(context, 0, intent); + } catch (PendingIntent.CanceledException e) { + nativePendingMap.remove(pendingIntent); + } + } else if (intent.hasExtra(KEY_LOCATION_CHANGED)) { PendingIntent pendingIntent = intent.getExtras().getParcelable(EXTRA_PENDING_INTENT); try { intent.putExtra(FusedLocationProviderApi.KEY_LOCATION_CHANGED, - intent.getParcelableExtra(LocationManager.KEY_LOCATION_CHANGED)); + intent.getParcelableExtra(KEY_LOCATION_CHANGED)); pendingIntent.send(context, 0, intent); pendingCount.put(pendingIntent, pendingCount.get(pendingIntent) - 1); if (pendingCount.get(pendingIntent) == 0) { From 7b897cd001376b841b3557b0e98d93ed4cb55e32 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sat, 11 Feb 2017 02:32:16 +0100 Subject: [PATCH 53/65] Add dummy SettingsApi --- .../gms/common/api/InstantPendingResult.java | 61 +++++++++++++++++++ .../gms/location/LocationServices.java | 18 ++++++ .../android/gms/location/SettingsApi.java | 41 +++++++++++++ .../microg/gms/location/SettingsApiImpl.java | 33 ++++++++++ 4 files changed, 153 insertions(+) create mode 100644 play-services-base/src/main/java/org/microg/gms/common/api/InstantPendingResult.java create mode 100644 play-services-location/src/main/java/com/google/android/gms/location/SettingsApi.java create mode 100644 play-services-location/src/main/java/org/microg/gms/location/SettingsApiImpl.java diff --git a/play-services-base/src/main/java/org/microg/gms/common/api/InstantPendingResult.java b/play-services-base/src/main/java/org/microg/gms/common/api/InstantPendingResult.java new file mode 100644 index 00000000..ba75725c --- /dev/null +++ b/play-services-base/src/main/java/org/microg/gms/common/api/InstantPendingResult.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 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 org.microg.gms.common.api; + +import com.google.android.gms.common.api.PendingResult; +import com.google.android.gms.common.api.Result; +import com.google.android.gms.common.api.ResultCallback; + +import java.util.concurrent.TimeUnit; + +public class InstantPendingResult implements PendingResult { + R value; + + public InstantPendingResult(R value) { + this.value = value; + } + + @Override + public R await() { + return value; + } + + @Override + public R await(long time, TimeUnit unit) { + return value; + } + + @Override + public void cancel() { + + } + + @Override + public boolean isCanceled() { + return false; + } + + @Override + public void setResultCallback(ResultCallback callback, long time, TimeUnit unit) { + callback.onResult(value); + } + + @Override + public void setResultCallback(ResultCallback callback) { + callback.onResult(value); + } +} diff --git a/play-services-location/src/main/java/com/google/android/gms/location/LocationServices.java b/play-services-location/src/main/java/com/google/android/gms/location/LocationServices.java index dc575bb9..23fce208 100644 --- a/play-services-location/src/main/java/com/google/android/gms/location/LocationServices.java +++ b/play-services-location/src/main/java/com/google/android/gms/location/LocationServices.java @@ -17,16 +17,34 @@ package com.google.android.gms.location; import com.google.android.gms.common.api.Api; +import com.google.android.gms.common.api.GoogleApiClient.Builder; import org.microg.gms.location.FusedLocationProviderApiImpl; import org.microg.gms.location.GeofencingApiImpl; import org.microg.gms.location.LocationServicesApiBuilder; +import org.microg.gms.location.SettingsApiImpl; /** * The main entry point for location services integration. */ public class LocationServices { + /** + * Token to pass to {@link Builder#addApi(Api)} to enable LocationServices. + */ public static final Api API = new Api(new LocationServicesApiBuilder()); + + /** + * Entry point to the fused location APIs. + */ public static final FusedLocationProviderApi FusedLocationApi = new FusedLocationProviderApiImpl(); + + /** + * Entry point to the geofencing APIs. + */ public static final GeofencingApi GeofencingApi = new GeofencingApiImpl(); + + /** + * Entry point to the location settings-enabler dialog APIs. + */ + public static final SettingsApi SettingsApi = new SettingsApiImpl(); } diff --git a/play-services-location/src/main/java/com/google/android/gms/location/SettingsApi.java b/play-services-location/src/main/java/com/google/android/gms/location/SettingsApi.java new file mode 100644 index 00000000..708d7d02 --- /dev/null +++ b/play-services-location/src/main/java/com/google/android/gms/location/SettingsApi.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 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.location; + +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.PendingResult; + +/** + * The main entry point for interacting with the location settings-enabler APIs. + *

+ * This API makes it easy for an app to ensure that the device's system settings are properly + * configured for the app's location needs. + */ +public interface SettingsApi { + /** + * Checks if the relevant system settings are enabled on the device to carry out the desired + * location requests. + * + * @param client an existing GoogleApiClient. It does not need to be connected + * at the time of this call, but the result will be delayed until + * the connection is complete. + * @param locationSettingsRequest an object that contains all the location requirements that the + * client is interested in. + * @return result containing the status of the request. + */ + PendingResult checkLocationSettings(GoogleApiClient client, LocationSettingsRequest locationSettingsRequest); +} diff --git a/play-services-location/src/main/java/org/microg/gms/location/SettingsApiImpl.java b/play-services-location/src/main/java/org/microg/gms/location/SettingsApiImpl.java new file mode 100644 index 00000000..b8fe0ffe --- /dev/null +++ b/play-services-location/src/main/java/org/microg/gms/location/SettingsApiImpl.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 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 org.microg.gms.location; + +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 com.google.android.gms.location.LocationSettingsRequest; +import com.google.android.gms.location.LocationSettingsResult; +import com.google.android.gms.location.SettingsApi; + +import org.microg.gms.common.api.InstantPendingResult; + +public class SettingsApiImpl implements SettingsApi { + @Override + public PendingResult checkLocationSettings(GoogleApiClient client, LocationSettingsRequest locationSettingsRequest) { + return new InstantPendingResult(new LocationSettingsResult(Status.CANCELED)); + } +} From b71d6ceb7e81b7df32147651a6b0db9be2733197 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Mon, 1 May 2017 12:39:50 +0200 Subject: [PATCH 54/65] Update build tools --- .travis.yml | 4 ++-- build.gradle | 6 +++--- extern/GmsApi | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 612507ec..8d79a24e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,8 +12,8 @@ script: android: components: - tools - - build-tools-24.0.3 - - android-24 + - build-tools-25.0.2 + - android-25 - extra-android-m2repository before_cache: - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock diff --git a/build.gradle b/build.gradle index be127829..5412a4de 100644 --- a/build.gradle +++ b/build.gradle @@ -19,17 +19,17 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.2.2' + classpath 'com.android.tools.build:gradle:2.3.1' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' } } allprojects { apply plugin: 'idea' - ext.androidBuildVersionTools = "24.0.3" + ext.androidBuildVersionTools = "25.0.2" } -def androidCompileSdk() { return 24 } +def androidCompileSdk() { return 25 } def androidTargetSdk() { return 24 } diff --git a/extern/GmsApi b/extern/GmsApi index 3f1fa813..e2fc0bd8 160000 --- a/extern/GmsApi +++ b/extern/GmsApi @@ -1 +1 @@ -Subproject commit 3f1fa81390d67053d3aa50b0bc3537953396bcf6 +Subproject commit e2fc0bd8284d276f85c863bf723d8133a5c6fe13 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2b30f04c..654f05ca 100755 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -14,9 +14,9 @@ # limitations under the License. # -#Wed Jan 14 11:55:04 CET 2015 +#Sat Apr 22 15:09:21 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip From 8b5813143ed11fbcb533d407ce58fb32a54fe18a Mon Sep 17 00:00:00 2001 From: Marvin W Date: Tue, 13 Jun 2017 00:29:13 +0200 Subject: [PATCH 55/65] Update sublib and copyright header --- extern/GmsApi | 2 +- play-services-base/src/main/AndroidManifest.xml | 2 +- .../com/google/android/gms/common/GoogleApiAvailability.java | 2 +- .../com/google/android/gms/common/GooglePlayServicesClient.java | 2 +- .../com/google/android/gms/common/GooglePlayServicesUtil.java | 2 +- .../java/com/google/android/gms/common/api/AccountInfo.java | 2 +- .../src/main/java/com/google/android/gms/common/api/Api.java | 2 +- .../java/com/google/android/gms/common/api/GoogleApiClient.java | 2 +- .../java/com/google/android/gms/common/api/PendingResult.java | 2 +- .../java/com/google/android/gms/common/data/DataBuffer.java | 2 +- .../src/main/java/org/microg/gms/common/DummyApiConnection.java | 2 +- .../java/org/microg/gms/common/ForwardConnectionCallbacks.java | 2 +- .../org/microg/gms/common/ForwardConnectionFailedListener.java | 2 +- .../src/main/java/org/microg/gms/common/GmsClient.java | 2 +- .../src/main/java/org/microg/gms/common/GmsConnector.java | 2 +- .../main/java/org/microg/gms/common/MultiConnectionKeeper.java | 2 +- .../java/org/microg/gms/common/api/AbstractPendingResult.java | 2 +- .../org/microg/gms/common/api/AbstractPlayServicesClient.java | 2 +- .../src/main/java/org/microg/gms/common/api/ApiBuilder.java | 2 +- .../src/main/java/org/microg/gms/common/api/ApiConnection.java | 2 +- .../java/org/microg/gms/common/api/GoogleApiClientImpl.java | 2 +- .../java/org/microg/gms/common/api/ResultCallbackHandler.java | 2 +- play-services-base/src/main/res/values/version.xml | 2 +- play-services-cast/src/main/AndroidManifest.xml | 2 +- .../src/main/java/com/google/android/gms/cast/Cast.java | 2 +- .../main/java/com/google/android/gms/cast/CastPresentation.java | 2 +- .../java/com/google/android/gms/cast/CastRemoteDisplay.java | 2 +- .../java/com/google/android/gms/cast/CastRemoteDisplayApi.java | 2 +- .../google/android/gms/cast/CastRemoteDisplayLocalService.java | 2 +- .../src/main/java/org/microg/gms/cast/CastApiBuilder.java | 2 +- .../src/main/java/org/microg/gms/cast/CastApiImpl.java | 2 +- .../src/main/java/org/microg/gms/cast/CastClientImpl.java | 2 +- .../java/org/microg/gms/cast/CastRemoteDisplayApiBuilder.java | 2 +- .../main/java/org/microg/gms/cast/CastRemoteDisplayApiImpl.java | 2 +- play-services-gcm/src/main/AndroidManifest.xml | 2 +- .../java/com/google/android/gms/gcm/GcmListenerService.java | 2 +- .../main/java/com/google/android/gms/gcm/GcmNetworkManager.java | 2 +- .../src/main/java/com/google/android/gms/gcm/GcmPubSub.java | 2 +- .../src/main/java/com/google/android/gms/gcm/GcmReceiver.java | 2 +- .../main/java/com/google/android/gms/gcm/GcmTaskService.java | 2 +- .../java/com/google/android/gms/gcm/GoogleCloudMessaging.java | 2 +- .../src/main/java/com/google/android/gms/gcm/OneoffTask.java | 2 +- .../main/java/com/google/android/gms/gcm/PendingCallback.java | 2 +- .../src/main/java/com/google/android/gms/gcm/PeriodicTask.java | 2 +- .../src/main/java/com/google/android/gms/gcm/Task.java | 2 +- .../src/main/java/com/google/android/gms/gcm/TaskParams.java | 2 +- .../src/main/java/org/microg/gms/gcm/CloudMessagingRpc.java | 2 +- play-services-iid/src/main/AndroidManifest.xml | 2 +- .../src/main/java/com/google/android/gms/iid/InstanceID.java | 2 +- .../com/google/android/gms/iid/InstanceIDListenerService.java | 2 +- .../src/main/java/org/microg/gms/iid/InstanceIdRpc.java | 2 +- .../src/main/java/org/microg/gms/iid/InstanceIdStore.java | 2 +- play-services-location/src/main/AndroidManifest.xml | 2 +- .../google/android/gms/location/FusedLocationProviderApi.java | 2 +- .../java/com/google/android/gms/location/GeofencingApi.java | 2 +- .../java/com/google/android/gms/location/LocationClient.java | 2 +- .../java/com/google/android/gms/location/LocationListener.java | 2 +- .../java/com/google/android/gms/location/LocationServices.java | 2 +- .../org/microg/gms/location/FusedLocationProviderApiImpl.java | 2 +- .../main/java/org/microg/gms/location/GeofencingApiImpl.java | 2 +- .../org/microg/gms/location/GoogleLocationManagerClient.java | 2 +- .../main/java/org/microg/gms/location/LocationClientImpl.java | 2 +- .../org/microg/gms/location/LocationServicesApiBuilder.java | 2 +- .../java/org/microg/gms/location/NativeLocationClientImpl.java | 2 +- play-services-tasks/src/main/AndroidManifest.xml | 2 +- .../main/java/com/google/android/gms/tasks/Continuation.java | 2 +- .../java/com/google/android/gms/tasks/OnCompleteListener.java | 2 +- .../java/com/google/android/gms/tasks/OnFailureListener.java | 2 +- .../java/com/google/android/gms/tasks/OnSuccessListener.java | 2 +- .../com/google/android/gms/tasks/RuntimeExecutionException.java | 2 +- .../src/main/java/com/google/android/gms/tasks/Task.java | 2 +- .../java/com/google/android/gms/tasks/TaskCompletionSource.java | 2 +- .../src/main/java/com/google/android/gms/tasks/Tasks.java | 2 +- play-services-wearable/src/main/AndroidManifest.xml | 2 +- .../java/com/google/android/gms/wearable/CapabilityApi.java | 2 +- .../src/main/java/com/google/android/gms/wearable/Channel.java | 2 +- .../main/java/com/google/android/gms/wearable/ChannelApi.java | 2 +- .../com/google/android/gms/wearable/ChannelIOException.java | 2 +- .../src/main/java/com/google/android/gms/wearable/DataApi.java | 2 +- .../main/java/com/google/android/gms/wearable/DataEvent.java | 2 +- .../java/com/google/android/gms/wearable/DataEventBuffer.java | 2 +- .../java/com/google/android/gms/wearable/DataItemBuffer.java | 2 +- .../src/main/java/com/google/android/gms/wearable/DataMap.java | 2 +- .../main/java/com/google/android/gms/wearable/DataMapItem.java | 2 +- .../main/java/com/google/android/gms/wearable/MessageApi.java | 2 +- .../src/main/java/com/google/android/gms/wearable/NodeApi.java | 2 +- .../java/com/google/android/gms/wearable/PutDataMapRequest.java | 2 +- .../src/main/java/com/google/android/gms/wearable/Wearable.java | 2 +- .../google/android/gms/wearable/WearableListenerService.java | 2 +- .../java/org/microg/gms/wearable/BaseWearableCallbacks.java | 2 +- .../src/main/java/org/microg/gms/wearable/ChannelImpl.java | 2 +- .../src/main/java/org/microg/gms/wearable/DataApiImpl.java | 2 +- .../src/main/java/org/microg/gms/wearable/MessageApiImpl.java | 2 +- .../src/main/java/org/microg/gms/wearable/NodeApiImpl.java | 2 +- .../main/java/org/microg/gms/wearable/WearableApiBuilder.java | 2 +- .../main/java/org/microg/gms/wearable/WearableClientImpl.java | 2 +- .../java/org/microg/gms/wearable/databundle/DataBundleUtil.java | 2 +- play-services/src/main/AndroidManifest.xml | 2 +- 98 files changed, 98 insertions(+), 98 deletions(-) diff --git a/extern/GmsApi b/extern/GmsApi index e2fc0bd8..b25ca172 160000 --- a/extern/GmsApi +++ b/extern/GmsApi @@ -1 +1 @@ -Subproject commit e2fc0bd8284d276f85c863bf723d8133a5c6fe13 +Subproject commit b25ca17225de59fef635f1b9d6ba3abc1144e17a diff --git a/play-services-base/src/main/AndroidManifest.xml b/play-services-base/src/main/AndroidManifest.xml index 5046e2a9..559a33ba 100644 --- a/play-services-base/src/main/AndroidManifest.xml +++ b/play-services-base/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@