mirror of
https://github.com/TeamVanced/VancedMicroG
synced 2024-10-14 16:59:40 +02:00
Big restructure and add GCM and IID client
This commit is contained in:
parent
d6d32cc203
commit
3bb89224dc
177
LICENSE
Normal file
177
LICENSE
Normal file
@ -0,0 +1,177 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
@ -15,16 +15,7 @@
|
||||
*/
|
||||
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
String getMyVersionName() {
|
||||
def stdout = new ByteArrayOutputStream()
|
||||
if (rootProject.file("gradlew").exists())
|
||||
exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout }
|
||||
else // automatic build system, don't tag dirty
|
||||
exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout }
|
||||
return stdout.toString().trim().substring(1)
|
||||
}
|
||||
|
||||
subprojects {
|
||||
group = 'org.microg'
|
||||
version = getMyVersionName()
|
||||
}
|
||||
|
2
extern/GmsApi
vendored
2
extern/GmsApi
vendored
@ -1 +1 @@
|
||||
Subproject commit 6aa110657beec0b3e6c26d1030943e0b97683335
|
||||
Subproject commit be6af2eeb76ba4bcc1700285f2ba87f217ab6ac2
|
@ -19,15 +19,24 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.0.0'
|
||||
classpath 'com.android.tools.build:gradle:2.1.2'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
String getMyVersionName() {
|
||||
def stdout = new ByteArrayOutputStream()
|
||||
if (rootProject.file("gradlew").exists())
|
||||
exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout }
|
||||
else // automatic build system, don't tag dirty
|
||||
exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout }
|
||||
return stdout.toString().trim().substring(1)
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.2"
|
||||
buildToolsVersion "23.0.3"
|
||||
|
||||
defaultConfig {
|
||||
versionName getMyVersionName()
|
||||
@ -39,6 +48,7 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'com.android.support:support-v4:23.3.0'
|
||||
compile project(':play-services-api')
|
||||
compile 'com.android.support:support-v4:23.4.0'
|
||||
compile project(':play-services-common-api')
|
||||
compile project(':play-services-tasks')
|
||||
}
|
||||
|
@ -15,10 +15,19 @@
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.android.gms">
|
||||
<manifest package="com.google.android.gms"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-sdk android:minSdkVersion="9" />
|
||||
<uses-sdk android:minSdkVersion="9"/>
|
||||
|
||||
<application />
|
||||
<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>
|
||||
|
@ -20,9 +20,12 @@ import android.app.Activity;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Contains all possible error codes for when a client fails to connect to Google Play services.
|
||||
* These error codes are used by {@link GoogleApiClient.OnConnectionFailedListener}.
|
||||
@ -128,8 +131,18 @@ public class ConnectionResult {
|
||||
@Deprecated
|
||||
public static final int DRIVE_EXTERNAL_STORAGE_REQUIRED = 1500;
|
||||
|
||||
private final PendingIntent pendingIntent;
|
||||
private final int statusCode;
|
||||
private final PendingIntent pendingIntent;
|
||||
private final String message;
|
||||
|
||||
/**
|
||||
* Creates a connection result.
|
||||
*
|
||||
* @param statusCode The status code.
|
||||
*/
|
||||
public ConnectionResult(int statusCode) {
|
||||
this(statusCode, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a connection result.
|
||||
@ -138,8 +151,89 @@ public class ConnectionResult {
|
||||
* @param pendingIntent A pending intent that will resolve the issue when started, or null.
|
||||
*/
|
||||
public ConnectionResult(int statusCode, PendingIntent pendingIntent) {
|
||||
this(statusCode, pendingIntent, getStatusString(statusCode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a connection result.
|
||||
*
|
||||
* @param statusCode The status code.
|
||||
* @param pendingIntent A pending intent that will resolve the issue when started, or null.
|
||||
* @param message An additional error message for the connection result, or null.
|
||||
*/
|
||||
public ConnectionResult(int statusCode, PendingIntent pendingIntent, String message) {
|
||||
this.statusCode = statusCode;
|
||||
this.pendingIntent = pendingIntent;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
static String getStatusString(int statusCode) {
|
||||
switch (statusCode) {
|
||||
case -1:
|
||||
return "UNKNOWN";
|
||||
case 0:
|
||||
return "SUCCESS";
|
||||
case 1:
|
||||
return "SERVICE_MISSING";
|
||||
case 2:
|
||||
return "SERVICE_VERSION_UPDATE_REQUIRED";
|
||||
case 3:
|
||||
return "SERVICE_DISABLED";
|
||||
case 4:
|
||||
return "SIGN_IN_REQUIRED";
|
||||
case 5:
|
||||
return "INVALID_ACCOUNT";
|
||||
case 6:
|
||||
return "RESOLUTION_REQUIRED";
|
||||
case 7:
|
||||
return "NETWORK_ERROR";
|
||||
case 8:
|
||||
return "INTERNAL_ERROR";
|
||||
case 9:
|
||||
return "SERVICE_INVALID";
|
||||
case 10:
|
||||
return "DEVELOPER_ERROR";
|
||||
case 11:
|
||||
return "LICENSE_CHECK_FAILED";
|
||||
case 13:
|
||||
return "CANCELED";
|
||||
case 14:
|
||||
return "TIMEOUT";
|
||||
case 15:
|
||||
return "INTERRUPTED";
|
||||
case 16:
|
||||
return "API_UNAVAILABLE";
|
||||
case 17:
|
||||
return "SIGN_IN_FAILED";
|
||||
case 18:
|
||||
return "SERVICE_UPDATING";
|
||||
case 19:
|
||||
return "SERVICE_MISSING_PERMISSION";
|
||||
case 20:
|
||||
return "RESTRICTED_PROFILE";
|
||||
case 21:
|
||||
return "API_VERSION_UPDATE_REQUIRED";
|
||||
case 42:
|
||||
return "UPDATE_ANDROID_WEAR";
|
||||
case 99:
|
||||
return "UNFINISHED";
|
||||
case 1500:
|
||||
return "DRIVE_EXTERNAL_STORAGE_REQUIRED";
|
||||
default:
|
||||
return "UNKNOWN_ERROR_CODE(" + statusCode + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
} else if (!(o instanceof ConnectionResult)) {
|
||||
return false;
|
||||
} else {
|
||||
ConnectionResult r = (ConnectionResult)o;
|
||||
return statusCode == r.statusCode && pendingIntent == null ? r.pendingIntent == null : pendingIntent.equals(r.pendingIntent) && TextUtils.equals(message, r.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -151,6 +245,15 @@ public class ConnectionResult {
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an error message for connection result.
|
||||
*
|
||||
* @return the message
|
||||
*/
|
||||
public String getErrorMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* A pending intent to resolve the connection failure. This intent can be started with
|
||||
* {@link Activity#startIntentSenderForResult(IntentSender, int, Intent, int, int, int)} to
|
||||
@ -162,6 +265,11 @@ public class ConnectionResult {
|
||||
return pendingIntent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(new Object[]{statusCode, pendingIntent, message});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if calling {@link #startResolutionForResult(Activity, int)} will start
|
||||
* any intents requiring user interaction.
|
||||
@ -196,8 +304,7 @@ public class ConnectionResult {
|
||||
public void startResolutionForResult(Activity activity, int requestCode) throws
|
||||
IntentSender.SendIntentException {
|
||||
if (hasResolution()) {
|
||||
activity.startIntentSenderForResult(pendingIntent.getIntentSender(), requestCode, null,
|
||||
0, 0, 0);
|
||||
activity.startIntentSenderForResult(pendingIntent.getIntentSender(), requestCode, null, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,290 @@
|
||||
/*
|
||||
* Copyright 2013-2016 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.common;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
|
||||
import com.google.android.gms.tasks.Task;
|
||||
import com.google.android.gms.tasks.Tasks;
|
||||
|
||||
import org.microg.gms.common.Constants;
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
import static com.google.android.gms.common.ConnectionResult.INTERNAL_ERROR;
|
||||
import static com.google.android.gms.common.ConnectionResult.INVALID_ACCOUNT;
|
||||
import static com.google.android.gms.common.ConnectionResult.NETWORK_ERROR;
|
||||
import static com.google.android.gms.common.ConnectionResult.RESOLUTION_REQUIRED;
|
||||
import static com.google.android.gms.common.ConnectionResult.SERVICE_DISABLED;
|
||||
import static com.google.android.gms.common.ConnectionResult.SERVICE_INVALID;
|
||||
import static com.google.android.gms.common.ConnectionResult.SERVICE_MISSING;
|
||||
import static com.google.android.gms.common.ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED;
|
||||
import static com.google.android.gms.common.ConnectionResult.SIGN_IN_REQUIRED;
|
||||
import static com.google.android.gms.common.ConnectionResult.SUCCESS;
|
||||
|
||||
@PublicApi
|
||||
public class GoogleApiAvailability {
|
||||
private static final String TAG = "GmsApiAvailability";
|
||||
|
||||
/**
|
||||
* Package name for Google Play services.
|
||||
*/
|
||||
public static final String GOOGLE_PLAY_SERVICES_PACKAGE = Constants.GMS_PACKAGE_NAME;
|
||||
|
||||
/**
|
||||
* Google Play services client library version (declared in library's AndroidManifest.xml android:versionCode).
|
||||
*/
|
||||
public static final int GOOGLE_PLAY_SERVICES_VERSION_CODE = Constants.MAX_REFERENCE_VERSION;
|
||||
|
||||
private static GoogleApiAvailability instance;
|
||||
|
||||
private GoogleApiAvailability() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the singleton instance of GoogleApiAvailability.
|
||||
*/
|
||||
public static GoogleApiAvailability getInstance() {
|
||||
if (instance == null) {
|
||||
synchronized (GoogleApiAvailability.class) {
|
||||
if (instance == null) {
|
||||
instance = new GoogleApiAvailability();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a dialog to address the provided errorCode. The returned dialog displays a localized
|
||||
* message about the error and upon user confirmation (by tapping on dialog) will direct them
|
||||
* to the Play Store if Google Play services is out of date or missing, or to system settings
|
||||
* if Google Play services is disabled on the device.
|
||||
*
|
||||
* @param activity parent activity for creating the dialog, also used for identifying language to display dialog in.
|
||||
* @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call.
|
||||
* If errorCode is {@link ConnectionResult#SUCCESS} then null is returned.
|
||||
* @param requestCode The requestCode given when calling startActivityForResult.
|
||||
*/
|
||||
public Dialog getErrorDialog(Activity activity, int errorCode, int requestCode) {
|
||||
return getErrorDialog(activity, errorCode, requestCode, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a dialog to address the provided errorCode. The returned dialog displays a localized
|
||||
* message about the error and upon user confirmation (by tapping on dialog) will direct them
|
||||
* to the Play Store if Google Play services is out of date or missing, or to system settings
|
||||
* if Google Play services is disabled on the device.
|
||||
*
|
||||
* @param activity parent activity for creating the dialog, also used for identifying language to display dialog in.
|
||||
* @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call.
|
||||
* If errorCode is {@link ConnectionResult#SUCCESS} then null is returned.
|
||||
* @param requestCode The requestCode given when calling startActivityForResult.
|
||||
* @param cancelListener The {@link DialogInterface.OnCancelListener} to invoke if the dialog is canceled.
|
||||
*/
|
||||
public Dialog getErrorDialog(Activity activity, int errorCode, int requestCode, DialogInterface.OnCancelListener cancelListener) {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a PendingIntent to address the provided connection failure.
|
||||
* <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
|
||||
}
|
||||
}
|
||||
}
|
@ -23,6 +23,7 @@ import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.util.Log;
|
||||
|
||||
import org.microg.gms.common.Constants;
|
||||
@ -35,69 +36,199 @@ import org.microg.gms.common.PublicApi;
|
||||
* <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 null; // TODO
|
||||
return getErrorDialog(errorCode, activity, requestCode, null);
|
||||
}
|
||||
|
||||
public static Dialog getErrorDialog(int errorCode, Activity activity, int requestCode,
|
||||
DialogInterface.OnCancelListener cancelListener) {
|
||||
return null; // TODO
|
||||
/**
|
||||
* Returns a dialog to address the provided errorCode. The returned dialog displays a localized
|
||||
* message about the error and upon user confirmation (by tapping on dialog) will direct them
|
||||
* to the Play Store if Google Play services is out of date or missing, or to system settings
|
||||
* if Google Play services is disabled on the device.
|
||||
*
|
||||
* @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call.
|
||||
* If errorCode is {@link ConnectionResult#SUCCESS} then null is returned.
|
||||
* @param activity parent activity for creating the dialog, also used for identifying
|
||||
* language to display dialog in.
|
||||
* @param requestCode The requestCode given when calling startActivityForResult.
|
||||
* @param cancelListener The {@link DialogInterface.OnCancelListener} to invoke if the dialog
|
||||
* is canceled.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Dialog getErrorDialog(int errorCode, Activity activity, int requestCode, DialogInterface.OnCancelListener cancelListener) {
|
||||
return GoogleApiAvailability.getInstance().getErrorDialog(activity, errorCode, requestCode, cancelListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a PendingIntent to address the provided errorCode. It will direct them to one of the
|
||||
* following places to either the Play Store if Google Play services is out of date or missing,
|
||||
* or system settings if Google Play services is disabled on the device.
|
||||
*
|
||||
* @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call.
|
||||
* If errorCode is {@link ConnectionResult#SUCCESS} then null is returned.
|
||||
* @param activity parent context for creating the PendingIntent.
|
||||
* @param requestCode The requestCode given when calling startActivityForResult.
|
||||
*/
|
||||
@Deprecated
|
||||
public static PendingIntent getErrorPendingIntent(int errorCode, Activity activity,
|
||||
int requestCode) {
|
||||
int requestCode) {
|
||||
return null; // TODO
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a human-readable string of the error code returned from {@link #isGooglePlayServicesAvailable(Context)}.
|
||||
*/
|
||||
@Deprecated
|
||||
public static String getErrorString(int errorCode) {
|
||||
return null; // TODO
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the open source software license information for the Google Play services
|
||||
* application, or null if Google Play services is not available on this device.
|
||||
*/
|
||||
@Deprecated
|
||||
public static String getOpenSourceSoftwareLicenseInfo(Context context) {
|
||||
return null; // TODO
|
||||
}
|
||||
|
||||
/**
|
||||
* This gets the Context object of the Buddy APK. This loads the Buddy APK code from the Buddy
|
||||
* APK into memory. This returned context can be used to create classes and obtain resources
|
||||
* defined in the Buddy APK.
|
||||
*
|
||||
* @return The Context object of the Buddy APK or null if the Buddy APK is not installed on the device.
|
||||
*/
|
||||
public static Context getRemoteContext(Context context) {
|
||||
return null; // TODO
|
||||
}
|
||||
|
||||
/**
|
||||
* This gets the Resources object of the Buddy APK.
|
||||
*
|
||||
* @return The Resources object of the Buddy APK or null if the Buddy APK is not installed on the device.
|
||||
*/
|
||||
public static Resources getRemoteResources(Context context) {
|
||||
return null; // TODO
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that Google Play services is installed and enabled on this device, and that the
|
||||
* version installed on this device is no older than the one required by this client.
|
||||
*
|
||||
* @return status code indicating whether there was an error. Can be one of following in
|
||||
* {@link ConnectionResult}: SUCCESS, SERVICE_MISSING, SERVICE_VERSION_UPDATE_REQUIRED,
|
||||
* SERVICE_DISABLED, SERVICE_INVALID
|
||||
*/
|
||||
@Deprecated
|
||||
public static int isGooglePlayServicesAvailable(Context context) {
|
||||
Log.d(TAG, "As we can't know right now if the later desired feature is available, " +
|
||||
"we just pretend it to be.");
|
||||
return ConnectionResult.SUCCESS;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static boolean isGoogleSignedUid(PackageManager packageManager, int uid) {
|
||||
return false; // TODO
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether an error is user-recoverable. If true, proceed by calling
|
||||
* {@link #getErrorDialog(int, Activity, int)} and showing the dialog.
|
||||
*
|
||||
* @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)}, or
|
||||
* returned to your application via {@link com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener#onConnectionFailed(ConnectionResult)}
|
||||
* @return true if the error is recoverable with {@link #getErrorDialog(int, Activity, int)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean isUserRecoverableError(int errorCode) {
|
||||
return false; // TODO
|
||||
}
|
||||
|
||||
public static boolean showErrorDialogFragment(int errorCode, Activity activity,
|
||||
int requestCode) {
|
||||
/**
|
||||
* Display a DialogFragment for an error code returned by {@link #isGooglePlayServicesAvailable(Context)}.
|
||||
*
|
||||
* @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call.
|
||||
* If errorCode is {@link ConnectionResult#SUCCESS} then null is returned.
|
||||
* @param activity parent activity for creating the dialog, also used for identifying
|
||||
* language to display dialog in.
|
||||
* @param requestCode The requestCode given when calling startActivityForResult.
|
||||
* @return true if the dialog is shown, false otherwise
|
||||
* @throws RuntimeException if API level is below 11 and activity is not a {@link android.support.v4.app.FragmentActivity}.
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean showErrorDialogFragment(int errorCode, Activity activity, int requestCode) {
|
||||
return showErrorDialogFragment(errorCode, activity, requestCode, null);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static boolean showErrorDialogFragment(int errorCode, Activity activity, Fragment fragment, int requestCode, DialogInterface.OnCancelListener cancelListener) {
|
||||
return false; // TODO
|
||||
}
|
||||
|
||||
public static boolean showErrorDialogFragment(int errorCode, Activity activity,
|
||||
Fragment fragment, int requestCode, DialogInterface.OnCancelListener cancelListener) {
|
||||
return false; // TODO
|
||||
}
|
||||
|
||||
public static boolean showErrorDialogFragment(int errorCode, Activity activity, int requestCode,
|
||||
DialogInterface.OnCancelListener cancelListener) {
|
||||
return false; // TODO
|
||||
/**
|
||||
* @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call.
|
||||
* If errorCode is {@link ConnectionResult#SUCCESS} then null is returned.
|
||||
* @param activity parent activity for creating the dialog, also used for identifying
|
||||
* language to display dialog in.
|
||||
* @param requestCode The requestCode given when calling startActivityForResult.
|
||||
* @param cancelListener The {@link DialogInterface.OnCancelListener} to invoke if the dialog
|
||||
* is canceled.
|
||||
* @return true if the dialog is shown, false otherwise.
|
||||
* @throws RuntimeException if API level is below 11 and activity is not a {@link android.support.v4.app.FragmentActivity}.
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean showErrorDialogFragment(int errorCode, Activity activity, int requestCode, DialogInterface.OnCancelListener cancelListener) {
|
||||
return showErrorDialogFragment(errorCode, activity, null, requestCode, cancelListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a notification relevant to the provided error code. This method is similar to
|
||||
* {@link #getErrorDialog(int, android.app.Activity, int)}, but is provided for background
|
||||
* tasks that cannot or shouldn't display dialogs.
|
||||
*
|
||||
* @param errorCode error code returned by {@link #isGooglePlayServicesAvailable(Context)} call.
|
||||
* If errorCode is {@link ConnectionResult#SUCCESS} then null is returned.
|
||||
* @param context used for identifying language to display dialog in as well as accessing the
|
||||
* {@link android.app.NotificationManager}.
|
||||
*/
|
||||
@Deprecated
|
||||
public static void showErrorNotification(int errorCode, Context context) {
|
||||
// TODO
|
||||
}
|
||||
|
@ -1,115 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2015 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.gcm;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class GoogleCloudMessaging {
|
||||
|
||||
/**
|
||||
* The GCM {@link #register(String...)} and {@link #unregister()} methods are blocking. You
|
||||
* should not run them in the main thread or in broadcast receivers.
|
||||
*/
|
||||
public static final String ERROR_MAIN_THREAD = "MAIN_THREAD";
|
||||
|
||||
/**
|
||||
* The device can't read the response, or there was a 500/503 from the server that can be
|
||||
* retried later. The application should use exponential back off and retry.
|
||||
*/
|
||||
public static final String ERROR_SERVICE_NOT_AVAILABLE = "SERVICE_NOT_AVAILABLE";
|
||||
|
||||
/**
|
||||
* Returned by {@link #getMessageType(Intent)} to indicate that the server deleted some
|
||||
* pending messages because they were collapsible.
|
||||
*/
|
||||
public static final String MESSAGE_TYPE_DELETED = "deleted_messages";
|
||||
|
||||
/**
|
||||
* Returned by {@link #getMessageType(Intent)} to indicate a regular message.
|
||||
*/
|
||||
public static final String MESSAGE_TYPE_MESSAGE = "gcm";
|
||||
|
||||
/**
|
||||
* Returned by {@link #getMessageType(Intent)} to indicate a send error. The intent includes
|
||||
* the message ID of the message and an error code.
|
||||
*/
|
||||
public static final String MESSAGE_TYPE_SEND_ERROR = "send_error";
|
||||
|
||||
/**
|
||||
* Return the singleton instance of GCM.
|
||||
*/
|
||||
public static synchronized GoogleCloudMessaging getInstance(Context context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be called when your application is done using GCM, to release internal resources.
|
||||
*/
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the message type from an intent passed into a client app's broadcast receiver.
|
||||
* There are two general categories of messages passed from the server: regular GCM messages,
|
||||
* and special GCM status messages. The possible types are:
|
||||
* <ul>
|
||||
* <li>{@link #MESSAGE_TYPE_MESSAGE}—regular message from your server.</li>
|
||||
* <li>{@link #MESSAGE_TYPE_DELETED}—special status message indicating that some messages have been collapsed by GCM.</li>
|
||||
* <li>{@link #MESSAGE_TYPE_SEND_ERROR}—special status message indicating that there were errors sending one of the messages.</li>
|
||||
* </ul>
|
||||
* You can use this method to filter based on message type. Since it is likely that GCM will
|
||||
* be extended in the future with new message types, just ignore any message types you're not
|
||||
* interested in, or that you don't recognize.
|
||||
*
|
||||
* @param intent
|
||||
* @return
|
||||
*/
|
||||
public String getMessageType(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String register(String... senderIds) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void send(String to, String msgId, long timeToLive, Bundle data) {
|
||||
|
||||
}
|
||||
|
||||
public void send(String to, String msgId, Bundle data) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister the application. Calling unregister() stops any messages from the server.
|
||||
* This is a blocking call—you shouldn't call it from the UI thread.
|
||||
* You should rarely (if ever) need to call this method. Not only is it expensive in terms of
|
||||
* resources, but it invalidates your registration ID, which you should never change
|
||||
* unnecessarily. A better approach is to simply have your server stop sending messages.
|
||||
* Only use unregister if you want to change your sender ID.
|
||||
*
|
||||
* @throws IOException if we can't connect to server to unregister.
|
||||
*/
|
||||
public void unregister() throws IOException {
|
||||
|
||||
}
|
||||
}
|
1
play-services-cast-api
Symbolic link
1
play-services-cast-api
Symbolic link
@ -0,0 +1 @@
|
||||
extern/GmsApi/play-services-cast-api/
|
@ -19,15 +19,24 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.0.0'
|
||||
classpath 'com.android.tools.build:gradle:2.1.2'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
String getMyVersionName() {
|
||||
def stdout = new ByteArrayOutputStream()
|
||||
if (rootProject.file("gradlew").exists())
|
||||
exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout }
|
||||
else // automatic build system, don't tag dirty
|
||||
exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout }
|
||||
return stdout.toString().trim().substring(1)
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.2"
|
||||
buildToolsVersion "23.0.3"
|
||||
|
||||
defaultConfig {
|
||||
versionName getMyVersionName()
|
||||
@ -40,4 +49,5 @@ android {
|
||||
|
||||
dependencies {
|
||||
compile project(':play-services-base')
|
||||
compile project(':play-services-cast-api')
|
||||
}
|
||||
|
1
play-services-common-api
Symbolic link
1
play-services-common-api
Symbolic link
@ -0,0 +1 @@
|
||||
extern/GmsApi/play-services-common-api/
|
@ -19,15 +19,24 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.0.0'
|
||||
classpath 'com.android.tools.build:gradle:2.1.2'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
String getMyVersionName() {
|
||||
def stdout = new ByteArrayOutputStream()
|
||||
if (rootProject.file("gradlew").exists())
|
||||
exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout }
|
||||
else // automatic build system, don't tag dirty
|
||||
exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout }
|
||||
return stdout.toString().trim().substring(1)
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.2"
|
||||
buildToolsVersion "23.0.3"
|
||||
|
||||
defaultConfig {
|
||||
versionName getMyVersionName()
|
||||
@ -39,5 +48,6 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':play-services-base')
|
||||
compile project(':play-services-iid')
|
||||
// compile project(':play-services-measurement')
|
||||
}
|
@ -20,5 +20,12 @@
|
||||
|
||||
<uses-sdk android:minSdkVersion="9" />
|
||||
|
||||
<!-- 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" />
|
||||
|
||||
<application />
|
||||
</manifest>
|
||||
|
@ -90,8 +90,9 @@ public class GcmPubSub {
|
||||
if (TextUtils.isEmpty(registrationToken))
|
||||
throw new IllegalArgumentException("No registration token!");
|
||||
if (TextUtils.isEmpty(topic) || !topicPattern.matcher(topic).matches())
|
||||
throw new IllegalArgumentException("Invalid topic!");
|
||||
throw new IllegalArgumentException("Invalid topic: " + topic);
|
||||
|
||||
if (extras == null) extras = new Bundle();
|
||||
extras.putString(EXTRA_TOPIC, topic);
|
||||
instanceId.getToken(registrationToken, topic, extras);
|
||||
}
|
||||
@ -110,7 +111,7 @@ public class GcmPubSub {
|
||||
*/
|
||||
public void unsubscribe(String registrationToken, String topic) throws IOException {
|
||||
if (TextUtils.isEmpty(topic) || !topicPattern.matcher(topic).matches())
|
||||
throw new IllegalArgumentException("Invalid topic!");
|
||||
throw new IllegalArgumentException("Invalid topic: " + topic);
|
||||
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString(EXTRA_TOPIC, topic);
|
||||
|
@ -16,9 +16,25 @@
|
||||
|
||||
package com.google.android.gms.gcm;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.os.Build;
|
||||
import android.support.v4.content.WakefulBroadcastReceiver;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTRATION;
|
||||
import static org.microg.gms.gcm.GcmConstants.ACTION_INSTANCE_ID;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_FROM;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_RAWDATA;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_RAWDATA_BASE64;
|
||||
import static org.microg.gms.gcm.GcmConstants.GCMID_INSTANCE_ID;
|
||||
import static org.microg.gms.gcm.GcmConstants.GCMID_REFRESH;
|
||||
|
||||
/**
|
||||
* <code>WakefulBroadcastReceiver</code> that receives GCM messages and delivers them to an
|
||||
@ -41,9 +57,71 @@ import android.support.v4.content.WakefulBroadcastReceiver;
|
||||
* services. This prevents other apps from invoking the broadcast receiver.
|
||||
*/
|
||||
public class GcmReceiver extends WakefulBroadcastReceiver {
|
||||
private static final String TAG = "GcmReceiver";
|
||||
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
throw new UnsupportedOperationException();
|
||||
sanitizeIntent(context, intent);
|
||||
enforceIntentClassName(context, intent);
|
||||
sendIntent(context, intent);
|
||||
if (getResultCode() == 0) setResultCodeIfOrdered(-1);
|
||||
}
|
||||
|
||||
private void sanitizeIntent(Context context, Intent intent) {
|
||||
intent.setComponent(null);
|
||||
intent.setPackage(context.getPackageName());
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||
intent.removeCategory(context.getPackageName());
|
||||
}
|
||||
String from = intent.getStringExtra(EXTRA_FROM);
|
||||
if (ACTION_C2DM_REGISTRATION.equals(intent.getAction()) || GCMID_INSTANCE_ID.equals(from) || GCMID_REFRESH.equals(from)) {
|
||||
intent.setAction(ACTION_INSTANCE_ID);
|
||||
}
|
||||
String base64encoded = intent.getStringExtra(EXTRA_RAWDATA_BASE64);
|
||||
if (base64encoded != null) {
|
||||
intent.putExtra(EXTRA_RAWDATA, Base64.decode(base64encoded, Base64.DEFAULT));
|
||||
intent.removeExtra(EXTRA_RAWDATA_BASE64);
|
||||
}
|
||||
}
|
||||
|
||||
private void enforceIntentClassName(Context context, Intent intent) {
|
||||
ResolveInfo resolveInfo = context.getPackageManager().resolveService(intent, 0);
|
||||
if (resolveInfo == null || resolveInfo.serviceInfo == null) {
|
||||
Log.e(TAG, "Failed to resolve target intent service, skipping classname enforcement");
|
||||
return;
|
||||
}
|
||||
ServiceInfo serviceInfo = resolveInfo.serviceInfo;
|
||||
if (!context.getPackageName().equals(serviceInfo.packageName) || serviceInfo.name == null) {
|
||||
Log.e(TAG, "Error resolving target intent service, skipping classname enforcement. Resolved service was: " + serviceInfo.packageName + "/" + serviceInfo.name);
|
||||
return;
|
||||
}
|
||||
intent.setClassName(context, serviceInfo.name.startsWith(".") ? (context.getPackageName() + serviceInfo.name) : serviceInfo.name);
|
||||
}
|
||||
|
||||
private void sendIntent(Context context, Intent intent) {
|
||||
setResultCodeIfOrdered(500);
|
||||
try {
|
||||
ComponentName startedComponent;
|
||||
if (context.checkCallingOrSelfPermission(Manifest.permission.WAKE_LOCK) == PackageManager.PERMISSION_GRANTED) {
|
||||
startedComponent = startWakefulService(context, intent);
|
||||
} else {
|
||||
Log.d(TAG, "Missing wake lock permission, service start may be delayed");
|
||||
startedComponent = context.startService(intent);
|
||||
}
|
||||
if (startedComponent == null) {
|
||||
Log.e(TAG, "Error while delivering the message: ServiceIntent not found.");
|
||||
setResultCodeIfOrdered(404);
|
||||
} else {
|
||||
setResultCodeIfOrdered(-1);
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
Log.e(TAG, "Error while delivering the message to the serviceIntent", e);
|
||||
setResultCodeIfOrdered(401);
|
||||
}
|
||||
}
|
||||
|
||||
private void setResultCodeIfOrdered(int code) {
|
||||
if (isOrderedBroadcast()) {
|
||||
setResultCode(code);
|
||||
}
|
||||
}
|
||||
}
|
@ -16,7 +16,6 @@
|
||||
|
||||
package com.google.android.gms.gcm;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
@ -26,21 +25,23 @@ import android.text.TextUtils;
|
||||
import com.google.android.gms.iid.InstanceID;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
import org.microg.gms.gcm.CloudMessagingRpc;
|
||||
import org.microg.gms.gcm.GcmConstants;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME;
|
||||
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_RECEIVE;
|
||||
import static org.microg.gms.gcm.GcmConstants.ACTION_GCM_SEND;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_DELAY;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_ERROR;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSAGE_ID;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSAGE_TYPE;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_REGISTRATION_ID;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_SENDER;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_SENDER_LEGACY;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_SEND_FROM;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_SEND_TO;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_TTL;
|
||||
import static org.microg.gms.gcm.GcmConstants.PERMISSION_GTALK;
|
||||
|
||||
/**
|
||||
* GoogleCloudMessaging (GCM) enables apps to communicate with their app servers
|
||||
@ -123,43 +124,33 @@ public class GoogleCloudMessaging {
|
||||
@Deprecated
|
||||
public static final String MESSAGE_TYPE_SEND_EVENT = GcmConstants.MESSAGE_TYPE_SEND_EVENT;
|
||||
|
||||
private static GoogleCloudMessaging INSTANCE;
|
||||
/**
|
||||
* Due to it's nature of being a monitored reference, Intents can be used to authenticate a package source.
|
||||
*/
|
||||
private PendingIntent selfAuthIntent;
|
||||
private static GoogleCloudMessaging instance;
|
||||
|
||||
private CloudMessagingRpc rpc;
|
||||
private Context context;
|
||||
|
||||
public GoogleCloudMessaging() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be called when your application is done using GCM, to release
|
||||
* internal resources.
|
||||
*/
|
||||
public void close() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private PendingIntent getSelfAuthIntent() {
|
||||
if (selfAuthIntent == null) {
|
||||
Intent intent = new Intent();
|
||||
intent.setPackage("com.google.example.invalidpackage");
|
||||
selfAuthIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
|
||||
}
|
||||
return selfAuthIntent;
|
||||
public synchronized void close() {
|
||||
instance = null;
|
||||
rpc.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the singleton instance of GCM.
|
||||
*/
|
||||
public static GoogleCloudMessaging getInstance(Context context) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new GoogleCloudMessaging();
|
||||
INSTANCE.context = context.getApplicationContext();
|
||||
if (instance == null) {
|
||||
instance = new GoogleCloudMessaging();
|
||||
instance.context = context.getApplicationContext();
|
||||
instance.rpc = new CloudMessagingRpc(instance.context);
|
||||
}
|
||||
return INSTANCE;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -204,16 +195,23 @@ public class GoogleCloudMessaging {
|
||||
@Deprecated
|
||||
public String register(String... senderIds) throws IOException {
|
||||
if (Looper.getMainLooper() == Looper.myLooper()) throw new IOException(ERROR_MAIN_THREAD);
|
||||
|
||||
if (senderIds == null || senderIds.length == 0) throw new IllegalArgumentException("No sender ids");
|
||||
StringBuilder sb = new StringBuilder(senderIds[0]);
|
||||
for (int i = 1; i < senderIds.length; i++) {
|
||||
sb.append(',').append(senderIds[i]);
|
||||
}
|
||||
// This seems to be a legacy variant
|
||||
// TODO: Implement latest version
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString(EXTRA_SENDER_LEGACY, sb.toString());
|
||||
return InstanceID.getInstance(context).getToken(sb.toString(), INSTANCE_ID_SCOPE, extras);
|
||||
String sender = sb.toString();
|
||||
|
||||
if (isLegacyFallback()) {
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString(EXTRA_SENDER_LEGACY, sender);
|
||||
return InstanceID.getInstance(context).getToken(sb.toString(), INSTANCE_ID_SCOPE, extras);
|
||||
} else {
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString(EXTRA_SENDER, sender);
|
||||
return rpc.handleRegisterMessageResult(rpc.sendRegisterMessageBlocking(extras));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -242,16 +240,27 @@ public class GoogleCloudMessaging {
|
||||
*/
|
||||
public void send(String to, String msgId, long timeToLive, Bundle data) throws IOException {
|
||||
if (TextUtils.isEmpty(to)) throw new IllegalArgumentException("Invalid 'to'");
|
||||
Intent intent = new Intent(ACTION_GCM_SEND);
|
||||
intent.setPackage(GMS_PACKAGE_NAME);
|
||||
if (data != null) intent.putExtras(data);
|
||||
intent.putExtra(EXTRA_APP, getSelfAuthIntent());
|
||||
intent.putExtra(EXTRA_SEND_TO, to);
|
||||
intent.putExtra(EXTRA_MESSAGE_ID, msgId);
|
||||
intent.putExtra(EXTRA_TTL, timeToLive);
|
||||
intent.putExtra(EXTRA_DELAY, -1);
|
||||
//intent.putExtra(EXTRA_SEND_FROM, TODO)
|
||||
context.sendOrderedBroadcast(intent, PERMISSION_GTALK);
|
||||
|
||||
if (isLegacyFallback()) {
|
||||
Bundle extras = new Bundle();
|
||||
for (String key : data.keySet()) {
|
||||
Object o = extras.get(key);
|
||||
if (o instanceof String) {
|
||||
extras.putString("gcm." + key, (String) o);
|
||||
}
|
||||
}
|
||||
extras.putString(EXTRA_SEND_TO, to);
|
||||
extras.putString(EXTRA_MESSAGE_ID, msgId);
|
||||
InstanceID.getInstance(context).requestToken("GCM", "upstream", extras);
|
||||
} else {
|
||||
Bundle extras = data != null ? new Bundle(data) : new Bundle();
|
||||
extras.putString(EXTRA_SEND_TO, to);
|
||||
extras.putString(EXTRA_SEND_FROM, getFrom(to));
|
||||
extras.putString(EXTRA_MESSAGE_ID, msgId);
|
||||
extras.putLong(EXTRA_TTL, timeToLive);
|
||||
extras.putInt(EXTRA_DELAY, -1);
|
||||
rpc.sendGcmMessage(extras);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -298,4 +307,16 @@ public class GoogleCloudMessaging {
|
||||
InstanceID.getInstance(context).deleteInstanceID();
|
||||
}
|
||||
|
||||
private boolean isLegacyFallback() {
|
||||
String gcmPackageName = CloudMessagingRpc.getGcmPackageName(context);
|
||||
return gcmPackageName != null && gcmPackageName.endsWith(".gsf");
|
||||
}
|
||||
|
||||
private String getFrom(String to) {
|
||||
int i = to.indexOf('@');
|
||||
if (i > 0) {
|
||||
to = to.substring(0, i);
|
||||
}
|
||||
return InstanceID.getInstance(context).getStore().get("", to, INSTANCE_ID_SCOPE);
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2013-2016 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.gcm;
|
||||
|
||||
import android.os.IBinder;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
public class PendingCallback implements Parcelable {
|
||||
private final IBinder binder;
|
||||
|
||||
public PendingCallback(IBinder binder) {
|
||||
this.binder = binder;
|
||||
}
|
||||
|
||||
private PendingCallback(Parcel in) {
|
||||
this.binder = in.readStrongBinder();
|
||||
}
|
||||
|
||||
public IBinder getBinder() {
|
||||
return binder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeStrongBinder(binder);
|
||||
}
|
||||
|
||||
public static final Creator<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,165 @@
|
||||
/*
|
||||
* Copyright 2013-2016 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.gcm;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.iid.InstanceID;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
import static com.google.android.gms.gcm.GoogleCloudMessaging.ERROR_SERVICE_NOT_AVAILABLE;
|
||||
import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME;
|
||||
import static org.microg.gms.common.Constants.GSF_PACKAGE_NAME;
|
||||
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTER;
|
||||
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTRATION;
|
||||
import static org.microg.gms.gcm.GcmConstants.ACTION_GCM_SEND;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_ERROR;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSAGE_ID;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSENGER;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_REGISTRATION_ID;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_UNREGISTERED;
|
||||
import static org.microg.gms.gcm.GcmConstants.PERMISSION_GTALK;
|
||||
import static org.microg.gms.gcm.GcmConstants.PERMISSION_RECEIVE;
|
||||
|
||||
public class CloudMessagingRpc {
|
||||
private static final AtomicInteger messageIdCounter = new AtomicInteger(1);
|
||||
private static String gcmPackageName;
|
||||
|
||||
private final BlockingQueue<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);
|
||||
}
|
||||
}
|
1
play-services-iid-api
Symbolic link
1
play-services-iid-api
Symbolic link
@ -0,0 +1 @@
|
||||
extern/GmsApi/play-services-iid-api
|
53
play-services-iid/build.gradle
Normal file
53
play-services-iid/build.gradle
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2013-2015 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.1.2'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
String getMyVersionName() {
|
||||
def stdout = new ByteArrayOutputStream()
|
||||
if (rootProject.file("gradlew").exists())
|
||||
exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout }
|
||||
else // automatic build system, don't tag dirty
|
||||
exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout }
|
||||
return stdout.toString().trim().substring(1)
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.3"
|
||||
|
||||
defaultConfig {
|
||||
versionName getMyVersionName()
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_6
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':play-services-base')
|
||||
compile project(':play-services-iid-api')
|
||||
}
|
28
play-services-iid/src/main/AndroidManifest.xml
Normal file
28
play-services-iid/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.microg.gms.iid">
|
||||
|
||||
<uses-sdk android:minSdkVersion="9" />
|
||||
|
||||
<!-- Permissions required for IID -->
|
||||
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application />
|
||||
</manifest>
|
@ -19,23 +19,46 @@ package com.google.android.gms.iid;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Looper;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
import org.microg.gms.gcm.GcmConstants;
|
||||
import org.microg.gms.iid.InstanceIdRpc;
|
||||
import org.microg.gms.iid.InstanceIdStore;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_DELETE;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_SCOPE;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_SENDER;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_SUBSCIPTION;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_SUBTYPE;
|
||||
|
||||
/**
|
||||
* Instance ID provides a unique identifier for each app instance and a mechanism
|
||||
* to authenticate and authorize actions (for example, sending a GCM message).
|
||||
* <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
|
||||
@ -65,13 +88,31 @@ public class InstanceID {
|
||||
*/
|
||||
public static final String ERROR_TIMEOUT = "TIMEOUT";
|
||||
|
||||
private static final int RSA_KEY_SIZE = 2048;
|
||||
private static final String TAG = "InstanceID";
|
||||
|
||||
private static InstanceIdStore storeInstance;
|
||||
private static InstanceIdRpc rpc;
|
||||
private static Map<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 {
|
||||
throw new UnsupportedOperationException();
|
||||
deleteToken("*", "*");
|
||||
creationTime = 0;
|
||||
storeInstance.delete(subtype + "|");
|
||||
keyPair = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -85,12 +126,25 @@ public class InstanceID {
|
||||
* @throws IOException if the request fails.
|
||||
*/
|
||||
public void deleteToken(String authorizedEntity, String scope) throws IOException {
|
||||
deleteToken(authorizedEntity, scope, new Bundle());
|
||||
deleteToken(authorizedEntity, scope, null);
|
||||
}
|
||||
|
||||
@PublicApi(exclude = true)
|
||||
public void deleteToken(String authorizedEntity, String scope, Bundle extras) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
if (Looper.getMainLooper() == Looper.myLooper()) throw new IOException(ERROR_MAIN_THREAD);
|
||||
|
||||
storeInstance.delete(subtype, authorizedEntity, scope);
|
||||
|
||||
if (extras == null) extras = new Bundle();
|
||||
extras.putString(EXTRA_SENDER, authorizedEntity);
|
||||
extras.putString(EXTRA_SUBSCIPTION, authorizedEntity);
|
||||
extras.putString(EXTRA_DELETE, "1");
|
||||
extras.putString("X-" + EXTRA_DELETE, "1");
|
||||
extras.putString(EXTRA_SUBTYPE, TextUtils.isEmpty(subtype) ? authorizedEntity : subtype);
|
||||
extras.putString("X-" + EXTRA_SUBTYPE, TextUtils.isEmpty(subtype) ? authorizedEntity : subtype);
|
||||
if (scope != null) extras.putString(EXTRA_SCOPE, scope);
|
||||
|
||||
rpc.handleRegisterMessageResult(rpc.sendRegisterMessageBlocking(extras, getKeyPair()));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -99,7 +153,13 @@ public class InstanceID {
|
||||
* @return Time when instance ID was created (milliseconds since Epoch).
|
||||
*/
|
||||
public long getCreationTime() {
|
||||
throw new UnsupportedOperationException();
|
||||
if (creationTime == 0) {
|
||||
String s = storeInstance.get(subtype, "cre");
|
||||
if (s != null) {
|
||||
creationTime = Long.parseLong(s);
|
||||
}
|
||||
}
|
||||
return creationTime;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -108,7 +168,7 @@ public class InstanceID {
|
||||
* @return The identifier for the application instance.
|
||||
*/
|
||||
public String getId() {
|
||||
throw new UnsupportedOperationException();
|
||||
return sha1KeyPair(getKeyPair());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -117,7 +177,17 @@ public class InstanceID {
|
||||
* @return InstanceID instance.
|
||||
*/
|
||||
public static InstanceID getInstance(Context context) {
|
||||
throw new UnsupportedOperationException();
|
||||
String subtype = "";
|
||||
if (storeInstance == null) {
|
||||
storeInstance = new InstanceIdStore(context.getApplicationContext());
|
||||
rpc = new InstanceIdRpc(context.getApplicationContext());
|
||||
}
|
||||
InstanceID instance = instances.get(subtype);
|
||||
if (instance == null) {
|
||||
instance = new InstanceID(subtype);
|
||||
instances.put(subtype, instance);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -163,4 +233,43 @@ public class InstanceID {
|
||||
return getToken(authorizedEntity, scope, null);
|
||||
}
|
||||
|
||||
@PublicApi(exclude = true)
|
||||
public InstanceIdStore getStore() {
|
||||
return storeInstance;
|
||||
}
|
||||
|
||||
@PublicApi(exclude = true)
|
||||
public String requestToken(String authorizedEntity, String scope, Bundle extras) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private synchronized KeyPair getKeyPair() {
|
||||
if (keyPair == null) {
|
||||
keyPair = storeInstance.getKeyPair(subtype);
|
||||
if (keyPair == null) {
|
||||
try {
|
||||
KeyPairGenerator rsaGenerator = KeyPairGenerator.getInstance("RSA");
|
||||
rsaGenerator.initialize(RSA_KEY_SIZE);
|
||||
keyPair = rsaGenerator.generateKeyPair();
|
||||
creationTime = System.currentTimeMillis();
|
||||
storeInstance.put(subtype, keyPair, creationTime);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
@PublicApi(exclude = true)
|
||||
public static String sha1KeyPair(KeyPair keyPair) {
|
||||
try {
|
||||
byte[] digest = MessageDigest.getInstance("SHA1").digest(keyPair.getPublic().getEncoded());
|
||||
digest[0] = (byte) (112 + (0xF & digest[0]) & 0xFF);
|
||||
return Base64.encodeToString(digest, 0, 8, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
Log.w(TAG, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -25,8 +25,7 @@ import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
|
||||
import com.google.android.gms.gcm.GcmReceiver;
|
||||
import android.support.v4.content.WakefulBroadcastReceiver;
|
||||
|
||||
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTRATION;
|
||||
import static org.microg.gms.gcm.GcmConstants.ACTION_INSTANCE_ID;
|
||||
@ -104,7 +103,8 @@ public class InstanceIDListenerService extends Service {
|
||||
|
||||
handleIntent(intent);
|
||||
|
||||
if (intent.hasExtra(EXTRA_FROM)) GcmReceiver.completeWakefulIntent(intent);
|
||||
if (intent.hasExtra(EXTRA_FROM))
|
||||
WakefulBroadcastReceiver.completeWakefulIntent(intent);
|
||||
}
|
||||
} finally {
|
||||
stop();
|
@ -0,0 +1,423 @@
|
||||
/*
|
||||
* Copyright 2013-2016 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.iid;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.ConditionVariable;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.Parcelable;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.iid.InstanceID;
|
||||
import com.google.android.gms.iid.MessengerCompat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.Signature;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
import static com.google.android.gms.iid.InstanceID.ERROR_BACKOFF;
|
||||
import static com.google.android.gms.iid.InstanceID.ERROR_MISSING_INSTANCEID_SERVICE;
|
||||
import static com.google.android.gms.iid.InstanceID.ERROR_SERVICE_NOT_AVAILABLE;
|
||||
import static com.google.android.gms.iid.InstanceID.ERROR_TIMEOUT;
|
||||
import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME;
|
||||
import static org.microg.gms.common.Constants.GSF_PACKAGE_NAME;
|
||||
import static org.microg.gms.common.Constants.MAX_REFERENCE_VERSION;
|
||||
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTER;
|
||||
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTRATION;
|
||||
import static org.microg.gms.gcm.GcmConstants.ACTION_INSTANCE_ID;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP_ID;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP_VERSION_CODE;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP_VERSION_NAME;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_CLIENT_VERSION;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_ERROR;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_GMS_VERSION;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_GSF_INTENT;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_IS_MESSENGER2;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_KID;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSENGER;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_OS_VERSION;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_PUBLIC_KEY;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_REGISTRATION_ID;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_SIGNATURE;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_UNREGISTERED;
|
||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_USE_GSF;
|
||||
import static org.microg.gms.gcm.GcmConstants.PERMISSION_RECEIVE;
|
||||
|
||||
public class InstanceIdRpc {
|
||||
private static final String TAG = "InstanceID/Rpc";
|
||||
|
||||
private static final int BLOCKING_WAIT_TIME = 30000;
|
||||
|
||||
private static String iidPackageName;
|
||||
private static int lastRequestId;
|
||||
private static int retryCount;
|
||||
private static Map<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 2013-2016 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.iid;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
|
||||
public class InstanceIdStore {
|
||||
private static final String TAG = "InstanceID/Store";
|
||||
private static final String PREF_NAME = "com.google.android.gms.appid";
|
||||
|
||||
private Context context;
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
public InstanceIdStore(Context context) {
|
||||
this.context = context;
|
||||
this.sharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
public synchronized String get(String key) {
|
||||
return sharedPreferences.getString(key, null);
|
||||
}
|
||||
|
||||
public String get(String subtype, String key) {
|
||||
return get(subtype + "|S|" + key);
|
||||
}
|
||||
|
||||
public String get(String subtype, String authorizedEntity, String scope) {
|
||||
return get(subtype + "|T|" + authorizedEntity + "|" + scope);
|
||||
}
|
||||
|
||||
public KeyPair getKeyPair(String subtype) {
|
||||
String pub = get(subtype, "|P|");
|
||||
String priv = get(subtype, "|K|");
|
||||
if (pub == null || priv == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
byte[] pubKey = Base64.decode(pub, Base64.URL_SAFE);
|
||||
byte[] privKey = Base64.decode(priv, Base64.URL_SAFE);
|
||||
KeyFactory rsaFactory = KeyFactory.getInstance("RSA");
|
||||
return new KeyPair(rsaFactory.generatePublic(new X509EncodedKeySpec(pubKey)), rsaFactory.generatePrivate(new PKCS8EncodedKeySpec(privKey)));
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Invalid key stored " + e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void put(String key, String value) {
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.putString(key, value);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public void put(String subtype, String key, String value) {
|
||||
put(subtype + "|S|" + key, value);
|
||||
}
|
||||
|
||||
public void put(String subtype, String authorizedEntity, String scope, String value) {
|
||||
put(subtype + "|T|" + authorizedEntity + "|" + scope, value);
|
||||
}
|
||||
|
||||
public synchronized void put(String subtype, KeyPair keyPair, long timestamp) {
|
||||
put(subtype, "|P|", Base64.encodeToString(keyPair.getPublic().getEncoded(), Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING));
|
||||
put(subtype, "|K|", Base64.encodeToString(keyPair.getPrivate().getEncoded(), Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING));
|
||||
put(subtype, "cre", Long.toString(timestamp));
|
||||
}
|
||||
|
||||
public synchronized void delete() {
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.clear();
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public synchronized void delete(String prefix) {
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
for (String key : sharedPreferences.getAll().keySet()) {
|
||||
if (key.startsWith(prefix)) {
|
||||
editor.remove(key);
|
||||
}
|
||||
}
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public synchronized void delete(String subtype, String authorizedEntity, String scope) {
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.remove(subtype + "|T|" + authorizedEntity + "|" + scope);
|
||||
editor.apply();
|
||||
}
|
||||
}
|
1
play-services-location-api
Symbolic link
1
play-services-location-api
Symbolic link
@ -0,0 +1 @@
|
||||
extern/GmsApi/play-services-location-api/
|
@ -19,15 +19,24 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.0.0'
|
||||
classpath 'com.android.tools.build:gradle:2.1.2'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
String getMyVersionName() {
|
||||
def stdout = new ByteArrayOutputStream()
|
||||
if (rootProject.file("gradlew").exists())
|
||||
exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout }
|
||||
else // automatic build system, don't tag dirty
|
||||
exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout }
|
||||
return stdout.toString().trim().substring(1)
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.2"
|
||||
buildToolsVersion "23.0.3"
|
||||
|
||||
defaultConfig {
|
||||
versionName getMyVersionName()
|
||||
@ -40,4 +49,5 @@ android {
|
||||
|
||||
dependencies {
|
||||
compile project(':play-services-base')
|
||||
compile project(':play-services-location-api')
|
||||
}
|
52
play-services-tasks/build.gradle
Normal file
52
play-services-tasks/build.gradle
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2013-2015 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.1.2'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
String getMyVersionName() {
|
||||
def stdout = new ByteArrayOutputStream()
|
||||
if (rootProject.file("gradlew").exists())
|
||||
exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout }
|
||||
else // automatic build system, don't tag dirty
|
||||
exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout }
|
||||
return stdout.toString().trim().substring(1)
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.3"
|
||||
|
||||
defaultConfig {
|
||||
versionName getMyVersionName()
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_6
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':play-services-common-api')
|
||||
}
|
24
play-services-tasks/src/main/AndroidManifest.xml
Normal file
24
play-services-tasks/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.microg.gms.tasks">
|
||||
|
||||
<uses-sdk android:minSdkVersion="9" />
|
||||
|
||||
<application />
|
||||
</manifest>
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2013-2016 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.tasks;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
/**
|
||||
* A function that is called to continue execution after completion of a {@link Task}.
|
||||
*
|
||||
* @see Task#continueWith(Continuation)
|
||||
* @see Task#continueWithTask(Continuation)
|
||||
*/
|
||||
@PublicApi
|
||||
public interface Continuation<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 2013-2016 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.tasks;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
/**
|
||||
* Listener called when a {@link Task} completes.
|
||||
*
|
||||
* @see Task#addOnCompleteListener(OnCompleteListener)
|
||||
*/
|
||||
@PublicApi
|
||||
public interface OnCompleteListener<TResult> {
|
||||
/**
|
||||
* Called when the Task completes.
|
||||
*
|
||||
* @param task the completed Task. Never null
|
||||
*/
|
||||
void onComplete(Task<TResult> task);
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2013-2016 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.tasks;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
/**
|
||||
* Listener called when a {@link Task} fails with an exception.
|
||||
*
|
||||
* @see Task#addOnFailureListener(OnFailureListener)
|
||||
*/
|
||||
@PublicApi
|
||||
public interface OnFailureListener {
|
||||
|
||||
/**
|
||||
* Called when the Task fails with an exception.
|
||||
*
|
||||
* @param e the exception that caused the Task to fail. Never null
|
||||
*/
|
||||
void onFailure(Exception e);
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2013-2016 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.tasks;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
/**
|
||||
* Listener called when a {@link Task} completes successfully.
|
||||
*
|
||||
* @see Task#addOnSuccessListener(OnSuccessListener)
|
||||
*/
|
||||
@PublicApi
|
||||
public interface OnSuccessListener<TResult> {
|
||||
/**
|
||||
* Called when the {@link Task} completes successfully.
|
||||
*
|
||||
* @param result the result of the Task
|
||||
*/
|
||||
void onSuccess(TResult result);
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2013-2016 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.tasks;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
/**
|
||||
* Runtime version of {@link ExecutionException}.
|
||||
*
|
||||
* @see Task#getResult(Class)
|
||||
*/
|
||||
@PublicApi
|
||||
public class RuntimeExecutionException extends RuntimeException {
|
||||
public RuntimeExecutionException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Copyright 2013-2016 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.tasks;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* Represents an asynchronous operation.
|
||||
*/
|
||||
@PublicApi
|
||||
public abstract class Task<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 2013-2016 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.tasks;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
/**
|
||||
* Provides the ability to create an incomplete {@link Task} and later complete it by either
|
||||
* calling {@link #setResult(TResult)} or {@link #setException(Exception)}.
|
||||
*/
|
||||
@PublicApi
|
||||
public class TaskCompletionSource<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 2013-2016 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.tasks;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* {@link Task} utility methods.
|
||||
*/
|
||||
@PublicApi
|
||||
public final class Tasks {
|
||||
|
||||
/**
|
||||
* Blocks until the specified Task is complete.
|
||||
*
|
||||
* @return the Task's result
|
||||
* @throws ExecutionException if the Task fails
|
||||
* @throws InterruptedException if an interrupt occurs while waiting for the Task to complete
|
||||
* @throws TimeoutException if the specified timeout is reached before the Task completes
|
||||
*/
|
||||
public static <TResult> 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));
|
||||
}
|
||||
}
|
1
play-services-wearable-api
Symbolic link
1
play-services-wearable-api
Symbolic link
@ -0,0 +1 @@
|
||||
extern/GmsApi/play-services-wearable-api/
|
@ -19,15 +19,24 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.0.0'
|
||||
classpath 'com.android.tools.build:gradle:2.1.2'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
String getMyVersionName() {
|
||||
def stdout = new ByteArrayOutputStream()
|
||||
if (rootProject.file("gradlew").exists())
|
||||
exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout }
|
||||
else // automatic build system, don't tag dirty
|
||||
exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout }
|
||||
return stdout.toString().trim().substring(1)
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.2"
|
||||
buildToolsVersion "23.0.3"
|
||||
|
||||
defaultConfig {
|
||||
versionName getMyVersionName()
|
||||
@ -40,4 +49,5 @@ android {
|
||||
|
||||
dependencies {
|
||||
compile project(':play-services-base')
|
||||
compile project(':play-services-wearable-api')
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ public class DataEventBuffer extends DataBuffer<DataEvent> implements Result {
|
||||
@PublicApi(exclude = true)
|
||||
public DataEventBuffer(DataHolder dataHolder) {
|
||||
super(dataHolder);
|
||||
status = new Status(dataHolder.statusCode);
|
||||
status = new Status(dataHolder.getStatusCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -30,7 +30,7 @@ public class DataItemBuffer extends DataBuffer<DataItem> implements Result {
|
||||
@PublicApi(exclude = true)
|
||||
public DataItemBuffer(DataHolder dataHolder) {
|
||||
super(dataHolder);
|
||||
status = new Status(dataHolder.statusCode);
|
||||
status = new Status(dataHolder.getStatusCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -19,12 +19,21 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.0.0'
|
||||
classpath 'com.android.tools.build:gradle:2.1.0'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
String getMyVersionName() {
|
||||
def stdout = new ByteArrayOutputStream()
|
||||
if (rootProject.file("gradlew").exists())
|
||||
exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout }
|
||||
else // automatic build system, don't tag dirty
|
||||
exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout }
|
||||
return stdout.toString().trim().substring(1)
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.2"
|
||||
|
@ -16,12 +16,19 @@
|
||||
|
||||
include ':safe-parcel'
|
||||
|
||||
include ':play-services-cast-api'
|
||||
include ':play-services-common-api'
|
||||
include ':play-services-iid-api'
|
||||
include ':play-services-location-api'
|
||||
include ':play-services-wearable-api'
|
||||
include ':play-services-api'
|
||||
|
||||
include ':play-services-base'
|
||||
include ':play-services-cast'
|
||||
include ':play-services-gcm'
|
||||
include ':play-services-iid'
|
||||
include ':play-services-location'
|
||||
include ':play-services-tasks'
|
||||
include ':play-services-wearable'
|
||||
|
||||
include ':play-services'
|
||||
|
Loading…
Reference in New Issue
Block a user