mirror of https://github.com/YTVanced/VancedMicroG
Merge GmsLib
This commit is contained in:
commit
a5bfecee6f
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
|
String getMyVersionName() {
|
||||||
|
def stdout = new ByteArrayOutputStream()
|
||||||
|
if (rootProject.file("gradlew").exists())
|
||||||
|
exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout }
|
||||||
|
else // automatic build system, don't tag dirty
|
||||||
|
exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout }
|
||||||
|
return stdout.toString().trim().substring(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion androidCompileSdk()
|
||||||
|
buildToolsVersion "$androidBuildVersionTools"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
versionName getMyVersionName()
|
||||||
|
minSdkVersion androidMinSdk()
|
||||||
|
targetSdkVersion androidTargetSdk()
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api project(':play-services-basement')
|
||||||
|
api project(':play-services-tasks')
|
||||||
|
|
||||||
|
implementation "androidx.fragment:fragment:$fragmentVersion"
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
#
|
||||||
|
# Copyright 2013-2016 microG Project Team
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
POM_NAME=Play Services Library Base
|
||||||
|
POM_DESCRIPTION=Base classes used by all Play Services Library modules
|
||||||
|
|
||||||
|
POM_PACKAGING=aar
|
||||||
|
|
||||||
|
POM_URL=https://github.com/microg/android_external_GmsLib
|
||||||
|
|
||||||
|
POM_SCM_URL=https://github.com/microg/android_external_GmsLib
|
||||||
|
POM_SCM_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git
|
||||||
|
POM_SCM_DEV_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git
|
||||||
|
|
||||||
|
POM_LICENCE_NAME=The Apache Software License, Version 2.0
|
||||||
|
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
POM_LICENCE_DIST=repo
|
||||||
|
|
||||||
|
POM_DEVELOPER_ID=mar-v-in
|
||||||
|
POM_DEVELOPER_NAME=Marvin W
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2013-2017 microG Project Team
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<manifest package="org.microg.gms.base"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="com.google.android.gms.version"
|
||||||
|
android:value="@integer/google_play_services_version"/>
|
||||||
|
|
||||||
|
<application>
|
||||||
|
<activity
|
||||||
|
android:name="com.google.android.gms.common.api.GoogleApiActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:theme="@android:style/Theme.Translucent.NoTitleBar"/>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
|
@ -0,0 +1,290 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.common;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
|
||||||
|
import com.google.android.gms.tasks.Task;
|
||||||
|
import com.google.android.gms.tasks.Tasks;
|
||||||
|
|
||||||
|
import org.microg.gms.common.Constants;
|
||||||
|
import org.microg.gms.common.PublicApi;
|
||||||
|
|
||||||
|
import static com.google.android.gms.common.ConnectionResult.INTERNAL_ERROR;
|
||||||
|
import static com.google.android.gms.common.ConnectionResult.INVALID_ACCOUNT;
|
||||||
|
import static com.google.android.gms.common.ConnectionResult.NETWORK_ERROR;
|
||||||
|
import static com.google.android.gms.common.ConnectionResult.RESOLUTION_REQUIRED;
|
||||||
|
import static com.google.android.gms.common.ConnectionResult.SERVICE_DISABLED;
|
||||||
|
import static com.google.android.gms.common.ConnectionResult.SERVICE_INVALID;
|
||||||
|
import static com.google.android.gms.common.ConnectionResult.SERVICE_MISSING;
|
||||||
|
import static com.google.android.gms.common.ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED;
|
||||||
|
import static com.google.android.gms.common.ConnectionResult.SIGN_IN_REQUIRED;
|
||||||
|
import static com.google.android.gms.common.ConnectionResult.SUCCESS;
|
||||||
|
|
||||||
|
@PublicApi
|
||||||
|
public class GoogleApiAvailability {
|
||||||
|
private static final String TAG = "GmsApiAvailability";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package name for Google Play services.
|
||||||
|
*/
|
||||||
|
public static final String GOOGLE_PLAY_SERVICES_PACKAGE = Constants.GMS_PACKAGE_NAME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Google Play services client library version (declared in library's AndroidManifest.xml android:versionCode).
|
||||||
|
*/
|
||||||
|
public static final int GOOGLE_PLAY_SERVICES_VERSION_CODE = Constants.MAX_REFERENCE_VERSION;
|
||||||
|
|
||||||
|
private static GoogleApiAvailability instance;
|
||||||
|
|
||||||
|
private GoogleApiAvailability() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the singleton instance of GoogleApiAvailability.
|
||||||
|
*/
|
||||||
|
public static GoogleApiAvailability getInstance() {
|
||||||
|
if (instance == null) {
|
||||||
|
synchronized (GoogleApiAvailability.class) {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new GoogleApiAvailability();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a dialog to address the provided errorCode. The returned dialog displays a localized
|
||||||
|
* message about the error and upon user confirmation (by tapping on dialog) will direct them
|
||||||
|
* to the Play Store if Google Play services is out of date or missing, or to system settings
|
||||||
|
* if Google Play services is disabled on the device.
|
||||||
|
*
|
||||||
|
* @param activity parent activity for creating the dialog, also used for identifying language to display dialog in.
|
||||||
|
* @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call.
|
||||||
|
* If errorCode is {@link ConnectionResult#SUCCESS} then null is returned.
|
||||||
|
* @param requestCode The requestCode given when calling startActivityForResult.
|
||||||
|
*/
|
||||||
|
public Dialog getErrorDialog(Activity activity, int errorCode, int requestCode) {
|
||||||
|
return getErrorDialog(activity, errorCode, requestCode, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a dialog to address the provided errorCode. The returned dialog displays a localized
|
||||||
|
* message about the error and upon user confirmation (by tapping on dialog) will direct them
|
||||||
|
* to the Play Store if Google Play services is out of date or missing, or to system settings
|
||||||
|
* if Google Play services is disabled on the device.
|
||||||
|
*
|
||||||
|
* @param activity parent activity for creating the dialog, also used for identifying language to display dialog in.
|
||||||
|
* @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call.
|
||||||
|
* If errorCode is {@link ConnectionResult#SUCCESS} then null is returned.
|
||||||
|
* @param requestCode The requestCode given when calling startActivityForResult.
|
||||||
|
* @param cancelListener The {@link DialogInterface.OnCancelListener} to invoke if the dialog is canceled.
|
||||||
|
*/
|
||||||
|
public Dialog getErrorDialog(Activity activity, int errorCode, int requestCode, DialogInterface.OnCancelListener cancelListener) {
|
||||||
|
// TODO
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a PendingIntent to address the provided connection failure.
|
||||||
|
* <p/>
|
||||||
|
* If {@link ConnectionResult#hasResolution()} is true, then {@link ConnectionResult#getResolution()}
|
||||||
|
* will be returned. Otherwise, the returned PendingIntent will direct the user to either the
|
||||||
|
* Play Store if Google Play services is out of date or missing, or system settings if Google
|
||||||
|
* Play services is disabled on the device.
|
||||||
|
*
|
||||||
|
* @param context parent context for creating the PendingIntent.
|
||||||
|
* @param result the connection failure. If successful or the error is not resolvable by the user, null is returned.
|
||||||
|
*/
|
||||||
|
public PendingIntent getErrorResolutionPendingIntent(Context context, ConnectionResult result) {
|
||||||
|
if (result.hasResolution()) {
|
||||||
|
return result.getResolution();
|
||||||
|
}
|
||||||
|
return getErrorResolutionPendingIntent(context, result.getErrorCode(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a PendingIntent to address the provided errorCode. It will direct the user to either
|
||||||
|
* the Play Store if Google Play services is out of date or missing, or system settings if
|
||||||
|
* Google Play services is disabled on the device.
|
||||||
|
*
|
||||||
|
* @param context parent context for creating the PendingIntent.
|
||||||
|
* @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call.
|
||||||
|
* If errorCode is {@link ConnectionResult#SUCCESS} then null is returned.
|
||||||
|
* @param requestCode The requestCode given when calling startActivityForResult.
|
||||||
|
*/
|
||||||
|
public PendingIntent getErrorResolutionPendingIntent(Context context, int errorCode, int requestCode) {
|
||||||
|
// TODO
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a human-readable string of the error code returned from {@link #isGooglePlayServicesAvailable(Context)}.
|
||||||
|
*/
|
||||||
|
public final String getErrorString(int errorCode) {
|
||||||
|
return ConnectionResult.getStatusString(errorCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that Google Play services is installed and enabled on this device, and that the
|
||||||
|
* version installed on this device is no older than the one required by this client.
|
||||||
|
*
|
||||||
|
* @return status code indicating whether there was an error. Can be one of following in
|
||||||
|
* {@link ConnectionResult}: SUCCESS, SERVICE_MISSING, SERVICE_UPDATING,
|
||||||
|
* SERVICE_VERSION_UPDATE_REQUIRED, SERVICE_DISABLED, SERVICE_INVALID
|
||||||
|
*/
|
||||||
|
public int isGooglePlayServicesAvailable(Context context) {
|
||||||
|
Log.d(TAG, "As we can't know right now if the later desired feature is available, " +
|
||||||
|
"we just pretend it to be.");
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether an error can be resolved via user action. If true, proceed by calling
|
||||||
|
* {@link #getErrorDialog(Activity, int, int)} and showing the dialog.
|
||||||
|
*
|
||||||
|
* @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)}, or
|
||||||
|
* returned to your application via {@link OnConnectionFailedListener#onConnectionFailed(ConnectionResult)}
|
||||||
|
* @return true if the error is resolvable with {@link #getErrorDialog(Activity, int, int)}
|
||||||
|
*/
|
||||||
|
public final boolean isUserResolvableError(int errorCode) {
|
||||||
|
switch (errorCode) {
|
||||||
|
case SERVICE_MISSING:
|
||||||
|
case SERVICE_VERSION_UPDATE_REQUIRED:
|
||||||
|
case SERVICE_DISABLED:
|
||||||
|
case SERVICE_INVALID:
|
||||||
|
return true;
|
||||||
|
case SIGN_IN_REQUIRED:
|
||||||
|
case INVALID_ACCOUNT:
|
||||||
|
case RESOLUTION_REQUIRED:
|
||||||
|
case NETWORK_ERROR:
|
||||||
|
case INTERNAL_ERROR:
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to make Google Play services available on this device. If Play Services is already
|
||||||
|
* available, the returned {@link Task} may complete immediately.
|
||||||
|
* <p/>
|
||||||
|
* If it is necessary to display UI in order to complete this request (e.g. sending the user
|
||||||
|
* to the Google Play store) the passed {@link Activity} will be used to display this UI.
|
||||||
|
* <p/>
|
||||||
|
* It is recommended to call this method from {@link Activity#onCreate(Bundle)}.
|
||||||
|
* If the passed {@link Activity} completes before the returned {@link Task} completes, the
|
||||||
|
* Task will fail with a {@link java.util.concurrent.CancellationException}.
|
||||||
|
* <p/>
|
||||||
|
* This method must be called from the main thread.
|
||||||
|
*
|
||||||
|
* @return A {@link Task}. If this Task completes without throwing an exception, Play Services
|
||||||
|
* is available on this device.
|
||||||
|
*/
|
||||||
|
public Task<Void> makeGooglePlayServicesAvailable(Activity activity) {
|
||||||
|
int status = isGooglePlayServicesAvailable(activity);
|
||||||
|
if (status == SUCCESS) {
|
||||||
|
return Tasks.forResult(null);
|
||||||
|
}
|
||||||
|
// TODO
|
||||||
|
return Tasks.forResult(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays a DialogFragment for an error code returned by {@link #isGooglePlayServicesAvailable(Context)}.
|
||||||
|
*
|
||||||
|
* @param activity parent activity for creating the dialog, also used for identifying language to display dialog in.
|
||||||
|
* @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call.
|
||||||
|
* If errorCode is {@link ConnectionResult#SUCCESS} then null is returned.
|
||||||
|
* @param requestCode The requestCode given when calling startActivityForResult.
|
||||||
|
* @return true if the dialog is shown, false otherwise
|
||||||
|
* @throws RuntimeException if API level is below 11 and activity is not a {@link FragmentActivity}.
|
||||||
|
* @see ErrorDialogFragment
|
||||||
|
* @see SupportErrorDialogFragmet
|
||||||
|
*/
|
||||||
|
public boolean showErrorDialogFragment(Activity activity, int errorCode, int requestCode) {
|
||||||
|
return showErrorDialogFragment(activity, errorCode, requestCode, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays a DialogFragment for an error code returned by {@link #isGooglePlayServicesAvailable(Context)}.
|
||||||
|
*
|
||||||
|
* @param activity parent activity for creating the dialog, also used for identifying language to display dialog in.
|
||||||
|
* @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call.
|
||||||
|
* If errorCode is {@link ConnectionResult#SUCCESS} then null is returned.
|
||||||
|
* @param requestCode The requestCode given when calling startActivityForResult.
|
||||||
|
* @param cancelListener The {@link DialogInterface.OnCancelListener} to invoke if the dialog is canceled.
|
||||||
|
* @return true if the dialog is shown, false otherwise
|
||||||
|
* @throws RuntimeException if API level is below 11 and activity is not a {@link FragmentActivity}.
|
||||||
|
* @see ErrorDialogFragment
|
||||||
|
* @see SupportErrorDialogFragmet
|
||||||
|
*/
|
||||||
|
public boolean showErrorDialogFragment(Activity activity, int errorCode, int requestCode, DialogInterface.OnCancelListener cancelListener) {
|
||||||
|
Dialog dialog = getErrorDialog(activity, errorCode, requestCode, cancelListener);
|
||||||
|
if (dialog == null) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// TODO
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays a notification for an error code returned from
|
||||||
|
* {@link #isGooglePlayServicesAvailable(Context)}, if it is resolvable by the user.
|
||||||
|
* <p/>
|
||||||
|
* This method is similar to {@link #getErrorDialog(int, android.app.Activity, int)}, but is
|
||||||
|
* provided for background tasks that cannot or should not display dialogs.
|
||||||
|
*
|
||||||
|
* @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call.
|
||||||
|
* If errorCode is {@link ConnectionResult#SUCCESS} then null is returned.
|
||||||
|
* @param context used for identifying language to display dialog in as well as accessing the
|
||||||
|
* {@link android.app.NotificationManager}.
|
||||||
|
*/
|
||||||
|
public void showErrorNotification(Context context, int errorCode) {
|
||||||
|
if (errorCode == RESOLUTION_REQUIRED) {
|
||||||
|
Log.e(TAG, "showErrorNotification(context, errorCode) is called for RESOLUTION_REQUIRED when showErrorNotification(context, result) should be called");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isUserResolvableError(errorCode)) {
|
||||||
|
GooglePlayServicesUtil.showErrorNotification(errorCode, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays a notification for a connection failure, if it is resolvable by the user.
|
||||||
|
*
|
||||||
|
* @param context The calling context used to display the notification.
|
||||||
|
* @param result The connection failure. If successful or the error is not resolvable by the
|
||||||
|
* user, no notification is shown.
|
||||||
|
*/
|
||||||
|
public void showErrorNotification(Context context, ConnectionResult result) {
|
||||||
|
PendingIntent pendingIntent = getErrorResolutionPendingIntent(context, result);
|
||||||
|
if (pendingIntent != null) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.common;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public interface GooglePlayServicesClient {
|
||||||
|
void connect();
|
||||||
|
|
||||||
|
void disconnect();
|
||||||
|
|
||||||
|
boolean isConnected();
|
||||||
|
|
||||||
|
boolean isConnecting();
|
||||||
|
|
||||||
|
void registerConnectionCallbacks(ConnectionCallbacks listener);
|
||||||
|
|
||||||
|
boolean isConnectionCallbacksRegistered(ConnectionCallbacks listener);
|
||||||
|
|
||||||
|
void unregisterConnectionCallbacks(ConnectionCallbacks listener);
|
||||||
|
|
||||||
|
void registerConnectionFailedListener(OnConnectionFailedListener listener);
|
||||||
|
|
||||||
|
boolean isConnectionFailedListenerRegistered(OnConnectionFailedListener listener);
|
||||||
|
|
||||||
|
void unregisterConnectionFailedListener(OnConnectionFailedListener listener);
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
interface OnConnectionFailedListener {
|
||||||
|
|
||||||
|
void onConnectionFailed(ConnectionResult result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
interface ConnectionCallbacks {
|
||||||
|
|
||||||
|
void onConnected(Bundle connectionHint);
|
||||||
|
|
||||||
|
void onDisconnected();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,235 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.common;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.app.Fragment;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.microg.gms.common.Constants;
|
||||||
|
import org.microg.gms.common.PublicApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for verifying that the Google Play services APK is available and up-to-date on
|
||||||
|
* this device. The same checks are performed if one uses {@link AdvertisingIdClient} or
|
||||||
|
* {@link GoogleAuthUtil} to connect to the service.
|
||||||
|
* <p/>
|
||||||
|
* TODO: methods :)
|
||||||
|
*/
|
||||||
|
@PublicApi
|
||||||
|
public class GooglePlayServicesUtil {
|
||||||
|
private static final String TAG = "GooglePlayServicesUtil";
|
||||||
|
|
||||||
|
public static final String GMS_ERROR_DIALOG = "GooglePlayServicesErrorDialog";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package name for Google Play services.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static final String GOOGLE_PLAY_SERVICES_PACKAGE = Constants.GMS_PACKAGE_NAME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Google Play services client library version (declared in library's AndroidManifest.xml android:versionCode).
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static final int GOOGLE_PLAY_SERVICES_VERSION_CODE = Constants.MAX_REFERENCE_VERSION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package name for Google Play Store.
|
||||||
|
*/
|
||||||
|
public static final String GOOGLE_PLAY_STORE_PACKAGE = "com.android.vending";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a dialog to address the provided errorCode. The returned dialog displays a localized
|
||||||
|
* message about the error and upon user confirmation (by tapping on dialog) will direct them
|
||||||
|
* to the Play Store if Google Play services is out of date or missing, or to system settings
|
||||||
|
* if Google Play services is disabled on the device.
|
||||||
|
*
|
||||||
|
* @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call.
|
||||||
|
* If errorCode is {@link ConnectionResult#SUCCESS} then null is returned.
|
||||||
|
* @param activity parent activity for creating the dialog, also used for identifying
|
||||||
|
* language to display dialog in.
|
||||||
|
* @param requestCode The requestCode given when calling startActivityForResult.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static Dialog getErrorDialog(int errorCode, Activity activity, int requestCode) {
|
||||||
|
return getErrorDialog(errorCode, activity, requestCode, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a dialog to address the provided errorCode. The returned dialog displays a localized
|
||||||
|
* message about the error and upon user confirmation (by tapping on dialog) will direct them
|
||||||
|
* to the Play Store if Google Play services is out of date or missing, or to system settings
|
||||||
|
* if Google Play services is disabled on the device.
|
||||||
|
*
|
||||||
|
* @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call.
|
||||||
|
* If errorCode is {@link ConnectionResult#SUCCESS} then null is returned.
|
||||||
|
* @param activity parent activity for creating the dialog, also used for identifying
|
||||||
|
* language to display dialog in.
|
||||||
|
* @param requestCode The requestCode given when calling startActivityForResult.
|
||||||
|
* @param cancelListener The {@link DialogInterface.OnCancelListener} to invoke if the dialog
|
||||||
|
* is canceled.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static Dialog getErrorDialog(int errorCode, Activity activity, int requestCode, DialogInterface.OnCancelListener cancelListener) {
|
||||||
|
return GoogleApiAvailability.getInstance().getErrorDialog(activity, errorCode, requestCode, cancelListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a PendingIntent to address the provided errorCode. It will direct them to one of the
|
||||||
|
* following places to either the Play Store if Google Play services is out of date or missing,
|
||||||
|
* or system settings if Google Play services is disabled on the device.
|
||||||
|
*
|
||||||
|
* @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call.
|
||||||
|
* If errorCode is {@link ConnectionResult#SUCCESS} then null is returned.
|
||||||
|
* @param activity parent context for creating the PendingIntent.
|
||||||
|
* @param requestCode The requestCode given when calling startActivityForResult.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static PendingIntent getErrorPendingIntent(int errorCode, Activity activity,
|
||||||
|
int requestCode) {
|
||||||
|
return null; // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a human-readable string of the error code returned from {@link #isGooglePlayServicesAvailable(Context)}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static String getErrorString(int errorCode) {
|
||||||
|
return null; // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the open source software license information for the Google Play services
|
||||||
|
* application, or null if Google Play services is not available on this device.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static String getOpenSourceSoftwareLicenseInfo(Context context) {
|
||||||
|
return null; // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This gets the Context object of the Buddy APK. This loads the Buddy APK code from the Buddy
|
||||||
|
* APK into memory. This returned context can be used to create classes and obtain resources
|
||||||
|
* defined in the Buddy APK.
|
||||||
|
*
|
||||||
|
* @return The Context object of the Buddy APK or null if the Buddy APK is not installed on the device.
|
||||||
|
*/
|
||||||
|
public static Context getRemoteContext(Context context) {
|
||||||
|
return null; // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This gets the Resources object of the Buddy APK.
|
||||||
|
*
|
||||||
|
* @return The Resources object of the Buddy APK or null if the Buddy APK is not installed on the device.
|
||||||
|
*/
|
||||||
|
public static Resources getRemoteResources(Context context) {
|
||||||
|
return null; // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that Google Play services is installed and enabled on this device, and that the
|
||||||
|
* version installed on this device is no older than the one required by this client.
|
||||||
|
*
|
||||||
|
* @return status code indicating whether there was an error. Can be one of following in
|
||||||
|
* {@link ConnectionResult}: SUCCESS, SERVICE_MISSING, SERVICE_VERSION_UPDATE_REQUIRED,
|
||||||
|
* SERVICE_DISABLED, SERVICE_INVALID
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static int isGooglePlayServicesAvailable(Context context) {
|
||||||
|
Log.d(TAG, "As we can't know right now if the later desired feature is available, " +
|
||||||
|
"we just pretend it to be.");
|
||||||
|
return ConnectionResult.SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public static boolean isGoogleSignedUid(PackageManager packageManager, int uid) {
|
||||||
|
return false; // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether an error is user-recoverable. If true, proceed by calling
|
||||||
|
* {@link #getErrorDialog(int, Activity, int)} and showing the dialog.
|
||||||
|
*
|
||||||
|
* @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)}, or
|
||||||
|
* returned to your application via {@link com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener#onConnectionFailed(ConnectionResult)}
|
||||||
|
* @return true if the error is recoverable with {@link #getErrorDialog(int, Activity, int)}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static boolean isUserRecoverableError(int errorCode) {
|
||||||
|
return false; // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a DialogFragment for an error code returned by {@link #isGooglePlayServicesAvailable(Context)}.
|
||||||
|
*
|
||||||
|
* @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call.
|
||||||
|
* If errorCode is {@link ConnectionResult#SUCCESS} then null is returned.
|
||||||
|
* @param activity parent activity for creating the dialog, also used for identifying
|
||||||
|
* language to display dialog in.
|
||||||
|
* @param requestCode The requestCode given when calling startActivityForResult.
|
||||||
|
* @return true if the dialog is shown, false otherwise
|
||||||
|
* @throws RuntimeException if API level is below 11 and activity is not a {@link android.support.v4.app.FragmentActivity}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static boolean showErrorDialogFragment(int errorCode, Activity activity, int requestCode) {
|
||||||
|
return showErrorDialogFragment(errorCode, activity, requestCode, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public static boolean showErrorDialogFragment(int errorCode, Activity activity, Fragment fragment, int requestCode, DialogInterface.OnCancelListener cancelListener) {
|
||||||
|
return false; // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call.
|
||||||
|
* If errorCode is {@link ConnectionResult#SUCCESS} then null is returned.
|
||||||
|
* @param activity parent activity for creating the dialog, also used for identifying
|
||||||
|
* language to display dialog in.
|
||||||
|
* @param requestCode The requestCode given when calling startActivityForResult.
|
||||||
|
* @param cancelListener The {@link DialogInterface.OnCancelListener} to invoke if the dialog
|
||||||
|
* is canceled.
|
||||||
|
* @return true if the dialog is shown, false otherwise.
|
||||||
|
* @throws RuntimeException if API level is below 11 and activity is not a {@link android.support.v4.app.FragmentActivity}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static boolean showErrorDialogFragment(int errorCode, Activity activity, int requestCode, DialogInterface.OnCancelListener cancelListener) {
|
||||||
|
return showErrorDialogFragment(errorCode, activity, null, requestCode, cancelListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays a notification relevant to the provided error code. This method is similar to
|
||||||
|
* {@link #getErrorDialog(int, android.app.Activity, int)}, but is provided for background
|
||||||
|
* tasks that cannot or shouldn't display dialogs.
|
||||||
|
*
|
||||||
|
* @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call.
|
||||||
|
* If errorCode is {@link ConnectionResult#SUCCESS} then null is returned.
|
||||||
|
* @param context used for identifying language to display dialog in as well as accessing the
|
||||||
|
* {@link android.app.NotificationManager}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static void showErrorNotification(int errorCode, Context context) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.common.api;
|
||||||
|
|
||||||
|
import org.microg.safeparcel.AutoSafeParcelable;
|
||||||
|
|
||||||
|
public class AccountInfo extends AutoSafeParcelable {
|
||||||
|
public static final Creator<AccountInfo> CREATOR = new AutoCreator<AccountInfo>(AccountInfo.class);
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.common.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.
|
||||||
|
* <p/>
|
||||||
|
* 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.
|
||||||
|
* <p/>
|
||||||
|
* See {@link GoogleApiClient.Builder} for usage examples.
|
||||||
|
*/
|
||||||
|
@PublicApi
|
||||||
|
public final class Api<O extends Api.ApiOptions> {
|
||||||
|
|
||||||
|
private final ApiBuilder<O> builder;
|
||||||
|
|
||||||
|
@PublicApi(exclude = true)
|
||||||
|
public Api(ApiBuilder<O> builder) {
|
||||||
|
this.builder = builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PublicApi(exclude = true)
|
||||||
|
public ApiBuilder<O> 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 {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,495 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.common.api;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
|
||||||
|
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.
|
||||||
|
* <p/>
|
||||||
|
* 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.
|
||||||
|
* <p/>
|
||||||
|
* 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.
|
||||||
|
* <p/>
|
||||||
|
* When your app is done using this client, call {@link #disconnect()}, even if the async result
|
||||||
|
* from {@link #connect()} has not yet been delivered.
|
||||||
|
* <p/>
|
||||||
|
* You should instantiate a client object in your Activity's {@link Activity#onCreate(Bundle)}
|
||||||
|
* method and then call {@link #connect()} in {@link Activity#onStart()} and {@link #disconnect()}
|
||||||
|
* in {@link Activity#onStop()}, regardless of the state.
|
||||||
|
*/
|
||||||
|
@PublicApi
|
||||||
|
public interface GoogleApiClient {
|
||||||
|
/**
|
||||||
|
* Connects the client to Google Play services. Blocks until the connection either succeeds or
|
||||||
|
* fails. This is not allowed on the UI thread.
|
||||||
|
*
|
||||||
|
* @return the result of the connection
|
||||||
|
*/
|
||||||
|
ConnectionResult blockingConnect();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connects the client to Google Play services. Blocks until the connection is set or failed or
|
||||||
|
* has timed out. This is not allowed on the UI thread.
|
||||||
|
*
|
||||||
|
* @param timeout the maximum time to wait
|
||||||
|
* @param unit the time unit of the {@code timeout} argument
|
||||||
|
* @return the result of the connection
|
||||||
|
*/
|
||||||
|
ConnectionResult blockingConnect(long timeout, TimeUnit unit);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the account selected by the user and reconnects the client asking the user to pick an
|
||||||
|
* account again if {@link Builder#useDefaultAccount()} was set.
|
||||||
|
*
|
||||||
|
* @return the pending result is fired once the default account has been cleared, but before
|
||||||
|
* the client is reconnected - for that {@link ConnectionCallbacks} can be used.
|
||||||
|
*/
|
||||||
|
PendingResult<Status> clearDefaultAccountAndReconnect();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connects the client to Google Play services. This method returns immediately, and connects
|
||||||
|
* to the service in the background. If the connection is successful,
|
||||||
|
* {@link ConnectionCallbacks#onConnected(Bundle)} is called and enqueued items are executed.
|
||||||
|
* On a failure, {@link OnConnectionFailedListener#onConnectionFailed(ConnectionResult)} is
|
||||||
|
* called.
|
||||||
|
*/
|
||||||
|
void connect();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the connection to Google Play services. No calls can be made using this client after
|
||||||
|
* calling this method. Any method calls that haven't executed yet will be canceled. That is
|
||||||
|
* {@link ResultCallback#onResult(Result)} won't be called, if connection to the service hasn't
|
||||||
|
* been established yet all calls already made will be canceled.
|
||||||
|
*
|
||||||
|
* @see #connect()
|
||||||
|
*/
|
||||||
|
void disconnect();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the client is currently connected to the service, so that requests to other
|
||||||
|
* methods will succeed. Applications should guard client actions caused by the user with a
|
||||||
|
* call to this method.
|
||||||
|
*
|
||||||
|
* @return {@code true} if the client is connected to the service.
|
||||||
|
*/
|
||||||
|
boolean isConnected();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the client is attempting to connect to the service.
|
||||||
|
*
|
||||||
|
* @return {@code true} if the client is attempting to connect to the service.
|
||||||
|
*/
|
||||||
|
boolean isConnecting();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if the specified listener is currently registered to receive connection
|
||||||
|
* events.
|
||||||
|
*
|
||||||
|
* @param listener The listener to check for.
|
||||||
|
* @return {@code true} if the specified listener is currently registered to receive connection
|
||||||
|
* events.
|
||||||
|
* @see #registerConnectionCallbacks(ConnectionCallbacks)
|
||||||
|
* @see #unregisterConnectionCallbacks(ConnectionCallbacks)
|
||||||
|
*/
|
||||||
|
boolean isConnectionCallbacksRegistered(ConnectionCallbacks listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if the specified listener is currently registered to receive connection
|
||||||
|
* failed events.
|
||||||
|
*
|
||||||
|
* @param listener The listener to check for.
|
||||||
|
* @return {@code true} if the specified listener is currently registered to receive connection
|
||||||
|
* failed events.
|
||||||
|
* @see #registerConnectionFailedListener(OnConnectionFailedListener)
|
||||||
|
* @see #unregisterConnectionFailedListener(OnConnectionFailedListener)
|
||||||
|
*/
|
||||||
|
public boolean isConnectionFailedListenerRegistered(OnConnectionFailedListener listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the current connection to Google Play services and creates a new connection.
|
||||||
|
* <p/>
|
||||||
|
* This method closes the current connection then returns immediately and reconnects to the
|
||||||
|
* service in the background.
|
||||||
|
* <p/>
|
||||||
|
* After calling this method, your application will receive
|
||||||
|
* {@link ConnectionCallbacks#onConnected(Bundle)} if the connection is successful, or
|
||||||
|
* {@link OnConnectionFailedListener#onConnectionFailed(ConnectionResult)} if the connection
|
||||||
|
* failed.
|
||||||
|
*
|
||||||
|
* @see #connect()
|
||||||
|
* @see #disconnect()
|
||||||
|
*/
|
||||||
|
void reconnect();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a listener to receive connection events from this {@link GoogleApiClient}. If the
|
||||||
|
* service is already connected, the listener's {@link ConnectionCallbacks#onConnected(Bundle)}
|
||||||
|
* method will be called immediately. Applications should balance calls to this method with
|
||||||
|
* calls to {@link #unregisterConnectionCallbacks(ConnectionCallbacks)} to avoid leaking
|
||||||
|
* resources.
|
||||||
|
* <p/>
|
||||||
|
* 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.
|
||||||
|
* <p/>
|
||||||
|
* Note that the order of messages received here may not be stable, so clients should not rely
|
||||||
|
* on the order that multiple listeners receive events in.
|
||||||
|
*
|
||||||
|
* @param listener the listener where the results of the asynchronous {@link #connect()} call
|
||||||
|
* are delivered.
|
||||||
|
*/
|
||||||
|
void registerConnectionCallbacks(ConnectionCallbacks listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a listener to receive connection failed events from this {@link GoogleApiClient}.
|
||||||
|
* Unlike {@link #registerConnectionCallbacks(ConnectionCallbacks)}, if the service is not
|
||||||
|
* already connected, the listener's
|
||||||
|
* {@link OnConnectionFailedListener#onConnectionFailed(ConnectionResult)} method will not be
|
||||||
|
* called immediately. Applications should balance calls to this method with calls to
|
||||||
|
* {@link #unregisterConnectionFailedListener(OnConnectionFailedListener)} to avoid leaking
|
||||||
|
* resources.
|
||||||
|
* <p/>
|
||||||
|
* If the specified listener is already registered to receive connection failed events, this
|
||||||
|
* method will not add a duplicate entry for the same listener.
|
||||||
|
* <p/>
|
||||||
|
* 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.).
|
||||||
|
* <p/>
|
||||||
|
* This method must be called from the main thread.
|
||||||
|
*
|
||||||
|
* @param lifecycleActivity the activity managing the client's lifecycle.
|
||||||
|
* @throws IllegalStateException if called from outside of the main thread.
|
||||||
|
* @see Builder#enableAutoManage(FragmentActivity, int, OnConnectionFailedListener)
|
||||||
|
*/
|
||||||
|
void stopAutoManager(FragmentActivity lifecycleActivity) throws IllegalStateException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a connection listener from this {@link GoogleApiClient}. Note that removing a
|
||||||
|
* listener does not generate any callbacks.
|
||||||
|
* <p/>
|
||||||
|
* If the specified listener is not currently registered to receive connection events, this
|
||||||
|
* method will have no effect.
|
||||||
|
*
|
||||||
|
* @param listener the listener to unregister.
|
||||||
|
*/
|
||||||
|
void unregisterConnectionCallbacks(ConnectionCallbacks listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a connection failed listener from the {@link GoogleApiClient}. Note that removing a
|
||||||
|
* listener does not generate any callbacks.
|
||||||
|
* <p/>
|
||||||
|
* If the specified listener is not currently registered to receive connection failed events,
|
||||||
|
* this method will have no effect.
|
||||||
|
*
|
||||||
|
* @param listener the listener to unregister.
|
||||||
|
*/
|
||||||
|
void unregisterConnectionFailedListener(OnConnectionFailedListener listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder to configure a {@link GoogleApiClient}.
|
||||||
|
*/
|
||||||
|
@PublicApi
|
||||||
|
class Builder {
|
||||||
|
private final Context context;
|
||||||
|
private final Map<Api, Api.ApiOptions> apis = new HashMap<Api, Api.ApiOptions>();
|
||||||
|
private final Set<ConnectionCallbacks> connectionCallbacks = new HashSet<ConnectionCallbacks>();
|
||||||
|
private final Set<OnConnectionFailedListener> connectionFailedListeners = new HashSet<OnConnectionFailedListener>();
|
||||||
|
private final Set<String> scopes = new HashSet<String>();
|
||||||
|
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 <O extends Api.ApiOptions.HasOptions> Builder addApi(Api<O> 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<? extends Api.ApiOptions.NotRequiredOptions> 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.
|
||||||
|
* <p/>
|
||||||
|
* If the specified listener is already registered to receive connection events, this
|
||||||
|
* method will not add a duplicate entry for the same listener.
|
||||||
|
* <p/>
|
||||||
|
* 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.
|
||||||
|
* <p/>
|
||||||
|
* If the specified listener is already registered to receive connection failed events,
|
||||||
|
* this method will not add a duplicate entry for the same listener.
|
||||||
|
* <p/>
|
||||||
|
* Note that the order of messages received here may not be stable, so clients should not
|
||||||
|
* rely on the order that multiple listeners receive events in.
|
||||||
|
*
|
||||||
|
* @param listener the listener where the results of the asynchronous {@link #connect()}
|
||||||
|
* call are delivered.
|
||||||
|
*/
|
||||||
|
public Builder addOnConnectionFailedListener(OnConnectionFailedListener listener) {
|
||||||
|
connectionFailedListeners.add(listener);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the OAuth 2.0 scopes requested by your app. See
|
||||||
|
* {@link com.google.android.gms.common.Scopes} for more information.
|
||||||
|
*
|
||||||
|
* @param scope The OAuth 2.0 scopes requested by your app.
|
||||||
|
* @see com.google.android.gms.common.Scopes
|
||||||
|
*/
|
||||||
|
public Builder addScope(Scope scope) {
|
||||||
|
scopes.add(scope.getScopeUri());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a new {@link GoogleApiClient} object for communicating with the Google APIs.
|
||||||
|
*
|
||||||
|
* @return The {@link GoogleApiClient} object.
|
||||||
|
*/
|
||||||
|
public GoogleApiClient build() {
|
||||||
|
return new GoogleApiClientImpl(context, looper, getAccountInfo(), apis,
|
||||||
|
connectionCallbacks, connectionFailedListeners, clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AccountInfo getAccountInfo() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder enableAutoManage(FragmentActivity fragmentActivity, int cliendId,
|
||||||
|
OnConnectionFailedListener unresolvedConnectionFailedListener)
|
||||||
|
throws NullPointerException, IllegalArgumentException, IllegalStateException {
|
||||||
|
this.fragmentActivity = fragmentActivity;
|
||||||
|
this.clientId = cliendId;
|
||||||
|
this.unresolvedConnectionFailedListener = unresolvedConnectionFailedListener;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify an account name on the device that should be used. If this is never called, the
|
||||||
|
* client will use the current default account for Google Play services for this
|
||||||
|
* application.
|
||||||
|
*
|
||||||
|
* @param accountName The account name on the device that should be used by
|
||||||
|
* {@link GoogleApiClient}.
|
||||||
|
*/
|
||||||
|
public Builder setAccountName(String accountName) {
|
||||||
|
this.accountName = accountName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the part of the screen at which games service popups (for example,
|
||||||
|
* "welcome back" or "achievement unlocked" popups) will be displayed using gravity.
|
||||||
|
*
|
||||||
|
* @param gravityForPopups The gravity which controls the placement of games service popups.
|
||||||
|
*/
|
||||||
|
public Builder setGravityForPopups(int gravityForPopups) {
|
||||||
|
this.gravityForPopups = gravityForPopups;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a {@link Handler} to indicate which thread to use when invoking callbacks. Will not
|
||||||
|
* be used directly to handle callbacks. If this is not called then the application's main
|
||||||
|
* thread will be used.
|
||||||
|
*/
|
||||||
|
public Builder setHandler(Handler handler) {
|
||||||
|
this.looper = handler.getLooper();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link View} to use as a content view for popups.
|
||||||
|
*
|
||||||
|
* @param viewForPopups The view to use as a content view for popups. View cannot be null.
|
||||||
|
*/
|
||||||
|
public Builder setViewForPopups(View viewForPopups) {
|
||||||
|
this.viewForPopups = viewForPopups;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify that the default account should be used when connecting to services.
|
||||||
|
*/
|
||||||
|
public Builder useDefaultAccount() {
|
||||||
|
this.accountName = AuthConstants.DEFAULT_ACCOUNT;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides callbacks that are called when the client is connected or disconnected from the
|
||||||
|
* service. Most applications implement {@link #onConnected(Bundle)} to start making requests.
|
||||||
|
*/
|
||||||
|
@PublicApi
|
||||||
|
interface ConnectionCallbacks {
|
||||||
|
/**
|
||||||
|
* A suspension cause informing that the service has been killed.
|
||||||
|
*/
|
||||||
|
int CAUSE_SERVICE_DISCONNECTED = 1;
|
||||||
|
/**
|
||||||
|
* A suspension cause informing you that a peer device connection was lost.
|
||||||
|
*/
|
||||||
|
int CAUSE_NETWORK_LOST = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* After calling {@link #connect()}, this method will be invoked asynchronously when the
|
||||||
|
* connect request has successfully completed. After this callback, the application can
|
||||||
|
* make requests on other methods provided by the client and expect that no user
|
||||||
|
* intervention is required to call methods that use account and scopes provided to the
|
||||||
|
* client constructor.
|
||||||
|
* <p/>
|
||||||
|
* Note that the contents of the {@code connectionHint} Bundle are defined by the specific
|
||||||
|
* services. Please see the documentation of the specific implementation of
|
||||||
|
* {@link GoogleApiClient} you are using for more information.
|
||||||
|
*
|
||||||
|
* @param connectionHint Bundle of data provided to clients by Google Play services. May
|
||||||
|
* be null if no content is provided by the service.
|
||||||
|
*/
|
||||||
|
void onConnected(Bundle connectionHint);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the client is temporarily in a disconnected state. This can happen if there
|
||||||
|
* is a problem with the remote service (e.g. a crash or resource problem causes it to be
|
||||||
|
* killed by the system). When called, all requests have been canceled and no outstanding
|
||||||
|
* listeners will be executed. GoogleApiClient will automatically attempt to restore the
|
||||||
|
* connection. Applications should disable UI components that require the service, and wait
|
||||||
|
* for a call to {@link #onConnected(Bundle)} to re-enable them.
|
||||||
|
*
|
||||||
|
* @param cause The reason for the disconnection. Defined by constants {@code CAUSE_*}.
|
||||||
|
*/
|
||||||
|
void onConnectionSuspended(int cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides callbacks for scenarios that result in a failed attempt to connect the client to
|
||||||
|
* the service. See {@link ConnectionResult} for a list of error codes and suggestions for
|
||||||
|
* resolution.
|
||||||
|
*/
|
||||||
|
@PublicApi
|
||||||
|
interface OnConnectionFailedListener {
|
||||||
|
/**
|
||||||
|
* Called when there was an error connecting the client to the service.
|
||||||
|
*
|
||||||
|
* @param result A {@link ConnectionResult} that can be used for resolving the error, and
|
||||||
|
* deciding what sort of error occurred. To resolve the error, the resolution
|
||||||
|
* must be started from an activity with a non-negative {@code requestCode}
|
||||||
|
* passed to {@link ConnectionResult#startResolutionForResult(Activity, int)}.
|
||||||
|
* Applications should implement {@link Activity#onActivityResult} in their
|
||||||
|
* Activity to call {@link #connect()} again if the user has resolved the
|
||||||
|
* issue (resultCode is {@link Activity#RESULT_OK}).
|
||||||
|
*/
|
||||||
|
void onConnectionFailed(ConnectionResult result);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.common.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.
|
||||||
|
* <p/>
|
||||||
|
* <ul>
|
||||||
|
* <li>via blocking calls to {@link #await()}, or {@link #await(long, TimeUnit)}, or</li>
|
||||||
|
* <li>via a callback by passing in an object implementing interface {@link ResultCallback} to
|
||||||
|
* {@link #setResultCallback(ResultCallback)}.</li>
|
||||||
|
* </ul>
|
||||||
|
* 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.
|
||||||
|
* <p/>
|
||||||
|
* TODO: Docs
|
||||||
|
*/
|
||||||
|
public interface PendingResult<R extends Result> {
|
||||||
|
/**
|
||||||
|
* 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<R> callback, long time, TimeUnit unit);
|
||||||
|
|
||||||
|
public void setResultCallback(ResultCallback<R> callback);
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.common.data;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.Releasable;
|
||||||
|
|
||||||
|
import org.microg.gms.common.PublicApi;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
*/
|
||||||
|
@PublicApi
|
||||||
|
public abstract class DataBuffer<T> implements Releasable, Iterable<T> {
|
||||||
|
|
||||||
|
private DataHolder dataHolder;
|
||||||
|
|
||||||
|
@PublicApi(exclude = true)
|
||||||
|
public DataBuffer(DataHolder dataHolder) {
|
||||||
|
this.dataHolder = dataHolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #release()} instead
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public final void close() {
|
||||||
|
release();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the item at the specified position. Note that the objects returned from subsequent
|
||||||
|
* invocations of this method for the same position may not be identical objects, but will be
|
||||||
|
* equal in value.
|
||||||
|
*
|
||||||
|
* @param position The position of the item to retrieve.
|
||||||
|
* @return the item at {@code position} in this buffer.
|
||||||
|
*/
|
||||||
|
public abstract T get(int position);
|
||||||
|
|
||||||
|
public int getCount() {
|
||||||
|
return dataHolder == null ? 0 : dataHolder.getCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated {@link #release()} is idempotent, and so is safe to call multiple times
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public boolean isClosed() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<T> iterator() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases resources used by the buffer. This method is idempotent.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void release() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In order to use this one should correctly override setDataRow(int) in his DataBufferRef
|
||||||
|
* implementation. Be careful: there will be single DataBufferRef while iterating.
|
||||||
|
* If you are not sure - DO NOT USE this iterator.
|
||||||
|
*/
|
||||||
|
public Iterator<T> singleRefIterator() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.common;
|
||||||
|
|
||||||
|
import org.microg.gms.common.api.ApiConnection;
|
||||||
|
|
||||||
|
public class DummyApiConnection implements ApiConnection {
|
||||||
|
private boolean connected = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void connect() {
|
||||||
|
connected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disconnect() {
|
||||||
|
connected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isConnected() {
|
||||||
|
return connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isConnecting() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.common;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.GooglePlayServicesClient;
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
|
||||||
|
public final class ForwardConnectionCallbacks implements GoogleApiClient.ConnectionCallbacks {
|
||||||
|
private final GooglePlayServicesClient.ConnectionCallbacks callbacks;
|
||||||
|
|
||||||
|
public ForwardConnectionCallbacks(GooglePlayServicesClient.ConnectionCallbacks callbacks) {
|
||||||
|
this.callbacks = callbacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return o instanceof ForwardConnectionCallbacks &&
|
||||||
|
callbacks.equals(((ForwardConnectionCallbacks) o).callbacks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return callbacks.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConnected(Bundle connectionHint) {
|
||||||
|
callbacks.onConnected(connectionHint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConnectionSuspended(int cause) {
|
||||||
|
callbacks.onDisconnected();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.common;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.ConnectionResult;
|
||||||
|
import com.google.android.gms.common.GooglePlayServicesClient;
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
|
||||||
|
public final class ForwardConnectionFailedListener
|
||||||
|
implements GoogleApiClient.OnConnectionFailedListener {
|
||||||
|
private final GooglePlayServicesClient.OnConnectionFailedListener listener;
|
||||||
|
|
||||||
|
public ForwardConnectionFailedListener(
|
||||||
|
GooglePlayServicesClient.OnConnectionFailedListener listener) {
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return o instanceof ForwardConnectionFailedListener &&
|
||||||
|
listener.equals(((ForwardConnectionFailedListener) o).listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return listener.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConnectionFailed(ConnectionResult result) {
|
||||||
|
listener.onConnectionFailed(result);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,184 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.common;
|
||||||
|
|
||||||
|
import android.accounts.Account;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.ServiceConnection;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.IInterface;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import 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;
|
||||||
|
|
||||||
|
import org.microg.gms.common.api.ApiConnection;
|
||||||
|
|
||||||
|
public abstract class GmsClient<I extends IInterface> implements ApiConnection {
|
||||||
|
private static final String TAG = "GmsClient";
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
protected final GoogleApiClient.ConnectionCallbacks callbacks;
|
||||||
|
protected final GoogleApiClient.OnConnectionFailedListener connectionFailedListener;
|
||||||
|
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, String actionString) {
|
||||||
|
this.context = context;
|
||||||
|
this.callbacks = callbacks;
|
||||||
|
this.connectionFailedListener = connectionFailedListener;
|
||||||
|
this.actionString = actionString;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void connect() {
|
||||||
|
Log.d(TAG, "connect()");
|
||||||
|
if (state == ConnectionState.CONNECTED || state == ConnectionState.CONNECTING) {
|
||||||
|
Log.d(TAG, "Already connected/connecting - nothing to do");
|
||||||
|
}
|
||||||
|
state = ConnectionState.CONNECTING;
|
||||||
|
if (serviceConnection != null) {
|
||||||
|
MultiConnectionKeeper.getInstance(context).unbind(actionString, serviceConnection);
|
||||||
|
}
|
||||||
|
serviceConnection = new GmsServiceConnection();
|
||||||
|
if (!MultiConnectionKeeper.getInstance(context).bind(actionString, serviceConnection)) {
|
||||||
|
state = ConnectionState.ERROR;
|
||||||
|
handleConnectionFailed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleConnectionFailed() {
|
||||||
|
connectionFailedListener.onConnectionFailed(new ConnectionResult(ConnectionResult
|
||||||
|
.API_UNAVAILABLE, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void disconnect() {
|
||||||
|
Log.d(TAG, "disconnect()");
|
||||||
|
if (state == ConnectionState.DISCONNECTING) return;
|
||||||
|
if (state == ConnectionState.CONNECTING) {
|
||||||
|
state = ConnectionState.DISCONNECTING;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
serviceInterface = null;
|
||||||
|
if (serviceConnection != null) {
|
||||||
|
MultiConnectionKeeper.getInstance(context).unbind(actionString, serviceConnection);
|
||||||
|
serviceConnection = null;
|
||||||
|
}
|
||||||
|
state = ConnectionState.NOT_CONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean isConnected() {
|
||||||
|
return state == ConnectionState.CONNECTED || state == ConnectionState.PSEUDO_CONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean isConnecting() {
|
||||||
|
return state == ConnectionState.CONNECTING;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean hasError() {
|
||||||
|
return state == ConnectionState.ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Context getContext() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized I getServiceInterface() {
|
||||||
|
if (isConnecting()) {
|
||||||
|
// TODO: wait for connection to be established and return afterwards.
|
||||||
|
throw new IllegalStateException("Waiting for connection");
|
||||||
|
} else if (!isConnected()) {
|
||||||
|
throw new IllegalStateException("interface only available once connected!");
|
||||||
|
}
|
||||||
|
return serviceInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected enum ConnectionState {
|
||||||
|
NOT_CONNECTED, CONNECTING, CONNECTED, DISCONNECTING, ERROR, PSEUDO_CONNECTED
|
||||||
|
}
|
||||||
|
|
||||||
|
private class GmsServiceConnection implements ServiceConnection {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
|
||||||
|
try {
|
||||||
|
Log.d(TAG, "ServiceConnection : onServiceConnected(" + componentName + ")");
|
||||||
|
onConnectedToBroker(IGmsServiceBroker.Stub.asInterface(iBinder),
|
||||||
|
new GmsCallbacks());
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceDisconnected(ComponentName componentName) {
|
||||||
|
synchronized (GmsClient.this) {
|
||||||
|
state = ConnectionState.NOT_CONNECTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GmsCallbacks extends IGmsCallbacks.Stub {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPostInitComplete(int statusCode, IBinder binder, Bundle params)
|
||||||
|
throws RemoteException {
|
||||||
|
synchronized (GmsClient.this) {
|
||||||
|
if (state == ConnectionState.DISCONNECTING) {
|
||||||
|
state = ConnectionState.CONNECTED;
|
||||||
|
disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state = ConnectionState.CONNECTED;
|
||||||
|
serviceInterface = interfaceFromBinder(binder);
|
||||||
|
}
|
||||||
|
Log.d(TAG, "GmsCallbacks : onPostInitComplete(" + serviceInterface + ")");
|
||||||
|
callbacks.onConnected(params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.common;
|
||||||
|
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.Api;
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
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<C extends ApiConnection, R extends Result> {
|
||||||
|
private static final String TAG = "GmsConnector";
|
||||||
|
|
||||||
|
private final GoogleApiClientImpl apiClient;
|
||||||
|
private final Api api;
|
||||||
|
private final Callback<C, R> callback;
|
||||||
|
|
||||||
|
public GmsConnector(GoogleApiClient apiClient, Api api, Callback<C, R> callback) {
|
||||||
|
this.apiClient = (GoogleApiClientImpl) apiClient;
|
||||||
|
this.api = api;
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <C extends ApiConnection, R extends Result> PendingResult<R> call(GoogleApiClient client, Api api, GmsConnector.Callback<C, R> callback) {
|
||||||
|
return new GmsConnector<C, R>(client, api, callback).connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AbstractPendingResult<R> connect() {
|
||||||
|
Log.d(TAG, "connect()");
|
||||||
|
apiClient.incrementUsageCounter();
|
||||||
|
apiClient.getApiConnection(api);
|
||||||
|
Looper looper = apiClient.getLooper();
|
||||||
|
final AbstractPendingResult<R> result = new AbstractPendingResult<R>(looper);
|
||||||
|
Message msg = new Message();
|
||||||
|
msg.obj = result;
|
||||||
|
new Handler(looper).sendMessage(msg);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Callback<C, R> {
|
||||||
|
void onClientAvailable(C client, ResultProvider<R> resultProvider) throws RemoteException;
|
||||||
|
|
||||||
|
interface ResultProvider<R> {
|
||||||
|
void onResultAvailable(R result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Handler extends android.os.Handler {
|
||||||
|
private Handler(Looper looper) {
|
||||||
|
super(looper);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
Log.d(TAG, "Handler : handleMessage");
|
||||||
|
final AbstractPendingResult<R> result = (AbstractPendingResult<R>) msg.obj;
|
||||||
|
try {
|
||||||
|
C connection = (C) apiClient.getApiConnection(api);
|
||||||
|
callback.onClientAvailable(connection, new GmsConnector.Callback.ResultProvider<R>() {
|
||||||
|
@Override
|
||||||
|
public void onResultAvailable(R realResult) {
|
||||||
|
result.deliverResult(realResult);
|
||||||
|
apiClient.decrementUsageCounter();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (RemoteException ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,176 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.common;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.ServiceConnection;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
import static android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
|
||||||
|
import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME;
|
||||||
|
|
||||||
|
public class MultiConnectionKeeper {
|
||||||
|
private static final String TAG = "GmsMultiConKeeper";
|
||||||
|
|
||||||
|
private static MultiConnectionKeeper INSTANCE;
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
private final Map<String, Connection> connections = new HashMap<String, Connection>();
|
||||||
|
|
||||||
|
public MultiConnectionKeeper(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized static MultiConnectionKeeper getInstance(Context context) {
|
||||||
|
if (INSTANCE == null)
|
||||||
|
INSTANCE = new MultiConnectionKeeper(context);
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean bind(String action, ServiceConnection connection) {
|
||||||
|
Log.d(TAG, "bind(" + action + ", " + connection + ")");
|
||||||
|
Connection con = connections.get(action);
|
||||||
|
if (con != null) {
|
||||||
|
if (!con.forwardsConnection(connection)) {
|
||||||
|
con.addConnectionForward(connection);
|
||||||
|
if (!con.isBound())
|
||||||
|
con.bind();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
con = new Connection(action);
|
||||||
|
con.addConnectionForward(connection);
|
||||||
|
con.bind();
|
||||||
|
connections.put(action, con);
|
||||||
|
}
|
||||||
|
return con.isBound();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void unbind(String action, ServiceConnection connection) {
|
||||||
|
Log.d(TAG, "unbind(" + action + ", " + connection + ")");
|
||||||
|
Connection con = connections.get(action);
|
||||||
|
if (con != null) {
|
||||||
|
con.removeConnectionForward(connection);
|
||||||
|
if (!con.hasForwards() && con.isBound()) {
|
||||||
|
con.unbind();
|
||||||
|
connections.remove(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Connection {
|
||||||
|
private final String actionString;
|
||||||
|
private final Set<ServiceConnection> connectionForwards = new HashSet<ServiceConnection>();
|
||||||
|
private boolean bound = false;
|
||||||
|
private boolean connected = false;
|
||||||
|
private IBinder binder;
|
||||||
|
private ComponentName component;
|
||||||
|
private ServiceConnection serviceConnection = new ServiceConnection() {
|
||||||
|
@Override
|
||||||
|
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
|
||||||
|
Log.d(TAG, "Connection(" + actionString + ") : ServiceConnection : " +
|
||||||
|
"onServiceConnected(" + componentName + ")");
|
||||||
|
binder = iBinder;
|
||||||
|
component = componentName;
|
||||||
|
for (ServiceConnection connection : connectionForwards) {
|
||||||
|
connection.onServiceConnected(componentName, iBinder);
|
||||||
|
}
|
||||||
|
connected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceDisconnected(ComponentName componentName) {
|
||||||
|
Log.d(TAG, "Connection(" + actionString + ") : ServiceConnection : " +
|
||||||
|
"onServiceDisconnected(" + componentName + ")");
|
||||||
|
binder = null;
|
||||||
|
component = componentName;
|
||||||
|
for (ServiceConnection connection : connectionForwards) {
|
||||||
|
connection.onServiceDisconnected(componentName);
|
||||||
|
}
|
||||||
|
connected = false;
|
||||||
|
bound = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public Connection(String actionString) {
|
||||||
|
this.actionString = actionString;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("InlinedApi")
|
||||||
|
public void bind() {
|
||||||
|
Log.d(TAG, "Connection(" + actionString + ") : bind()");
|
||||||
|
Intent intent = new Intent(actionString).setPackage(GMS_PACKAGE_NAME);
|
||||||
|
int flags = Context.BIND_AUTO_CREATE;
|
||||||
|
if (SDK_INT >= ICE_CREAM_SANDWICH) {
|
||||||
|
flags |= Context.BIND_ADJUST_WITH_ACTIVITY;
|
||||||
|
}
|
||||||
|
bound = context.bindService(intent, serviceConnection, flags);
|
||||||
|
if (!bound) {
|
||||||
|
context.unbindService(serviceConnection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBound() {
|
||||||
|
return bound;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IBinder getBinder() {
|
||||||
|
return binder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unbind() {
|
||||||
|
Log.d(TAG, "Connection(" + actionString + ") : unbind()");
|
||||||
|
try {
|
||||||
|
context.unbindService(serviceConnection);
|
||||||
|
} catch (IllegalArgumentException e) { // not bound (whatever reason)
|
||||||
|
Log.w(TAG, e);
|
||||||
|
}
|
||||||
|
bound = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addConnectionForward(ServiceConnection connection) {
|
||||||
|
connectionForwards.add(connection);
|
||||||
|
if (connected) {
|
||||||
|
connection.onServiceConnected(component, binder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeConnectionForward(ServiceConnection connection) {
|
||||||
|
connectionForwards.remove(connection);
|
||||||
|
if (connected) {
|
||||||
|
connection.onServiceDisconnected(component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean forwardsConnection(ServiceConnection connection) {
|
||||||
|
return connectionForwards.contains(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasForwards() {
|
||||||
|
return !connectionForwards.isEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.common.api;
|
||||||
|
|
||||||
|
import android.os.Looper;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.PendingResult;
|
||||||
|
import com.google.android.gms.common.api.Result;
|
||||||
|
import com.google.android.gms.common.api.ResultCallback;
|
||||||
|
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class AbstractPendingResult<R extends Result> implements PendingResult<R> {
|
||||||
|
private final Object lock = new Object();
|
||||||
|
private final CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||||
|
private final ResultCallbackHandler<R> handler;
|
||||||
|
private boolean canceled;
|
||||||
|
private R result;
|
||||||
|
private ResultCallback<R> resultCallback;
|
||||||
|
|
||||||
|
public AbstractPendingResult(Looper looper) {
|
||||||
|
handler = new ResultCallbackHandler<R>(looper);
|
||||||
|
}
|
||||||
|
|
||||||
|
private R getResult() {
|
||||||
|
synchronized (lock) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R await() {
|
||||||
|
try {
|
||||||
|
countDownLatch.await();
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
}
|
||||||
|
return getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R await(long time, TimeUnit unit) {
|
||||||
|
try {
|
||||||
|
countDownLatch.await(time, unit);
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
}
|
||||||
|
return getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancel() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCanceled() {
|
||||||
|
synchronized (lock) {
|
||||||
|
return canceled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isReady() {
|
||||||
|
return this.countDownLatch.getCount() == 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setResultCallback(ResultCallback<R> callback, long time, TimeUnit unit) {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (!isCanceled()) {
|
||||||
|
if (isReady()) {
|
||||||
|
handler.sendResultCallback(callback, getResult());
|
||||||
|
} else {
|
||||||
|
handler.sendTimeoutResultCallback(this, unit.toMillis(time));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setResultCallback(ResultCallback<R> callback) {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (!isCanceled()) {
|
||||||
|
if (isReady()) {
|
||||||
|
handler.sendResultCallback(callback, getResult());
|
||||||
|
} else {
|
||||||
|
resultCallback = callback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deliverResult(R result) {
|
||||||
|
this.result = result;
|
||||||
|
countDownLatch.countDown();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.common.api;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.GooglePlayServicesClient;
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
|
||||||
|
import org.microg.gms.common.ForwardConnectionCallbacks;
|
||||||
|
import org.microg.gms.common.ForwardConnectionFailedListener;
|
||||||
|
|
||||||
|
public class AbstractPlayServicesClient implements GooglePlayServicesClient {
|
||||||
|
private static final String TAG = "GmsPlayServicesClient";
|
||||||
|
|
||||||
|
protected final GoogleApiClient googleApiClient;
|
||||||
|
|
||||||
|
public AbstractPlayServicesClient(GoogleApiClient googleApiClient) {
|
||||||
|
this.googleApiClient = googleApiClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assertConnected() {
|
||||||
|
if (!isConnected()) throw new IllegalStateException("Not connected!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void connect() {
|
||||||
|
Log.d(TAG, "connect()");
|
||||||
|
googleApiClient.connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disconnect() {
|
||||||
|
Log.d(TAG, "disconnect()");
|
||||||
|
//TODO googleApiClient.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isConnected() {
|
||||||
|
return googleApiClient.isConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isConnecting() {
|
||||||
|
return googleApiClient.isConnecting();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerConnectionCallbacks(final ConnectionCallbacks listener) {
|
||||||
|
googleApiClient.registerConnectionCallbacks(new ForwardConnectionCallbacks(listener));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isConnectionCallbacksRegistered(ConnectionCallbacks listener) {
|
||||||
|
return googleApiClient
|
||||||
|
.isConnectionCallbacksRegistered(new ForwardConnectionCallbacks(listener));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unregisterConnectionCallbacks(
|
||||||
|
ConnectionCallbacks listener) {
|
||||||
|
googleApiClient.unregisterConnectionCallbacks(new ForwardConnectionCallbacks(listener));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerConnectionFailedListener(
|
||||||
|
OnConnectionFailedListener listener) {
|
||||||
|
googleApiClient.registerConnectionFailedListener(
|
||||||
|
new ForwardConnectionFailedListener(listener));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isConnectionFailedListenerRegistered(
|
||||||
|
OnConnectionFailedListener listener) {
|
||||||
|
return googleApiClient.isConnectionFailedListenerRegistered(
|
||||||
|
new ForwardConnectionFailedListener(listener));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unregisterConnectionFailedListener(
|
||||||
|
OnConnectionFailedListener listener) {
|
||||||
|
googleApiClient.unregisterConnectionFailedListener(
|
||||||
|
new ForwardConnectionFailedListener(listener));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package 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<O extends Api.ApiOptions> {
|
||||||
|
ApiConnection build(Context context, Looper looper, O options, AccountInfo accountInfo,
|
||||||
|
GoogleApiClient.ConnectionCallbacks callbacks,
|
||||||
|
GoogleApiClient.OnConnectionFailedListener connectionFailedListener);
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.common.api;
|
||||||
|
|
||||||
|
public interface ApiConnection {
|
||||||
|
void connect();
|
||||||
|
|
||||||
|
void disconnect();
|
||||||
|
|
||||||
|
boolean isConnected();
|
||||||
|
|
||||||
|
boolean isConnecting();
|
||||||
|
}
|
|
@ -0,0 +1,242 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.common.api;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.os.Message;
|
||||||
|
import androidx.fragment.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<Api, Api.ApiOptions> apis = new HashMap<Api, Api.ApiOptions>();
|
||||||
|
private final Map<Api, ApiConnection> apiConnections = new HashMap<Api, ApiConnection>();
|
||||||
|
private final Handler handler;
|
||||||
|
private final Set<ConnectionCallbacks> connectionCallbacks = new HashSet<ConnectionCallbacks>();
|
||||||
|
private final Set<OnConnectionFailedListener> connectionFailedListeners = new HashSet<OnConnectionFailedListener>();
|
||||||
|
private final int clientId;
|
||||||
|
private final ConnectionCallbacks baseConnectionCallbacks = new ConnectionCallbacks() {
|
||||||
|
@Override
|
||||||
|
public void onConnected(Bundle connectionHint) {
|
||||||
|
Log.d(TAG, "ConnectionCallbacks : onConnected()");
|
||||||
|
for (ConnectionCallbacks callback : connectionCallbacks) {
|
||||||
|
callback.onConnected(connectionHint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConnectionSuspended(int cause) {
|
||||||
|
Log.d(TAG, "ConnectionCallbacks : onConnectionSuspended()");
|
||||||
|
for (ConnectionCallbacks callback : connectionCallbacks) {
|
||||||
|
callback.onConnectionSuspended(cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private final OnConnectionFailedListener baseConnectionFailedListener = new
|
||||||
|
OnConnectionFailedListener() {
|
||||||
|
@Override
|
||||||
|
public void onConnectionFailed(ConnectionResult result) {
|
||||||
|
Log.d(TAG, "OnConnectionFailedListener : onConnectionFailed()");
|
||||||
|
for (OnConnectionFailedListener listener : connectionFailedListeners) {
|
||||||
|
listener.onConnectionFailed(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private int usageCounter = 0;
|
||||||
|
private boolean shouldDisconnect = false;
|
||||||
|
|
||||||
|
public GoogleApiClientImpl(Context context, Looper looper, AccountInfo accountInfo,
|
||||||
|
Map<Api, Api.ApiOptions> apis,
|
||||||
|
Set<ConnectionCallbacks> connectionCallbacks,
|
||||||
|
Set<OnConnectionFailedListener> connectionFailedListeners, int clientId) {
|
||||||
|
this.context = context;
|
||||||
|
this.looper = looper;
|
||||||
|
this.handler = new Handler(looper);
|
||||||
|
this.accountInfo = accountInfo;
|
||||||
|
this.apis.putAll(apis);
|
||||||
|
this.connectionCallbacks.addAll(connectionCallbacks);
|
||||||
|
this.connectionFailedListeners.addAll(connectionFailedListeners);
|
||||||
|
this.clientId = clientId;
|
||||||
|
|
||||||
|
for (Api api : apis.keySet()) {
|
||||||
|
apiConnections.put(api, api.getBuilder().build(context, looper,
|
||||||
|
apis.get(api), accountInfo, baseConnectionCallbacks,
|
||||||
|
baseConnectionFailedListener));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void incrementUsageCounter() {
|
||||||
|
usageCounter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void decrementUsageCounter() {
|
||||||
|
usageCounter--;
|
||||||
|
if (shouldDisconnect) disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Looper getLooper() {
|
||||||
|
return looper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApiConnection getApiConnection(Api api) {
|
||||||
|
return apiConnections.get(api);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConnectionResult blockingConnect() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConnectionResult blockingConnect(long timeout, TimeUnit unit) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingResult<Status> clearDefaultAccountAndReconnect() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void connect() {
|
||||||
|
Log.d(TAG, "connect()");
|
||||||
|
if (isConnected() || isConnecting()) {
|
||||||
|
if (shouldDisconnect) {
|
||||||
|
shouldDisconnect = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Log.d(TAG, "Already connected/connecting, nothing to do");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (ApiConnection connection : apiConnections.values()) {
|
||||||
|
if (!connection.isConnected()) {
|
||||||
|
connection.connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void disconnect() {
|
||||||
|
if (usageCounter > 0) {
|
||||||
|
shouldDisconnect = true;
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "disconnect()");
|
||||||
|
for (ApiConnection connection : apiConnections.values()) {
|
||||||
|
if (connection.isConnected()) {
|
||||||
|
connection.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean isConnected() {
|
||||||
|
for (ApiConnection connection : apiConnections.values()) {
|
||||||
|
if (!connection.isConnected()) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean isConnecting() {
|
||||||
|
for (ApiConnection connection : apiConnections.values()) {
|
||||||
|
if (connection.isConnecting()) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isConnectionCallbacksRegistered(ConnectionCallbacks listener) {
|
||||||
|
return connectionCallbacks.contains(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isConnectionFailedListenerRegistered(
|
||||||
|
OnConnectionFailedListener listener) {
|
||||||
|
return connectionFailedListeners.contains(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void reconnect() {
|
||||||
|
Log.d(TAG, "reconnect()");
|
||||||
|
disconnect();
|
||||||
|
connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerConnectionCallbacks(ConnectionCallbacks listener) {
|
||||||
|
connectionCallbacks.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerConnectionFailedListener(OnConnectionFailedListener listener) {
|
||||||
|
connectionFailedListeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stopAutoManager(FragmentActivity lifecycleActivity) throws IllegalStateException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unregisterConnectionCallbacks(ConnectionCallbacks listener) {
|
||||||
|
connectionCallbacks.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unregisterConnectionFailedListener(OnConnectionFailedListener listener) {
|
||||||
|
connectionFailedListeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Handler extends android.os.Handler {
|
||||||
|
private Handler(Looper looper) {
|
||||||
|
super(looper);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
if (msg.what == 0 && msg.obj instanceof Runnable) {
|
||||||
|
((Runnable) msg.obj).run();
|
||||||
|
} else {
|
||||||
|
super.handleMessage(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendRunnable(Runnable runnable) {
|
||||||
|
sendMessage(obtainMessage(1, runnable));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.common.api;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.PendingResult;
|
||||||
|
import com.google.android.gms.common.api.Result;
|
||||||
|
import com.google.android.gms.common.api.ResultCallback;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class InstantPendingResult<R extends Result> implements PendingResult<R> {
|
||||||
|
R value;
|
||||||
|
|
||||||
|
public InstantPendingResult(R value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R await() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R await(long time, TimeUnit unit) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancel() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCanceled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setResultCallback(ResultCallback<R> callback, long time, TimeUnit unit) {
|
||||||
|
callback.onResult(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setResultCallback(ResultCallback<R> callback) {
|
||||||
|
callback.onResult(value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.common.api;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.Result;
|
||||||
|
import com.google.android.gms.common.api.ResultCallback;
|
||||||
|
|
||||||
|
class ResultCallbackHandler<R extends Result> extends Handler {
|
||||||
|
private static final String TAG = "GmsResultCbackHandler";
|
||||||
|
public static final int CALLBACK_ON_COMPLETE = 1;
|
||||||
|
public static final int CALLBACK_ON_TIMEOUT = 2;
|
||||||
|
|
||||||
|
public ResultCallbackHandler(Looper looper) {
|
||||||
|
super(looper);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
switch (msg.what) {
|
||||||
|
case CALLBACK_ON_COMPLETE:
|
||||||
|
OnCompleteObject<R> o = (OnCompleteObject<R>) msg.obj;
|
||||||
|
Log.d(TAG, "handleMessage() : onResult(" + o.result + ")");
|
||||||
|
o.callback.onResult(o.result);
|
||||||
|
break;
|
||||||
|
case CALLBACK_ON_TIMEOUT:
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendResultCallback(ResultCallback<R> callback, R result) {
|
||||||
|
Message message = new Message();
|
||||||
|
message.what = CALLBACK_ON_COMPLETE;
|
||||||
|
message.obj = new OnCompleteObject<R>(callback, result);
|
||||||
|
sendMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendTimeoutResultCallback(AbstractPendingResult pendingResult, long millis) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class OnCompleteObject<R extends Result> {
|
||||||
|
public ResultCallback<R> callback;
|
||||||
|
public R result;
|
||||||
|
|
||||||
|
public OnCompleteObject(ResultCallback<R> callback, R result) {
|
||||||
|
this.callback = callback;
|
||||||
|
this.result = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2013-2017 microG Project Team
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<resources>
|
||||||
|
<integer name="google_play_services_version">6599436</integer>
|
||||||
|
</resources>
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
|
String getMyVersionName() {
|
||||||
|
def stdout = new ByteArrayOutputStream()
|
||||||
|
if (rootProject.file("gradlew").exists())
|
||||||
|
exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout }
|
||||||
|
else // automatic build system, don't tag dirty
|
||||||
|
exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout }
|
||||||
|
return stdout.toString().trim().substring(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion androidCompileSdk()
|
||||||
|
buildToolsVersion "$androidBuildVersionTools"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
versionName getMyVersionName()
|
||||||
|
minSdkVersion androidMinSdk()
|
||||||
|
targetSdkVersion androidTargetSdk()
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api project(':play-services-base')
|
||||||
|
api project(':play-services-cast-api')
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
#
|
||||||
|
# Copyright 2013-2016 microG Project Team
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
POM_NAME=Play Services Cast Library
|
||||||
|
POM_DESCRIPTION=The Play Services Library module to access the Cast API
|
||||||
|
|
||||||
|
POM_PACKAGING=aar
|
||||||
|
|
||||||
|
POM_URL=https://github.com/microg/android_external_GmsLib
|
||||||
|
|
||||||
|
POM_SCM_URL=https://github.com/microg/android_external_GmsLib
|
||||||
|
POM_SCM_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git
|
||||||
|
POM_SCM_DEV_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git
|
||||||
|
|
||||||
|
POM_LICENCE_NAME=The Apache Software License, Version 2.0
|
||||||
|
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
POM_LICENCE_DIST=repo
|
||||||
|
|
||||||
|
POM_DEVELOPER_ID=mar-v-in
|
||||||
|
POM_DEVELOPER_NAME=Marvin W
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2013-2017 microG Project Team
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<manifest package="org.microg.gms.cast"/>
|
|
@ -0,0 +1,223 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.cast;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.Api;
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
import com.google.android.gms.common.api.PendingResult;
|
||||||
|
import com.google.android.gms.common.api.Result;
|
||||||
|
import com.google.android.gms.common.api.Status;
|
||||||
|
|
||||||
|
import org.microg.gms.cast.CastApiBuilder;
|
||||||
|
import org.microg.gms.cast.CastApiImpl;
|
||||||
|
import org.microg.gms.common.PublicApi;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@PublicApi
|
||||||
|
public final class Cast {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A constant indicating that the Google Cast device is not the currently active video input.
|
||||||
|
*/
|
||||||
|
public static final int ACTIVE_INPUT_STATE_NO = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A constant indicating that it is not known (and/or not possible to know) whether the Google Cast device is
|
||||||
|
* the currently active video input. Active input state can only be reported when the Google Cast device is
|
||||||
|
* connected to a TV or AVR with CEC support.
|
||||||
|
*/
|
||||||
|
public static final int ACTIVE_INPUT_STATE_UNKNOWN = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A constant indicating that the Google Cast device is the currently active video input.
|
||||||
|
*/
|
||||||
|
public static final int ACTIVE_INPUT_STATE_YES = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean extra for the connection hint bundle passed to
|
||||||
|
* {@link GoogleApiClient.ConnectionCallbacks#onConnected(Bundle)} that indicates that the connection was
|
||||||
|
* re-established, but the receiver application that was in use at the time of the connection loss is no longer
|
||||||
|
* running on the receiver.
|
||||||
|
*/
|
||||||
|
public static final String EXTRA_APP_NO_LONGER_RUNNING = "com.google.android.gms.cast.EXTRA_APP_NO_LONGER_RUNNING";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum raw message length (in bytes) that is supported by a Cast channel.
|
||||||
|
*/
|
||||||
|
public static final int MAX_MESSAGE_LENGTH = 65536;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum length (in characters) of a namespace name.
|
||||||
|
*/
|
||||||
|
public static final int MAX_NAMESPACE_LENGTH = 128;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A constant indicating that the Google Cast device is not currently in standby.
|
||||||
|
*/
|
||||||
|
public static final int STANDBY_STATE_NO = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A constant indicating that it is not known (and/or not possible to know) whether the Google Cast device is
|
||||||
|
* currently in standby. Standby state can only be reported when the Google Cast device is connected to a TV or
|
||||||
|
* AVR with CEC support.
|
||||||
|
*/
|
||||||
|
public static final int STANDBY_STATE_UNKNOWN = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A constant indicating that the Google Cast device is currently in standby.
|
||||||
|
*/
|
||||||
|
public static final int STANDBY_STATE_YES = 1;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Token to pass to {@link GoogleApiClient.Builder#addApi(Api)} to enable the Cast features.
|
||||||
|
*/
|
||||||
|
public static final Api<CastOptions> API = new Api<CastOptions>(new CastApiBuilder());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of the CastApi interface. The interface is used to interact with a cast device.
|
||||||
|
*/
|
||||||
|
public static final Cast.CastApi CastApi = new CastApiImpl();
|
||||||
|
|
||||||
|
private Cast() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ApplicationConnectionResult extends Result {
|
||||||
|
ApplicationMetadata getApplicationMetadata();
|
||||||
|
|
||||||
|
String getApplicationStatus();
|
||||||
|
|
||||||
|
String getSessionId();
|
||||||
|
|
||||||
|
boolean getWasLaunched();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface CastApi {
|
||||||
|
int getActiveInputState(GoogleApiClient client);
|
||||||
|
|
||||||
|
ApplicationMetadata getApplicationMetadata(GoogleApiClient client);
|
||||||
|
|
||||||
|
String getApplicationStatus(GoogleApiClient client);
|
||||||
|
|
||||||
|
int getStandbyState(GoogleApiClient client);
|
||||||
|
|
||||||
|
double getVolume(GoogleApiClient client);
|
||||||
|
|
||||||
|
boolean isMute(GoogleApiClient client);
|
||||||
|
|
||||||
|
PendingResult<Cast.ApplicationConnectionResult> joinApplication(GoogleApiClient client);
|
||||||
|
|
||||||
|
PendingResult<Cast.ApplicationConnectionResult> joinApplication(GoogleApiClient client, String applicationId, String sessionId);
|
||||||
|
|
||||||
|
PendingResult<Cast.ApplicationConnectionResult> joinApplication(GoogleApiClient client, String applicationId);
|
||||||
|
|
||||||
|
PendingResult<Cast.ApplicationConnectionResult> launchApplication(GoogleApiClient client, String applicationId, LaunchOptions launchOptions);
|
||||||
|
|
||||||
|
PendingResult<Cast.ApplicationConnectionResult> launchApplication(GoogleApiClient client, String applicationId);
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
PendingResult<Cast.ApplicationConnectionResult> launchApplication(GoogleApiClient client, String applicationId, boolean relaunchIfRunning);
|
||||||
|
|
||||||
|
PendingResult<Status> leaveApplication(GoogleApiClient client);
|
||||||
|
|
||||||
|
void removeMessageReceivedCallbacks(GoogleApiClient client, String namespace) throws IOException;
|
||||||
|
|
||||||
|
void requestStatus(GoogleApiClient client) throws IOException;
|
||||||
|
|
||||||
|
PendingResult<Status> sendMessage(GoogleApiClient client, String namespace, String message);
|
||||||
|
|
||||||
|
void setMessageReceivedCallbacks(GoogleApiClient client, String namespace, Cast.MessageReceivedCallback callbacks) throws IOException;
|
||||||
|
|
||||||
|
void setMute(GoogleApiClient client, boolean mute) throws IOException;
|
||||||
|
|
||||||
|
void setVolume(GoogleApiClient client, double volume) throws IOException;
|
||||||
|
|
||||||
|
PendingResult<Status> stopApplication(GoogleApiClient client);
|
||||||
|
|
||||||
|
PendingResult<Status> stopApplication(GoogleApiClient client, String sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CastOptions implements Api.ApiOptions.HasOptions {
|
||||||
|
private final CastDevice castDevice;
|
||||||
|
private final Listener castListener;
|
||||||
|
private final boolean verboseLoggingEnabled;
|
||||||
|
|
||||||
|
public CastOptions(CastDevice castDevice, Listener castListener, boolean verboseLoggingEnabled) {
|
||||||
|
this.castDevice = castDevice;
|
||||||
|
this.castListener = castListener;
|
||||||
|
this.verboseLoggingEnabled = verboseLoggingEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public static Builder builder(CastDevice castDevice, Listener castListener) {
|
||||||
|
return new Builder(castDevice, castListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
private final CastDevice castDevice;
|
||||||
|
private final Listener castListener;
|
||||||
|
private boolean verboseLoggingEnabled;
|
||||||
|
|
||||||
|
public Builder(CastDevice castDevice, Listener castListener) {
|
||||||
|
this.castDevice = castDevice;
|
||||||
|
this.castListener = castListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CastOptions build() {
|
||||||
|
return new CastOptions(castDevice, castListener, verboseLoggingEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setVerboseLoggingEnabled(boolean verboseLoggingEnabled) {
|
||||||
|
this.verboseLoggingEnabled = verboseLoggingEnabled;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Listener {
|
||||||
|
public void onActiveInputStateChanged(int activeInputState) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onApplicationDisconnected(int statusCode) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onApplicationMetadataChanged(ApplicationMetadata applicationMetadata) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onApplicationStatusChanged() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onStandbyStateChanged(int standbyState) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onVolumeChanged() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface MessageReceivedCallback {
|
||||||
|
void onMessageReceived(CastDevice castDevice, String namespace, String message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.cast;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.app.Presentation;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.Display;
|
||||||
|
|
||||||
|
@TargetApi(17)
|
||||||
|
public class CastPresentation extends Presentation {
|
||||||
|
public CastPresentation(Context outerContext, Display display) {
|
||||||
|
super(outerContext, display);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CastPresentation(Context outerContext, Display display, int theme) {
|
||||||
|
super(outerContext, display, theme);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.cast;
|
||||||
|
|
||||||
|
import android.view.Display;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.Api;
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
import com.google.android.gms.common.api.Result;
|
||||||
|
import com.google.android.gms.common.api.Status;
|
||||||
|
|
||||||
|
import org.microg.gms.cast.CastRemoteDisplayApiBuilder;
|
||||||
|
import org.microg.gms.cast.CastRemoteDisplayApiImpl;
|
||||||
|
import org.microg.gms.common.PublicApi;
|
||||||
|
|
||||||
|
@PublicApi
|
||||||
|
public final class CastRemoteDisplay {
|
||||||
|
/**
|
||||||
|
* Token to pass to {@link GoogleApiClient.Builder#addApi(Api)} to enable the CastRemoteDisplay features.
|
||||||
|
*/
|
||||||
|
public static final Api<CastRemoteDisplayOptions> API = new Api<CastRemoteDisplayOptions>(new CastRemoteDisplayApiBuilder());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of the CastRemoteDisplayAPI interface. The interface is used to interact with a cast device.
|
||||||
|
*/
|
||||||
|
public static final CastRemoteDisplayApi CastApi = new CastRemoteDisplayApiImpl();
|
||||||
|
|
||||||
|
private CastRemoteDisplay() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class CastRemoteDisplayOptions implements Api.ApiOptions.HasOptions {
|
||||||
|
private CastDevice castDevice;
|
||||||
|
private CastRemoteDisplaySessionCallbacks callbacks;
|
||||||
|
|
||||||
|
private CastRemoteDisplayOptions(CastDevice castDevice, CastRemoteDisplaySessionCallbacks callbacks) {
|
||||||
|
this.castDevice = castDevice;
|
||||||
|
this.callbacks = callbacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Builder {
|
||||||
|
private CastDevice castDevice;
|
||||||
|
private CastRemoteDisplaySessionCallbacks callbacks;
|
||||||
|
|
||||||
|
public Builder(CastDevice castDevice, CastRemoteDisplaySessionCallbacks callbacks) {
|
||||||
|
this.castDevice = castDevice;
|
||||||
|
this.callbacks = callbacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CastRemoteDisplayOptions build() {
|
||||||
|
return new CastRemoteDisplayOptions(castDevice, callbacks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface CastRemoteDisplaySessionCallbacks {
|
||||||
|
void onRemoteDisplayEnded(Status status);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface CastRemoteDisplaySessionResult extends Result {
|
||||||
|
Display getPresentationDisplay();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.cast;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
import com.google.android.gms.common.api.PendingResult;
|
||||||
|
|
||||||
|
public interface CastRemoteDisplayApi {
|
||||||
|
PendingResult<CastRemoteDisplay.CastRemoteDisplaySessionResult> startRemoteDisplay(GoogleApiClient apiClient, String applicationId);
|
||||||
|
|
||||||
|
PendingResult<CastRemoteDisplay.CastRemoteDisplaySessionResult> stopRemoteDisplay(GoogleApiClient apiClient);
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.cast;
|
||||||
|
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.IBinder;
|
||||||
|
|
||||||
|
public class CastRemoteDisplayLocalService extends Service {
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.cast;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Looper;
|
||||||
|
|
||||||
|
import com.google.android.gms.cast.Cast;
|
||||||
|
import com.google.android.gms.common.api.AccountInfo;
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
|
||||||
|
import org.microg.gms.common.api.ApiBuilder;
|
||||||
|
import org.microg.gms.common.api.ApiConnection;
|
||||||
|
|
||||||
|
public class CastApiBuilder implements ApiBuilder<Cast.CastOptions>{
|
||||||
|
@Override
|
||||||
|
public ApiConnection build(Context context, Looper looper, Cast.CastOptions options, AccountInfo accountInfo, GoogleApiClient.ConnectionCallbacks callbacks, GoogleApiClient.OnConnectionFailedListener connectionFailedListener) {
|
||||||
|
return new CastClientImpl(context, options, callbacks, connectionFailedListener);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,134 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.cast;
|
||||||
|
|
||||||
|
import com.google.android.gms.cast.ApplicationMetadata;
|
||||||
|
import com.google.android.gms.cast.Cast;
|
||||||
|
import com.google.android.gms.cast.LaunchOptions;
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
import com.google.android.gms.common.api.PendingResult;
|
||||||
|
import com.google.android.gms.common.api.Status;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
public class CastApiImpl implements Cast.CastApi {
|
||||||
|
@Override
|
||||||
|
public int getActiveInputState(GoogleApiClient client) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ApplicationMetadata getApplicationMetadata(GoogleApiClient client) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getApplicationStatus(GoogleApiClient client) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getStandbyState(GoogleApiClient client) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getVolume(GoogleApiClient client) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMute(GoogleApiClient client) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingResult<Cast.ApplicationConnectionResult> joinApplication(GoogleApiClient client) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingResult<Cast.ApplicationConnectionResult> joinApplication(GoogleApiClient client, String applicationId, String sessionId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingResult<Cast.ApplicationConnectionResult> joinApplication(GoogleApiClient client, String applicationId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingResult<Cast.ApplicationConnectionResult> launchApplication(GoogleApiClient client, String applicationId, LaunchOptions launchOptions) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingResult<Cast.ApplicationConnectionResult> launchApplication(GoogleApiClient client, String applicationId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingResult<Cast.ApplicationConnectionResult> launchApplication(GoogleApiClient client, String applicationId, boolean relaunchIfRunning) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingResult<Status> leaveApplication(GoogleApiClient client) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeMessageReceivedCallbacks(GoogleApiClient client, String namespace) throws IOException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void requestStatus(GoogleApiClient client) throws IOException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingResult<Status> sendMessage(GoogleApiClient client, String namespace, String message) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setMessageReceivedCallbacks(GoogleApiClient client, String namespace, Cast.MessageReceivedCallback callbacks) throws IOException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setMute(GoogleApiClient client, boolean mute) throws IOException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setVolume(GoogleApiClient client, double volume) throws IOException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingResult<Status> stopApplication(GoogleApiClient client) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingResult<Status> stopApplication(GoogleApiClient client, String sessionId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.cast;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.google.android.gms.cast.Cast;
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
|
||||||
|
import org.microg.gms.common.DummyApiConnection;
|
||||||
|
|
||||||
|
public class CastClientImpl extends DummyApiConnection {
|
||||||
|
public CastClientImpl(Context context, Cast.CastOptions options, GoogleApiClient.ConnectionCallbacks callbacks, GoogleApiClient.OnConnectionFailedListener connectionFailedListener) {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.cast;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Looper;
|
||||||
|
|
||||||
|
import com.google.android.gms.cast.CastRemoteDisplay;
|
||||||
|
import com.google.android.gms.common.api.AccountInfo;
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
|
||||||
|
import org.microg.gms.common.DummyApiConnection;
|
||||||
|
import org.microg.gms.common.api.ApiBuilder;
|
||||||
|
import org.microg.gms.common.api.ApiConnection;
|
||||||
|
|
||||||
|
public class CastRemoteDisplayApiBuilder implements ApiBuilder<CastRemoteDisplay.CastRemoteDisplayOptions> {
|
||||||
|
@Override
|
||||||
|
public ApiConnection build(Context context, Looper looper, CastRemoteDisplay.CastRemoteDisplayOptions options, AccountInfo accountInfo, GoogleApiClient.ConnectionCallbacks callbacks, GoogleApiClient.OnConnectionFailedListener connectionFailedListener) {
|
||||||
|
return new DummyApiConnection();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.cast;
|
||||||
|
|
||||||
|
import com.google.android.gms.cast.CastRemoteDisplay;
|
||||||
|
import com.google.android.gms.cast.CastRemoteDisplayApi;
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
import com.google.android.gms.common.api.PendingResult;
|
||||||
|
|
||||||
|
public class CastRemoteDisplayApiImpl implements CastRemoteDisplayApi {
|
||||||
|
@Override
|
||||||
|
public PendingResult<CastRemoteDisplay.CastRemoteDisplaySessionResult> startRemoteDisplay(GoogleApiClient apiClient, String applicationId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingResult<CastRemoteDisplay.CastRemoteDisplaySessionResult> stopRemoteDisplay(GoogleApiClient apiClient) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
|
String getMyVersionName() {
|
||||||
|
def stdout = new ByteArrayOutputStream()
|
||||||
|
if (rootProject.file("gradlew").exists())
|
||||||
|
exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout }
|
||||||
|
else // automatic build system, don't tag dirty
|
||||||
|
exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout }
|
||||||
|
return stdout.toString().trim().substring(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion androidCompileSdk()
|
||||||
|
buildToolsVersion "$androidBuildVersionTools"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
versionName getMyVersionName()
|
||||||
|
minSdkVersion androidMinSdk()
|
||||||
|
targetSdkVersion androidTargetSdk()
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api project(':play-services-iid')
|
||||||
|
// compile project(':play-services-measurement')
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
#
|
||||||
|
# Copyright 2013-2016 microG Project Team
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
POM_NAME=Play Services GCM Library
|
||||||
|
POM_DESCRIPTION=The Play Services Library module to access Google Cloud Messaging
|
||||||
|
|
||||||
|
POM_PACKAGING=aar
|
||||||
|
|
||||||
|
POM_URL=https://github.com/microg/android_external_GmsLib
|
||||||
|
|
||||||
|
POM_SCM_URL=https://github.com/microg/android_external_GmsLib
|
||||||
|
POM_SCM_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git
|
||||||
|
POM_SCM_DEV_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git
|
||||||
|
|
||||||
|
POM_LICENCE_NAME=The Apache Software License, Version 2.0
|
||||||
|
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
POM_LICENCE_DIST=repo
|
||||||
|
|
||||||
|
POM_DEVELOPER_ID=mar-v-in
|
||||||
|
POM_DEVELOPER_NAME=Marvin W
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2013-2017 microG Project Team
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<manifest package="org.microg.gms.gcm"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<!-- Permissions required for GCM -->
|
||||||
|
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
|
||||||
|
<!-- Move to play-services-measurement -->
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||||
|
</manifest>
|
|
@ -0,0 +1,194 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.gcm;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.microg.gms.common.PublicApi;
|
||||||
|
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_RECEIVE;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.ACTION_NOTIFICATION_OPEN;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_ERROR;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_FROM;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSAGE_ID;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSAGE_TYPE;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_PENDING_INTENT;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.MESSAGE_TYPE_DELETED_MESSAGE;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.MESSAGE_TYPE_GCM;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.MESSAGE_TYPE_SEND_ERROR;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.MESSAGE_TYPE_SEND_EVENT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for communicating with Google Cloud Messaging.
|
||||||
|
* <p/>
|
||||||
|
* It also provides functionality such as automatically displaying
|
||||||
|
* <a href="https://developer.android.com/google/gcm/server.html">notifications when requested by app server</a>.
|
||||||
|
* <p/>
|
||||||
|
* Override base class methods to handle any events required by the application.
|
||||||
|
* Methods are invoked asynchronously.
|
||||||
|
* <p/>
|
||||||
|
* Include the following in the manifest:
|
||||||
|
* <pre>
|
||||||
|
* <service
|
||||||
|
* android:name=".YourGcmListenerService"
|
||||||
|
* android:exported="false" >
|
||||||
|
* <intent-filter>
|
||||||
|
* <action android:name="com.google.android.c2dm.intent.RECEIVE" />
|
||||||
|
* </intent-filter>
|
||||||
|
* </service></pre>
|
||||||
|
*/
|
||||||
|
@PublicApi
|
||||||
|
public abstract class GcmListenerService extends Service {
|
||||||
|
private static final String TAG = "GcmListenerService";
|
||||||
|
|
||||||
|
private final Object lock = new Object();
|
||||||
|
private int startId;
|
||||||
|
private int counter = 0;
|
||||||
|
|
||||||
|
public final IBinder onBind(Intent intent) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when GCM server deletes pending messages due to exceeded
|
||||||
|
* storage limits, for example, when the device cannot be reached
|
||||||
|
* for an extended period of time.
|
||||||
|
* <p/>
|
||||||
|
* It is recommended to retrieve any missing messages directly from the
|
||||||
|
* app server.
|
||||||
|
*/
|
||||||
|
public void onDeletedMessages() {
|
||||||
|
// To be overwritten
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a message is received.
|
||||||
|
*
|
||||||
|
* @param from describes message sender.
|
||||||
|
* @param data message data as String key/value pairs.
|
||||||
|
*/
|
||||||
|
public void onMessageReceived(String from, Bundle data) {
|
||||||
|
// To be overwritten
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an upstream message has been successfully sent to the
|
||||||
|
* GCM connection server.
|
||||||
|
*
|
||||||
|
* @param msgId of the upstream message sent using
|
||||||
|
* {@link com.google.android.gms.gcm.GoogleCloudMessaging#send(java.lang.String, java.lang.String, android.os.Bundle)}.
|
||||||
|
*/
|
||||||
|
public void onMessageSent(String msgId) {
|
||||||
|
// To be overwritten
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when there was an error sending an upstream message.
|
||||||
|
*
|
||||||
|
* @param msgId of the upstream message sent using
|
||||||
|
* {@link com.google.android.gms.gcm.GoogleCloudMessaging#send(java.lang.String, java.lang.String, android.os.Bundle)}.
|
||||||
|
* @param error description of the error.
|
||||||
|
*/
|
||||||
|
public void onSendError(String msgId, String error) {
|
||||||
|
// To be overwritten
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int onStartCommand(final Intent intent, int flags, int startId) {
|
||||||
|
synchronized (lock) {
|
||||||
|
this.startId = startId;
|
||||||
|
this.counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intent != null) {
|
||||||
|
if (ACTION_NOTIFICATION_OPEN.equals(intent.getAction())) {
|
||||||
|
handlePendingNotification(intent);
|
||||||
|
finishCounter();
|
||||||
|
GcmReceiver.completeWakefulIntent(intent);
|
||||||
|
} else if (ACTION_C2DM_RECEIVE.equals(intent.getAction())) {
|
||||||
|
new AsyncTask<Void, Void, Void>() {
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground(Void... params) {
|
||||||
|
handleC2dmMessage(intent);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Unknown intent action: " + intent.getAction());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return START_REDELIVER_INTENT;
|
||||||
|
} else {
|
||||||
|
finishCounter();
|
||||||
|
return START_NOT_STICKY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleC2dmMessage(Intent intent) {
|
||||||
|
try {
|
||||||
|
String messageType = intent.getStringExtra(EXTRA_MESSAGE_TYPE);
|
||||||
|
if (messageType == null || MESSAGE_TYPE_GCM.equals(messageType)) {
|
||||||
|
String from = intent.getStringExtra(EXTRA_FROM);
|
||||||
|
Bundle data = intent.getExtras();
|
||||||
|
data.remove(EXTRA_MESSAGE_TYPE);
|
||||||
|
data.remove("android.support.content.wakelockid"); // WakefulBroadcastReceiver.EXTRA_WAKE_LOCK_ID
|
||||||
|
data.remove(EXTRA_FROM);
|
||||||
|
onMessageReceived(from, data);
|
||||||
|
} else if (MESSAGE_TYPE_DELETED_MESSAGE.equals(messageType)) {
|
||||||
|
onDeletedMessages();
|
||||||
|
} else if (MESSAGE_TYPE_SEND_EVENT.equals(messageType)) {
|
||||||
|
onMessageSent(intent.getStringExtra(EXTRA_MESSAGE_ID));
|
||||||
|
} else if (MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {
|
||||||
|
onSendError(intent.getStringExtra(EXTRA_MESSAGE_ID), intent.getStringExtra(EXTRA_ERROR));
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Unknown message type: " + messageType);
|
||||||
|
}
|
||||||
|
finishCounter();
|
||||||
|
} finally {
|
||||||
|
GcmReceiver.completeWakefulIntent(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handlePendingNotification(Intent intent) {
|
||||||
|
PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_PENDING_INTENT);
|
||||||
|
if (pendingIntent != null) {
|
||||||
|
try {
|
||||||
|
pendingIntent.send();
|
||||||
|
} catch (PendingIntent.CanceledException e) {
|
||||||
|
Log.w(TAG, "Notification cancelled", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Notification was null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void finishCounter() {
|
||||||
|
synchronized (lock) {
|
||||||
|
this.counter--;
|
||||||
|
if (counter == 0) {
|
||||||
|
stopSelfResult(startId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,244 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.gcm;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.ResolveInfo;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.ACTION_SCHEDULE;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.ACTION_TASK_READY;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_COMPONENT;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_SCHEDULER_ACTION;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_TAG;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.SCHEDULER_ACTION_CANCEL;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.SCHEDULER_ACTION_CANCEL_ALL;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.SCHEDULER_ACTION_SCHEDULE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class to create apps with robust "send-to-sync", which is the mechanism to sync data with
|
||||||
|
* servers where new information is available.
|
||||||
|
* <p/>
|
||||||
|
* You can use the API to schedule network-oriented tasks, and let Google Play services batch
|
||||||
|
* network operations across the system. This greatly simplifies the implementation of common
|
||||||
|
* patterns, such as waiting for network connectivity, network retries, and backoff.
|
||||||
|
* <p/>
|
||||||
|
* Tasks must be scheduled based on an execution window in time. During this execution window
|
||||||
|
* the scheduler will use its discretion in picking an optimal execution time, based on network
|
||||||
|
* availability (whether the device has connectivity), network activity (whether packages are
|
||||||
|
* actively being transferred). and load (how many other pending tasks are available for
|
||||||
|
* execution at that point in time). <strong>If none of these factors are influential, the
|
||||||
|
* scheduler will always wait until the end of the specified window.</strong>
|
||||||
|
* <p/>
|
||||||
|
* To receive the notification from the scheduler that a task is ready to be executed, your
|
||||||
|
* client app must implement a {@link com.google.android.gms.gcm.GcmTaskService} and filter
|
||||||
|
* on the action {@link com.google.android.gms.gcm.GcmTaskService#SERVICE_ACTION_EXECUTE_TASK}.
|
||||||
|
* <p/>
|
||||||
|
* Note that tags of arbitrary length are <strong>not</strong> allowed; if the tag you
|
||||||
|
* provide is greater than 100 characters an exception will be thrown when you try to create your
|
||||||
|
* {@link com.google.android.gms.gcm.Task} object.
|
||||||
|
* <p/>
|
||||||
|
* The service should be protected by the permission
|
||||||
|
* com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE which is used by Google Play
|
||||||
|
* Services. This prevents other code from invoking the broadcast receiver. Here is an excerpt from
|
||||||
|
* a sample manifest:
|
||||||
|
* <pre>
|
||||||
|
* <service android:name=".MyUploadService"
|
||||||
|
* android:exported="true"
|
||||||
|
* android:permission="com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE" >
|
||||||
|
* <intent-filter>
|
||||||
|
* <action android:name="com.google.android.gms.gcm.ACTION_TASK_READY" />
|
||||||
|
* </intent-filter>
|
||||||
|
* </service>
|
||||||
|
* </pre>
|
||||||
|
* An execution contains the tag identifier which your client app provides. This identifies
|
||||||
|
* one "task", or piece of work, that you mean to perform. Consider the tag to be the key to which
|
||||||
|
* your task logic is paired.
|
||||||
|
* <pre>
|
||||||
|
* // Schedule a task to occur between five and fifteen seconds from now:
|
||||||
|
* OneoffTask myTask = new OneoffTask.Builder()
|
||||||
|
* .setService(MyGcmTaskService.class)
|
||||||
|
* .setExecutionWindow(
|
||||||
|
* 5 * DateUtil.MINUTE_IN_SECONDS, 15 * DateUtil.MINUTE_IN_SECONDS)
|
||||||
|
* .setTag("test-upload")
|
||||||
|
* .build();
|
||||||
|
* GcmNetworkManager.get(this).schedule(myTask);
|
||||||
|
* ...
|
||||||
|
* // Implement service logic to be notified when the task elapses:
|
||||||
|
* MyUploadService extends GcmTaskService {
|
||||||
|
*
|
||||||
|
* @Override public int onRunTask(TaskParams params) {
|
||||||
|
* // Do some upload work.
|
||||||
|
* return GcmNetworkManager.RESULT_SUCCESS;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
* To help in debugging your tasks, run
|
||||||
|
* <code>adb shell dumpsys activity service GcmService --endpoints [...]</code>
|
||||||
|
* If you want to execute your task immediately (for debugging) you can execute tasks from the
|
||||||
|
* command line via:
|
||||||
|
* <code>adb shell am broadcast -a "com.google.android.gms.gcm.ACTION_TRIGGER_TASK" \
|
||||||
|
* -e component <COMPONENT_NAME> -e tag <TAG></code>
|
||||||
|
* Where <strong>COMPONENT_NAME</strong>: The full
|
||||||
|
* {@link ComponentName#flattenToString()} returned for your implementation of
|
||||||
|
* {@link com.google.android.gms.gcm.GcmTaskService}.
|
||||||
|
* <strong>TAG</strong>: the tag you want to have land in
|
||||||
|
* {@link com.google.android.gms.gcm.GcmTaskService#onRunTask(com.google.android.gms.gcm.TaskParams)}
|
||||||
|
* Example usage for the gradle target GcmTestProxy service:
|
||||||
|
* <code>adb shell am broadcast -a "com.google.android.gms.gcm.ACTION_TRIGGER_TASK" \
|
||||||
|
* -e component "com.google.android.gms.gcm.test.proxy/.internal.nstest.TestNetworkTaskService" \
|
||||||
|
* -e tag "upload"</code>
|
||||||
|
* <strong>This is only available if the device is a test-keys build. This will replace any
|
||||||
|
* previously scheduled task with the same tag!</strong> This will have especially awkward effects
|
||||||
|
* if your original task was a periodic, because the debug task is scheduled as a one-off.
|
||||||
|
*/
|
||||||
|
public class GcmNetworkManager {
|
||||||
|
/**
|
||||||
|
* Indicates a task has failed, but not to reschedule.
|
||||||
|
*/
|
||||||
|
public static final int RESULT_FAILURE = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates a task has failed to execute, and must be retried with back-off.
|
||||||
|
*/
|
||||||
|
public static final int RESULT_RESCHEDULE = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates a task has successfully been executed, and can be removed from the queue.
|
||||||
|
*/
|
||||||
|
public static final int RESULT_SUCCESS = 0;
|
||||||
|
|
||||||
|
private static GcmNetworkManager INSTANCE;
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
|
private GcmNetworkManager(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels all tasks previously scheduled against the provided GcmTaskService. Note that a
|
||||||
|
* cancel will have no effect on an in-flight task.
|
||||||
|
*
|
||||||
|
* @param gcmTaskService The endpoint for which you want to cancel all outstanding tasks.
|
||||||
|
*/
|
||||||
|
public void cancelAllTasks(Class<? extends GcmTaskService> gcmTaskService) {
|
||||||
|
validateService(gcmTaskService.getName());
|
||||||
|
Intent scheduleIntent = createScheduleIntent();
|
||||||
|
if (scheduleIntent != null) {
|
||||||
|
scheduleIntent.putExtra(EXTRA_SCHEDULER_ACTION, SCHEDULER_ACTION_CANCEL_ALL);
|
||||||
|
scheduleIntent.putExtra(EXTRA_COMPONENT, new ComponentName(context, gcmTaskService));
|
||||||
|
context.sendBroadcast(scheduleIntent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel a task, specified by tag. Note that a cancel will have no effect on an in-flight
|
||||||
|
* task.
|
||||||
|
*
|
||||||
|
* @param tag The tag to uniquely identify this task on this endpoint.
|
||||||
|
* @param gcmTaskService The endpoint for which you want to cancel all outstanding tasks.
|
||||||
|
*/
|
||||||
|
public void cancelTask(String tag, Class<? extends GcmTaskService> gcmTaskService) {
|
||||||
|
if (TextUtils.isEmpty(tag) || tag.length() < 100) throw new IllegalArgumentException("tag invalid");
|
||||||
|
validateService(gcmTaskService.getName());
|
||||||
|
Intent scheduleIntent = createScheduleIntent();
|
||||||
|
if (scheduleIntent != null) {
|
||||||
|
scheduleIntent.putExtra(EXTRA_SCHEDULER_ACTION, SCHEDULER_ACTION_CANCEL);
|
||||||
|
scheduleIntent.putExtra(EXTRA_TAG, tag);
|
||||||
|
scheduleIntent.putExtra(EXTRA_COMPONENT, new ComponentName(context, gcmTaskService));
|
||||||
|
context.sendBroadcast(scheduleIntent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to access the GcmNetworkManager API.
|
||||||
|
*
|
||||||
|
* @param context Context of the calling app.
|
||||||
|
* @return GcmNetworkManager object.
|
||||||
|
*/
|
||||||
|
public static GcmNetworkManager getInstance(Context context) {
|
||||||
|
synchronized (GcmNetworkManager.class) {
|
||||||
|
if (INSTANCE == null) {
|
||||||
|
INSTANCE = new GcmNetworkManager(context);
|
||||||
|
}
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entry point to schedule a task with the network manager.
|
||||||
|
* <p/>
|
||||||
|
* If GooglePlayServices is unavailable (upgrading, missing, etc). This call will fail silently.
|
||||||
|
* You should wrap it in a call to {@link com.google.android.gms.common.GooglePlayServicesUtil#isGooglePlayServicesAvailable(android.content.Context)}</p>
|
||||||
|
*
|
||||||
|
* @param task Task constructed using {@link com.google.android.gms.gcm.Task.Builder}. Can be
|
||||||
|
* an instance of {@link com.google.android.gms.gcm.PeriodicTask} or
|
||||||
|
* {@link com.google.android.gms.gcm.OneoffTask}.
|
||||||
|
*/
|
||||||
|
public void schedule(Task task) {
|
||||||
|
validateService(task.getServiceName());
|
||||||
|
Intent scheduleIntent = createScheduleIntent();
|
||||||
|
if (scheduleIntent != null) {
|
||||||
|
Bundle extras = scheduleIntent.getExtras();
|
||||||
|
extras.putString(EXTRA_SCHEDULER_ACTION, SCHEDULER_ACTION_SCHEDULE);
|
||||||
|
task.toBundle(extras);
|
||||||
|
scheduleIntent.putExtras(extras);
|
||||||
|
context.sendBroadcast(scheduleIntent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Intent createScheduleIntent() {
|
||||||
|
if (!packageExists(GMS_PACKAGE_NAME)) return null;
|
||||||
|
Intent scheduleIntent = new Intent(ACTION_SCHEDULE);
|
||||||
|
scheduleIntent.setPackage(GMS_PACKAGE_NAME);
|
||||||
|
scheduleIntent.putExtra("app", PendingIntent.getBroadcast(context, 0, new Intent(), 0));
|
||||||
|
return scheduleIntent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean packageExists(String packageName) {
|
||||||
|
try {
|
||||||
|
PackageManager pm = context.getPackageManager();
|
||||||
|
pm.getPackageInfo(packageName, 0);
|
||||||
|
return true;
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateService(String serviceName) {
|
||||||
|
if (serviceName == null) throw new NullPointerException("No service provided");
|
||||||
|
Intent taskIntent = new Intent(ACTION_TASK_READY);
|
||||||
|
taskIntent.setPackage(context.getPackageName());
|
||||||
|
PackageManager pm = context.getPackageManager();
|
||||||
|
List<ResolveInfo> serviceResolves = pm.queryIntentServices(taskIntent, 0);
|
||||||
|
if (serviceResolves == null || serviceResolves.isEmpty())
|
||||||
|
throw new IllegalArgumentException("No service found");
|
||||||
|
for (ResolveInfo info : serviceResolves) {
|
||||||
|
if (serviceName.equals(info.serviceInfo.name)) return;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Service not supported.");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.gcm;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import com.google.android.gms.iid.InstanceID;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_TOPIC;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GcmPubSub provides a publish-subscribe model for sending GCM topic messages.
|
||||||
|
* <p/>
|
||||||
|
* An app can subscribe to different topics defined by the
|
||||||
|
* developer. The app server can then send messages to the subscribed devices
|
||||||
|
* without having to maintain topic-subscribers mapping. Topics do not
|
||||||
|
* need to be explicitly created before subscribing or publishing—they
|
||||||
|
* are automatically created when publishing or subscribing.
|
||||||
|
* <pre>
|
||||||
|
* String topic = "/topics/myTopic";
|
||||||
|
* String registrationToken = InstanceID.getInstance(context)
|
||||||
|
* .getToken(SENDER_ID, GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
|
||||||
|
* GcmPubSub.getInstance(context).subscribe(registrationToken, topic, null);
|
||||||
|
* // Messages published to the topic will be received as regular GCM messages
|
||||||
|
* // with 'from' set to "/topics/myTopic"</pre>
|
||||||
|
* To publish to a topic, see
|
||||||
|
* <a href="https://developer.android.com/google/gcm/server.html">GCM server documentation</a>.
|
||||||
|
*/
|
||||||
|
public class GcmPubSub {
|
||||||
|
|
||||||
|
private static final Pattern topicPattern = Pattern.compile("/topics/[a-zA-Z0-9-_.~%]{1,900}");
|
||||||
|
private static GcmPubSub INSTANCE;
|
||||||
|
|
||||||
|
private final InstanceID instanceId;
|
||||||
|
|
||||||
|
public GcmPubSub(Context context) {
|
||||||
|
this.instanceId = InstanceID.getInstance(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an instance of GCM PubSub.
|
||||||
|
*
|
||||||
|
* @return GcmPubSub instance
|
||||||
|
*/
|
||||||
|
public static synchronized GcmPubSub getInstance(Context context) {
|
||||||
|
if (INSTANCE == null) {
|
||||||
|
INSTANCE = new GcmPubSub(context);
|
||||||
|
}
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribes an app instance to a topic, enabling it to receive messages
|
||||||
|
* sent to that topic.
|
||||||
|
* <p/>
|
||||||
|
* The topic sender must be authorized to send messages to the
|
||||||
|
* app instance. To authorize it, call {@link com.google.android.gms.iid.InstanceID#getToken(java.lang.String, java.lang.String)}
|
||||||
|
* with the sender ID and {@link com.google.android.gms.gcm.GoogleCloudMessaging#INSTANCE_ID_SCOPE}
|
||||||
|
* <p/>
|
||||||
|
* Do not call this function on the main thread.
|
||||||
|
*
|
||||||
|
* @param registrationToken {@link com.google.android.gms.iid.InstanceID} token that authorizes topic
|
||||||
|
* sender to send messages to the app instance.
|
||||||
|
* @param topic developer defined topic name.
|
||||||
|
* Must match the following regular expression:
|
||||||
|
* "/topics/[a-zA-Z0-9-_.~%]{1,900}".
|
||||||
|
* @param extras (optional) additional information.
|
||||||
|
* @throws IOException if the request fails.
|
||||||
|
*/
|
||||||
|
public void subscribe(String registrationToken, String topic, Bundle extras) throws IOException {
|
||||||
|
if (TextUtils.isEmpty(registrationToken))
|
||||||
|
throw new IllegalArgumentException("No registration token!");
|
||||||
|
if (TextUtils.isEmpty(topic) || !topicPattern.matcher(topic).matches())
|
||||||
|
throw new IllegalArgumentException("Invalid topic: " + topic);
|
||||||
|
|
||||||
|
if (extras == null) extras = new Bundle();
|
||||||
|
extras.putString(EXTRA_TOPIC, topic);
|
||||||
|
instanceId.getToken(registrationToken, topic, extras);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsubscribes an app instance from a topic, stopping it from receiving
|
||||||
|
* any further messages sent to that topic.
|
||||||
|
* <p/>
|
||||||
|
* Do not call this function on the main thread.
|
||||||
|
*
|
||||||
|
* @param registrationToken {@link com.google.android.gms.iid.InstanceID} token
|
||||||
|
* for the same sender and scope that was previously
|
||||||
|
* used for subscribing to the topic.
|
||||||
|
* @param topic from which to stop receiving messages.
|
||||||
|
* @throws IOException if the request fails.
|
||||||
|
*/
|
||||||
|
public void unsubscribe(String registrationToken, String topic) throws IOException {
|
||||||
|
if (TextUtils.isEmpty(topic) || !topicPattern.matcher(topic).matches())
|
||||||
|
throw new IllegalArgumentException("Invalid topic: " + topic);
|
||||||
|
|
||||||
|
Bundle extras = new Bundle();
|
||||||
|
extras.putString(EXTRA_TOPIC, topic);
|
||||||
|
instanceId.deleteToken(registrationToken, topic, extras);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.gcm;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.ResolveInfo;
|
||||||
|
import android.content.pm.ServiceInfo;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.support.v4.content.WakefulBroadcastReceiver;
|
||||||
|
import android.util.Base64;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTRATION;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.ACTION_INSTANCE_ID;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_FROM;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_RAWDATA;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_RAWDATA_BASE64;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.GCMID_INSTANCE_ID;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.GCMID_REFRESH;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>WakefulBroadcastReceiver</code> that receives GCM messages and delivers them to an
|
||||||
|
* application-specific {@link com.google.android.gms.gcm.GcmListenerService} subclass.
|
||||||
|
* <p/>
|
||||||
|
* This receiver should be declared in your application's manifest file as follows:
|
||||||
|
* <p/>
|
||||||
|
* <pre>
|
||||||
|
* <receiver
|
||||||
|
* android:name="com.google.android.gms.gcm.GcmReceiver"
|
||||||
|
* android:exported="true"
|
||||||
|
* android:permission="com.google.android.c2dm.permission.SEND" >
|
||||||
|
* <intent-filter>
|
||||||
|
* <action android:name="com.google.android.c2dm.intent.RECEIVE" />
|
||||||
|
* <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
|
||||||
|
* <category android:name="YOUR_PACKAGE_NAME" />
|
||||||
|
* </intent-filter>
|
||||||
|
* </receiver></pre>
|
||||||
|
* The <code>com.google.android.c2dm.permission.SEND</code> permission is held by Google Play
|
||||||
|
* services. This prevents other apps from invoking the broadcast receiver.
|
||||||
|
*/
|
||||||
|
public class GcmReceiver extends WakefulBroadcastReceiver {
|
||||||
|
private static final String TAG = "GcmReceiver";
|
||||||
|
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
sanitizeIntent(context, intent);
|
||||||
|
enforceIntentClassName(context, intent);
|
||||||
|
sendIntent(context, intent);
|
||||||
|
if (getResultCode() == 0) setResultCodeIfOrdered(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sanitizeIntent(Context context, Intent intent) {
|
||||||
|
intent.setComponent(null);
|
||||||
|
intent.setPackage(context.getPackageName());
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||||
|
intent.removeCategory(context.getPackageName());
|
||||||
|
}
|
||||||
|
String from = intent.getStringExtra(EXTRA_FROM);
|
||||||
|
if (ACTION_C2DM_REGISTRATION.equals(intent.getAction()) || GCMID_INSTANCE_ID.equals(from) || GCMID_REFRESH.equals(from)) {
|
||||||
|
intent.setAction(ACTION_INSTANCE_ID);
|
||||||
|
}
|
||||||
|
String base64encoded = intent.getStringExtra(EXTRA_RAWDATA_BASE64);
|
||||||
|
if (base64encoded != null) {
|
||||||
|
intent.putExtra(EXTRA_RAWDATA, Base64.decode(base64encoded, Base64.DEFAULT));
|
||||||
|
intent.removeExtra(EXTRA_RAWDATA_BASE64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enforceIntentClassName(Context context, Intent intent) {
|
||||||
|
ResolveInfo resolveInfo = context.getPackageManager().resolveService(intent, 0);
|
||||||
|
if (resolveInfo == null || resolveInfo.serviceInfo == null) {
|
||||||
|
Log.e(TAG, "Failed to resolve target intent service, skipping classname enforcement");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ServiceInfo serviceInfo = resolveInfo.serviceInfo;
|
||||||
|
if (!context.getPackageName().equals(serviceInfo.packageName) || serviceInfo.name == null) {
|
||||||
|
Log.e(TAG, "Error resolving target intent service, skipping classname enforcement. Resolved service was: " + serviceInfo.packageName + "/" + serviceInfo.name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
intent.setClassName(context, serviceInfo.name.startsWith(".") ? (context.getPackageName() + serviceInfo.name) : serviceInfo.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendIntent(Context context, Intent intent) {
|
||||||
|
setResultCodeIfOrdered(500);
|
||||||
|
try {
|
||||||
|
ComponentName startedComponent;
|
||||||
|
if (context.checkCallingOrSelfPermission(Manifest.permission.WAKE_LOCK) == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
startedComponent = startWakefulService(context, intent);
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "Missing wake lock permission, service start may be delayed");
|
||||||
|
startedComponent = context.startService(intent);
|
||||||
|
}
|
||||||
|
if (startedComponent == null) {
|
||||||
|
Log.e(TAG, "Error while delivering the message: ServiceIntent not found.");
|
||||||
|
setResultCodeIfOrdered(404);
|
||||||
|
} else {
|
||||||
|
setResultCodeIfOrdered(-1);
|
||||||
|
}
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
Log.e(TAG, "Error while delivering the message to the serviceIntent", e);
|
||||||
|
setResultCodeIfOrdered(401);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setResultCodeIfOrdered(int code) {
|
||||||
|
if (isOrderedBroadcast()) {
|
||||||
|
setResultCode(code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.gcm;
|
||||||
|
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import android.os.PowerManager;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.microg.gms.common.PublicApi;
|
||||||
|
import org.microg.gms.gcm.GcmConstants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implemented by the client application to provide an endpoint for the {@link com.google.android.gms.gcm.GcmNetworkManager}
|
||||||
|
* to call back to when a task is ready to be executed.
|
||||||
|
* <p/>
|
||||||
|
* Clients must add this service to their manifest and implement
|
||||||
|
* {@link com.google.android.gms.gcm.GcmTaskService#onRunTask(com.google.android.gms.gcm.TaskParams)}.
|
||||||
|
* This service must provide an {@link IntentFilter} on the action
|
||||||
|
* {@link com.google.android.gms.gcm.GcmTaskService#SERVICE_ACTION_EXECUTE_TASK}. Here's an example:
|
||||||
|
* <pre>
|
||||||
|
* <service android:name="MyTaskService"
|
||||||
|
* android:permission="com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE"
|
||||||
|
* android:exported="true">
|
||||||
|
* <intent-filter>
|
||||||
|
* <action android:name="com.google.android.gms.gcm.ACTION_TASK_READY"/>
|
||||||
|
* </intent-filter>
|
||||||
|
* </service>
|
||||||
|
* </pre>
|
||||||
|
* The return value of onRunTask(TaskParams) will determine what the manager does with subsequent
|
||||||
|
* executions of this task. Specifically you can return {@link com.google.android.gms.gcm.GcmNetworkManager#RESULT_RESCHEDULE}
|
||||||
|
* to have this task be re-executed again shortly subject to exponential back-off. Returning
|
||||||
|
* {@link com.google.android.gms.gcm.GcmNetworkManager#RESULT_FAILURE} for a periodic task will only affect the executing
|
||||||
|
* instance of the task, and future tasks will be executed as normal.
|
||||||
|
* <p/>
|
||||||
|
* Once a task is running it will not be cancelled, however a newly scheduled task with the same
|
||||||
|
* tag will not be executed until the active task has completed. This newly scheduled task will
|
||||||
|
* replace the previous task, regardless of whether the previous task returned
|
||||||
|
* {@link com.google.android.gms.gcm.GcmNetworkManager#RESULT_RESCHEDULE}.
|
||||||
|
* <p/>
|
||||||
|
* Bear in mind that your service may receive multiple calls from the scheduler at once
|
||||||
|
* (specifically if you've made multiple schedule requests that overlap). If this is the case, your
|
||||||
|
* implementation of {@link com.google.android.gms.gcm.GcmTaskService#onRunTask(com.google.android.gms.gcm.TaskParams)} must be thread-safe.
|
||||||
|
* <p/>
|
||||||
|
* The scheduler will hold a {@link PowerManager.WakeLock} for your service, however
|
||||||
|
* <strong>after three minutes of execution if your task has not returned it will be considered to
|
||||||
|
* have timed out, and the wakelock will be released.</strong> Rescheduling your task at this point
|
||||||
|
* will have no effect.
|
||||||
|
* If you suspect your task will run longer than this you should start your own service
|
||||||
|
* explicitly or use some other mechanism; this API is intended for relatively quick network
|
||||||
|
* operations.
|
||||||
|
* <p/>
|
||||||
|
* Your task will run at priority Process.THREAD_PRIORITY_BACKGROUND. If this
|
||||||
|
* is not appropriate, you should start your own service with suitably
|
||||||
|
* conditioned threads.
|
||||||
|
*/
|
||||||
|
@PublicApi
|
||||||
|
public abstract class GcmTaskService extends Service {
|
||||||
|
private static final String TAG = "GcmTaskService";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action broadcast by the GcmNetworkManager to the requesting package when
|
||||||
|
* a scheduled task is ready for execution.
|
||||||
|
*/
|
||||||
|
public static final String SERVICE_ACTION_EXECUTE_TASK = GcmConstants.ACTION_TASK_READY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action that a {@link com.google.android.gms.gcm.GcmTaskService} is started with when the service needs to initialize
|
||||||
|
* its tasks.
|
||||||
|
*/
|
||||||
|
public static final String SERVICE_ACTION_INITIALIZE = GcmConstants.ACTION_TASK_INITIALZE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* You must protect your service with this permission to avoid being bound to by an
|
||||||
|
* application other than Google Play Services.
|
||||||
|
*/
|
||||||
|
public static final String SERVICE_PERMISSION = GcmConstants.PERMISSION_NETWORK_TASK;
|
||||||
|
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When your package is removed or updated, all of its network tasks are cleared by the
|
||||||
|
* GcmNetworkManager. You can override this method to reschedule them in the case of an
|
||||||
|
* updated package. This is not called when your application is first installed.
|
||||||
|
* <p/>
|
||||||
|
* This is called on your application's main thread.
|
||||||
|
*/
|
||||||
|
public void onInitializeTasks() {
|
||||||
|
// To be overwritten
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override this function to provide the logic for your task execution.
|
||||||
|
*
|
||||||
|
* @param params Parameters provided at schedule time with
|
||||||
|
* {@link com.google.android.gms.gcm.OneoffTask.Builder#setTag(java.lang.String)}
|
||||||
|
* @return One of {@link com.google.android.gms.gcm.GcmNetworkManager#RESULT_SUCCESS},
|
||||||
|
* {@link com.google.android.gms.gcm.GcmNetworkManager#RESULT_RESCHEDULE}, or
|
||||||
|
* {@link com.google.android.gms.gcm.GcmNetworkManager#RESULT_FAILURE}.
|
||||||
|
*/
|
||||||
|
public abstract int onRunTask(TaskParams params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receives the command to begin doing work, for which it spawns another thread.
|
||||||
|
*/
|
||||||
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
|
intent.setExtrasClassLoader(PendingCallback.class.getClassLoader());
|
||||||
|
if (SERVICE_ACTION_EXECUTE_TASK.equals(intent.getAction())) {
|
||||||
|
String tag = intent.getStringExtra("tag");
|
||||||
|
Parcelable callback = intent.getParcelableExtra("callback");
|
||||||
|
Bundle extras = intent.getBundleExtra("extras");
|
||||||
|
if (callback == null || !(callback instanceof PendingCallback)) {
|
||||||
|
Log.w(TAG, tag + ": Invalid callback!");
|
||||||
|
return START_NOT_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO ensure single instance
|
||||||
|
|
||||||
|
// TODO run task in new thread
|
||||||
|
} else if (SERVICE_ACTION_INITIALIZE.equals(intent.getAction())) {
|
||||||
|
this.onInitializeTasks();
|
||||||
|
|
||||||
|
// TODO ensure single instance
|
||||||
|
}
|
||||||
|
|
||||||
|
return START_NOT_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,322 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.gcm;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import com.google.android.gms.iid.InstanceID;
|
||||||
|
|
||||||
|
import org.microg.gms.common.PublicApi;
|
||||||
|
import org.microg.gms.gcm.CloudMessagingRpc;
|
||||||
|
import org.microg.gms.gcm.GcmConstants;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_RECEIVE;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_DELAY;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_ERROR;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSAGE_ID;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSAGE_TYPE;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_REGISTRATION_ID;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_SENDER;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_SENDER_LEGACY;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_SEND_FROM;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_SEND_TO;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_TTL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GoogleCloudMessaging (GCM) enables apps to communicate with their app servers
|
||||||
|
* using simple messages.
|
||||||
|
* <p/>
|
||||||
|
* To send or receive messages, the app must get a
|
||||||
|
* registrationToken from {@link com.google.android.gms.iid.InstanceID#getToken(java.lang.String, java.lang.String)}, which authorizes an
|
||||||
|
* app server to send messages to an app instance. Pass sender ID and
|
||||||
|
* {@link com.google.android.gms.gcm.GoogleCloudMessaging#INSTANCE_ID_SCOPE} as parameters to the method.
|
||||||
|
* A sender ID is a project number acquired from the API console, as described in
|
||||||
|
* <a href="http://developer.android.com/google/gcm/gs.html">Getting Started</a>.
|
||||||
|
* <p/>
|
||||||
|
* In order to receive GCM messages, declare {@link com.google.android.gms.gcm.GcmReceiver}
|
||||||
|
* and an implementation of {@link com.google.android.gms.gcm.GcmListenerService} in the app manifest.
|
||||||
|
* {@link com.google.android.gms.gcm.GcmReceiver} will pass the incoming messages to the implementation
|
||||||
|
* of {@link com.google.android.gms.gcm.GcmListenerService}. To process messages, override base class
|
||||||
|
* methods to handle any events required by the application.
|
||||||
|
* <p/>
|
||||||
|
* Client apps can send upstream messages back to the app server using the XMPP-based
|
||||||
|
* <a href="http://developer.android.com/google/gcm/ccs.html">Cloud Connection Server</a>,
|
||||||
|
* For example:
|
||||||
|
* <p/>
|
||||||
|
* gcm.send(SENDER_ID + "@gcm.googleapis.com", id, data);</pre>
|
||||||
|
* See <a href="https://developers.google.com/cloud-messaging/android/client">Implementing GCM Client on Android</a> for more details.
|
||||||
|
*/
|
||||||
|
@PublicApi
|
||||||
|
public class GoogleCloudMessaging {
|
||||||
|
/**
|
||||||
|
* The GCM {@link com.google.android.gms.gcm.GoogleCloudMessaging#register(java.lang.String...)} and {@link com.google.android.gms.gcm.GoogleCloudMessaging#unregister()} methods are
|
||||||
|
* blocking. Blocking methods must not be called on the main thread.
|
||||||
|
*/
|
||||||
|
public static final String ERROR_MAIN_THREAD = "MAIN_THREAD";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The device can't read the response, or there was a 500/503 from the
|
||||||
|
* server that can be retried later. The application should use exponential
|
||||||
|
* back off and retry.
|
||||||
|
*/
|
||||||
|
public static final String ERROR_SERVICE_NOT_AVAILABLE = GcmConstants.ERROR_SERVICE_NOT_AVAILABLE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies scope used in obtaining GCM registrationToken when calling
|
||||||
|
* {@link com.google.android.gms.iid.InstanceID#getToken(java.lang.String, java.lang.String)}
|
||||||
|
*/
|
||||||
|
public static final String INSTANCE_ID_SCOPE = GcmConstants.INSTANCE_ID_SCOPE_GCM;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returned by {@link com.google.android.gms.gcm.GoogleCloudMessaging#getMessageType(android.content.Intent)} to indicate that the server deleted
|
||||||
|
* some pending messages because they exceeded the storage limits. The
|
||||||
|
* application should contact the server to retrieve the discarded messages.
|
||||||
|
*
|
||||||
|
* @deprecated Instead implement {@link com.google.android.gms.gcm.GcmListenerService#onDeletedMessages()}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static final String MESSAGE_TYPE_DELETED = GcmConstants.MESSAGE_TYPE_DELETED_MESSAGE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returned by {@link com.google.android.gms.gcm.GoogleCloudMessaging#getMessageType(android.content.Intent)} to indicate a regular message.
|
||||||
|
*
|
||||||
|
* @deprecated Instead implement {@link com.google.android.gms.gcm.GcmListenerService#onMessageReceived(java.lang.String, android.os.Bundle)}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static final String MESSAGE_TYPE_MESSAGE = GcmConstants.MESSAGE_TYPE_GCM;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returned by {@link com.google.android.gms.gcm.GoogleCloudMessaging#getMessageType(android.content.Intent)} to indicate a send error.
|
||||||
|
* The intent includes the message ID of the message and an error code.
|
||||||
|
*
|
||||||
|
* @deprecated Instead implement {@link com.google.android.gms.gcm.GcmListenerService#onSendError(java.lang.String, java.lang.String)}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static final String MESSAGE_TYPE_SEND_ERROR = GcmConstants.MESSAGE_TYPE_SEND_ERROR;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returned by {@link com.google.android.gms.gcm.GoogleCloudMessaging#getMessageType(android.content.Intent)} to indicate a sent message has been received by the GCM
|
||||||
|
* server. The intent includes the message ID of the message.
|
||||||
|
*
|
||||||
|
* @deprecated Instead implement {@link com.google.android.gms.gcm.GcmListenerService#onMessageSent(java.lang.String)}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static final String MESSAGE_TYPE_SEND_EVENT = GcmConstants.MESSAGE_TYPE_SEND_EVENT;
|
||||||
|
|
||||||
|
private static GoogleCloudMessaging instance;
|
||||||
|
|
||||||
|
private CloudMessagingRpc rpc;
|
||||||
|
private Context context;
|
||||||
|
|
||||||
|
public GoogleCloudMessaging() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Must be called when your application is done using GCM, to release
|
||||||
|
* internal resources.
|
||||||
|
*/
|
||||||
|
public synchronized void close() {
|
||||||
|
instance = null;
|
||||||
|
rpc.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the singleton instance of GCM.
|
||||||
|
*/
|
||||||
|
public static GoogleCloudMessaging getInstance(Context context) {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new GoogleCloudMessaging();
|
||||||
|
instance.context = context.getApplicationContext();
|
||||||
|
instance.rpc = new CloudMessagingRpc(instance.context);
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the message type from an intent passed into a client app's broadcast receiver. There
|
||||||
|
* are two general categories of messages passed from the server: regular GCM messages,
|
||||||
|
* and special GCM status messages.
|
||||||
|
* <p/>
|
||||||
|
* The possible types are:
|
||||||
|
* {@link #MESSAGE_TYPE_MESSAGE}, {@link #MESSAGE_TYPE_DELETED}, {@link #MESSAGE_TYPE_SEND_EVENT} and {@link #MESSAGE_TYPE_SEND_ERROR}
|
||||||
|
* <p/>
|
||||||
|
* You can use this method to filter based on message type. Since it is likely that GCM will
|
||||||
|
* be extended in the future with new message types, just ignore any message types you're not
|
||||||
|
* interested in, or that you don't recognize.
|
||||||
|
*
|
||||||
|
* @return The message type or null if the intent is not a GCM intent
|
||||||
|
*/
|
||||||
|
public String getMessageType(Intent intent) throws IOException {
|
||||||
|
if (intent == null || !ACTION_C2DM_RECEIVE.equals(intent.getAction())) return null;
|
||||||
|
if (!intent.hasExtra(EXTRA_MESSAGE_TYPE)) return MESSAGE_TYPE_MESSAGE;
|
||||||
|
return intent.getStringExtra(EXTRA_MESSAGE_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the application for GCM and return the registration ID. You must call this once,
|
||||||
|
* when your application is installed, and send the returned registration ID to the server.
|
||||||
|
* <p/>
|
||||||
|
* Repeated calls to this method will return the original registration ID.
|
||||||
|
* <p/>
|
||||||
|
* If you want to modify the list of senders, you must call <code>unregister()</code> first.
|
||||||
|
* <p/>
|
||||||
|
* Most applications use a single sender ID. You may use multiple senders if different
|
||||||
|
* servers may send messages to the app or for testing.</p>
|
||||||
|
*
|
||||||
|
* @param senderIds list of project numbers or Google accounts identifying who is allowed to
|
||||||
|
* send messages to this application.
|
||||||
|
* @return registration id
|
||||||
|
* @throws IOException
|
||||||
|
* @deprecated Instead, for GCM registration, use
|
||||||
|
* {@link com.google.android.gms.iid.InstanceID#getToken(java.lang.String, java.lang.String)}.
|
||||||
|
* Set authorizedEntity to a sender ID and scope to {@link com.google.android.gms.gcm.GoogleCloudMessaging#INSTANCE_ID_SCOPE}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public String register(String... senderIds) throws IOException {
|
||||||
|
if (Looper.getMainLooper() == Looper.myLooper()) throw new IOException(ERROR_MAIN_THREAD);
|
||||||
|
|
||||||
|
if (senderIds == null || senderIds.length == 0) throw new IllegalArgumentException("No sender ids");
|
||||||
|
StringBuilder sb = new StringBuilder(senderIds[0]);
|
||||||
|
for (int i = 1; i < senderIds.length; i++) {
|
||||||
|
sb.append(',').append(senderIds[i]);
|
||||||
|
}
|
||||||
|
String sender = sb.toString();
|
||||||
|
|
||||||
|
if (isLegacyFallback()) {
|
||||||
|
Bundle extras = new Bundle();
|
||||||
|
extras.putString(EXTRA_SENDER_LEGACY, sender);
|
||||||
|
return InstanceID.getInstance(context).getToken(sb.toString(), INSTANCE_ID_SCOPE, extras);
|
||||||
|
} else {
|
||||||
|
Bundle extras = new Bundle();
|
||||||
|
extras.putString(EXTRA_SENDER, sender);
|
||||||
|
return rpc.handleRegisterMessageResult(rpc.sendRegisterMessageBlocking(extras));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send an upstream ("device to cloud") message. You can only use the upstream feature
|
||||||
|
* if your GCM implementation uses the XMPP-based
|
||||||
|
* <a href="http://developer.android.com/google/gcm/ccs.html">Cloud Connection Server</a>.
|
||||||
|
* <p/>
|
||||||
|
* The current limits for max storage time and number of outstanding messages per
|
||||||
|
* application are documented in the
|
||||||
|
* <a href="http://developer.android.com/google/gcm/index.html">GCM Developers Guide</a>.</p>
|
||||||
|
*
|
||||||
|
* @param to string identifying the receiver of the message in the format of
|
||||||
|
* <code>SENDER_ID@gcm.googleapis.com</code>. The <code>SENDER_ID</code> should be one of the sender
|
||||||
|
* IDs used when calling {@link com.google.android.gms.iid.InstanceID#getToken(java.lang.String, java.lang.String)}
|
||||||
|
* @param msgId ID of the message. This is generated by the application. It must be
|
||||||
|
* unique for each message. This allows error callbacks and debugging.
|
||||||
|
* @param timeToLive If 0, we'll attempt to send immediately and return an
|
||||||
|
* error if we're not connected. Otherwise, the message will be queued.
|
||||||
|
* As for server-side messages, we don't return an error if the message has been
|
||||||
|
* dropped because of TTL—this can happen on the server side, and it would require
|
||||||
|
* extra communication.
|
||||||
|
* @param data key/value pairs to be sent. Values must be String, any other type will
|
||||||
|
* be ignored.
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void send(String to, String msgId, long timeToLive, Bundle data) throws IOException {
|
||||||
|
if (TextUtils.isEmpty(to)) throw new IllegalArgumentException("Invalid 'to'");
|
||||||
|
|
||||||
|
if (isLegacyFallback()) {
|
||||||
|
Bundle extras = new Bundle();
|
||||||
|
for (String key : data.keySet()) {
|
||||||
|
Object o = extras.get(key);
|
||||||
|
if (o instanceof String) {
|
||||||
|
extras.putString("gcm." + key, (String) o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
extras.putString(EXTRA_SEND_TO, to);
|
||||||
|
extras.putString(EXTRA_MESSAGE_ID, msgId);
|
||||||
|
InstanceID.getInstance(context).requestToken("GCM", "upstream", extras);
|
||||||
|
} else {
|
||||||
|
Bundle extras = data != null ? new Bundle(data) : new Bundle();
|
||||||
|
extras.putString(EXTRA_SEND_TO, to);
|
||||||
|
extras.putString(EXTRA_SEND_FROM, getFrom(to));
|
||||||
|
extras.putString(EXTRA_MESSAGE_ID, msgId);
|
||||||
|
extras.putLong(EXTRA_TTL, timeToLive);
|
||||||
|
extras.putInt(EXTRA_DELAY, -1);
|
||||||
|
rpc.sendGcmMessage(extras);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send an upstream ("device to cloud") message. You can only use the upstream feature
|
||||||
|
* if your GCM implementation uses the XMPP-based
|
||||||
|
* <a href="http://developer.android.com/google/gcm/ccs.html">Cloud Connection Server</a>.
|
||||||
|
* <p/>
|
||||||
|
* When there is an active connection the message will be sent immediately, otherwise the
|
||||||
|
* message will be queued for the maximum interval.
|
||||||
|
*
|
||||||
|
* @param to string identifying the receiver of the message in the format of
|
||||||
|
* <code>SENDER_ID@gcm.googleapis.com</code>. The <code>SENDER_ID</code> should be one of the sender
|
||||||
|
* IDs used when calling {@link com.google.android.gms.iid.InstanceID#getToken(java.lang.String, java.lang.String)}
|
||||||
|
* @param msgId ID of the message. This is generated by the application. It must be
|
||||||
|
* unique for each message. This allows error callbacks and debugging.
|
||||||
|
* @param data key/value pairs to be sent. Values must be String—any other type will
|
||||||
|
* be ignored.
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void send(String to, String msgId, Bundle data) throws IOException {
|
||||||
|
send(to, msgId, -1, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister the application. Calling <code>unregister()</code> stops any
|
||||||
|
* messages from the server. This is a blocking call—you shouldn't call
|
||||||
|
* it from the UI thread.
|
||||||
|
* <p/>
|
||||||
|
* You should rarely (if ever) need to call this method. Not only is it
|
||||||
|
* expensive in terms of resources, but it invalidates all your registration IDs
|
||||||
|
* returned from register() or subscribe(). This should not be done
|
||||||
|
* unnecessarily. A better approach is to simply have your server stop
|
||||||
|
* sending messages.
|
||||||
|
*
|
||||||
|
* @throws IOException if we can't connect to server to unregister.
|
||||||
|
* @deprecated Instead use
|
||||||
|
* {@link com.google.android.gms.iid.InstanceID#deleteToken(java.lang.String, java.lang.String)} or
|
||||||
|
* {@link com.google.android.gms.iid.InstanceID#deleteInstanceID()}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void unregister() throws IOException {
|
||||||
|
if (Looper.getMainLooper() == Looper.myLooper()) throw new IOException(ERROR_MAIN_THREAD);
|
||||||
|
InstanceID.getInstance(context).deleteInstanceID();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isLegacyFallback() {
|
||||||
|
String gcmPackageName = CloudMessagingRpc.getGcmPackageName(context);
|
||||||
|
return gcmPackageName != null && gcmPackageName.endsWith(".gsf");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getFrom(String to) {
|
||||||
|
int i = to.indexOf('@');
|
||||||
|
if (i > 0) {
|
||||||
|
to = to.substring(0, i);
|
||||||
|
}
|
||||||
|
return InstanceID.getInstance(context).getStore().get("", to, INSTANCE_ID_SCOPE);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,221 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.gcm;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Parcel;
|
||||||
|
|
||||||
|
import org.microg.gms.common.PublicApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A task that will execute once,at some point within the specified window.
|
||||||
|
* If one of {@link com.google.android.gms.gcm.GcmNetworkManager#cancelTask(java.lang.String, java.lang.Class<? extends com.google.android.gms.gcm.GcmTaskService>)} or
|
||||||
|
* {@link com.google.android.gms.gcm.GcmNetworkManager#cancelAllTasks(java.lang.Class<? extends com.google.android.gms.gcm.GcmTaskService>)} is called before this
|
||||||
|
* executes it will be cancelled.
|
||||||
|
* <p/>
|
||||||
|
* Note that you can request a one-off task to be executed at any point in the future, but to
|
||||||
|
* prevent abuse the scheduler will only set an alarm at a minimum of 30 seconds in the
|
||||||
|
* future. Your task can still be run earlier than this if some network event occurs to wake up
|
||||||
|
* the scheduler.
|
||||||
|
*/
|
||||||
|
@PublicApi
|
||||||
|
public class OneoffTask extends com.google.android.gms.gcm.Task {
|
||||||
|
private final long windowStart;
|
||||||
|
private final long windowEnd;
|
||||||
|
|
||||||
|
private OneoffTask(Builder builder) {
|
||||||
|
super(builder);
|
||||||
|
this.windowStart = builder.windowStart;
|
||||||
|
this.windowEnd = builder.windowEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
private OneoffTask(Parcel source) {
|
||||||
|
super(source);
|
||||||
|
this.windowStart = source.readLong();
|
||||||
|
this.windowEnd = source.readLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The number of seconds from now by which this task must have executed.
|
||||||
|
*/
|
||||||
|
public long getWindowEnd() {
|
||||||
|
return windowEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The number of seconds from now at which this task is eligible for execution.
|
||||||
|
*/
|
||||||
|
public long getWindowStart() {
|
||||||
|
return windowStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert the task object into the provided bundle for IPC. Use #fromBundle to recreate the
|
||||||
|
* object on the other side.
|
||||||
|
*/
|
||||||
|
public void toBundle(Bundle bundle) {
|
||||||
|
super.toBundle(bundle);
|
||||||
|
bundle.putLong("window_start", this.windowStart);
|
||||||
|
bundle.putLong("window_end", this.windowEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return super.toString()
|
||||||
|
+ " windowStart=" + this.getWindowStart()
|
||||||
|
+ " windowEnd=" + this.getWindowEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeToParcel(Parcel parcel, int flags) {
|
||||||
|
super.writeToParcel(parcel, flags);
|
||||||
|
parcel.writeLong(this.windowStart);
|
||||||
|
parcel.writeLong(this.windowEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Creator<OneoffTask> CREATOR = new Creator<OneoffTask>() {
|
||||||
|
@Override
|
||||||
|
public OneoffTask createFromParcel(Parcel source) {
|
||||||
|
return new OneoffTask(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OneoffTask[] newArray(int size) {
|
||||||
|
return new OneoffTask[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static class Builder extends Task.Builder {
|
||||||
|
private long windowStart = -1;
|
||||||
|
private long windowEnd = -1;
|
||||||
|
|
||||||
|
public Builder() {
|
||||||
|
this.isPersisted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OneoffTask build() {
|
||||||
|
return new OneoffTask(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mandatory setter for creating a one-off task. You specify the earliest point in
|
||||||
|
* time in the future from which your task might start executing, as well as the
|
||||||
|
* latest point in time in the future at which your task must have executed.
|
||||||
|
*
|
||||||
|
* @param windowStartDelaySeconds Earliest point from which your task is eligible to
|
||||||
|
* run.
|
||||||
|
* @param windowEndDelaySeconds Latest point at which your task must be run.
|
||||||
|
*/
|
||||||
|
public OneoffTask.Builder setExecutionWindow(long windowStartDelaySeconds, long windowEndDelaySeconds) {
|
||||||
|
this.windowEnd = windowEndDelaySeconds;
|
||||||
|
this.windowStart = windowStartDelaySeconds;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional setter for specifying any extra parameters necessary for the task.
|
||||||
|
*/
|
||||||
|
public OneoffTask.Builder setExtras(Bundle extras) {
|
||||||
|
this.extras = extras;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional setter to specify whether this task should be persisted across reboots..
|
||||||
|
* Callers <strong>must</strong> hold the permission
|
||||||
|
* android.Manifest.permission.RECEIVE_BOOT_COMPLETED, otherwise this setter is
|
||||||
|
* ignored.
|
||||||
|
*
|
||||||
|
* @param isPersisted True if this task should be persisted across device reboots.
|
||||||
|
*/
|
||||||
|
public OneoffTask.Builder setPersisted(boolean isPersisted) {
|
||||||
|
this.isPersisted = isPersisted;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the network state your task requires to run. <strong>If the specified network is
|
||||||
|
* unavailable your task will not be executed until it becomes available.</strong>
|
||||||
|
* <p/>
|
||||||
|
* The default for either a periodic or one-off task is
|
||||||
|
* {@link com.google.android.gms.gcm.Task#NETWORK_STATE_CONNECTED}. Note that changing this to
|
||||||
|
* {@link com.google.android.gms.gcm.Task#NETWORK_STATE_ANY} means there is no guarantee that data will be available
|
||||||
|
* when your task executes.
|
||||||
|
* <p/>
|
||||||
|
* In addition, the only guarantee for connectivity is at the moment of execution - it is
|
||||||
|
* possible for the device to lose data shortly after your task begins executing.
|
||||||
|
*/
|
||||||
|
public OneoffTask.Builder setRequiredNetwork(int requiredNetworkState) {
|
||||||
|
this.requiredNetworkState = requiredNetworkState;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether your task requires that the device be connected to power in order to
|
||||||
|
* execute.
|
||||||
|
* <p/>
|
||||||
|
* Use this to defer nonessential operations whenever possible. Note that if you set this
|
||||||
|
* field and the device is not connected to power <strong>your task will not run</strong>
|
||||||
|
* until the device is plugged in.
|
||||||
|
* <p/>
|
||||||
|
* One way to deal with your task not executing until the constraint is met is to schedule
|
||||||
|
* another task without the constraints that is subject to some deadline that you can abide.
|
||||||
|
* This task would be responsible for executing your fallback logic.
|
||||||
|
*/
|
||||||
|
public OneoffTask.Builder setRequiresCharging(boolean requiresCharging) {
|
||||||
|
this.requiresCharging = requiresCharging;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whichever {@link com.google.android.gms.gcm.GcmTaskService} you implement to execute the logic for this task.
|
||||||
|
*
|
||||||
|
* @param gcmTaskService Endpoint against which you're scheduling this task.
|
||||||
|
*/
|
||||||
|
public OneoffTask.Builder setService(Class<? extends GcmTaskService> gcmTaskService) {
|
||||||
|
this.gcmTaskService = gcmTaskService.getName();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mandatory setter for specifying the tag identifer for this task. This tag will be
|
||||||
|
* returned at execution time to your endpoint. See
|
||||||
|
* {@link com.google.android.gms.gcm.GcmTaskService#onRunTask(com.google.android.gms.gcm.TaskParams)}
|
||||||
|
* Maximum tag length is 100.<
|
||||||
|
*
|
||||||
|
* @param tag String identifier for this task. Consecutive schedule calls for the same
|
||||||
|
* tag will update any preexisting task with the same tag.
|
||||||
|
*/
|
||||||
|
public OneoffTask.Builder setTag(String tag) {
|
||||||
|
this.tag = tag;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional setter to specify whether this task should override any preexisting tasks
|
||||||
|
* with the same tag. This defaults to false, which means that a new task will not
|
||||||
|
* override an existing one.
|
||||||
|
*
|
||||||
|
* @param updateCurrent True to update the current task with the parameters of the new.
|
||||||
|
* Default false.
|
||||||
|
*/
|
||||||
|
public OneoffTask.Builder setUpdateCurrent(boolean updateCurrent) {
|
||||||
|
this.updateCurrent = updateCurrent;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.gcm;
|
||||||
|
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
public class PendingCallback implements Parcelable {
|
||||||
|
private final IBinder binder;
|
||||||
|
|
||||||
|
public PendingCallback(IBinder binder) {
|
||||||
|
this.binder = binder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PendingCallback(Parcel in) {
|
||||||
|
this.binder = in.readStrongBinder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IBinder getBinder() {
|
||||||
|
return binder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
dest.writeStrongBinder(binder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Creator<PendingCallback> CREATOR = new Creator<PendingCallback>() {
|
||||||
|
@Override
|
||||||
|
public PendingCallback createFromParcel(Parcel source) {
|
||||||
|
return new PendingCallback(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingCallback[] newArray(int size) {
|
||||||
|
return new PendingCallback[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,235 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.gcm;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Parcel;
|
||||||
|
|
||||||
|
import org.microg.gms.common.PublicApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A periodic task is one that will recur at the specified interval, without needing to be
|
||||||
|
* rescheduled.
|
||||||
|
* Schedule a task that will recur until the user calls one of
|
||||||
|
* {@link com.google.android.gms.gcm.GcmNetworkManager#cancelAllTasks(java.lang.Class<? extends com.google.android.gms.gcm.GcmTaskService>)}, or
|
||||||
|
* {@link com.google.android.gms.gcm.GcmNetworkManager#cancelTask(java.lang.String, java.lang.Class<? extends com.google.android.gms.gcm.GcmTaskService>)} with
|
||||||
|
* an identifying tag.
|
||||||
|
* <p/>
|
||||||
|
* Periodic tasks will not be scheduled if their period is below a certain minimum
|
||||||
|
* (currently 30 seconds).
|
||||||
|
*/
|
||||||
|
@PublicApi
|
||||||
|
public class PeriodicTask extends com.google.android.gms.gcm.Task {
|
||||||
|
|
||||||
|
protected long mFlexInSeconds;
|
||||||
|
|
||||||
|
protected long mIntervalInSeconds;
|
||||||
|
|
||||||
|
private PeriodicTask(Builder builder) {
|
||||||
|
super(builder);
|
||||||
|
this.mIntervalInSeconds = builder.periodInSeconds;
|
||||||
|
this.mFlexInSeconds = Math.min(builder.flexInSeconds, mIntervalInSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PeriodicTask(Parcel source) {
|
||||||
|
super(source);
|
||||||
|
mIntervalInSeconds = source.readLong();
|
||||||
|
mFlexInSeconds = Math.min(source.readLong(), mIntervalInSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The number of seconds before the end of the period returned via
|
||||||
|
* {@link com.google.android.gms.gcm.PeriodicTask#getPeriod()} that this periodic task can be executed early.
|
||||||
|
*/
|
||||||
|
public long getFlex() {
|
||||||
|
return mFlexInSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The period for this task. The number of seconds between subsequent executions.
|
||||||
|
*/
|
||||||
|
public long getPeriod() {
|
||||||
|
return mIntervalInSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert the task object into the provided bundle for IPC. Use #fromBundle to recreate the
|
||||||
|
* object on the other side.
|
||||||
|
*/
|
||||||
|
public void toBundle(Bundle bundle) {
|
||||||
|
super.toBundle(bundle);
|
||||||
|
bundle.putLong("period", this.mIntervalInSeconds);
|
||||||
|
bundle.putLong("period_flex", this.mFlexInSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return super.toString() + " period=" + this.getPeriod() + " flex=" + this.getFlex();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeToParcel(Parcel parcel, int flags) {
|
||||||
|
super.writeToParcel(parcel, flags);
|
||||||
|
parcel.writeLong(this.mIntervalInSeconds);
|
||||||
|
parcel.writeLong(this.mFlexInSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Creator<PeriodicTask> CREATOR = new Creator<PeriodicTask>() {
|
||||||
|
@Override
|
||||||
|
public PeriodicTask createFromParcel(Parcel source) {
|
||||||
|
return new PeriodicTask(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PeriodicTask[] newArray(int size) {
|
||||||
|
return new PeriodicTask[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static class Builder extends com.google.android.gms.gcm.Task.Builder {
|
||||||
|
private long flexInSeconds = -1;
|
||||||
|
private long periodInSeconds = -1;
|
||||||
|
|
||||||
|
public Builder() {
|
||||||
|
isPersisted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PeriodicTask build() {
|
||||||
|
return new PeriodicTask(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional setter for specifying any extra parameters necessary for the task.
|
||||||
|
*/
|
||||||
|
public PeriodicTask.Builder setExtras(Bundle extras) {
|
||||||
|
this.extras = extras;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional setter for specifying how close to the end of the period set in
|
||||||
|
* {@link com.google.android.gms.gcm.PeriodicTask.Builder#setPeriod(long)} you are willing to execute.
|
||||||
|
* <p/>
|
||||||
|
* For example, specifying a period of 30 seconds, with a flex value of 10 seconds
|
||||||
|
* will allow the scheduler to determine the best moment between the 20th and 30th
|
||||||
|
* second at which to execute your task.
|
||||||
|
*/
|
||||||
|
public PeriodicTask.Builder setFlex(long flexInSeconds) {
|
||||||
|
this.flexInSeconds = flexInSeconds;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mandatory setter for creating a periodic task. This specifies that you would like
|
||||||
|
* this task to recur at most once every <code>mIntervalInSeconds.</code>
|
||||||
|
* <p/>
|
||||||
|
* By default you have no control over where within this period the task will execute.
|
||||||
|
* If you want to restrict the task to run within a certain timeframe from the end of
|
||||||
|
* the period, use {@link com.google.android.gms.gcm.PeriodicTask.Builder#setFlex(long)}
|
||||||
|
*/
|
||||||
|
public PeriodicTask.Builder setPeriod(long periodInSeconds) {
|
||||||
|
this.periodInSeconds = periodInSeconds;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional setter to specify whether this task should be persisted across reboots. This
|
||||||
|
* defaults to true for periodic tasks,
|
||||||
|
* <p/>
|
||||||
|
* Callers <strong>must</strong> hold the permission
|
||||||
|
* android.Manifest.permission.RECEIVE_BOOT_COMPLETED, otherwise this setter is
|
||||||
|
* ignored.
|
||||||
|
*
|
||||||
|
* @param isPersisted True if this task should be persisted across device reboots.
|
||||||
|
*/
|
||||||
|
public PeriodicTask.Builder setPersisted(boolean isPersisted) {
|
||||||
|
this.isPersisted = isPersisted;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the network state your task requires to run. <strong>If the specified network is
|
||||||
|
* unavailable your task will not be executed until it becomes available.</strong>
|
||||||
|
* <p/>
|
||||||
|
* The default for either a periodic or one-off task is
|
||||||
|
* {@link com.google.android.gms.gcm.Task#NETWORK_STATE_CONNECTED}. Note that changing this to
|
||||||
|
* {@link com.google.android.gms.gcm.Task#NETWORK_STATE_ANY} means there is no guarantee that data will be available
|
||||||
|
* when your task executes.
|
||||||
|
* <p/>
|
||||||
|
* In addition, the only guarantee for connectivity is at the moment of execution - it is
|
||||||
|
* possible for the device to lose data shortly after your task begins executing.
|
||||||
|
*/
|
||||||
|
public PeriodicTask.Builder setRequiredNetwork(int requiredNetworkState) {
|
||||||
|
this.requiredNetworkState = requiredNetworkState;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether your task requires that the device be connected to power in order to
|
||||||
|
* execute.
|
||||||
|
* <p/>
|
||||||
|
* Use this to defer nonessential operations whenever possible. Note that if you set this
|
||||||
|
* field and the device is not connected to power <strong>your task will not run</strong>
|
||||||
|
* until the device is plugged in.
|
||||||
|
* <p/>
|
||||||
|
* One way to deal with your task not executing until the constraint is met is to schedule
|
||||||
|
* another task without the constraints that is subject to some deadline that you can abide.
|
||||||
|
* This task would be responsible for executing your fallback logic.
|
||||||
|
*/
|
||||||
|
public PeriodicTask.Builder setRequiresCharging(boolean requiresCharging) {
|
||||||
|
this.requiresCharging = requiresCharging;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Set whichever {@link com.google.android.gms.gcm.GcmTaskService} you implement to execute the logic for this task.</p>
|
||||||
|
*
|
||||||
|
* @param gcmTaskService Endpoint against which you're scheduling this task.
|
||||||
|
*/
|
||||||
|
public PeriodicTask.Builder setService(Class<? extends GcmTaskService> gcmTaskService) {
|
||||||
|
this.gcmTaskService = gcmTaskService.getName();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mandatory setter for specifying the tag identifer for this task. This tag will be
|
||||||
|
* returned at execution time to your endpoint. See
|
||||||
|
* {@link com.google.android.gms.gcm.GcmTaskService#onRunTask(com.google.android.gms.gcm.TaskParams)}
|
||||||
|
* <p/>
|
||||||
|
* Maximum tag length is 100.
|
||||||
|
*
|
||||||
|
* @param tag String identifier for this task. Consecutive schedule calls for the same
|
||||||
|
* tag will update any preexisting task with the same tag.
|
||||||
|
*/
|
||||||
|
public PeriodicTask.Builder setTag(String tag) {
|
||||||
|
this.tag = tag;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional setter to specify whether this task should override any preexisting tasks
|
||||||
|
* with the same tag. This defaults to false, which means that a new task will not
|
||||||
|
* override an existing one.
|
||||||
|
*
|
||||||
|
* @param updateCurrent True to update the current task with the parameters of the new.
|
||||||
|
* Default false.
|
||||||
|
*/
|
||||||
|
public PeriodicTask.Builder setUpdateCurrent(boolean updateCurrent) {
|
||||||
|
this.updateCurrent = updateCurrent;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,271 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.gcm;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
import org.microg.gms.common.PublicApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encapsulates the parameters of a task that you will schedule on the
|
||||||
|
* {@link com.google.android.gms.gcm.GcmNetworkManager}.
|
||||||
|
* <p/>
|
||||||
|
* Construct instances of either {@link com.google.android.gms.gcm.PeriodicTask} or
|
||||||
|
* {@link com.google.android.gms.gcm.OneoffTask} with the desired parameters/behaviour and
|
||||||
|
* schedule them using {@link com.google.android.gms.gcm.GcmNetworkManager#schedule(com.google.android.gms.gcm.Task)}.
|
||||||
|
*/
|
||||||
|
@PublicApi
|
||||||
|
public abstract class Task implements Parcelable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>The maximum size allowed for extras bundle in bytes.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public static final int EXTRAS_LIMIT_BYTES = 10240;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Specify using {@link com.google.android.gms.gcm.Task.Builder#setRequiredNetwork(int)}
|
||||||
|
* that your task will execute [...] of whether network is available.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public static final int NETWORK_STATE_ANY = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Specify using {@link com.google.android.gms.gcm.Task.Builder#setRequiredNetwork(int)}
|
||||||
|
* that your task will only execute if [...] sort of data connection is available -
|
||||||
|
* either metered or unmetered. <strong>This is the default.</strong></p>
|
||||||
|
*/
|
||||||
|
public static final int NETWORK_STATE_CONNECTED = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Specify using {@link com.google.android.gms.gcm.Task.Builder#setRequiredNetwork(int)}
|
||||||
|
* that your task will only execute if there is an unmetered network connection available.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public static final int NETWORK_STATE_UNMETERED = 1;
|
||||||
|
|
||||||
|
protected static final long UNINITIALIZED = -1;
|
||||||
|
|
||||||
|
private final String serviceName;
|
||||||
|
private final String tag;
|
||||||
|
private final boolean updateCurrent;
|
||||||
|
private final boolean persisted;
|
||||||
|
private final int requiredNetwork;
|
||||||
|
private final boolean requiresCharging;
|
||||||
|
private final Bundle extras;
|
||||||
|
|
||||||
|
Task(Builder builder) {
|
||||||
|
this.serviceName = builder.gcmTaskService;
|
||||||
|
this.tag = builder.tag;
|
||||||
|
this.updateCurrent = builder.updateCurrent;
|
||||||
|
this.persisted = builder.isPersisted;
|
||||||
|
this.requiredNetwork = builder.requiredNetworkState;
|
||||||
|
this.requiresCharging = builder.requiresCharging;
|
||||||
|
this.extras = builder.extras;
|
||||||
|
}
|
||||||
|
|
||||||
|
Task(Parcel in) {
|
||||||
|
this.serviceName = in.readString();
|
||||||
|
this.tag = in.readString();
|
||||||
|
this.updateCurrent = in.readInt() == 1;
|
||||||
|
this.persisted = in.readInt() == 1;
|
||||||
|
this.requiredNetwork = NETWORK_STATE_ANY;
|
||||||
|
this.requiresCharging = false;
|
||||||
|
this.extras = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The extra parameters for the task set by the client.
|
||||||
|
*/
|
||||||
|
public Bundle getExtras() {
|
||||||
|
return extras;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the specified network is unavailable, your task <strong>will not be run</strong> until
|
||||||
|
* it is.
|
||||||
|
*
|
||||||
|
* @return The network type that this task requires in order to run. See the NETWORK_TYPE_*
|
||||||
|
* flavours for an explanation of what this value can be.
|
||||||
|
*/
|
||||||
|
public int getRequiredNetwork() {
|
||||||
|
return requiredNetwork;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the device is not charging and this is set to true, your task <strong>will not be run
|
||||||
|
* </strong> until it is.
|
||||||
|
*
|
||||||
|
* @return Whether or not this task depends on the device being connected to power in order to
|
||||||
|
* execute.
|
||||||
|
*/
|
||||||
|
public boolean getRequiresCharging() {
|
||||||
|
return requiresCharging;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The {@link com.google.android.gms.gcm.GcmTaskService} component that this task
|
||||||
|
* will execute on.
|
||||||
|
*/
|
||||||
|
public String getServiceName() {
|
||||||
|
return serviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The String identifier for this task, that is returned to
|
||||||
|
* {@link com.google.android.gms.gcm.GcmTaskService#onRunTask(com.google.android.gms.gcm.TaskParams)}
|
||||||
|
* when this task executes.
|
||||||
|
*/
|
||||||
|
public String getTag() {
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Whether this task will be persisted across devices restarts or Google Play Services
|
||||||
|
* crashes.
|
||||||
|
*/
|
||||||
|
public boolean isPersisted() {
|
||||||
|
return persisted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Whether or not this task will update a pre-existing task in the scheduler queue.
|
||||||
|
*/
|
||||||
|
public boolean isUpdateCurrent() {
|
||||||
|
return updateCurrent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void toBundle(Bundle bundle) {
|
||||||
|
bundle.putString("tag", this.tag);
|
||||||
|
bundle.putBoolean("update_current", this.updateCurrent);
|
||||||
|
bundle.putBoolean("persisted", this.persisted);
|
||||||
|
bundle.putString("service", this.serviceName);
|
||||||
|
bundle.putInt("requiredNetwork", this.requiredNetwork);
|
||||||
|
bundle.putBoolean("requiresCharging", this.requiresCharging);
|
||||||
|
bundle.putBundle("retryStrategy", null); // TODO
|
||||||
|
bundle.putBundle("extras", this.extras);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeToParcel(Parcel parcel, int i) {
|
||||||
|
parcel.writeString(serviceName);
|
||||||
|
parcel.writeString(tag);
|
||||||
|
parcel.writeInt(updateCurrent ? 1 : 0);
|
||||||
|
parcel.writeInt(persisted ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Builder object to construct these tasks before sending them to the network manager. Use
|
||||||
|
* either {@link com.google.android.gms.gcm.PeriodicTask.Builder} or
|
||||||
|
* {@link com.google.android.gms.gcm.Task.Builder}</p>
|
||||||
|
*/
|
||||||
|
public abstract static class Builder {
|
||||||
|
protected Bundle extras;
|
||||||
|
protected String gcmTaskService;
|
||||||
|
protected boolean isPersisted;
|
||||||
|
protected int requiredNetworkState;
|
||||||
|
protected boolean requiresCharging;
|
||||||
|
protected String tag;
|
||||||
|
protected boolean updateCurrent;
|
||||||
|
|
||||||
|
public Builder() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract Task build();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional setter for specifying any extra parameters necessary for the task.
|
||||||
|
*/
|
||||||
|
public abstract Task.Builder setExtras(Bundle extras);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional setter to specify whether this task should be persisted across reboots. This
|
||||||
|
* defaults to true for periodic tasks, and is not supported for one-off tasks.
|
||||||
|
* <p/>
|
||||||
|
* Callers <strong>must</strong> hold the permission
|
||||||
|
* android.Manifest.permission.RECEIVE_BOOT_COMPLETED, otherwise this setter is
|
||||||
|
* ignored.
|
||||||
|
*
|
||||||
|
* @param isPersisted True if this task should be persisted across device reboots.
|
||||||
|
*/
|
||||||
|
public abstract Task.Builder setPersisted(boolean isPersisted);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the network state your task requires to run. <strong>If the specified network is
|
||||||
|
* unavailable your task will not be executed until it becomes available.</strong>
|
||||||
|
* <p/>
|
||||||
|
* The default for either a periodic or one-off task is
|
||||||
|
* {@link com.google.android.gms.gcm.Task#NETWORK_STATE_CONNECTED}. Note that changing this to
|
||||||
|
* {@link com.google.android.gms.gcm.Task#NETWORK_STATE_ANY} means there is no guarantee that data will be available
|
||||||
|
* when your task executes.
|
||||||
|
* <p/>
|
||||||
|
* In addition, the only guarantee for connectivity is at the moment of execution - it is
|
||||||
|
* possible for the device to lose data shortly after your task begins executing.
|
||||||
|
*/
|
||||||
|
public abstract Task.Builder setRequiredNetwork(int requiredNetworkState);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether your task requires that the device be connected to power in order to
|
||||||
|
* execute.
|
||||||
|
* <p/>
|
||||||
|
* Use this to defer nonessential operations whenever possible. Note that if you set this
|
||||||
|
* field and the device is not connected to power <strong>your task will not run</strong>
|
||||||
|
* until the device is plugged in.
|
||||||
|
* <p/>
|
||||||
|
* One way to deal with your task not executing until the constraint is met is to schedule
|
||||||
|
* another task without the constraints that is subject to some deadline that you can abide.
|
||||||
|
* This task would be responsible for executing your fallback logic.
|
||||||
|
*/
|
||||||
|
public abstract Task.Builder setRequiresCharging(boolean requiresCharging);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whichever {@link com.google.android.gms.gcm.GcmTaskService} you implement to execute the logic for this task.
|
||||||
|
*
|
||||||
|
* @param gcmTaskService Endpoint against which you're scheduling this task.
|
||||||
|
*/
|
||||||
|
public abstract Task.Builder setService(Class<? extends GcmTaskService> gcmTaskService);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mandatory setter for specifying the tag identifer for this task. This tag will be
|
||||||
|
* returned at execution time to your endpoint. See
|
||||||
|
* {@link com.google.android.gms.gcm.GcmTaskService#onRunTask(com.google.android.gms.gcm.TaskParams)}
|
||||||
|
* <p/>
|
||||||
|
* Maximum tag length is 100.
|
||||||
|
*
|
||||||
|
* @param tag String identifier for this task. Consecutive schedule calls for the same tag
|
||||||
|
* will update any preexisting task with the same tag.
|
||||||
|
*/
|
||||||
|
public abstract Task.Builder setTag(String tag);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional setter to specify whether this task should override any preexisting tasks with
|
||||||
|
* the same tag. This defaults to false, which means that a new task will not override an
|
||||||
|
* existing one.
|
||||||
|
*
|
||||||
|
* @param updateCurrent True to update the current task with the parameters of the new.
|
||||||
|
* Default false.
|
||||||
|
*/
|
||||||
|
public abstract Task.Builder setUpdateCurrent(boolean updateCurrent);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.gcm;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import org.microg.gms.common.PublicApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Container of parameters handed off to the client app in
|
||||||
|
* {@link com.google.android.gms.gcm.GcmTaskService#onRunTask(com.google.android.gms.gcm.TaskParams)}.
|
||||||
|
*/
|
||||||
|
@PublicApi
|
||||||
|
public class TaskParams {
|
||||||
|
private final String tag;
|
||||||
|
private final Bundle extras;
|
||||||
|
|
||||||
|
public TaskParams(String tag) {
|
||||||
|
this(tag, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TaskParams(String tag, Bundle extras) {
|
||||||
|
this.tag = tag;
|
||||||
|
this.extras = extras;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bundle getExtras() {
|
||||||
|
return extras;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTag() {
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,165 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.gcm;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.ResolveInfo;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.os.Messenger;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.google.android.gms.iid.InstanceID;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||||
|
import static com.google.android.gms.gcm.GoogleCloudMessaging.ERROR_SERVICE_NOT_AVAILABLE;
|
||||||
|
import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME;
|
||||||
|
import static org.microg.gms.common.Constants.GSF_PACKAGE_NAME;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTER;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTRATION;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.ACTION_GCM_SEND;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_ERROR;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSAGE_ID;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSENGER;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_REGISTRATION_ID;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_UNREGISTERED;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.PERMISSION_GTALK;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.PERMISSION_RECEIVE;
|
||||||
|
|
||||||
|
public class CloudMessagingRpc {
|
||||||
|
private static final AtomicInteger messageIdCounter = new AtomicInteger(1);
|
||||||
|
private static String gcmPackageName;
|
||||||
|
|
||||||
|
private final BlockingQueue<Intent> messengerResponseQueue = new LinkedBlockingQueue<Intent>();
|
||||||
|
private final Messenger messenger = new Messenger(new Handler(Looper.getMainLooper()) {
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
if (msg == null || !(msg.obj instanceof Intent)) {
|
||||||
|
// Invalid message -> drop
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Intent intent = (Intent) msg.obj;
|
||||||
|
if (ACTION_C2DM_REGISTRATION.equals(intent.getAction())) {
|
||||||
|
messengerResponseQueue.add(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Due to it's nature of being a monitored reference, Intents can be used to authenticate a package source.
|
||||||
|
*/
|
||||||
|
private PendingIntent selfAuthIntent;
|
||||||
|
private Context context;
|
||||||
|
|
||||||
|
public CloudMessagingRpc(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getGcmPackageName(Context context) {
|
||||||
|
if (gcmPackageName != null) {
|
||||||
|
return gcmPackageName;
|
||||||
|
}
|
||||||
|
PackageManager packageManager = context.getPackageManager();
|
||||||
|
for (ResolveInfo resolveInfo : packageManager.queryIntentServices(new Intent(ACTION_C2DM_REGISTER), 0)) {
|
||||||
|
if (packageManager.checkPermission(PERMISSION_RECEIVE, resolveInfo.serviceInfo.packageName) == PERMISSION_GRANTED) {
|
||||||
|
return gcmPackageName = resolveInfo.serviceInfo.packageName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
ApplicationInfo appInfo = packageManager.getApplicationInfo(GMS_PACKAGE_NAME, 0);
|
||||||
|
return gcmPackageName = appInfo.packageName;
|
||||||
|
} catch (PackageManager.NameNotFoundException ignored) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
ApplicationInfo appInfo = packageManager.getApplicationInfo(GSF_PACKAGE_NAME, 0);
|
||||||
|
return gcmPackageName = appInfo.packageName;
|
||||||
|
} catch (PackageManager.NameNotFoundException ex3) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
// Cancel the authentication
|
||||||
|
if (selfAuthIntent != null) {
|
||||||
|
selfAuthIntent.cancel();
|
||||||
|
selfAuthIntent = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PendingIntent getSelfAuthIntent() {
|
||||||
|
if (selfAuthIntent == null) {
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setPackage("com.google.example.invalidpackage");
|
||||||
|
selfAuthIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
|
||||||
|
}
|
||||||
|
return selfAuthIntent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Intent sendRegisterMessageBlocking(Bundle extras) throws IOException {
|
||||||
|
sendRegisterMessage(extras);
|
||||||
|
Intent resultIntent;
|
||||||
|
try {
|
||||||
|
resultIntent = messengerResponseQueue.poll(30, TimeUnit.SECONDS);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new IOException(e.getMessage());
|
||||||
|
}
|
||||||
|
if (resultIntent == null) {
|
||||||
|
throw new IOException(ERROR_SERVICE_NOT_AVAILABLE);
|
||||||
|
}
|
||||||
|
return resultIntent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendRegisterMessage(Bundle extras) {
|
||||||
|
Intent intent = new Intent(ACTION_C2DM_REGISTER);
|
||||||
|
intent.setPackage(getGcmPackageName(context));
|
||||||
|
extras.putString(EXTRA_MESSAGE_ID, "google.rpc" + messageIdCounter.getAndIncrement());
|
||||||
|
intent.putExtras(extras);
|
||||||
|
intent.putExtra(EXTRA_MESSENGER, messenger);
|
||||||
|
intent.putExtra(EXTRA_APP, getSelfAuthIntent());
|
||||||
|
context.startService(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendGcmMessage(Bundle extras) {
|
||||||
|
Intent intent = new Intent(ACTION_GCM_SEND);
|
||||||
|
intent.setPackage(GMS_PACKAGE_NAME);
|
||||||
|
intent.putExtras(extras);
|
||||||
|
intent.putExtra(EXTRA_APP, getSelfAuthIntent());
|
||||||
|
context.sendOrderedBroadcast(intent, PERMISSION_GTALK);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String handleRegisterMessageResult(Intent resultIntent) throws IOException {
|
||||||
|
if (resultIntent == null) throw new IOException(InstanceID.ERROR_SERVICE_NOT_AVAILABLE);
|
||||||
|
String result = resultIntent.getStringExtra(EXTRA_REGISTRATION_ID);
|
||||||
|
if (result == null) result = resultIntent.getStringExtra(EXTRA_UNREGISTERED);
|
||||||
|
if (result != null) return result;
|
||||||
|
result = resultIntent.getStringExtra(EXTRA_ERROR);
|
||||||
|
throw new IOException(result != null ? result : InstanceID.ERROR_SERVICE_NOT_AVAILABLE);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
|
String getMyVersionName() {
|
||||||
|
def stdout = new ByteArrayOutputStream()
|
||||||
|
if (rootProject.file("gradlew").exists())
|
||||||
|
exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout }
|
||||||
|
else // automatic build system, don't tag dirty
|
||||||
|
exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout }
|
||||||
|
return stdout.toString().trim().substring(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion androidCompileSdk()
|
||||||
|
buildToolsVersion "$androidBuildVersionTools"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
versionName getMyVersionName()
|
||||||
|
minSdkVersion androidMinSdk()
|
||||||
|
targetSdkVersion androidTargetSdk()
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api project(':play-services-base')
|
||||||
|
api project(':play-services-iid-api')
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
#
|
||||||
|
# Copyright 2013-2016 microG Project Team
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
POM_NAME=Play Services IID Library
|
||||||
|
POM_DESCRIPTION=The Play Services Library module to access the InstanceID API
|
||||||
|
|
||||||
|
POM_PACKAGING=aar
|
||||||
|
|
||||||
|
POM_URL=https://github.com/microg/android_external_GmsLib
|
||||||
|
|
||||||
|
POM_SCM_URL=https://github.com/microg/android_external_GmsLib
|
||||||
|
POM_SCM_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git
|
||||||
|
POM_SCM_DEV_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git
|
||||||
|
|
||||||
|
POM_LICENCE_NAME=The Apache Software License, Version 2.0
|
||||||
|
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
POM_LICENCE_DIST=repo
|
||||||
|
|
||||||
|
POM_DEVELOPER_ID=mar-v-in
|
||||||
|
POM_DEVELOPER_NAME=Marvin W
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2013-2017 microG Project Team
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<manifest package="org.microg.gms.iid"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<!-- Permissions required for IID -->
|
||||||
|
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
</manifest>
|
|
@ -0,0 +1,275 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.iid;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Base64;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.microg.gms.common.PublicApi;
|
||||||
|
import org.microg.gms.gcm.GcmConstants;
|
||||||
|
import org.microg.gms.iid.InstanceIdRpc;
|
||||||
|
import org.microg.gms.iid.InstanceIdStore;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.KeyPairGenerator;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_DELETE;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_SCOPE;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_SENDER;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_SUBSCIPTION;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_SUBTYPE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instance ID provides a unique identifier for each app instance and a mechanism
|
||||||
|
* to authenticate and authorize actions (for example, sending a GCM message).
|
||||||
|
* <p/>
|
||||||
|
* Instance ID is stable but may become invalid, if:
|
||||||
|
* <ul>
|
||||||
|
* <li>App deletes Instance ID</li>
|
||||||
|
* <li>Device is factory reset</li>
|
||||||
|
* <li>User uninstalls the app</li>
|
||||||
|
* <li>User clears app data</li>
|
||||||
|
* </ul>
|
||||||
|
* If Instance ID has become invalid, the app can call {@link com.google.android.gms.iid.InstanceID#getId()}
|
||||||
|
* to request a new Instance ID.
|
||||||
|
* To prove ownership of Instance ID and to allow servers to access data or
|
||||||
|
* services associated with the app, call {@link com.google.android.gms.iid.InstanceID#getToken(java.lang.String, java.lang.String)}.
|
||||||
|
*/
|
||||||
|
@PublicApi
|
||||||
|
public class InstanceID {
|
||||||
|
/**
|
||||||
|
* Error returned when failed requests are retried too often. Use
|
||||||
|
* exponential backoff when retrying requests
|
||||||
|
*/
|
||||||
|
public static final String ERROR_BACKOFF = "RETRY_LATER";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blocking methods must not be called on the main thread.
|
||||||
|
*/
|
||||||
|
public static final String ERROR_MAIN_THREAD = "MAIN_THREAD";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tokens can't be generated. Only devices with Google Play are supported.
|
||||||
|
*/
|
||||||
|
public static final String ERROR_MISSING_INSTANCEID_SERVICE = "MISSING_INSTANCEID_SERVICE";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The device cannot read the response, or there was a server error.
|
||||||
|
* Application should retry the request later using exponential backoff
|
||||||
|
* and retry (on each subsequent failure increase delay before retrying).
|
||||||
|
*/
|
||||||
|
public static final String ERROR_SERVICE_NOT_AVAILABLE = GcmConstants.ERROR_SERVICE_NOT_AVAILABLE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timeout waiting for a response.
|
||||||
|
*/
|
||||||
|
public static final String ERROR_TIMEOUT = "TIMEOUT";
|
||||||
|
|
||||||
|
private static final int RSA_KEY_SIZE = 2048;
|
||||||
|
private static final String TAG = "InstanceID";
|
||||||
|
|
||||||
|
private static InstanceIdStore storeInstance;
|
||||||
|
private static InstanceIdRpc rpc;
|
||||||
|
private static Map<String, InstanceID> instances = new HashMap<String, InstanceID>();
|
||||||
|
|
||||||
|
private final String subtype;
|
||||||
|
private KeyPair keyPair;
|
||||||
|
private long creationTime;
|
||||||
|
|
||||||
|
private InstanceID(String subtype) {
|
||||||
|
this.subtype = subtype == null ? "" : subtype;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets Instance ID and revokes all tokens.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void deleteInstanceID() throws IOException {
|
||||||
|
deleteToken("*", "*");
|
||||||
|
creationTime = 0;
|
||||||
|
storeInstance.delete(subtype + "|");
|
||||||
|
keyPair = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revokes access to a scope (action) for an entity previously
|
||||||
|
* authorized by {@link com.google.android.gms.iid.InstanceID#getToken(java.lang.String, java.lang.String)}.
|
||||||
|
* <p/>
|
||||||
|
* Do not call this function on the main thread.
|
||||||
|
*
|
||||||
|
* @param authorizedEntity Entity that must no longer have access.
|
||||||
|
* @param scope Action that entity is no longer authorized to perform.
|
||||||
|
* @throws IOException if the request fails.
|
||||||
|
*/
|
||||||
|
public void deleteToken(String authorizedEntity, String scope) throws IOException {
|
||||||
|
deleteToken(authorizedEntity, scope, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PublicApi(exclude = true)
|
||||||
|
public void deleteToken(String authorizedEntity, String scope, Bundle extras) throws IOException {
|
||||||
|
if (Looper.getMainLooper() == Looper.myLooper()) throw new IOException(ERROR_MAIN_THREAD);
|
||||||
|
|
||||||
|
storeInstance.delete(subtype, authorizedEntity, scope);
|
||||||
|
|
||||||
|
if (extras == null) extras = new Bundle();
|
||||||
|
extras.putString(EXTRA_SENDER, authorizedEntity);
|
||||||
|
extras.putString(EXTRA_SUBSCIPTION, authorizedEntity);
|
||||||
|
extras.putString(EXTRA_DELETE, "1");
|
||||||
|
extras.putString("X-" + EXTRA_DELETE, "1");
|
||||||
|
extras.putString(EXTRA_SUBTYPE, TextUtils.isEmpty(subtype) ? authorizedEntity : subtype);
|
||||||
|
extras.putString("X-" + EXTRA_SUBTYPE, TextUtils.isEmpty(subtype) ? authorizedEntity : subtype);
|
||||||
|
if (scope != null) extras.putString(EXTRA_SCOPE, scope);
|
||||||
|
|
||||||
|
rpc.handleRegisterMessageResult(rpc.sendRegisterMessageBlocking(extras, getKeyPair()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns time when instance ID was created.
|
||||||
|
*
|
||||||
|
* @return Time when instance ID was created (milliseconds since Epoch).
|
||||||
|
*/
|
||||||
|
public long getCreationTime() {
|
||||||
|
if (creationTime == 0) {
|
||||||
|
String s = storeInstance.get(subtype, "cre");
|
||||||
|
if (s != null) {
|
||||||
|
creationTime = Long.parseLong(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return creationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a stable identifier that uniquely identifies the app instance.
|
||||||
|
*
|
||||||
|
* @return The identifier for the application instance.
|
||||||
|
*/
|
||||||
|
public String getId() {
|
||||||
|
return sha1KeyPair(getKeyPair());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an instance of this class.
|
||||||
|
*
|
||||||
|
* @return InstanceID instance.
|
||||||
|
*/
|
||||||
|
public static InstanceID getInstance(Context context) {
|
||||||
|
String subtype = "";
|
||||||
|
if (storeInstance == null) {
|
||||||
|
storeInstance = new InstanceIdStore(context.getApplicationContext());
|
||||||
|
rpc = new InstanceIdRpc(context.getApplicationContext());
|
||||||
|
}
|
||||||
|
InstanceID instance = instances.get(subtype);
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new InstanceID(subtype);
|
||||||
|
instances.put(subtype, instance);
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a token that authorizes an Entity (example: cloud service) to perform
|
||||||
|
* an action on behalf of the application identified by Instance ID.
|
||||||
|
* <p/>
|
||||||
|
* This is similar to an OAuth2 token except, it applies to the
|
||||||
|
* application instance instead of a user.
|
||||||
|
* <p/>
|
||||||
|
* Do not call this function on the main thread.
|
||||||
|
*
|
||||||
|
* @param authorizedEntity Entity authorized by the token.
|
||||||
|
* @param scope Action authorized for authorizedEntity.
|
||||||
|
* @param extras additional parameters specific to each token scope.
|
||||||
|
* Bundle keys starting with 'GCM.' and 'GOOGLE.' are
|
||||||
|
* reserved.
|
||||||
|
* @return a token that can identify and authorize the instance of the
|
||||||
|
* application on the device.
|
||||||
|
* @throws IOException if the request fails.
|
||||||
|
*/
|
||||||
|
public String getToken(String authorizedEntity, String scope, Bundle extras) throws IOException {
|
||||||
|
if (Looper.getMainLooper() == Looper.myLooper()) throw new IOException(ERROR_MAIN_THREAD);
|
||||||
|
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a token that authorizes an Entity (example: cloud service) to perform
|
||||||
|
* an action on behalf of the application identified by Instance ID.
|
||||||
|
* <p/>
|
||||||
|
* This is similar to an OAuth2 token except, it applies to the
|
||||||
|
* application instance instead of a user.
|
||||||
|
* <p/>
|
||||||
|
* Do not call this function on the main thread.
|
||||||
|
*
|
||||||
|
* @param authorizedEntity Entity authorized by the token.
|
||||||
|
* @param scope Action authorized for authorizedEntity.
|
||||||
|
* @return a token that can identify and authorize the instance of the
|
||||||
|
* application on the device.
|
||||||
|
* @throws IOException if the request fails.
|
||||||
|
*/
|
||||||
|
public String getToken(String authorizedEntity, String scope) throws IOException {
|
||||||
|
return getToken(authorizedEntity, scope, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PublicApi(exclude = true)
|
||||||
|
public InstanceIdStore getStore() {
|
||||||
|
return storeInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PublicApi(exclude = true)
|
||||||
|
public String requestToken(String authorizedEntity, String scope, Bundle extras) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized KeyPair getKeyPair() {
|
||||||
|
if (keyPair == null) {
|
||||||
|
keyPair = storeInstance.getKeyPair(subtype);
|
||||||
|
if (keyPair == null) {
|
||||||
|
try {
|
||||||
|
KeyPairGenerator rsaGenerator = KeyPairGenerator.getInstance("RSA");
|
||||||
|
rsaGenerator.initialize(RSA_KEY_SIZE);
|
||||||
|
keyPair = rsaGenerator.generateKeyPair();
|
||||||
|
creationTime = System.currentTimeMillis();
|
||||||
|
storeInstance.put(subtype, keyPair, creationTime);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return keyPair;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PublicApi(exclude = true)
|
||||||
|
public static String sha1KeyPair(KeyPair keyPair) {
|
||||||
|
try {
|
||||||
|
byte[] digest = MessageDigest.getInstance("SHA1").digest(keyPair.getPublic().getEncoded());
|
||||||
|
digest[0] = (byte) (112 + (0xF & digest[0]) & 0xFF);
|
||||||
|
return Base64.encodeToString(digest, 0, 8, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.iid;
|
||||||
|
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.support.v4.content.WakefulBroadcastReceiver;
|
||||||
|
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTRATION;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.ACTION_INSTANCE_ID;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_FROM;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_GSF_INTENT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class to handle Instance ID service notifications on token
|
||||||
|
* refresh.
|
||||||
|
* <p/>
|
||||||
|
* Any app using Instance ID or GCM must include a class extending
|
||||||
|
* InstanceIDListenerService and implement {@link com.google.android.gms.iid.InstanceIDListenerService#onTokenRefresh()}.
|
||||||
|
* <p/>
|
||||||
|
* Include the following in the manifest:
|
||||||
|
* <pre>
|
||||||
|
* <service android:name=".YourInstanceIDListenerService" android:exported="false">
|
||||||
|
* <intent-filter>
|
||||||
|
* <action android:name="com.google.android.gms.iid.InstanceID"/>
|
||||||
|
* </intent-filter>
|
||||||
|
* </service></pre>
|
||||||
|
* Do not export this service. Instead, keep it private to prevent other apps
|
||||||
|
* accessing your service.
|
||||||
|
*/
|
||||||
|
public class InstanceIDListenerService extends Service {
|
||||||
|
|
||||||
|
private BroadcastReceiver registrationReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
handleIntent(intent);
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private MessengerCompat messengerCompat = new MessengerCompat(new Handler(Looper.getMainLooper()) {
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
handleIntent((Intent) msg.obj);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
private int counter = 0;
|
||||||
|
private int startId = -1;
|
||||||
|
|
||||||
|
private void handleIntent(Intent intent) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
if (intent != null && ACTION_INSTANCE_ID.equals(intent.getAction())) {
|
||||||
|
return messengerCompat.getBinder();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCreate() {
|
||||||
|
IntentFilter filter = new IntentFilter(ACTION_C2DM_REGISTRATION);
|
||||||
|
filter.addCategory(getPackageName());
|
||||||
|
registerReceiver(registrationReceiver, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDestroy() {
|
||||||
|
unregisterReceiver(registrationReceiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
|
synchronized (this) {
|
||||||
|
this.counter++;
|
||||||
|
if (startId > this.startId) this.startId = startId;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (intent != null) {
|
||||||
|
if (ACTION_INSTANCE_ID.equals(intent.getAction()) && intent.hasExtra(EXTRA_GSF_INTENT)) {
|
||||||
|
startService((Intent) intent.getParcelableExtra(EXTRA_GSF_INTENT));
|
||||||
|
return START_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleIntent(intent);
|
||||||
|
|
||||||
|
if (intent.hasExtra(EXTRA_FROM))
|
||||||
|
WakefulBroadcastReceiver.completeWakefulIntent(intent);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
return START_NOT_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the system determines that the tokens need to be refreshed. The application
|
||||||
|
* should call getToken() and send the tokens to all application servers.
|
||||||
|
* <p/>
|
||||||
|
* This will not be called very frequently, it is needed for key rotation and to handle special
|
||||||
|
* cases.
|
||||||
|
* <p/>
|
||||||
|
* The system will throttle the refresh event across all devices to avoid overloading
|
||||||
|
* application servers with token updates.
|
||||||
|
*/
|
||||||
|
public void onTokenRefresh() {
|
||||||
|
// To be overwritten
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stop() {
|
||||||
|
synchronized (this) {
|
||||||
|
counter--;
|
||||||
|
if (counter <= 0) {
|
||||||
|
stopSelf(startId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,423 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.iid;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.ResolveInfo;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.ConditionVariable;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.os.Messenger;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Base64;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.google.android.gms.iid.InstanceID;
|
||||||
|
import com.google.android.gms.iid.MessengerCompat;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.Signature;
|
||||||
|
import java.security.interfaces.RSAPrivateKey;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||||
|
import static com.google.android.gms.iid.InstanceID.ERROR_BACKOFF;
|
||||||
|
import static com.google.android.gms.iid.InstanceID.ERROR_MISSING_INSTANCEID_SERVICE;
|
||||||
|
import static com.google.android.gms.iid.InstanceID.ERROR_SERVICE_NOT_AVAILABLE;
|
||||||
|
import static com.google.android.gms.iid.InstanceID.ERROR_TIMEOUT;
|
||||||
|
import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME;
|
||||||
|
import static org.microg.gms.common.Constants.GSF_PACKAGE_NAME;
|
||||||
|
import static org.microg.gms.common.Constants.MAX_REFERENCE_VERSION;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTER;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTRATION;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.ACTION_INSTANCE_ID;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP_ID;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP_VERSION_CODE;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP_VERSION_NAME;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_CLIENT_VERSION;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_ERROR;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_GMS_VERSION;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_GSF_INTENT;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_IS_MESSENGER2;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_KID;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSENGER;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_OS_VERSION;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_PUBLIC_KEY;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_REGISTRATION_ID;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_SIGNATURE;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_UNREGISTERED;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_USE_GSF;
|
||||||
|
import static org.microg.gms.gcm.GcmConstants.PERMISSION_RECEIVE;
|
||||||
|
|
||||||
|
public class InstanceIdRpc {
|
||||||
|
private static final String TAG = "InstanceID/Rpc";
|
||||||
|
|
||||||
|
private static final int BLOCKING_WAIT_TIME = 30000;
|
||||||
|
|
||||||
|
private static String iidPackageName;
|
||||||
|
private static int lastRequestId;
|
||||||
|
private static int retryCount;
|
||||||
|
private static Map<String, Object> blockingResponses = new HashMap<String, Object>();
|
||||||
|
|
||||||
|
private long nextAttempt;
|
||||||
|
private int interval;
|
||||||
|
private Context context;
|
||||||
|
private PendingIntent selfAuthToken;
|
||||||
|
private Messenger messenger;
|
||||||
|
private Messenger myMessenger;
|
||||||
|
private MessengerCompat messengerCompat;
|
||||||
|
|
||||||
|
public InstanceIdRpc(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getIidPackageName(Context context) {
|
||||||
|
if (iidPackageName != null) {
|
||||||
|
return iidPackageName;
|
||||||
|
}
|
||||||
|
PackageManager packageManager = context.getPackageManager();
|
||||||
|
for (ResolveInfo resolveInfo : packageManager.queryIntentServices(new Intent(ACTION_C2DM_REGISTER), 0)) {
|
||||||
|
if (packageManager.checkPermission(PERMISSION_RECEIVE, resolveInfo.serviceInfo.packageName) == PERMISSION_GRANTED) {
|
||||||
|
return iidPackageName = resolveInfo.serviceInfo.packageName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
ApplicationInfo appInfo = packageManager.getApplicationInfo(GMS_PACKAGE_NAME, 0);
|
||||||
|
return iidPackageName = appInfo.packageName;
|
||||||
|
} catch (PackageManager.NameNotFoundException ignored) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
ApplicationInfo appInfo = packageManager.getApplicationInfo(GSF_PACKAGE_NAME, 0);
|
||||||
|
return iidPackageName = appInfo.packageName;
|
||||||
|
} catch (PackageManager.NameNotFoundException ex3) {
|
||||||
|
Log.w(TAG, "Both Google Play Services and legacy GSF package are missing");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getGmsVersionCode(final Context context) {
|
||||||
|
final PackageManager packageManager = context.getPackageManager();
|
||||||
|
try {
|
||||||
|
return packageManager.getPackageInfo(getIidPackageName(context), 0).versionCode;
|
||||||
|
} catch (PackageManager.NameNotFoundException ex) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getSelfVersionCode(final Context context) {
|
||||||
|
try {
|
||||||
|
return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode;
|
||||||
|
} catch (PackageManager.NameNotFoundException neverHappens) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getSelfVersionName(final Context context) {
|
||||||
|
try {
|
||||||
|
return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName;
|
||||||
|
} catch (PackageManager.NameNotFoundException neverHappens) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void initialize() {
|
||||||
|
if (myMessenger != null) return;
|
||||||
|
getIidPackageName(context);
|
||||||
|
myMessenger = new Messenger(new Handler(Looper.getMainLooper()) {
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
if (msg == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (msg.obj instanceof Intent) {
|
||||||
|
Intent intent = (Intent) msg.obj;
|
||||||
|
intent.setExtrasClassLoader(MessengerCompat.class.getClassLoader());
|
||||||
|
if (intent.hasExtra(EXTRA_MESSENGER)) {
|
||||||
|
Parcelable messengerCandidate = intent.getParcelableExtra(EXTRA_MESSENGER);
|
||||||
|
if (messengerCandidate instanceof MessengerCompat) {
|
||||||
|
messengerCompat = (MessengerCompat) messengerCandidate;
|
||||||
|
} else if (messengerCandidate instanceof Messenger) {
|
||||||
|
messenger = (Messenger) messengerCandidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleResponseInternal(intent);
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Dropping invalid message");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleResponseInternal(Intent resultIntent) {
|
||||||
|
if (resultIntent == null) return;
|
||||||
|
if (!ACTION_C2DM_REGISTRATION.equals(resultIntent.getAction()) && !ACTION_INSTANCE_ID.equals(resultIntent.getAction()))
|
||||||
|
return;
|
||||||
|
String result = resultIntent.getStringExtra(EXTRA_REGISTRATION_ID);
|
||||||
|
if (result == null) result = resultIntent.getStringExtra(EXTRA_UNREGISTERED);
|
||||||
|
if (result == null) {
|
||||||
|
handleError(resultIntent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
retryCount = 0;
|
||||||
|
nextAttempt = 0;
|
||||||
|
interval = 0;
|
||||||
|
|
||||||
|
String requestId = null;
|
||||||
|
if (result.startsWith("|")) {
|
||||||
|
// parse structured response
|
||||||
|
String[] split = result.split("\\|");
|
||||||
|
if (!"ID".equals(split[1])) {
|
||||||
|
Log.w(TAG, "Unexpected structured response " + result);
|
||||||
|
}
|
||||||
|
requestId = split[2];
|
||||||
|
if (split.length > 4) {
|
||||||
|
if ("SYNC".equals(split[3])) {
|
||||||
|
// TODO: sync
|
||||||
|
} else if("RST".equals(split[3])) {
|
||||||
|
// TODO: rst
|
||||||
|
resultIntent.removeExtra(EXTRA_REGISTRATION_ID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = split[split.length-1];
|
||||||
|
if (result.startsWith(":"))
|
||||||
|
result = result.substring(1);
|
||||||
|
resultIntent.putExtra(EXTRA_REGISTRATION_ID, result);
|
||||||
|
}
|
||||||
|
setResponse(requestId, resultIntent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleError(Intent resultIntent) {
|
||||||
|
String error = resultIntent.getStringExtra("error");
|
||||||
|
if (error == null) return;
|
||||||
|
String requestId = null;
|
||||||
|
if (error.startsWith("|")) {
|
||||||
|
// parse structured error message
|
||||||
|
String[] split = error.split("\\|");
|
||||||
|
if (!"ID".equals(split[1])) {
|
||||||
|
Log.w(TAG, "Unexpected structured response " + error);
|
||||||
|
}
|
||||||
|
if (split.length > 2) {
|
||||||
|
requestId = split[2];
|
||||||
|
error = split[3];
|
||||||
|
if (error.startsWith(":"))
|
||||||
|
error = error.substring(1);
|
||||||
|
} else {
|
||||||
|
error = "UNKNOWN";
|
||||||
|
}
|
||||||
|
resultIntent.putExtra("error", error);
|
||||||
|
}
|
||||||
|
setResponse(requestId, resultIntent);
|
||||||
|
long retryAfter = resultIntent.getLongExtra("Retry-After", 0);
|
||||||
|
if (retryAfter > 0) {
|
||||||
|
interval = (int) (retryAfter * 1000);
|
||||||
|
nextAttempt = SystemClock.elapsedRealtime() + interval;
|
||||||
|
Log.d(TAG, "Server requested retry delay: " + interval);
|
||||||
|
} else if (ERROR_SERVICE_NOT_AVAILABLE.equals(error) || "AUTHENTICATION_FAILED".equals(error)
|
||||||
|
&& GSF_PACKAGE_NAME.equals(getIidPackageName(context))) {
|
||||||
|
retryCount++;
|
||||||
|
if (retryCount < 3) return;
|
||||||
|
if (retryCount == 3) interval = 1000 + new Random().nextInt(1000);
|
||||||
|
interval = interval * 2;
|
||||||
|
nextAttempt = SystemClock.elapsedRealtime() + interval;
|
||||||
|
Log.d(TAG, "Setting retry delay to " + interval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized PendingIntent getSelfAuthToken() {
|
||||||
|
if (selfAuthToken == null) {
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setPackage("com.google.example.invalidpackage");
|
||||||
|
selfAuthToken = PendingIntent.getBroadcast(context, 0, intent, 0);
|
||||||
|
}
|
||||||
|
return selfAuthToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static synchronized String getRequestId() {
|
||||||
|
return Integer.toString(lastRequestId++);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendRegisterMessage(Bundle data, KeyPair keyPair, String requestId) throws IOException {
|
||||||
|
long elapsedRealtime = SystemClock.elapsedRealtime();
|
||||||
|
if (nextAttempt != 0 && elapsedRealtime <= nextAttempt) {
|
||||||
|
Log.w(TAG, "Had to wait for " + interval + ", that's still " + (nextAttempt - elapsedRealtime));
|
||||||
|
throw new IOException(ERROR_BACKOFF);
|
||||||
|
}
|
||||||
|
initialize();
|
||||||
|
if (iidPackageName == null) {
|
||||||
|
throw new IOException(ERROR_MISSING_INSTANCEID_SERVICE);
|
||||||
|
}
|
||||||
|
Intent intent = new Intent(ACTION_C2DM_REGISTER);
|
||||||
|
intent.setPackage(iidPackageName);
|
||||||
|
data.putString(EXTRA_GMS_VERSION, Integer.toString(getGmsVersionCode(context)));
|
||||||
|
data.putString(EXTRA_OS_VERSION, Integer.toString(Build.VERSION.SDK_INT));
|
||||||
|
data.putString(EXTRA_APP_VERSION_CODE, Integer.toString(getSelfVersionCode(context)));
|
||||||
|
data.putString(EXTRA_APP_VERSION_NAME, getSelfVersionName(context));
|
||||||
|
data.putString(EXTRA_CLIENT_VERSION, "iid-" + MAX_REFERENCE_VERSION);
|
||||||
|
data.putString(EXTRA_APP_ID, InstanceID.sha1KeyPair(keyPair));
|
||||||
|
String pub = base64encode(keyPair.getPublic().getEncoded());
|
||||||
|
data.putString(EXTRA_PUBLIC_KEY, pub);
|
||||||
|
data.putString(EXTRA_SIGNATURE, sign(keyPair, context.getPackageName(), pub));
|
||||||
|
intent.putExtras(data);
|
||||||
|
intent.putExtra(EXTRA_APP, getSelfAuthToken());
|
||||||
|
sendRequest(intent, requestId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String sign(KeyPair keyPair, String... payload) {
|
||||||
|
byte[] bytes;
|
||||||
|
try {
|
||||||
|
bytes = TextUtils.join("\n", payload).getBytes("UTF-8");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
Log.e(TAG, "Unable to encode", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
PrivateKey privateKey = keyPair.getPrivate();
|
||||||
|
try {
|
||||||
|
Signature signature = Signature.getInstance(privateKey instanceof RSAPrivateKey ? "SHA256withRSA" : "SHA256withECDSA");
|
||||||
|
signature.initSign(privateKey);
|
||||||
|
signature.update(bytes);
|
||||||
|
return base64encode(signature.sign());
|
||||||
|
} catch (GeneralSecurityException e) {
|
||||||
|
Log.e(TAG, "Unable to sign", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String base64encode(byte[] bytes) {
|
||||||
|
return Base64.encodeToString(bytes, Base64.URL_SAFE + Base64.NO_PADDING + Base64.NO_WRAP);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendRequest(Intent intent, String requestId) {
|
||||||
|
intent.putExtra(EXTRA_KID, "|ID|" + requestId + "|");
|
||||||
|
intent.putExtra("X-" + EXTRA_KID, "|ID|" + requestId + "|");
|
||||||
|
Log.d(TAG, "Sending " + intent.getExtras());
|
||||||
|
if (messenger != null) {
|
||||||
|
intent.putExtra(EXTRA_MESSENGER, myMessenger);
|
||||||
|
Message msg = Message.obtain();
|
||||||
|
msg.obj = intent;
|
||||||
|
try {
|
||||||
|
messenger.send(msg);
|
||||||
|
return;
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.d(TAG, "Messenger failed, falling back to service");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean useGsf = iidPackageName.endsWith(".gsf");
|
||||||
|
if (intent.hasExtra(EXTRA_USE_GSF))
|
||||||
|
useGsf = "1".equals(intent.getStringExtra(EXTRA_USE_GSF));
|
||||||
|
|
||||||
|
if (useGsf) {
|
||||||
|
Intent holder = new Intent(ACTION_INSTANCE_ID);
|
||||||
|
holder.setPackage(context.getPackageName());
|
||||||
|
holder.putExtra(EXTRA_GSF_INTENT, intent);
|
||||||
|
context.startService(holder);
|
||||||
|
} else {
|
||||||
|
intent.putExtra(EXTRA_MESSENGER, myMessenger);
|
||||||
|
intent.putExtra(EXTRA_IS_MESSENGER2, "1");
|
||||||
|
if (messengerCompat != null) {
|
||||||
|
Message msg = Message.obtain();
|
||||||
|
msg.obj = intent;
|
||||||
|
try {
|
||||||
|
messengerCompat.send(msg);
|
||||||
|
return;
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.d(TAG, "Messenger failed, falling back to service");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
context.startService(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Intent sendRegisterMessageBlocking(Bundle data, KeyPair keyPair) throws IOException {
|
||||||
|
Intent intent = sendRegisterMessageBlockingInternal(data, keyPair);
|
||||||
|
if (intent != null && intent.hasExtra(EXTRA_MESSENGER)) {
|
||||||
|
// Now with a messenger
|
||||||
|
intent = sendRegisterMessageBlockingInternal(data, keyPair);
|
||||||
|
}
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Intent sendRegisterMessageBlockingInternal(Bundle data, KeyPair keyPair) throws IOException {
|
||||||
|
ConditionVariable cv = new ConditionVariable();
|
||||||
|
String requestId = getRequestId();
|
||||||
|
synchronized (InstanceIdRpc.class) {
|
||||||
|
blockingResponses.put(requestId, cv);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendRegisterMessage(data, keyPair, requestId);
|
||||||
|
|
||||||
|
cv.block(BLOCKING_WAIT_TIME);
|
||||||
|
synchronized (InstanceIdRpc.class) {
|
||||||
|
Object res = blockingResponses.remove(requestId);
|
||||||
|
if (res instanceof Intent) {
|
||||||
|
return (Intent) res;
|
||||||
|
} else if (res instanceof String) {
|
||||||
|
throw new IOException((String) res);
|
||||||
|
}
|
||||||
|
Log.w(TAG, "No response " + res);
|
||||||
|
throw new IOException(ERROR_TIMEOUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String handleRegisterMessageResult(Intent resultIntent) throws IOException {
|
||||||
|
if (resultIntent == null) throw new IOException(ERROR_SERVICE_NOT_AVAILABLE);
|
||||||
|
String result = resultIntent.getStringExtra(EXTRA_REGISTRATION_ID);
|
||||||
|
if (result == null) result = resultIntent.getStringExtra(EXTRA_UNREGISTERED);
|
||||||
|
if (result != null) return result;
|
||||||
|
result = resultIntent.getStringExtra(EXTRA_ERROR);
|
||||||
|
throw new IOException(result != null ? result : ERROR_SERVICE_NOT_AVAILABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setResponse(String requestId, Object response) {
|
||||||
|
if (requestId == null) {
|
||||||
|
for (String r : blockingResponses.keySet()) {
|
||||||
|
setResponse(r, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Object old = blockingResponses.get(requestId);
|
||||||
|
blockingResponses.put(requestId, response);
|
||||||
|
if (old instanceof ConditionVariable) {
|
||||||
|
((ConditionVariable) old).open();
|
||||||
|
} else if (old instanceof Messenger) {
|
||||||
|
Message msg = Message.obtain();
|
||||||
|
msg.obj = response;
|
||||||
|
try {
|
||||||
|
((Messenger) old).send(msg);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.w(TAG, "Failed to send response", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.iid;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.util.Base64;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
|
||||||
|
public class InstanceIdStore {
|
||||||
|
private static final String TAG = "InstanceID/Store";
|
||||||
|
private static final String PREF_NAME = "com.google.android.gms.appid";
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
private SharedPreferences sharedPreferences;
|
||||||
|
|
||||||
|
public InstanceIdStore(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
this.sharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized String get(String key) {
|
||||||
|
return sharedPreferences.getString(key, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String get(String subtype, String key) {
|
||||||
|
return get(subtype + "|S|" + key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String get(String subtype, String authorizedEntity, String scope) {
|
||||||
|
return get(subtype + "|T|" + authorizedEntity + "|" + scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeyPair getKeyPair(String subtype) {
|
||||||
|
String pub = get(subtype, "|P|");
|
||||||
|
String priv = get(subtype, "|K|");
|
||||||
|
if (pub == null || priv == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
byte[] pubKey = Base64.decode(pub, Base64.URL_SAFE);
|
||||||
|
byte[] privKey = Base64.decode(priv, Base64.URL_SAFE);
|
||||||
|
KeyFactory rsaFactory = KeyFactory.getInstance("RSA");
|
||||||
|
return new KeyPair(rsaFactory.generatePublic(new X509EncodedKeySpec(pubKey)), rsaFactory.generatePrivate(new PKCS8EncodedKeySpec(privKey)));
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w(TAG, "Invalid key stored " + e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void put(String key, String value) {
|
||||||
|
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||||
|
editor.putString(key, value);
|
||||||
|
editor.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void put(String subtype, String key, String value) {
|
||||||
|
put(subtype + "|S|" + key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void put(String subtype, String authorizedEntity, String scope, String value) {
|
||||||
|
put(subtype + "|T|" + authorizedEntity + "|" + scope, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void put(String subtype, KeyPair keyPair, long timestamp) {
|
||||||
|
put(subtype, "|P|", Base64.encodeToString(keyPair.getPublic().getEncoded(), Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING));
|
||||||
|
put(subtype, "|K|", Base64.encodeToString(keyPair.getPrivate().getEncoded(), Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING));
|
||||||
|
put(subtype, "cre", Long.toString(timestamp));
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void delete() {
|
||||||
|
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||||
|
editor.clear();
|
||||||
|
editor.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void delete(String prefix) {
|
||||||
|
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||||
|
for (String key : sharedPreferences.getAll().keySet()) {
|
||||||
|
if (key.startsWith(prefix)) {
|
||||||
|
editor.remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
editor.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void delete(String subtype, String authorizedEntity, String scope) {
|
||||||
|
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||||
|
editor.remove(subtype + "|T|" + authorizedEntity + "|" + scope);
|
||||||
|
editor.apply();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
|
String getMyVersionName() {
|
||||||
|
def stdout = new ByteArrayOutputStream()
|
||||||
|
if (rootProject.file("gradlew").exists())
|
||||||
|
exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout }
|
||||||
|
else // automatic build system, don't tag dirty
|
||||||
|
exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout }
|
||||||
|
return stdout.toString().trim().substring(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion androidCompileSdk()
|
||||||
|
buildToolsVersion "$androidBuildVersionTools"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
versionName getMyVersionName()
|
||||||
|
minSdkVersion androidMinSdk()
|
||||||
|
targetSdkVersion androidTargetSdk()
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api project(':play-services-base')
|
||||||
|
api project(':play-services-location-api')
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
#
|
||||||
|
# Copyright 2013-2016 microG Project Team
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
POM_NAME=Play Services Cast Library
|
||||||
|
POM_DESCRIPTION=The Play Services Library module to access Google Location Services
|
||||||
|
|
||||||
|
POM_PACKAGING=aar
|
||||||
|
|
||||||
|
POM_URL=https://github.com/microg/android_external_GmsLib
|
||||||
|
|
||||||
|
POM_SCM_URL=https://github.com/microg/android_external_GmsLib
|
||||||
|
POM_SCM_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git
|
||||||
|
POM_SCM_DEV_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git
|
||||||
|
|
||||||
|
POM_LICENCE_NAME=The Apache Software License, Version 2.0
|
||||||
|
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
POM_LICENCE_DIST=repo
|
||||||
|
|
||||||
|
POM_DEVELOPER_ID=mar-v-in
|
||||||
|
POM_DEVELOPER_NAME=Marvin W
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2013-2017 microG Project Team
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<manifest package="org.microg.gms.location"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<application>
|
||||||
|
<receiver android:name="org.microg.gms.location.NativeLocationClientImpl$NativePendingIntentForwarder"/>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.location;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.Api;
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient.Builder;
|
||||||
|
|
||||||
|
import org.microg.gms.location.ActivityRecognitionApiBuilder;
|
||||||
|
import org.microg.gms.location.ActivityRecognitionApiImpl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main entry point for activity recognition integration.
|
||||||
|
*/
|
||||||
|
public class ActivityRecognition {
|
||||||
|
public static final String CLIENT_NAME = "activity_recognition";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Token to pass to {@link Builder#addApi(Api)} to enable ContextServices.
|
||||||
|
*/
|
||||||
|
public static final Api<Api.ApiOptions.NoOptions> API = new Api<Api.ApiOptions.NoOptions>(new ActivityRecognitionApiBuilder());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entry point to the activity recognition APIs.
|
||||||
|
*/
|
||||||
|
public static final ActivityRecognitionApi ActivityRecognitionApi = new ActivityRecognitionApiImpl();
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.location;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
|
||||||
|
import com.google.android.gms.common.api.PendingResult;
|
||||||
|
import com.google.android.gms.common.api.Status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main entry point for interacting with activity recognition.
|
||||||
|
* <p>
|
||||||
|
* The methods must be used in conjunction with a GoogleApiClient. E.g.
|
||||||
|
* <pre>
|
||||||
|
* new GoogleApiClient.Builder(context)
|
||||||
|
* .addApi(ActivityRecognition.API)
|
||||||
|
* .addConnectionCallbacks(this)
|
||||||
|
* .addOnConnectionFailedListener(this)
|
||||||
|
* .build()
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public interface ActivityRecognitionApi {
|
||||||
|
/**
|
||||||
|
* Removes all activity updates for the specified PendingIntent.
|
||||||
|
* <p>
|
||||||
|
* Calling this function requires the com.google.android.gms.permission.ACTIVITY_RECOGNITION
|
||||||
|
* permission.
|
||||||
|
*
|
||||||
|
* @param client An existing GoogleApiClient. It must be connected at the time of this
|
||||||
|
* call, which is normally achieved by calling {@link GoogleApiClient#connect()}
|
||||||
|
* and waiting for {@link ConnectionCallbacks#onConnected(Bundle)} to be
|
||||||
|
* called.
|
||||||
|
* @param callbackIntent the PendingIntent that was used in {@code #requestActivityUpdates(GoogleApiClient, long, PendingIntent)}
|
||||||
|
* or is equal as defined by {@link Object#equals(Object)}.
|
||||||
|
* @return a PendingResult for the call, check {@link Status#isSuccess()} to determine if it
|
||||||
|
* was successful.
|
||||||
|
*/
|
||||||
|
PendingResult<Status> removeActivityUpdates(GoogleApiClient client, PendingIntent callbackIntent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register for activity recognition updates.
|
||||||
|
* <p>
|
||||||
|
* The activities are detected by periodically waking up the device and reading short bursts of
|
||||||
|
* sensor data. It only makes use of low power sensors in order to keep the power usage to a
|
||||||
|
* minimum. For example, it can detect if the user is currently on foot, in a car, on a bicycle
|
||||||
|
* or still. See {@link DetectedActivity} for more details.
|
||||||
|
* <p>
|
||||||
|
* The activity detection update interval can be controlled with the detectionIntervalMillis
|
||||||
|
* parameter. Larger values will result in fewer activity detections while improving battery
|
||||||
|
* life. Smaller values will result in more frequent activity detections but will consume more
|
||||||
|
* power since the device must be woken up more frequently. {@code Long.MAX_VALUE} means it only
|
||||||
|
* monitors the results requested by other clients without consuming additional power.
|
||||||
|
* <p>
|
||||||
|
* Activities may be received more frequently than the detectionIntervalMillis parameter if
|
||||||
|
* another application has also requested activity updates at a faster rate. It may also receive
|
||||||
|
* updates faster when the activity detection service receives a signal that the current
|
||||||
|
* activity may change, such as if the device has been still for a long period of time and is
|
||||||
|
* then unplugged from a phone charger.
|
||||||
|
* <p>
|
||||||
|
* Activities may arrive several seconds after the requested detectionIntervalMillis if the
|
||||||
|
* activity detection service requires more samples to make a more accurate prediction.
|
||||||
|
* <p>
|
||||||
|
* To conserve battery, activity reporting may stop when the device is 'STILL' for an extended
|
||||||
|
* period of time. It will resume once the device moves again. This only happens on devices that
|
||||||
|
* support the Sensor.TYPE_SIGNIFICANT_MOTION hardware.
|
||||||
|
* <p>
|
||||||
|
* Beginning in API 21, activities may be received less frequently than the
|
||||||
|
* detectionIntervalMillis parameter if the device is in power save mode and the screen is off.
|
||||||
|
* <p>
|
||||||
|
* A common use case is that an application wants to monitor activities in the background and
|
||||||
|
* perform an action when a specific activity is detected. To do this without needing a service
|
||||||
|
* that is always on in the background consuming resources, detected activities are delivered
|
||||||
|
* via an intent. The application specifies a PendingIntent callback (typically an
|
||||||
|
* IntentService) which will be called with an intent when activities are detected. The intent
|
||||||
|
* recipient can extract the {@link ActivityRecognitionResult} using {@link ActivityRecognitionResult#extractResult(android.content.Intent)}.
|
||||||
|
* See the documentation of {@link PendingIntent} for more details.
|
||||||
|
* <p>
|
||||||
|
* Any requests previously registered with {@link #requestActivityUpdates(GoogleApiClient, long, PendingIntent)}
|
||||||
|
* that have the same PendingIntent (as defined by {@link Object#equals(Object)}) will be
|
||||||
|
* replaced by this request.
|
||||||
|
* <p>
|
||||||
|
* Calling this function requires the com.google.android.gms.permission.ACTIVITY_RECOGNITION
|
||||||
|
* permission.
|
||||||
|
*
|
||||||
|
* @param client An existing GoogleApiClient. It must be connected at the time
|
||||||
|
* of this call, which is normally achieved by calling {@link GoogleApiClient#connect()}
|
||||||
|
* and waiting for {@link ConnectionCallbacks#onConnected(Bundle)}
|
||||||
|
* to be called.
|
||||||
|
* @param detectionIntervalMillis the desired time between activity detections. Larger values
|
||||||
|
* will result in fewer activity detections while improving
|
||||||
|
* battery life. A value of 0 will result in activity detections
|
||||||
|
* at the fastest possible rate.
|
||||||
|
* @param callbackIntent a PendingIntent to be sent for each activity detection.
|
||||||
|
* @return a PendingResult for the call, check {@link Status#isSuccess()} to determine if it
|
||||||
|
* was successful.
|
||||||
|
*/
|
||||||
|
PendingResult<Status> requestActivityUpdates(GoogleApiClient client, long detectionIntervalMillis, PendingIntent callbackIntent);
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.location;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.os.Looper;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
import com.google.android.gms.common.api.PendingResult;
|
||||||
|
import com.google.android.gms.common.api.Status;
|
||||||
|
|
||||||
|
import org.microg.gms.location.LocationConstants;
|
||||||
|
|
||||||
|
public interface FusedLocationProviderApi {
|
||||||
|
@Deprecated
|
||||||
|
String KEY_LOCATION_CHANGED = "com.google.android.location.LOCATION";
|
||||||
|
String KEY_MOCK_LOCATION = LocationConstants.KEY_MOCK_LOCATION;
|
||||||
|
|
||||||
|
Location getLastLocation(GoogleApiClient client);
|
||||||
|
|
||||||
|
PendingResult<Status> requestLocationUpdates(GoogleApiClient client, LocationRequest request,
|
||||||
|
LocationListener listener);
|
||||||
|
|
||||||
|
PendingResult<Status> requestLocationUpdates(GoogleApiClient client, LocationRequest request,
|
||||||
|
LocationListener listener, Looper looper);
|
||||||
|
|
||||||
|
PendingResult<Status> requestLocationUpdates(GoogleApiClient client, LocationRequest request,
|
||||||
|
PendingIntent callbackIntent);
|
||||||
|
|
||||||
|
PendingResult<Status> removeLocationUpdates(GoogleApiClient client, LocationListener listener);
|
||||||
|
|
||||||
|
PendingResult<Status> removeLocationUpdates(GoogleApiClient client,
|
||||||
|
PendingIntent callbackIntent);
|
||||||
|
|
||||||
|
PendingResult<Status> setMockMode(GoogleApiClient client, boolean isMockMode);
|
||||||
|
|
||||||
|
PendingResult<Status> setMockLocation(GoogleApiClient client, Location mockLocation);
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.location;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
import com.google.android.gms.common.api.PendingResult;
|
||||||
|
import com.google.android.gms.common.api.Status;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main entry point for interacting with the geofencing APIs.
|
||||||
|
* <p>
|
||||||
|
* The methods must be used in conjunction with a GoogleApiClient. E.g.
|
||||||
|
* <pre>
|
||||||
|
* new GoogleApiClient.Builder(context)
|
||||||
|
* .addApi(LocationServices.API)
|
||||||
|
* .addConnectionCallbacks(this)
|
||||||
|
* .addOnConnectionFailedListener(this)
|
||||||
|
* .build()
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public interface GeofencingApi {
|
||||||
|
PendingResult<Status> addGeofences(GoogleApiClient client, GeofencingRequest geofencingRequest, PendingIntent pendingIntent);
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
PendingResult<Status> addGeofences(GoogleApiClient client, List<Geofence> geofences, PendingIntent pendingIntent);
|
||||||
|
|
||||||
|
PendingResult<Status> removeGeofences(GoogleApiClient client, List<String> geofenceRequestIds);
|
||||||
|
|
||||||
|
PendingResult<Status> removeGeofences(GoogleApiClient client, PendingIntent pendingIntent);
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.location;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.os.Looper;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
import com.google.android.gms.common.api.PendingResult;
|
||||||
|
|
||||||
|
import org.microg.gms.common.ForwardConnectionCallbacks;
|
||||||
|
import org.microg.gms.common.ForwardConnectionFailedListener;
|
||||||
|
import org.microg.gms.common.api.AbstractPlayServicesClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is deprecated as of play services 6.5, do not use it in production systems,
|
||||||
|
* it's just a forwarder for the {@link FusedLocationProviderApi}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public class LocationClient extends AbstractPlayServicesClient {
|
||||||
|
public static final String KEY_LOCATION_CHANGED = "com.google.android.location.LOCATION";
|
||||||
|
|
||||||
|
public LocationClient(Context context, ConnectionCallbacks callbacks,
|
||||||
|
OnConnectionFailedListener connectionFailedListener) {
|
||||||
|
super(new GoogleApiClient.Builder(context)
|
||||||
|
.addApi(LocationServices.API)
|
||||||
|
.addConnectionCallbacks(new ForwardConnectionCallbacks(callbacks))
|
||||||
|
.addOnConnectionFailedListener(new ForwardConnectionFailedListener
|
||||||
|
(connectionFailedListener))
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Location getLastLocation() {
|
||||||
|
assertConnected();
|
||||||
|
return LocationServices.FusedLocationApi.getLastLocation(googleApiClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestLocationUpdates(LocationRequest request,
|
||||||
|
LocationListener listener) {
|
||||||
|
assertConnected();
|
||||||
|
LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, request,
|
||||||
|
listener).await();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestLocationUpdates(LocationRequest request,
|
||||||
|
LocationListener listener, Looper looper) {
|
||||||
|
assertConnected();
|
||||||
|
LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, request,
|
||||||
|
listener, looper).await();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestLocationUpdates(LocationRequest request,
|
||||||
|
PendingIntent callbackIntent) {
|
||||||
|
assertConnected();
|
||||||
|
LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, request,
|
||||||
|
callbackIntent).await();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeLocationUpdates(LocationListener listener) {
|
||||||
|
assertConnected();
|
||||||
|
LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, listener).await();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeLocationUpdates(PendingIntent callbackIntent) {
|
||||||
|
assertConnected();
|
||||||
|
LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient,
|
||||||
|
callbackIntent).await();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMockMode(boolean isMockMode) {
|
||||||
|
assertConnected();
|
||||||
|
LocationServices.FusedLocationApi.setMockMode(googleApiClient, isMockMode).await();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMockLocation(Location mockLocation) {
|
||||||
|
assertConnected();
|
||||||
|
LocationServices.FusedLocationApi.setMockLocation(googleApiClient, mockLocation).await();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.location;
|
||||||
|
|
||||||
|
import android.location.Location;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for receiving notifications from the {@link FusedLocationProviderApi} when the location has
|
||||||
|
* changed. The methods are called if the LocationListener has been registered with the location
|
||||||
|
* client.
|
||||||
|
*/
|
||||||
|
public interface LocationListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the location has changed.
|
||||||
|
*
|
||||||
|
* @param location The updated location.
|
||||||
|
*/
|
||||||
|
public void onLocationChanged(Location location);
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.location;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.Api;
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient.Builder;
|
||||||
|
|
||||||
|
import org.microg.gms.location.FusedLocationProviderApiImpl;
|
||||||
|
import org.microg.gms.location.GeofencingApiImpl;
|
||||||
|
import org.microg.gms.location.LocationServicesApiBuilder;
|
||||||
|
import org.microg.gms.location.SettingsApiImpl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main entry point for location services integration.
|
||||||
|
*/
|
||||||
|
public class LocationServices {
|
||||||
|
/**
|
||||||
|
* Token to pass to {@link Builder#addApi(Api)} to enable LocationServices.
|
||||||
|
*/
|
||||||
|
public static final Api<Api.ApiOptions.NoOptions> API = new Api<Api.ApiOptions.NoOptions>(new LocationServicesApiBuilder());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entry point to the fused location APIs.
|
||||||
|
*/
|
||||||
|
public static final FusedLocationProviderApi FusedLocationApi = new FusedLocationProviderApiImpl();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entry point to the geofencing APIs.
|
||||||
|
*/
|
||||||
|
public static final GeofencingApi GeofencingApi = new GeofencingApiImpl();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entry point to the location settings-enabler dialog APIs.
|
||||||
|
*/
|
||||||
|
public static final SettingsApi SettingsApi = new SettingsApiImpl();
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.location;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public class LocationStatusCodes {
|
||||||
|
public static final int ERROR = 1;
|
||||||
|
public static final int GEOFENCE_NOT_AVAILABLE = 1000;
|
||||||
|
public static final int GEOFENCE_TOO_MANY_GEOFENCES = 1001;
|
||||||
|
public static final int GEOFENCE_TOO_MANY_PENDING_INTENTS = 1002;
|
||||||
|
public static final int SUCCESS = 0;
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.location;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
import com.google.android.gms.common.api.PendingResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main entry point for interacting with the location settings-enabler APIs.
|
||||||
|
* <p>
|
||||||
|
* This API makes it easy for an app to ensure that the device's system settings are properly
|
||||||
|
* configured for the app's location needs.
|
||||||
|
*/
|
||||||
|
public interface SettingsApi {
|
||||||
|
/**
|
||||||
|
* Checks if the relevant system settings are enabled on the device to carry out the desired
|
||||||
|
* location requests.
|
||||||
|
*
|
||||||
|
* @param client an existing GoogleApiClient. It does not need to be connected
|
||||||
|
* at the time of this call, but the result will be delayed until
|
||||||
|
* the connection is complete.
|
||||||
|
* @param locationSettingsRequest an object that contains all the location requirements that the
|
||||||
|
* client is interested in.
|
||||||
|
* @return result containing the status of the request.
|
||||||
|
*/
|
||||||
|
PendingResult<LocationSettingsResult> checkLocationSettings(GoogleApiClient client, LocationSettingsRequest locationSettingsRequest);
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.location;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Looper;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.AccountInfo;
|
||||||
|
import com.google.android.gms.common.api.Api;
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
|
||||||
|
import org.microg.gms.common.api.ApiBuilder;
|
||||||
|
import org.microg.gms.common.api.ApiConnection;
|
||||||
|
|
||||||
|
public class ActivityRecognitionApiBuilder implements ApiBuilder<Api.ApiOptions.NoOptions> {
|
||||||
|
@Override
|
||||||
|
public ApiConnection build(Context context, Looper looper, Api.ApiOptions.NoOptions options, AccountInfo accountInfo, GoogleApiClient.ConnectionCallbacks callbacks, GoogleApiClient.OnConnectionFailedListener connectionFailedListener) {
|
||||||
|
return new ActivityRecognitionClientImpl(context, callbacks, connectionFailedListener);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.location;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
import com.google.android.gms.common.api.PendingResult;
|
||||||
|
import com.google.android.gms.common.api.Status;
|
||||||
|
import com.google.android.gms.location.ActivityRecognition;
|
||||||
|
import com.google.android.gms.location.ActivityRecognitionApi;
|
||||||
|
|
||||||
|
import org.microg.gms.common.GmsConnector;
|
||||||
|
|
||||||
|
public class ActivityRecognitionApiImpl implements ActivityRecognitionApi {
|
||||||
|
private static final String TAG = "GmsActivityApiImpl";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingResult<Status> removeActivityUpdates(GoogleApiClient client, final PendingIntent callbackIntent) {
|
||||||
|
return callVoid(client, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run(ActivityRecognitionClientImpl client) throws RemoteException {
|
||||||
|
client.removeActivityUpdates(callbackIntent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingResult<Status> requestActivityUpdates(GoogleApiClient client, final long detectionIntervalMillis, final PendingIntent callbackIntent) {
|
||||||
|
return callVoid(client, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run(ActivityRecognitionClientImpl client) throws RemoteException {
|
||||||
|
client.requestActivityUpdates(detectionIntervalMillis, callbackIntent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private PendingResult<Status> callVoid(GoogleApiClient client, final Runnable runnable) {
|
||||||
|
return GmsConnector.call(client, ActivityRecognition.API, new GmsConnector.Callback<ActivityRecognitionClientImpl, Status>() {
|
||||||
|
@Override
|
||||||
|
public void onClientAvailable(ActivityRecognitionClientImpl client, ResultProvider<Status> resultProvider) throws RemoteException {
|
||||||
|
runnable.run(client);
|
||||||
|
resultProvider.onResultAvailable(Status.SUCCESS);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface Runnable {
|
||||||
|
void run(ActivityRecognitionClientImpl client) throws RemoteException;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.location;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
|
||||||
|
public class ActivityRecognitionClientImpl extends GoogleLocationManagerClient {
|
||||||
|
public ActivityRecognitionClientImpl(Context context, GoogleApiClient.ConnectionCallbacks callbacks, GoogleApiClient.OnConnectionFailedListener connectionFailedListener) {
|
||||||
|
super(context, callbacks, connectionFailedListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestActivityUpdates(long detectionIntervalMillis, PendingIntent callbackIntent) throws RemoteException {
|
||||||
|
getServiceInterface().requestActivityUpdates(detectionIntervalMillis, true, callbackIntent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeActivityUpdates(PendingIntent callbackIntent) throws RemoteException {
|
||||||
|
getServiceInterface().removeActivityUpdates(callbackIntent);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,140 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.location;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
import com.google.android.gms.common.api.PendingResult;
|
||||||
|
import com.google.android.gms.common.api.Result;
|
||||||
|
import com.google.android.gms.common.api.Status;
|
||||||
|
import com.google.android.gms.location.FusedLocationProviderApi;
|
||||||
|
import com.google.android.gms.location.LocationListener;
|
||||||
|
import com.google.android.gms.location.LocationRequest;
|
||||||
|
import com.google.android.gms.location.LocationServices;
|
||||||
|
|
||||||
|
import org.microg.gms.common.GmsConnector;
|
||||||
|
import org.microg.gms.common.api.ApiConnection;
|
||||||
|
|
||||||
|
public class FusedLocationProviderApiImpl implements FusedLocationProviderApi {
|
||||||
|
private static final String TAG = "GmsFusedApiImpl";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Location getLastLocation(GoogleApiClient client) {
|
||||||
|
try {
|
||||||
|
Log.d(TAG, "getLastLocation(" + client + ")");
|
||||||
|
return LocationClientImpl.get(client).getLastLocation();
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingResult<Status> requestLocationUpdates(GoogleApiClient client,
|
||||||
|
final LocationRequest request, final LocationListener listener) {
|
||||||
|
return callVoid(client, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run(LocationClientImpl client) throws RemoteException {
|
||||||
|
client.requestLocationUpdates(request, listener);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingResult<Status> requestLocationUpdates(GoogleApiClient client,
|
||||||
|
final LocationRequest request, final LocationListener listener,
|
||||||
|
final Looper looper) {
|
||||||
|
return callVoid(client, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run(LocationClientImpl client) throws RemoteException {
|
||||||
|
client.requestLocationUpdates(request, listener, looper);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingResult<Status> requestLocationUpdates(GoogleApiClient client,
|
||||||
|
final LocationRequest request, final PendingIntent callbackIntent) {
|
||||||
|
return callVoid(client, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run(LocationClientImpl client) throws RemoteException {
|
||||||
|
client.requestLocationUpdates(request, callbackIntent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingResult<Status> removeLocationUpdates(GoogleApiClient client,
|
||||||
|
final LocationListener listener) {
|
||||||
|
return callVoid(client, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run(LocationClientImpl client) throws RemoteException {
|
||||||
|
client.removeLocationUpdates(listener);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingResult<Status> removeLocationUpdates(GoogleApiClient client,
|
||||||
|
final PendingIntent callbackIntent) {
|
||||||
|
return callVoid(client, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run(LocationClientImpl client) throws RemoteException {
|
||||||
|
client.removeLocationUpdates(callbackIntent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingResult<Status> setMockMode(GoogleApiClient client, final boolean isMockMode) {
|
||||||
|
return callVoid(client, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run(LocationClientImpl client) throws RemoteException {
|
||||||
|
client.setMockMode(isMockMode);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingResult<Status> setMockLocation(GoogleApiClient client, final Location mockLocation) {
|
||||||
|
return callVoid(client, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run(LocationClientImpl client) throws RemoteException {
|
||||||
|
client.setMockLocation(mockLocation);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private PendingResult<Status> callVoid(GoogleApiClient client, final Runnable runnable) {
|
||||||
|
return GmsConnector.call(client, LocationServices.API, new GmsConnector.Callback<LocationClientImpl, Status>() {
|
||||||
|
@Override
|
||||||
|
public void onClientAvailable(LocationClientImpl client, ResultProvider<Status> resultProvider) throws RemoteException {
|
||||||
|
runnable.run(client);
|
||||||
|
resultProvider.onResultAvailable(Status.SUCCESS);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface Runnable {
|
||||||
|
void run(LocationClientImpl client) throws RemoteException;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,116 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.location;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
import com.google.android.gms.common.api.PendingResult;
|
||||||
|
import com.google.android.gms.common.api.Status;
|
||||||
|
import com.google.android.gms.location.Geofence;
|
||||||
|
import com.google.android.gms.location.GeofencingApi;
|
||||||
|
import com.google.android.gms.location.GeofencingRequest;
|
||||||
|
import com.google.android.gms.location.LocationServices;
|
||||||
|
import com.google.android.gms.location.internal.IGeofencerCallbacks;
|
||||||
|
import com.google.android.gms.location.internal.ParcelableGeofence;
|
||||||
|
|
||||||
|
import org.microg.gms.common.GmsConnector;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class GeofencingApiImpl implements GeofencingApi {
|
||||||
|
@Override
|
||||||
|
public PendingResult<Status> addGeofences(GoogleApiClient client, final GeofencingRequest geofencingRequest, final PendingIntent pendingIntent) {
|
||||||
|
return callGeofencer(client, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run(LocationClientImpl client, IGeofencerCallbacks callbacks) throws RemoteException {
|
||||||
|
client.addGeofences(geofencingRequest, pendingIntent, callbacks);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingResult<Status> addGeofences(GoogleApiClient client, final List<Geofence> geofences, final PendingIntent pendingIntent) {
|
||||||
|
final List<ParcelableGeofence> geofenceList = new ArrayList<ParcelableGeofence>();
|
||||||
|
for (Geofence geofence : geofences) {
|
||||||
|
if (geofence instanceof ParcelableGeofence) geofenceList.add((ParcelableGeofence) geofence);
|
||||||
|
}
|
||||||
|
return callGeofencer(client, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run(LocationClientImpl client, IGeofencerCallbacks callbacks) throws RemoteException {
|
||||||
|
client.addGeofences(geofenceList, pendingIntent, callbacks);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingResult<Status> removeGeofences(GoogleApiClient client, final List<String> geofenceRequestIds) {
|
||||||
|
return callGeofencer(client, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run(LocationClientImpl client, IGeofencerCallbacks callbacks) throws RemoteException {
|
||||||
|
client.removeGeofences(geofenceRequestIds, callbacks);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingResult<Status> removeGeofences(GoogleApiClient client, final PendingIntent pendingIntent) {
|
||||||
|
return callGeofencer(client, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run(LocationClientImpl client, IGeofencerCallbacks callbacks) throws RemoteException {
|
||||||
|
client.removeGeofences(pendingIntent, callbacks);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private IGeofencerCallbacks.Stub createGeofencerCallbacks(final GmsConnector.Callback.ResultProvider<Status> resultProvider) {
|
||||||
|
return new IGeofencerCallbacks.Stub(){
|
||||||
|
@Override
|
||||||
|
public void onAddGeofenceResult(int statusCode, String[] requestIds) throws RemoteException {
|
||||||
|
resultProvider.onResultAvailable(new Status(statusCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRemoveGeofencesByRequestIdsResult(int statusCode, String[] requestIds) throws RemoteException {
|
||||||
|
resultProvider.onResultAvailable(new Status(statusCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRemoveGeofencesByPendingIntentResult(int statusCode, PendingIntent pendingIntent) throws RemoteException {
|
||||||
|
resultProvider.onResultAvailable(new Status(statusCode));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private PendingResult<Status> callGeofencer(GoogleApiClient client, final Runnable runnable) {
|
||||||
|
return GmsConnector.call(client, LocationServices.API, new GmsConnector.Callback<LocationClientImpl, Status>() {
|
||||||
|
@Override
|
||||||
|
public void onClientAvailable(LocationClientImpl client, ResultProvider<Status> resultProvider) throws RemoteException {
|
||||||
|
runnable.run(client, createGeofencerCallbacks(resultProvider));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface Runnable {
|
||||||
|
void run(LocationClientImpl client, IGeofencerCallbacks callbacks) throws RemoteException;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.location;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
import com.google.android.gms.common.internal.IGmsServiceBroker;
|
||||||
|
import com.google.android.gms.location.internal.IGoogleLocationManagerService;
|
||||||
|
|
||||||
|
import org.microg.gms.common.Constants;
|
||||||
|
import org.microg.gms.common.GmsClient;
|
||||||
|
import org.microg.gms.common.GmsService;
|
||||||
|
|
||||||
|
public abstract class GoogleLocationManagerClient extends GmsClient<IGoogleLocationManagerService> {
|
||||||
|
public GoogleLocationManagerClient(Context context, GoogleApiClient.ConnectionCallbacks
|
||||||
|
callbacks, GoogleApiClient.OnConnectionFailedListener connectionFailedListener) {
|
||||||
|
super(context, callbacks, connectionFailedListener, GmsService.LOCATION_MANAGER.ACTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IGoogleLocationManagerService interfaceFromBinder(IBinder binder) {
|
||||||
|
return IGoogleLocationManagerService.Stub.asInterface(binder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onConnectedToBroker(IGmsServiceBroker broker, GmsCallbacks callbacks)
|
||||||
|
throws RemoteException {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putString("client_name", "locationServices");
|
||||||
|
broker.getGoogleLocationManagerService(callbacks, Constants.MAX_REFERENCE_VERSION,
|
||||||
|
getContext().getPackageName(), bundle);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,179 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.location;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
import com.google.android.gms.location.GeofencingRequest;
|
||||||
|
import com.google.android.gms.location.ILocationListener;
|
||||||
|
import com.google.android.gms.location.LocationListener;
|
||||||
|
import com.google.android.gms.location.LocationRequest;
|
||||||
|
import com.google.android.gms.location.LocationServices;
|
||||||
|
import com.google.android.gms.location.internal.IGeofencerCallbacks;
|
||||||
|
import com.google.android.gms.location.internal.ParcelableGeofence;
|
||||||
|
|
||||||
|
import org.microg.gms.common.api.GoogleApiClientImpl;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class LocationClientImpl extends GoogleLocationManagerClient {
|
||||||
|
private static final String TAG = "GmsLocationClientImpl";
|
||||||
|
private NativeLocationClientImpl nativeLocation = null;
|
||||||
|
private Map<LocationListener, ILocationListener> listenerMap = new HashMap<LocationListener, ILocationListener>();
|
||||||
|
|
||||||
|
|
||||||
|
public LocationClientImpl(Context context, GoogleApiClient.ConnectionCallbacks callbacks,
|
||||||
|
GoogleApiClient.OnConnectionFailedListener connectionFailedListener) {
|
||||||
|
super(context, callbacks, connectionFailedListener);
|
||||||
|
Log.d(TAG, "<init>");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LocationClientImpl get(GoogleApiClient apiClient) {
|
||||||
|
if (apiClient instanceof GoogleApiClientImpl) {
|
||||||
|
return (LocationClientImpl) ((GoogleApiClientImpl) apiClient)
|
||||||
|
.getApiConnection(LocationServices.API);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addGeofences(GeofencingRequest request, PendingIntent pendingIntent, IGeofencerCallbacks callbacks) throws RemoteException {
|
||||||
|
if (nativeLocation != null) {
|
||||||
|
nativeLocation.addGeofences(request, pendingIntent, callbacks);
|
||||||
|
} else {
|
||||||
|
getServiceInterface().addGeofences(request, pendingIntent, callbacks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addGeofences(List<ParcelableGeofence> request, PendingIntent pendingIntent, IGeofencerCallbacks callbacks) throws RemoteException {
|
||||||
|
if (nativeLocation != null) {
|
||||||
|
nativeLocation.addGeofences(request, pendingIntent, callbacks);
|
||||||
|
} else {
|
||||||
|
getServiceInterface().addGeofencesList(request, pendingIntent, callbacks, getContext().getPackageName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeGeofences(List<String> geofenceRequestIds, IGeofencerCallbacks callbacks) throws RemoteException {
|
||||||
|
if (nativeLocation != null) {
|
||||||
|
nativeLocation.removeGeofences(geofenceRequestIds, callbacks);
|
||||||
|
} else {
|
||||||
|
getServiceInterface().removeGeofencesById(geofenceRequestIds.toArray(new String[geofenceRequestIds.size()]), callbacks, getContext().getPackageName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeGeofences(PendingIntent pendingIntent, IGeofencerCallbacks callbacks) throws RemoteException {
|
||||||
|
if (nativeLocation != null) {
|
||||||
|
nativeLocation.removeGeofences(pendingIntent, callbacks);
|
||||||
|
} else {
|
||||||
|
getServiceInterface().removeGeofencesByIntent(pendingIntent, callbacks, getContext().getPackageName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Location getLastLocation() throws RemoteException {
|
||||||
|
Log.d(TAG, "getLastLocation()");
|
||||||
|
if (nativeLocation != null) {
|
||||||
|
return nativeLocation.getLastLocation();
|
||||||
|
} else {
|
||||||
|
return getServiceInterface().getLastLocation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestLocationUpdates(LocationRequest request, final LocationListener listener)
|
||||||
|
throws RemoteException {
|
||||||
|
if (nativeLocation != null) {
|
||||||
|
nativeLocation.requestLocationUpdates(request, listener);
|
||||||
|
} else {
|
||||||
|
if (!listenerMap.containsKey(listener)) {
|
||||||
|
listenerMap.put(listener, new ILocationListener.Stub() {
|
||||||
|
@Override
|
||||||
|
public void onLocationChanged(Location location) throws RemoteException {
|
||||||
|
listener.onLocationChanged(location);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
getServiceInterface().requestLocationUpdatesWithPackage(request,
|
||||||
|
listenerMap.get(listener), getContext().getPackageName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestLocationUpdates(LocationRequest request, PendingIntent pendingIntent)
|
||||||
|
throws RemoteException {
|
||||||
|
if (nativeLocation != null) {
|
||||||
|
nativeLocation.requestLocationUpdates(request, pendingIntent);
|
||||||
|
} else {
|
||||||
|
getServiceInterface().requestLocationUpdatesWithIntent(request, pendingIntent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestLocationUpdates(LocationRequest request, LocationListener listener,
|
||||||
|
Looper looper) throws RemoteException {
|
||||||
|
if (nativeLocation != null) {
|
||||||
|
nativeLocation.requestLocationUpdates(request, listener, looper);
|
||||||
|
}
|
||||||
|
requestLocationUpdates(request, listener); // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeLocationUpdates(LocationListener listener) throws RemoteException {
|
||||||
|
if (nativeLocation != null) {
|
||||||
|
nativeLocation.removeLocationUpdates(listener);
|
||||||
|
} else {
|
||||||
|
getServiceInterface().removeLocationUpdatesWithListener(listenerMap.get(listener));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeLocationUpdates(PendingIntent pendingIntent) throws RemoteException {
|
||||||
|
if (nativeLocation != null) {
|
||||||
|
nativeLocation.removeLocationUpdates(pendingIntent);
|
||||||
|
} else {
|
||||||
|
getServiceInterface().removeLocationUpdatesWithIntent(pendingIntent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMockMode(boolean isMockMode) throws RemoteException {
|
||||||
|
if (nativeLocation != null) {
|
||||||
|
nativeLocation.setMockMode(isMockMode);
|
||||||
|
} else {
|
||||||
|
getServiceInterface().setMockMode(isMockMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMockLocation(Location mockLocation) throws RemoteException {
|
||||||
|
if (nativeLocation != null) {
|
||||||
|
nativeLocation.setMockLocation(mockLocation);
|
||||||
|
} else {
|
||||||
|
getServiceInterface().setMockLocation(mockLocation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleConnectionFailed() {
|
||||||
|
// DO NOT call super here, because fails are not really problems :)
|
||||||
|
nativeLocation = new NativeLocationClientImpl(this);
|
||||||
|
state = ConnectionState.PSEUDO_CONNECTED;
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putBoolean("fallback_to_native_active", true);
|
||||||
|
callbacks.onConnected(bundle);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.location;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Looper;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.AccountInfo;
|
||||||
|
import com.google.android.gms.common.api.Api;
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
|
||||||
|
import org.microg.gms.common.api.ApiBuilder;
|
||||||
|
import org.microg.gms.common.api.ApiConnection;
|
||||||
|
|
||||||
|
public class LocationServicesApiBuilder implements ApiBuilder<Api.ApiOptions.NoOptions> {
|
||||||
|
@Override
|
||||||
|
public ApiConnection build(Context context, Looper looper,
|
||||||
|
Api.ApiOptions.NoOptions options,
|
||||||
|
AccountInfo accountInfo, GoogleApiClient.ConnectionCallbacks callbacks,
|
||||||
|
GoogleApiClient.OnConnectionFailedListener connectionFailedListener) {
|
||||||
|
return new LocationClientImpl(context, callbacks, connectionFailedListener);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,264 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.location;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.location.Criteria;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.location.LocationManager;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.CommonStatusCodes;
|
||||||
|
import com.google.android.gms.location.FusedLocationProviderApi;
|
||||||
|
import com.google.android.gms.location.Geofence;
|
||||||
|
import com.google.android.gms.location.GeofenceStatusCodes;
|
||||||
|
import com.google.android.gms.location.GeofencingEvent;
|
||||||
|
import com.google.android.gms.location.GeofencingRequest;
|
||||||
|
import com.google.android.gms.location.LocationListener;
|
||||||
|
import com.google.android.gms.location.LocationRequest;
|
||||||
|
import com.google.android.gms.location.internal.IGeofencerCallbacks;
|
||||||
|
import com.google.android.gms.location.internal.ParcelableGeofence;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static android.location.LocationManager.KEY_LOCATION_CHANGED;
|
||||||
|
import static android.location.LocationManager.KEY_PROXIMITY_ENTERING;
|
||||||
|
|
||||||
|
@SuppressWarnings("MissingPermission")
|
||||||
|
public class NativeLocationClientImpl {
|
||||||
|
private final static String TAG = "GmsToNativeLocClient";
|
||||||
|
private final static Criteria DEFAULT_CRITERIA = new Criteria();
|
||||||
|
private final static Map<PendingIntent, Integer> pendingCount = new HashMap<PendingIntent, Integer>();
|
||||||
|
private final static Map<PendingIntent, PendingIntent> nativePendingMap = new HashMap<PendingIntent, PendingIntent>();
|
||||||
|
private static final String EXTRA_PENDING_INTENT = "pending_intent";
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
private final LocationManager locationManager;
|
||||||
|
private final Map<LocationListener, NativeListener> nativeListenerMap = new HashMap<LocationListener, NativeListener>();
|
||||||
|
|
||||||
|
public NativeLocationClientImpl(LocationClientImpl client) {
|
||||||
|
context = client.getContext();
|
||||||
|
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Criteria makeNativeCriteria(LocationRequest request) {
|
||||||
|
Criteria criteria = new Criteria();
|
||||||
|
switch (request.getPriority()) {
|
||||||
|
case LocationRequest.PRIORITY_HIGH_ACCURACY:
|
||||||
|
criteria.setAccuracy(Criteria.ACCURACY_FINE);
|
||||||
|
criteria.setPowerRequirement(Criteria.POWER_HIGH);
|
||||||
|
break;
|
||||||
|
case LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY:
|
||||||
|
default:
|
||||||
|
criteria.setAccuracy(Criteria.ACCURACY_COARSE);
|
||||||
|
criteria.setPowerRequirement(Criteria.POWER_MEDIUM);
|
||||||
|
break;
|
||||||
|
case LocationRequest.PRIORITY_NO_POWER:
|
||||||
|
case LocationRequest.PRIORITY_LOW_POWER:
|
||||||
|
criteria.setAccuracy(Criteria.ACCURACY_COARSE);
|
||||||
|
criteria.setPowerRequirement(Criteria.POWER_LOW);
|
||||||
|
}
|
||||||
|
return criteria;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addGeofences(GeofencingRequest geofencingRequest, PendingIntent pendingIntent, IGeofencerCallbacks callbacks) throws RemoteException {
|
||||||
|
Log.d(TAG, "addGeofences(GeofencingRequest)");
|
||||||
|
callbacks.onAddGeofenceResult(GeofenceStatusCodes.GEOFENCE_NOT_AVAILABLE, new String[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addGeofences(List<ParcelableGeofence> geofences, PendingIntent pendingIntent, IGeofencerCallbacks callbacks) throws RemoteException {
|
||||||
|
Log.d(TAG, "addGeofences(List<ParcelableGeofence>)");
|
||||||
|
Intent i = new Intent(context, NativePendingIntentForwarder.class);
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putParcelable(EXTRA_PENDING_INTENT, pendingIntent);
|
||||||
|
i.putExtras(bundle);
|
||||||
|
nativePendingMap.put(pendingIntent, PendingIntent.getActivity(context, 0, i, 0));
|
||||||
|
List<String> requestIds = new ArrayList<String>();
|
||||||
|
for (ParcelableGeofence geofence : geofences) {
|
||||||
|
locationManager.addProximityAlert(geofence.latitude, geofence.longitude, geofence.radius,
|
||||||
|
geofence.expirationTime - SystemClock.elapsedRealtime(), nativePendingMap.get(pendingIntent));
|
||||||
|
requestIds.add(geofence.getRequestId());
|
||||||
|
}
|
||||||
|
callbacks.onAddGeofenceResult(CommonStatusCodes.SUCCESS, requestIds.toArray(new String[requestIds.size()]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeGeofences(List<String> requestIds, IGeofencerCallbacks callbacks) throws RemoteException {
|
||||||
|
Log.d(TAG, "removeGeofences(List<RequestId>)");
|
||||||
|
callbacks.onRemoveGeofencesByRequestIdsResult(GeofenceStatusCodes.ERROR, requestIds.toArray(new String[requestIds.size()]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeGeofences(PendingIntent pendingIntent, IGeofencerCallbacks callbacks) throws RemoteException {
|
||||||
|
Log.d(TAG, "removeGeofences(PendingIntent)");
|
||||||
|
locationManager.removeProximityAlert(nativePendingMap.get(pendingIntent));
|
||||||
|
nativePendingMap.remove(pendingIntent);
|
||||||
|
callbacks.onRemoveGeofencesByPendingIntentResult(CommonStatusCodes.SUCCESS, pendingIntent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Location getLastLocation() {
|
||||||
|
Log.d(TAG, "getLastLocation()");
|
||||||
|
return locationManager.getLastKnownLocation(locationManager.getBestProvider(DEFAULT_CRITERIA, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestLocationUpdates(LocationRequest request, LocationListener listener) {
|
||||||
|
requestLocationUpdates(request, listener, Looper.getMainLooper());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestLocationUpdates(LocationRequest request, PendingIntent pendingIntent) {
|
||||||
|
Log.d(TAG, "requestLocationUpdates()");
|
||||||
|
Intent i = new Intent(context, NativePendingIntentForwarder.class);
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putParcelable(EXTRA_PENDING_INTENT, pendingIntent);
|
||||||
|
i.putExtras(bundle);
|
||||||
|
pendingCount.put(pendingIntent, request.getNumUpdates());
|
||||||
|
nativePendingMap.put(pendingIntent, PendingIntent.getActivity(context, 0, i, 0));
|
||||||
|
locationManager.requestLocationUpdates(request.getInterval(), request.getSmallestDesplacement(),
|
||||||
|
makeNativeCriteria(request), nativePendingMap.get(pendingIntent));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestLocationUpdates(LocationRequest request, LocationListener listener, Looper
|
||||||
|
looper) {
|
||||||
|
Log.d(TAG, "requestLocationUpdates()");
|
||||||
|
if (nativeListenerMap.containsKey(listener)) {
|
||||||
|
removeLocationUpdates(listener);
|
||||||
|
}
|
||||||
|
nativeListenerMap.put(listener, new NativeListener(listener, request.getNumUpdates()));
|
||||||
|
locationManager.requestLocationUpdates(request.getInterval(),
|
||||||
|
request.getSmallestDesplacement(), makeNativeCriteria(request),
|
||||||
|
nativeListenerMap.get(listener), looper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeLocationUpdates(LocationListener listener) {
|
||||||
|
Log.d(TAG, "removeLocationUpdates()");
|
||||||
|
locationManager.removeUpdates(nativeListenerMap.get(listener));
|
||||||
|
nativeListenerMap.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeLocationUpdates(PendingIntent pendingIntent) {
|
||||||
|
Log.d(TAG, "removeLocationUpdates()");
|
||||||
|
locationManager.removeUpdates(nativePendingMap.get(pendingIntent));
|
||||||
|
nativePendingMap.remove(pendingIntent);
|
||||||
|
pendingCount.remove(pendingIntent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMockMode(boolean isMockMode) {
|
||||||
|
Log.d(TAG, "setMockMode()");
|
||||||
|
// not yet supported
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMockLocation(Location mockLocation) {
|
||||||
|
Log.d(TAG, "setMockLocation()");
|
||||||
|
// not yet supported
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class NativePendingIntentForwarder extends BroadcastReceiver {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (intent.hasExtra(KEY_PROXIMITY_ENTERING)) {
|
||||||
|
PendingIntent pendingIntent = intent.getExtras().getParcelable(EXTRA_PENDING_INTENT);
|
||||||
|
try {
|
||||||
|
intent.putExtra(GeofencingEvent.EXTRA_TRANSITION, intent.getBooleanExtra(KEY_PROXIMITY_ENTERING, false) ? Geofence.GEOFENCE_TRANSITION_ENTER : Geofence.GEOFENCE_TRANSITION_EXIT);
|
||||||
|
pendingIntent.send(context, 0, intent);
|
||||||
|
} catch (PendingIntent.CanceledException e) {
|
||||||
|
nativePendingMap.remove(pendingIntent);
|
||||||
|
}
|
||||||
|
} else if (intent.hasExtra(KEY_LOCATION_CHANGED)) {
|
||||||
|
PendingIntent pendingIntent = intent.getExtras().getParcelable(EXTRA_PENDING_INTENT);
|
||||||
|
try {
|
||||||
|
intent.putExtra(FusedLocationProviderApi.KEY_LOCATION_CHANGED,
|
||||||
|
intent.<Location>getParcelableExtra(KEY_LOCATION_CHANGED));
|
||||||
|
pendingIntent.send(context, 0, intent);
|
||||||
|
pendingCount.put(pendingIntent, pendingCount.get(pendingIntent) - 1);
|
||||||
|
if (pendingCount.get(pendingIntent) == 0) {
|
||||||
|
((LocationManager) context.getSystemService(Context.LOCATION_SERVICE))
|
||||||
|
.removeUpdates(nativePendingMap.get(pendingIntent));
|
||||||
|
nativePendingMap.remove(pendingIntent);
|
||||||
|
pendingCount.remove(pendingIntent);
|
||||||
|
}
|
||||||
|
} catch (PendingIntent.CanceledException e) {
|
||||||
|
((LocationManager) context.getSystemService(Context.LOCATION_SERVICE))
|
||||||
|
.removeUpdates(nativePendingMap.get(pendingIntent));
|
||||||
|
nativePendingMap.remove(pendingIntent);
|
||||||
|
pendingCount.remove(pendingIntent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NativeListener implements android.location.LocationListener {
|
||||||
|
|
||||||
|
private final LocationListener listener;
|
||||||
|
private int count;
|
||||||
|
|
||||||
|
private NativeListener(LocationListener listener, int count) {
|
||||||
|
this.listener = listener;
|
||||||
|
this.count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLocationChanged(Location location) {
|
||||||
|
listener.onLocationChanged(location);
|
||||||
|
count--;
|
||||||
|
if (count == 0) {
|
||||||
|
locationManager.removeUpdates(this);
|
||||||
|
nativeListenerMap.remove(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStatusChanged(String provider, int status, Bundle extras) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProviderEnabled(String provider) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProviderDisabled(String provider) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
NativeListener that = (NativeListener) o;
|
||||||
|
|
||||||
|
if (!listener.equals(that.listener)) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return listener.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.location;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
import com.google.android.gms.common.api.PendingResult;
|
||||||
|
import com.google.android.gms.common.api.Status;
|
||||||
|
import com.google.android.gms.location.LocationSettingsRequest;
|
||||||
|
import com.google.android.gms.location.LocationSettingsResult;
|
||||||
|
import com.google.android.gms.location.SettingsApi;
|
||||||
|
|
||||||
|
import org.microg.gms.common.api.InstantPendingResult;
|
||||||
|
|
||||||
|
public class SettingsApiImpl implements SettingsApi {
|
||||||
|
@Override
|
||||||
|
public PendingResult<LocationSettingsResult> checkLocationSettings(GoogleApiClient client, LocationSettingsRequest locationSettingsRequest) {
|
||||||
|
return new InstantPendingResult<LocationSettingsResult>(new LocationSettingsResult(Status.CANCELED));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
|
String getMyVersionName() {
|
||||||
|
def stdout = new ByteArrayOutputStream()
|
||||||
|
if (rootProject.file("gradlew").exists())
|
||||||
|
exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout }
|
||||||
|
else // automatic build system, don't tag dirty
|
||||||
|
exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout }
|
||||||
|
return stdout.toString().trim().substring(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion androidCompileSdk()
|
||||||
|
buildToolsVersion "$androidBuildVersionTools"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
versionName getMyVersionName()
|
||||||
|
minSdkVersion androidMinSdk()
|
||||||
|
targetSdkVersion androidTargetSdk()
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api project(':play-services-basement')
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
#
|
||||||
|
# Copyright 2013-2016 microG Project Team
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
POM_NAME=Play Services Library Tasks
|
||||||
|
POM_DESCRIPTION=Classes used by some Play Services Library modules to abstract tasks
|
||||||
|
|
||||||
|
POM_PACKAGING=aar
|
||||||
|
|
||||||
|
POM_URL=https://github.com/microg/android_external_GmsLib
|
||||||
|
|
||||||
|
POM_SCM_URL=https://github.com/microg/android_external_GmsLib
|
||||||
|
POM_SCM_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git
|
||||||
|
POM_SCM_DEV_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git
|
||||||
|
|
||||||
|
POM_LICENCE_NAME=The Apache Software License, Version 2.0
|
||||||
|
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
POM_LICENCE_DIST=repo
|
||||||
|
|
||||||
|
POM_DEVELOPER_ID=mar-v-in
|
||||||
|
POM_DEVELOPER_NAME=Marvin W
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2013-2017 microG Project Team
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<manifest package="org.microg.gms.tasks"/>
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.tasks;
|
||||||
|
|
||||||
|
import org.microg.gms.common.PublicApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function that is called to continue execution after completion of a {@link Task}.
|
||||||
|
*
|
||||||
|
* @see Task#continueWith(Continuation)
|
||||||
|
* @see Task#continueWithTask(Continuation)
|
||||||
|
*/
|
||||||
|
@PublicApi
|
||||||
|
public interface Continuation<TResult, TContinuationResult> {
|
||||||
|
/**
|
||||||
|
* Returns the result of applying this Continuation to {@code task}.
|
||||||
|
* <p/>
|
||||||
|
* To propagate failure from the completed Task call {@link Task#getResult()} and allow the
|
||||||
|
* {@link RuntimeExecutionException} to propagate. The RuntimeExecutionException will be
|
||||||
|
* unwrapped such that the Task returned by {@link Task#continueWith(Continuation)} or
|
||||||
|
* {@link Task#continueWithTask(Continuation)} fails with the original exception.
|
||||||
|
* <p/>
|
||||||
|
* To suppress specific failures call {@link Task#getResult(Class)} and catch the exception
|
||||||
|
* types of interest:
|
||||||
|
* <pre>task.continueWith(new Continuation<String, String>() {
|
||||||
|
* @Override
|
||||||
|
* public String then(Task<String> task) {
|
||||||
|
* try {
|
||||||
|
* return task.getResult(IOException.class);
|
||||||
|
* } catch (FileNotFoundException e) {
|
||||||
|
* return "Not found";
|
||||||
|
* } catch (IOException e) {
|
||||||
|
* return "Read failed";
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }</pre>
|
||||||
|
* <p/>
|
||||||
|
* To suppress all failures guard any calls to {@link Task#getResult()} with {@link Task#isSuccessful()}:
|
||||||
|
* <pre>task.continueWith(new Continuation<String, String>() {
|
||||||
|
* @Override
|
||||||
|
* public String then(Task<String> task) {
|
||||||
|
* if (task.isSuccessful()) {
|
||||||
|
* return task.getResult();
|
||||||
|
* } else {
|
||||||
|
* return DEFAULT_VALUE;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @param task the completed Task. Never null
|
||||||
|
* @throws Exception if the result couldn't be produced
|
||||||
|
*/
|
||||||
|
TContinuationResult then(Task<TResult> task);
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.tasks;
|
||||||
|
|
||||||
|
import org.microg.gms.common.PublicApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener called when a {@link Task} completes.
|
||||||
|
*
|
||||||
|
* @see Task#addOnCompleteListener(OnCompleteListener)
|
||||||
|
*/
|
||||||
|
@PublicApi
|
||||||
|
public interface OnCompleteListener<TResult> {
|
||||||
|
/**
|
||||||
|
* Called when the Task completes.
|
||||||
|
*
|
||||||
|
* @param task the completed Task. Never null
|
||||||
|
*/
|
||||||
|
void onComplete(Task<TResult> task);
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.tasks;
|
||||||
|
|
||||||
|
import org.microg.gms.common.PublicApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener called when a {@link Task} fails with an exception.
|
||||||
|
*
|
||||||
|
* @see Task#addOnFailureListener(OnFailureListener)
|
||||||
|
*/
|
||||||
|
@PublicApi
|
||||||
|
public interface OnFailureListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the Task fails with an exception.
|
||||||
|
*
|
||||||
|
* @param e the exception that caused the Task to fail. Never null
|
||||||
|
*/
|
||||||
|
void onFailure(Exception e);
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.tasks;
|
||||||
|
|
||||||
|
import org.microg.gms.common.PublicApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener called when a {@link Task} completes successfully.
|
||||||
|
*
|
||||||
|
* @see Task#addOnSuccessListener(OnSuccessListener)
|
||||||
|
*/
|
||||||
|
@PublicApi
|
||||||
|
public interface OnSuccessListener<TResult> {
|
||||||
|
/**
|
||||||
|
* Called when the {@link Task} completes successfully.
|
||||||
|
*
|
||||||
|
* @param result the result of the Task
|
||||||
|
*/
|
||||||
|
void onSuccess(TResult result);
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.tasks;
|
||||||
|
|
||||||
|
import org.microg.gms.common.PublicApi;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runtime version of {@link ExecutionException}.
|
||||||
|
*
|
||||||
|
* @see Task#getResult(Class)
|
||||||
|
*/
|
||||||
|
@PublicApi
|
||||||
|
public class RuntimeExecutionException extends RuntimeException {
|
||||||
|
public RuntimeExecutionException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,224 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.tasks;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
|
||||||
|
import org.microg.gms.common.PublicApi;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an asynchronous operation.
|
||||||
|
*/
|
||||||
|
@PublicApi
|
||||||
|
public abstract class Task<TResult> {
|
||||||
|
|
||||||
|
public Task() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a listener that is called when the Task completes.
|
||||||
|
* <p/>
|
||||||
|
* The listener will be called on main application thread. If the Task is already complete, a
|
||||||
|
* call to the listener will be immediately scheduled. If multiple listeners are added, they
|
||||||
|
* will be called in the order in which they were added.
|
||||||
|
*
|
||||||
|
* @return this Task
|
||||||
|
*/
|
||||||
|
public Task<TResult> addOnCompleteListener(OnCompleteListener<TResult> listener) {
|
||||||
|
throw new UnsupportedOperationException("addOnCompleteListener is not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an Activity-scoped listener that is called when the Task completes.
|
||||||
|
* <p/>
|
||||||
|
* The listener will be called on main application thread. If the Task is already complete, a
|
||||||
|
* call to the listener will be immediately scheduled. If multiple listeners are added, they
|
||||||
|
* will be called in the order in which they were added.
|
||||||
|
* <p/>
|
||||||
|
* The listener will be automatically removed during {@link Activity#onStop()}.
|
||||||
|
*
|
||||||
|
* @return this Task
|
||||||
|
*/
|
||||||
|
public Task<TResult> addOnCompleteListener(Activity activity, OnCompleteListener<TResult> listener) {
|
||||||
|
throw new UnsupportedOperationException("addOnCompleteListener is not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a listener that is called when the Task completes.
|
||||||
|
* <p/>
|
||||||
|
* If the Task is already complete, a call to the listener will be immediately scheduled. If
|
||||||
|
* multiple listeners are added, they will be called in the order in which they were added.
|
||||||
|
*
|
||||||
|
* @param executor the executor to use to call the listener
|
||||||
|
* @return this Task
|
||||||
|
*/
|
||||||
|
public Task<TResult> addOnCompleteListener(Executor executor, OnCompleteListener<TResult> listener) {
|
||||||
|
throw new UnsupportedOperationException("addOnCompleteListener is not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an Activity-scoped listener that is called if the Task fails.
|
||||||
|
* <p/>
|
||||||
|
* The listener will be called on main application thread. If the Task has already failed, a
|
||||||
|
* call to the listener will be immediately scheduled. If multiple listeners are added, they
|
||||||
|
* will be called in the order in which they were added.
|
||||||
|
* <p/>
|
||||||
|
* The listener will be automatically removed during {@link Activity#onStop()}.
|
||||||
|
*
|
||||||
|
* @return this Task
|
||||||
|
*/
|
||||||
|
public abstract Task<TResult> addOnFailureListener(Activity activity, OnFailureListener listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an Activity-scoped listener that is called if the Task fails.
|
||||||
|
* <p/>
|
||||||
|
* The listener will be called on main application thread. If the Task has already failed, a
|
||||||
|
* call to the listener will be immediately scheduled. If multiple listeners are added, they
|
||||||
|
* will be called in the order in which they were added.
|
||||||
|
*
|
||||||
|
* @return this Task
|
||||||
|
*/
|
||||||
|
public abstract Task<TResult> addOnFailureListener(OnFailureListener listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a listener that is called if the Task fails.
|
||||||
|
* <p/>
|
||||||
|
* If the Task has already failed, a call to the listener will be immediately scheduled. If
|
||||||
|
* multiple listeners are added, they will be called in the order in which they were added.
|
||||||
|
*
|
||||||
|
* @param executor the executor to use to call the listener
|
||||||
|
* @return this Task
|
||||||
|
*/
|
||||||
|
public abstract Task<TResult> addOnFailureListener(Executor executor, OnFailureListener listener);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a listener that is called if the Task completes successfully.
|
||||||
|
* <p/>
|
||||||
|
* If multiple listeners are added, they will be called in the order in which they were added. If
|
||||||
|
* the Task has already completed successfully, a call to the listener will be immediately scheduled.
|
||||||
|
*
|
||||||
|
* @param executor the executor to use to call the listener
|
||||||
|
* @return this Task
|
||||||
|
*/
|
||||||
|
public abstract Task<TResult> addOnSuccessListener(Executor executor, OnSuccessListener<? super TResult> listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a listener that is called if the Task completes successfully.
|
||||||
|
* <p/>
|
||||||
|
* The listener will be called on the main application thread. If the Task has already
|
||||||
|
* completed successfully, a call to the listener will be immediately scheduled. If multiple
|
||||||
|
* listeners are added, they will be called in the order in which they were added.
|
||||||
|
*
|
||||||
|
* @return this Task
|
||||||
|
*/
|
||||||
|
public abstract Task<TResult> addOnSuccessListener(OnSuccessListener<? super TResult> listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an Activity-scoped listener that is called if the Task completes successfully.
|
||||||
|
* <p/>
|
||||||
|
* The listener will be called on the main application thread. If the Task has already
|
||||||
|
* completed successfully, a call to the listener will be immediately scheduled. If multiple
|
||||||
|
* listeners are added, they will be called in the order in which they were added.
|
||||||
|
* <p/>
|
||||||
|
* The listener will be automatically removed during {@link Activity#onStop()}.
|
||||||
|
*
|
||||||
|
* @return this Task
|
||||||
|
*/
|
||||||
|
public abstract Task<TResult> addOnSuccessListener(Activity activity, OnSuccessListener<? super TResult> listener);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new Task that will be completed with the result of applying the specified
|
||||||
|
* Continuation to this Task.
|
||||||
|
* <p/>
|
||||||
|
* The Continuation will be called on the main application thread.
|
||||||
|
*
|
||||||
|
* @see Continuation#then(Task)
|
||||||
|
*/
|
||||||
|
public <TContinuationResult> Task<TContinuationResult> continueWith(Continuation<TResult, TContinuationResult> continuation) {
|
||||||
|
throw new UnsupportedOperationException("continueWith is not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new Task that will be completed with the result of applying the specified Continuation to this Task.
|
||||||
|
*
|
||||||
|
* @param executor the executor to use to call the Continuation
|
||||||
|
* @see Continuation#then(Task)
|
||||||
|
*/
|
||||||
|
public <TContinuationResult> Task<TContinuationResult> continueWith(Executor executor, Continuation<TResult, TContinuationResult> continuation) {
|
||||||
|
throw new UnsupportedOperationException("continueWith is not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new Task that will be completed with the result of applying the specified
|
||||||
|
* Continuation to this Task.
|
||||||
|
* <p/>
|
||||||
|
* The Continuation will be called on the main application thread.
|
||||||
|
*
|
||||||
|
* @see Continuation#then(Task)
|
||||||
|
*/
|
||||||
|
public <TContinuationResult> Task<TContinuationResult> continueWithTask(Continuation<TResult, Task<TContinuationResult>> continuation) {
|
||||||
|
throw new UnsupportedOperationException("continueWithTask is not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new Task that will be completed with the result of applying the specified Continuation to this Task.
|
||||||
|
*
|
||||||
|
* @param executor the executor to use to call the Continuation
|
||||||
|
* @see Continuation#then(Task)
|
||||||
|
*/
|
||||||
|
public <TContinuationResult> Task<TContinuationResult> continueWithTask(Executor executor, Continuation<TResult, Task<TContinuationResult>> var2) {
|
||||||
|
throw new UnsupportedOperationException("continueWithTask is not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the exception that caused the Task to fail. Returns {@code null} if the Task is not
|
||||||
|
* yet complete, or completed successfully.
|
||||||
|
*/
|
||||||
|
public abstract Exception getException();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the result of the Task, if it has already completed.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if the Task is not yet complete
|
||||||
|
* @throws RuntimeExecutionException if the Task failed with an exception
|
||||||
|
*/
|
||||||
|
public abstract TResult getResult();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the result of the Task, if it has already completed.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if the Task is not yet complete
|
||||||
|
* @throws X if the Task failed with an exception of type X
|
||||||
|
* @throws RuntimeExecutionException if the Task failed with an exception that was not of type X
|
||||||
|
*/
|
||||||
|
public abstract <X extends Throwable> TResult getResult(Class<X> exceptionType) throws X;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if the Task is complete; {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
public abstract boolean isComplete();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if the Task has completed successfully; {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
public abstract boolean isSuccessful();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.tasks;
|
||||||
|
|
||||||
|
import org.microg.gms.common.PublicApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the ability to create an incomplete {@link Task} and later complete it by either
|
||||||
|
* calling {@link #setResult(TResult)} or {@link #setException(Exception)}.
|
||||||
|
*/
|
||||||
|
@PublicApi
|
||||||
|
public class TaskCompletionSource<TResult> {
|
||||||
|
public TaskCompletionSource() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Task.
|
||||||
|
*/
|
||||||
|
public Task<TResult> getTask() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Completes the Task with the specified exception.
|
||||||
|
* @throws IllegalStateException if the Task is already complete
|
||||||
|
*/
|
||||||
|
public void setException(Exception e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Completes the Task with the specified result.
|
||||||
|
* @throws IllegalStateException if the Task is already complete
|
||||||
|
*/
|
||||||
|
public void setResult(TResult result) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,116 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.tasks;
|
||||||
|
|
||||||
|
import org.microg.gms.common.PublicApi;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Task} utility methods.
|
||||||
|
*/
|
||||||
|
@PublicApi
|
||||||
|
public final class Tasks {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blocks until the specified Task is complete.
|
||||||
|
*
|
||||||
|
* @return the Task's result
|
||||||
|
* @throws ExecutionException if the Task fails
|
||||||
|
* @throws InterruptedException if an interrupt occurs while waiting for the Task to complete
|
||||||
|
* @throws TimeoutException if the specified timeout is reached before the Task completes
|
||||||
|
*/
|
||||||
|
public static <TResult> TResult await(Task<TResult> task, long timeout, TimeUnit unit) {
|
||||||
|
// TODO
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blocks until the specified Task is complete.
|
||||||
|
*
|
||||||
|
* @return the Task's result
|
||||||
|
* @throws ExecutionException if the Task fails
|
||||||
|
* @throws InterruptedException if an interrupt occurs while waiting for the Task to complete
|
||||||
|
*/
|
||||||
|
public static <TResult> TResult await(Task<TResult> task) {
|
||||||
|
// TODO
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a Task that will be completed with the result of the specified Callable.
|
||||||
|
* <p/>
|
||||||
|
* The Callable will be called on the main application thread.
|
||||||
|
*/
|
||||||
|
public static <TResult> Task<TResult> call(Callable<TResult> callable) {
|
||||||
|
// TODO
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a Task that will be completed with the result of the specified Callable.
|
||||||
|
*
|
||||||
|
* @param executor the Executor to use to call the Callable
|
||||||
|
*/
|
||||||
|
public static <TResult> Task<TResult> call(Executor executor, Callable<TResult> callable) {
|
||||||
|
// TODO
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a completed Task with the specified exception.
|
||||||
|
*/
|
||||||
|
public static <TResult> Task<TResult> forException(Exception e) {
|
||||||
|
// TODO
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a completed Task with the specified result.
|
||||||
|
*/
|
||||||
|
public static <TResult> Task<TResult> forResult(TResult result) {
|
||||||
|
// TODO
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a Task that completes successfully when all of the specified Tasks complete
|
||||||
|
* successfully. Does not accept nulls.
|
||||||
|
*
|
||||||
|
* @throws NullPointerException if any of the provided Tasks are null
|
||||||
|
*/
|
||||||
|
public static Task<Void> whenAll(Collection<? extends Task<?>> tasks) {
|
||||||
|
// TODO
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a Task that completes successfully when all of the specified Tasks complete
|
||||||
|
* successfully. Does not accept nulls.
|
||||||
|
*
|
||||||
|
* @throws NullPointerException if any of the provided Tasks are null
|
||||||
|
*/
|
||||||
|
public static Task<Void> whenAll(Task<?>... tasks) {
|
||||||
|
return whenAll(Arrays.asList(tasks));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
|
String getMyVersionName() {
|
||||||
|
def stdout = new ByteArrayOutputStream()
|
||||||
|
if (rootProject.file("gradlew").exists())
|
||||||
|
exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout }
|
||||||
|
else // automatic build system, don't tag dirty
|
||||||
|
exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout }
|
||||||
|
return stdout.toString().trim().substring(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion androidCompileSdk()
|
||||||
|
buildToolsVersion "$androidBuildVersionTools"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
versionName getMyVersionName()
|
||||||
|
minSdkVersion androidMinSdk()
|
||||||
|
targetSdkVersion androidTargetSdk()
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java.srcDirs += 'src/main/protos-java'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
lintOptions {
|
||||||
|
disable 'InvalidPackage'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api project(':play-services-base')
|
||||||
|
api project(':play-services-wearable-api')
|
||||||
|
implementation 'com.squareup.wire:wire-runtime:1.6.1'
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
#
|
||||||
|
# Copyright 2013-2016 microG Project Team
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
POM_NAME=Play Services Wearable Library
|
||||||
|
POM_DESCRIPTION=The Play Services Library module to access the Wearable API
|
||||||
|
|
||||||
|
POM_PACKAGING=aar
|
||||||
|
|
||||||
|
POM_URL=https://github.com/microg/android_external_GmsLib
|
||||||
|
|
||||||
|
POM_SCM_URL=https://github.com/microg/android_external_GmsLib
|
||||||
|
POM_SCM_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git
|
||||||
|
POM_SCM_DEV_CONNECTION=scm:git@github.com:microg/android_external_GmsLib.git
|
||||||
|
|
||||||
|
POM_LICENCE_NAME=The Apache Software License, Version 2.0
|
||||||
|
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||||
|
POM_LICENCE_DIST=repo
|
||||||
|
|
||||||
|
POM_DEVELOPER_ID=mar-v-in
|
||||||
|
POM_DEVELOPER_NAME=Marvin W
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2013-2017 microG Project Team
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<manifest package="org.microg.gms.wearable"/>
|
|
@ -0,0 +1,193 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.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.
|
||||||
|
* <p/>
|
||||||
|
* Capabilities are local to an application.
|
||||||
|
*/
|
||||||
|
@PublicApi
|
||||||
|
public interface CapabilityApi {
|
||||||
|
/**
|
||||||
|
* Capability changed action for use in manifest-based listener filters.
|
||||||
|
* <p/>
|
||||||
|
* 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.
|
||||||
|
* <p/>
|
||||||
|
* 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)}).
|
||||||
|
* <p/>
|
||||||
|
* Callers wishing to be notified of events in the background should use {@link WearableListenerService}.
|
||||||
|
*/
|
||||||
|
PendingResult<Status> 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.
|
||||||
|
* <p/>
|
||||||
|
* {@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 <data>} element of {@code <intent-filter>}. 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:
|
||||||
|
* <p/>
|
||||||
|
* <pre>wear://* /<capability_name></pre>
|
||||||
|
* 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<Status> addListener(GoogleApiClient client, CapabilityListener listener, Uri uri, @CapabilityFilterType int filterType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Announces that a capability has become available on the local node.
|
||||||
|
*/
|
||||||
|
PendingResult<AddLocalCapabilityResult> 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}.
|
||||||
|
* <p/>
|
||||||
|
* The local node will never be returned in the set of nodes.
|
||||||
|
*/
|
||||||
|
PendingResult<GetAllCapabilitiesResult> 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}.
|
||||||
|
* <p/>
|
||||||
|
* The local node will never be returned in the set of nodes.
|
||||||
|
*/
|
||||||
|
PendingResult<GetCapabilityResult> 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<Status> 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<Status> 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<RemoveLocalCapabilityResult> 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<String, CapabilityInfo> 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 {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.wearable;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
import com.google.android.gms.common.api.PendingResult;
|
||||||
|
import com.google.android.gms.common.api.Releasable;
|
||||||
|
import com.google.android.gms.common.api.Result;
|
||||||
|
import com.google.android.gms.common.api.Status;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A channel created through {@link ChannelApi#openChannel(GoogleApiClient, String, String)}.
|
||||||
|
* <p/>
|
||||||
|
* The implementation of this interface is parcelable and immutable, and implements reasonable {@link #equals(Object)}
|
||||||
|
* and {@link #hashCode()} methods, so can be used in collections.
|
||||||
|
*/
|
||||||
|
public interface Channel extends Parcelable {
|
||||||
|
|
||||||
|
PendingResult<Status> addListener(GoogleApiClient client, ChannelApi.ChannelListener listener);
|
||||||
|
|
||||||
|
PendingResult<Status> close(GoogleApiClient client, int errorCode);
|
||||||
|
|
||||||
|
PendingResult<Status> close(GoogleApiClient client);
|
||||||
|
|
||||||
|
PendingResult<GetInputStreamResult> getInputStream(GoogleApiClient client);
|
||||||
|
|
||||||
|
PendingResult<GetOutputStreamResult> getOutputStream(GoogleApiClient client);
|
||||||
|
|
||||||
|
String getPath();
|
||||||
|
|
||||||
|
PendingResult<Status> receiveFile(GoogleApiClient client, Uri uri, boolean append);
|
||||||
|
|
||||||
|
PendingResult<Status> removeListener(GoogleApiClient client, ChannelApi.ChannelListener listener);
|
||||||
|
|
||||||
|
PendingResult<Status> sendFile(GoogleApiClient client, Uri uri);
|
||||||
|
|
||||||
|
PendingResult<Status> sendFile(GoogleApiClient client, Uri uri, long startOffset, long length);
|
||||||
|
|
||||||
|
interface GetInputStreamResult extends Releasable, Result {
|
||||||
|
/**
|
||||||
|
* Returns an input stream which can read data from the remote node. The stream should be
|
||||||
|
* closed when no longer needed. This method will only return {@code null} if this result's
|
||||||
|
* {@linkplain #getStatus() status} was not {@linkplain Status#isSuccess() success}.
|
||||||
|
* <p/>
|
||||||
|
* The returned stream will throw {@link IOException} on read if any connection errors
|
||||||
|
* occur. This exception might be a {@link ChannelIOException}.
|
||||||
|
* <p/>
|
||||||
|
* Since data for this stream comes over the network, reads may block for a long time.
|
||||||
|
* <p/>
|
||||||
|
* Multiple calls to this method will return the same instance.
|
||||||
|
*/
|
||||||
|
InputStream getInputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GetOutputStreamResult extends Releasable, Result {
|
||||||
|
/**
|
||||||
|
* Returns an output stream which can send data to a remote node. The stream should be
|
||||||
|
* closed when no longer needed. This method will only return {@code null} if this result's
|
||||||
|
* {@linkplain #getStatus() status} was not {@linkplain Status#isSuccess() success}.
|
||||||
|
* <p/>
|
||||||
|
* The returned stream will throw {@link IOException} on read if any connection errors
|
||||||
|
* occur. This exception might be a {@link ChannelIOException}.
|
||||||
|
* <p/>
|
||||||
|
* Since data for this stream comes over the network, reads may block for a long time.
|
||||||
|
* <p/>
|
||||||
|
* Data written to this stream is buffered. If you wish to send the current data without
|
||||||
|
* waiting for the buffer to fill up, {@linkplain OutputStream#flush() flush} the stream.
|
||||||
|
* <p/>
|
||||||
|
* Multiple calls to this method will return the same instance.
|
||||||
|
*/
|
||||||
|
OutputStream getOutputStream();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,185 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.wearable;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient.Builder;
|
||||||
|
import com.google.android.gms.common.api.PendingResult;
|
||||||
|
import com.google.android.gms.common.api.Result;
|
||||||
|
import com.google.android.gms.common.api.Status;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Client interface for Wearable Channel API. Allows apps on a wearable device to send and receive
|
||||||
|
* data from other wearable nodes.
|
||||||
|
* <p/>
|
||||||
|
* Channels are bidirectional. Each side, both the initiator and the receiver may both read and
|
||||||
|
* write to the channel by using {@link Channel#getOutputStream(GoogleApiClient)} and {@link Channel#getInputStream(GoogleApiClient)}.
|
||||||
|
* Once a channel is established, the API for the initiator and receiver are identical.
|
||||||
|
* <p/>
|
||||||
|
* Channels are only available when the wearable nodes are connected. When the remote node
|
||||||
|
* disconnects, all existing channels will be closed. Any listeners (added through {@link #addListener(GoogleApiClient, ChannelListener)}
|
||||||
|
* and any installed {@link WearableListenerService}) will be notified of the channel closing.
|
||||||
|
*/
|
||||||
|
public interface ChannelApi {
|
||||||
|
/**
|
||||||
|
* Channel action for use in listener filters.
|
||||||
|
*
|
||||||
|
* @see WearableListenerService
|
||||||
|
*/
|
||||||
|
String ACTION_CHANNEL_EVENT = "com.google.android.gms.wearable.CHANNEL_EVENT";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a listener to be notified of channel events. Calls to this method should be
|
||||||
|
* balanced with calls to {@link #removeListener(GoogleApiClient, ChannelListener)} to avoid
|
||||||
|
* leaking resources.
|
||||||
|
* <p/>
|
||||||
|
* 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)}).
|
||||||
|
* <p/>
|
||||||
|
* Callers wishing to be notified of events in the background should use {@link WearableListenerService}.
|
||||||
|
*
|
||||||
|
* @param client a connected client
|
||||||
|
* @param listener a listener which will be notified of changes to any channel
|
||||||
|
*/
|
||||||
|
PendingResult<Status> addListener(GoogleApiClient client, ChannelListener listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a channel to exchange data with a remote node.
|
||||||
|
* <p/>
|
||||||
|
* Channel which are no longer needed should be closed using {@link Channel#close(GoogleApiClient)}.
|
||||||
|
* <p/>
|
||||||
|
* This call involves a network round trip, so may be long running. {@code client} must remain
|
||||||
|
* connected during that time, or the request will be cancelled (like any other Play Services
|
||||||
|
* API calls).
|
||||||
|
*
|
||||||
|
* @param client a connected client
|
||||||
|
* @param nodeId the node ID of a wearable node, as returned from {@link NodeApi#getConnectedNodes(GoogleApiClient)}
|
||||||
|
* @param path an app-specific identifier for the channel
|
||||||
|
*/
|
||||||
|
PendingResult<OpenChannelResult> openChannel(GoogleApiClient client, String nodeId, String path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a listener which was previously added through {@link #addListener(GoogleApiClient, ChannelListener)}.
|
||||||
|
*
|
||||||
|
* @param client a connected client
|
||||||
|
* @param listener a listener which was added using {@link #addListener(GoogleApiClient, ChannelListener)}
|
||||||
|
*/
|
||||||
|
PendingResult<Status> removeListener(GoogleApiClient client, ChannelListener listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A listener which will be notified on changes to channels.
|
||||||
|
*/
|
||||||
|
interface ChannelListener {
|
||||||
|
/**
|
||||||
|
* Value passed to {@link #onChannelClosed(Channel, int, int)}, {@link #onInputClosed(Channel, int, int)}
|
||||||
|
* and {@link #onOutputClosed(Channel, int, int)} when the closing is due to a remote node
|
||||||
|
* being disconnected.
|
||||||
|
*/
|
||||||
|
int CLOSE_REASON_DISCONNECTED = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value passed to {@link #onChannelClosed(Channel, int, int)}, {@link #onInputClosed(Channel, int, int)}
|
||||||
|
* and {@link #onOutputClosed(Channel, int, int)} when the stream is closed due to the
|
||||||
|
* local node calling {@link Channel#close(GoogleApiClient)} or {@link Channel#close(GoogleApiClient, int)}.
|
||||||
|
*/
|
||||||
|
int CLOSE_REASON_LOCAL_CLOSE = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value passed to {@link #onInputClosed(Channel, int, int)} or {@link #onOutputClosed(Channel, int, int)}
|
||||||
|
* (but not {@link #onChannelClosed(Channel, int, int)}), when the stream was closed under
|
||||||
|
* normal conditions, e.g the whole file was read, or the OutputStream on the remote node
|
||||||
|
* was closed normally.
|
||||||
|
*/
|
||||||
|
int CLOSE_REASON_NORMAL = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value passed to {@link #onChannelClosed(Channel, int, int)}, {@link #onInputClosed(Channel, int, int)}
|
||||||
|
* and {@link #onOutputClosed(Channel, int, int)} when the stream is closed due to the
|
||||||
|
* remote node calling {@link Channel#close(GoogleApiClient)} or {@link Channel#close(GoogleApiClient, int)}.
|
||||||
|
*/
|
||||||
|
int CLOSE_REASON_REMOTE_CLOSE = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a channel is closed. This can happen through an explicit call to {@link Channel#close(GoogleApiClient)}
|
||||||
|
* or {@link #close(GoogleApiClient, int)} on either side of the connection, or due to
|
||||||
|
* disconnecting from the remote node.
|
||||||
|
*
|
||||||
|
* @param closeReason the reason for the channel closing. One of {@link #CLOSE_REASON_DISCONNECTED},
|
||||||
|
* {@link #CLOSE_REASON_REMOTE_CLOSE}, or {@link #CLOSE_REASON_LOCAL_CLOSE}.
|
||||||
|
* @param appSpecificErrorCode the error code specified on {@link Channel#close(GoogleApiClient, int)},
|
||||||
|
* or 0 if closeReason is {@link #CLOSE_REASON_DISCONNECTED}.
|
||||||
|
*/
|
||||||
|
void onChannelClosed(Channel channel, int closeReason, int appSpecificErrorCode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a new channel is opened by a remote node.
|
||||||
|
*/
|
||||||
|
void onChannelOpened(Channel channel);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the input side of a channel is closed.
|
||||||
|
*
|
||||||
|
* @param closeReason the reason for the channel closing. One of {@link #CLOSE_REASON_DISCONNECTED},
|
||||||
|
* {@link #CLOSE_REASON_REMOTE_CLOSE}, {@link #CLOSE_REASON_LOCAL_CLOSE}
|
||||||
|
* or {@link #CLOSE_REASON_NORMAL}.
|
||||||
|
* @param appSpecificErrorCode the error code specified on {@link Channel#close(GoogleApiClient, int)},
|
||||||
|
* or 0 if closeReason is {@link #CLOSE_REASON_DISCONNECTED} or
|
||||||
|
* {@link #CLOSE_REASON_NORMAL}.
|
||||||
|
*/
|
||||||
|
void onInputClosed(Channel channel, @CloseReason int closeReason, int appSpecificErrorCode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the output side of a channel is closed.
|
||||||
|
*
|
||||||
|
* @param closeReason the reason for the channel closing. One of {@link #CLOSE_REASON_DISCONNECTED},
|
||||||
|
* {@link #CLOSE_REASON_REMOTE_CLOSE}, {@link #CLOSE_REASON_LOCAL_CLOSE}
|
||||||
|
* or {@link #CLOSE_REASON_NORMAL}.
|
||||||
|
* @param appSpecificErrorCode the error code specified on {@link Channel#close(GoogleApiClient, int)},
|
||||||
|
* or 0 if closeReason is {@link #CLOSE_REASON_DISCONNECTED} or
|
||||||
|
* {@link #CLOSE_REASON_NORMAL}.
|
||||||
|
*/
|
||||||
|
void onOutputClosed(Channel channel, @CloseReason int closeReason, int appSpecificErrorCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An annotation for values passed to {@link ChannelListener#onChannelClosed(Channel, int, int)},
|
||||||
|
* and other methods on the {@link ChannelListener} interface. Annotated method parameters will
|
||||||
|
* always take one of the following values:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link ChannelListener#CLOSE_REASON_DISCONNECTED}</li>
|
||||||
|
* <li>{@link ChannelListener#CLOSE_REASON_NORMAL}</li>
|
||||||
|
* <li>{@link ChannelListener#CLOSE_REASON_LOCAL_CLOSE}</li>
|
||||||
|
* <li>{@link ChannelListener#CLOSE_REASON_REMOTE_CLOSE}</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@interface CloseReason {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of {@link #openChannel(GoogleApiClient, String, String)}.
|
||||||
|
*/
|
||||||
|
interface OpenChannelResult extends Result {
|
||||||
|
/**
|
||||||
|
* Returns the newly created channel, or {@code null}, if the connection couldn't be opened.
|
||||||
|
*/
|
||||||
|
Channel getChannel();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2017 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.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;
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue