Merge GmsLib

This commit is contained in:
Marvin W 2020-06-14 23:30:02 +02:00
commit a5bfecee6f
No known key found for this signature in database
GPG key ID: 072E9235DB996F2A
127 changed files with 12949 additions and 0 deletions

View file

@ -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"
}

View file

@ -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

View file

@ -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>

View file

@ -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
}
}
}

View file

@ -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();
}
}

View file

@ -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
}
}

View file

@ -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);
}

View file

@ -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 {
}
}
}

View file

@ -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);
}
}

View file

@ -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);
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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();
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}
}

View file

@ -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) {
}
}
}
}

View file

@ -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();
}
}
}

View file

@ -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();
}
}

View file

@ -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));
}
}

View file

@ -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);
}

Some files were not shown because too many files have changed in this diff Show more