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. - *

- *

- * 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'