Repository: googlesamples/easygoogle
Branch: master
Commit: 6660e3f51b6b
Files: 48
Total size: 136.6 KB
Directory structure:
gitextract_vqm55ndh/
├── .gitignore
├── LICENSE
├── README.md
├── RELEASENOTES.md
├── app/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── pub/
│ │ └── devrel/
│ │ └── easygoogle/
│ │ └── sample/
│ │ ├── MainActivity.java
│ │ ├── MainFragmentActivity.java
│ │ └── MessagingService.java
│ └── res/
│ ├── layout/
│ │ └── activity_main.xml
│ ├── values/
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── values-w820dp/
│ └── dimens.xml
├── build.gradle
├── easygoogle/
│ ├── .gitignore
│ ├── bintray.gradle
│ ├── build.gradle
│ ├── constants.gradle
│ ├── maven.gradle
│ ├── proguard-rules.pro
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── pub/
│ │ └── devrel/
│ │ └── easygoogle/
│ │ ├── FragmentUtils.java
│ │ ├── Google.java
│ │ ├── gac/
│ │ │ ├── AppInvites.java
│ │ │ ├── AppInvitesReferralReceiver.java
│ │ │ ├── GacFragment.java
│ │ │ ├── GacModule.java
│ │ │ ├── SignIn.java
│ │ │ └── SmartLock.java
│ │ └── gcm/
│ │ ├── EasyMessageService.java
│ │ ├── GCMUtils.java
│ │ ├── IDListenerService.java
│ │ ├── IDRegisterService.java
│ │ ├── MessageListenerService.java
│ │ ├── MessageSenderService.java
│ │ ├── Messaging.java
│ │ ├── MessagingFragment.java
│ │ └── PubSubService.java
│ └── res/
│ └── values/
│ └── strings.xml
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
.idea/
*.iml
build/
/local.properties
/captures
google-services.json
================================================
FILE: LICENSE
================================================
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:
You must give any other recipients of the Work or Derivative Works a copy of
this License; and
You must cause any modified files to carry prominent notices stating that You
changed the files; and
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
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
APPENDIX: How to apply the Apache License to your work
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets "[]" replaced with your own
identifying information. (Don't include the brackets!) The text should be
enclosed in the appropriate comment syntax for the file format. We also
recommend that a file or class name and description of purpose be included on
the same "printed page" as the copyright notice for easier identification within
third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
# EasyGoogle
## Project status

This project is no longer actively maintained, and remains here as an archive of this work.
EasyGoogle was created as an experimental improvement to the developer experience for certain
Google APIs. In the time since its creation, most of these APIs have improved (and many in ways
that resemble EasyGoogle). For this reason it no longer makes sense to maintain this library.
Thank you to everyone who submitted issues or gave feedback, your thoughts influenced API
development at Google.
## Introduction
EasyGoogle is a wrapper library to simplify basic integrations with Google Play
Services. The library wraps the following APIs (for now):
* [Google Sign-In](https://developers.google.com/identity/sign-in/android/)
* [Google Cloud Messaging](https://developers.google.com/cloud-messaging/)
* [Google App Invites](https://developers.google.com/app-invites/)
* [Google SmartLock for Passwords](https://developers.google.com/identity/smartlock-passwords/android/)
## Installation
EasyGoogle is installed by adding the following dependency to your
`build.gradle` file:
dependencies {
compile 'pub.devrel:easygoogle:0.2.5+'
}
## Usage
### Enabling Services
Before you begin, visit [this page](https://developers.google.com/mobile/add)
to select Google services and add them to your Android app. Make sure to enable any services
you plan to use and follow all of the steps, including modifying your `build.gradle` files
to enable the `google-services` plugin.
Once you have a `google-services.json` file in the proper place you can proceed to use
EasyGoogle.
### Basic
EasyGoogle makes use of `Fragments` to manage the lifecycle of the
`GoogleApiClient`, so any Activity which uses EasyGoogle must extend
`FragmentActivity`.
All interaction with EasyGoogle is through the `Google` class, which is
instantiated like this:
```java
public class MainActivity extends AppCompatActivity {
private Google mGoogle;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mGoogle = new Google.Builder(this).build();
}
}
```
Of course, instantiating a `Google` object like this won't do anything at all,
you need to enable features individually.
### Sign-In
To enable Google Sign-In, call the appropriate method on `Google.Builder` and
implement the `SignIn.SignInListener` interface:
```java
public class MainActivity extends AppCompatActivity implements
SignIn.SignInListener {
private Google mGoogle;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// This is optional, pass 'null' if no ID token is required.
String serverClientId = getString(R.string.default_web_client_id);
mGoogle = new Google.Builder(this)
.enableSignIn(this, serverClientId)
.build();
}
@Override
public void onSignedIn(GoogleSignInAccount account) {
// Sign in was successful.
}
@Override
public void onSignedOut() {
// Sign out was successful.
}
@Override
public void onSignInFailed() {
// Sign in failed for some reason and should not be attempted again
// unless the user requests it.
}
}
```
Then, use the `SignIn` object from `mGoogle.getSignIn()` to access API methods
like `SignIn#getCurrentUser()`, `SignIn#signIn`, and `SignIn#signOut`.
### Cloud Messaging
To enable Cloud Messaging, you will have to implement a simple `Service` in your application.
First, pick a unique permission name and make the following string resource in your `strings.xml` file.
It is important to pick a unique name:
```xml
your.unique.gcm.permission.name.here
```
Next, add the following to your `AndroidManifest.xml` inside the `application` tag, making sure
that the value of the `android:permission` element is the same value you specified in your
`strings.xml` file above:
```xml
```
Next, add the following permission to your `AndroidManifest.xml` file before the `application` tag,
replacing `` with your Android package name:
```xml
```
Then implement a class called `MessagingService` that extends `EasyMessageService`. Below is
one example of such a class:
```java
public class MessagingService extends EasyMessageService {
@Override
public void onMessageReceived(String from, Bundle data) {
// If there is a running Activity that implements MessageListener, it should handle
// this message.
if (!forwardToListener(from, data)) {
// There is no active MessageListener to get this, I should fire a notification with
// a PendingIntent to an activity that can handle this.
PendingIntent pendingIntent = createMessageIntent(from, data, MainActivity.class);
Notification notif = new NotificationCompat.Builder(this)
.setContentTitle("Message from: " + from)
.setContentText(data.getString("message"))
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.build();
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, notif);
}
}
@Override
public void onNewToken(String token) {
// Send a registration message to the server with our new token
String senderId = getString(R.string.gcm_defaultSenderId);
sendRegistrationMessage(senderId, token);
}
}
```
Note the use of the helper methods `forwardToListener` and `createMessageIntent`, which make
it easier for you to either launch an Activity or create a Notification to handle the message.
The `forwardToListener` method checks to see if there is an Activity that implements
`Messaging.MessagingListener` in the foreground. If there is, it sends the GCM message to the
Activity to be handled. To implement `Messaging.MessagingListener`, call the appropriate
method on `Google.Builder` in your `Activity` and implement the interface:
```java
public class MainActivity extends AppCompatActivity implements
Messaging.MessagingListener {
private Google mGoogle;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mGoogle = new Google.Builder(this)
.enableMessaging(this, getString(R.string.gcm_defaultSenderId))
.build();
}
@Override
public void onMessageReceived(String from, Bundle message) {
// GCM message received.
}
}
```
Then, use the `Messaging` object from `mGoogle.getMessaging()` to access API
methodslike `Messaging#send`.
### App Invites
To enable App Invites, call the appropriate method on `Google.Builder` and
implement the `AppInvites.AppInviteListener` interface:
```java
public class MainActivity extends AppCompatActivity implements
AppInvites.AppInviteListener {
private Google mGoogle;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mGoogle = new Google.Builder(this)
.enableAppInvites(this)
.build();
}
@Override
public void onInvitationReceived(String invitationId, String deepLink) {
// Invitation recieved in the app.
}
@Override
public void onInvitationsSent(String[] ids) {
// The user selected contacts and invitations sent successfully.
}
@Override
public void onInvitationsFailed() {
// The user either canceled sending invitations or they failed to
// send due to some configuration error.
}
}
```
Then, use the `AppInvites` object from `mGoogle.getAppInvites()` to access API
methods like `AppInvites#sendInvitation`.
### SmartLock for Passwords
To enable Smart Lock for Passwords, call the appropriate method on `Google.Builder` and
implement the `SmartLock.SmartLockListener` interface:
```java
public class MainActivity extends AppCompatActivity implements
SmartLock.SmartLockListener {
private Google mGoogle;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mGoogle = new Google.Builder(this)
.enableSmartLock(this)
.build();
}
@Override
public void onCredentialRetrieved(Credential credential) {
// Successfully retrieved a Credential for the current device user.
}
@Override
public void onShouldShowCredentialPicker() {
// In order to retrieve a Credential, the app must show the picker dialog
// using the SmartLock#showCredentialPicker() method.
}
@Override
public void onCredentialRetrievalFailed() {
// The user has no stored credentials, or the retrieval operation failed or
// was canceled.
}
}
```
Then, use the `SmartLock` object from `mGoogle.getSmartLock()` to access API
methods like `SmartLock#getCredentials()` and `SmartLock#save()`.
### Advanced Usage
If you would like to perform some action using one of the enabled Google
services but it is not properly wrapped by the EasyGoogle library, just call
`Google#getGoogleApiClient()` to get access to the underlying `GoogleApiClient`
held by the `Google` object.
================================================
FILE: RELEASENOTES.md
================================================
# 0.2.1
* Added new module for SmartLock for Passwords.
# 0.2.0
* Upgraded to Google Play Services version 8.3.0.
* Upgraded gradle plugin version, target SDK version, and appcompat version.
* Updated SignIn to use new `GoogleSignIn` API and added the method `SignIn#getCurrentUser`.
* Removed dependency on `play-services-plus` and replaced all instances of `Person` with
`GoogleSignInAccount` (breaking interface change).
# 0.1.0
* Initial Release
================================================
FILE: app/.gitignore
================================================
/build
================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
applicationId "pub.devrel.easygoogle.sample"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
repositories {
mavenLocal()
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.1.1'
// compile 'pub.devrel:easygoogle:+'
compile project(':easygoogle')
}
================================================
FILE: app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /usr/local/Android/Sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: app/src/main/AndroidManifest.xml
================================================
================================================
FILE: app/src/main/java/pub/devrel/easygoogle/sample/MainActivity.java
================================================
/*
* Copyright Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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 pub.devrel.easygoogle.sample;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.TextView;
import com.google.android.gms.auth.api.credentials.Credential;
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
import pub.devrel.easygoogle.Google;
import pub.devrel.easygoogle.gac.AppInvites;
import pub.devrel.easygoogle.gac.SignIn;
import pub.devrel.easygoogle.gac.SmartLock;
import pub.devrel.easygoogle.gcm.Messaging;
/**
* Simple Activity demonstrating how to use the EasyGoogle library to quickly integrate
* Sign-In, App Invites, Google Cloud Messaging, and SmartLock for Passwords.
*/
public class MainActivity extends AppCompatActivity implements
SignIn.SignInListener,
Messaging.MessagingListener,
AppInvites.AppInviteListener,
SmartLock.SmartLockListener,
View.OnClickListener {
public static String TAG = "sample.MainActivity";
private Google mGoogle;
// SmartLock data/fields
private Credential mCredential;
private EditText mUsernameField;
private EditText mPasswordField;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initialize the Google object with access to Cloud Messaging, Sign-In, and App Invites.
// All APIs are accessed through the Google object and the result of asynchronous operations
// are returned through API-specific listener classes like {@link SignIn.SignInListener}.
mGoogle = new Google.Builder(this)
.enableMessaging(this, getString(R.string.gcm_defaultSenderId))
.enableSignIn(this, null)
.enableAppInvites(this)
.enableSmartLock(this)
.build();
// Inject sign-in button, automatically configured to initiate sign-in when clicked.
mGoogle.getSignIn().createSignInButton(this, (ViewGroup) findViewById(R.id.layout_sign_in));
// Click listeners for buttons
findViewById(R.id.send_message_button).setOnClickListener(this);
findViewById(R.id.sign_out_button).setOnClickListener(this);
findViewById(R.id.send_invites_button).setOnClickListener(this);
findViewById(R.id.button_smartlock_load).setOnClickListener(this);
findViewById(R.id.button_smartlock_save).setOnClickListener(this);
findViewById(R.id.button_smartlock_delete).setOnClickListener(this);
// Other views
mUsernameField = ((EditText) findViewById(R.id.field_smartlock_username));
mPasswordField = ((EditText) findViewById(R.id.field_smartlock_password));
}
@Override
public void onStart() {
super.onStart();
// Subscribe to the "easygoogle" topic
mGoogle.getMessaging().subscribeTo("/topics/easygoogle");
}
@Override
public void onSignedIn(GoogleSignInAccount account) {
Log.d(TAG, "onSignedIn:" + account.getEmail());
((TextView) findViewById(R.id.sign_in_status)).setText(
getString(R.string.status_signed_in_fmt, account.getDisplayName(), account.getEmail()));
}
@Override
public void onSignedOut() {
((TextView) findViewById(R.id.sign_in_status)).setText(R.string.status_signed_out);
}
@Override
public void onSignInFailed() {
((TextView) findViewById(R.id.sign_in_status)).setText(R.string.status_sign_in_failed);
}
@Override
public void onMessageReceived(String from, Bundle message) {
((TextView) findViewById(R.id.messaging_status)).setText(
getString(R.string.status_message_fmt, from));
}
@Override
public void onInvitationReceived(String invitationId, String deepLink) {
((TextView) findViewById(R.id.app_invites_status)).setText(
getString(R.string.status_invitation_fmt, invitationId, deepLink));
}
@Override
public void onInvitationsSent(String[] ids) {
((TextView) findViewById(R.id.app_invites_status)).setText(
getString(R.string.status_invitation_sent_fmt, ids.length));
}
@Override
public void onInvitationsFailed() {
((TextView) findViewById(R.id.app_invites_status)).setText(R.string.status_invitation_failed);
}
@Override
public void onCredentialRetrieved(Credential credential) {
((TextView) findViewById(R.id.smartlock_status)).setText(R.string.status_credential_retrieved);
mCredential = credential;
mUsernameField.setText(credential.getId());
mPasswordField.setText(credential.getPassword());
}
@Override
public void onShouldShowCredentialPicker() {
mGoogle.getSmartLock().showCredentialPicker();
}
@Override
public void onCredentialRetrievalFailed() {
((TextView) findViewById(R.id.smartlock_status)).setText(R.string.status_credential_failed);
mUsernameField.setText(null);
mPasswordField.setText(null);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.sign_out_button:
// Sign out with Google
mGoogle.getSignIn().signOut();
break;
case R.id.send_message_button:
// Send a GCM message
Bundle b = new Bundle();
b.putString("message", "I am a banana!");
mGoogle.getMessaging().send(b);
break;
case R.id.send_invites_button:
// Send App Invites
mGoogle.getAppInvites().sendInvitation(
"Title", "Message", Uri.parse("http://example.com/id/12345"));
break;
case R.id.button_smartlock_load:
// Begin loading Credentials
mGoogle.getSmartLock().getCredentials();
break;
case R.id.button_smartlock_save:
// Save Credential
String id = mUsernameField.getText().toString();
String password = mPasswordField.getText().toString();
mGoogle.getSmartLock().save(id, password);
((TextView) findViewById(R.id.smartlock_status)).setText(null);
mUsernameField.setText(null);
mPasswordField.setText(null);
break;
case R.id.button_smartlock_delete:
// Delete Credential and clear fields
if (mCredential != null) {
mGoogle.getSmartLock().delete(mCredential);
mCredential = null;
((TextView) findViewById(R.id.smartlock_status)).setText(null);
mUsernameField.setText(null);
mPasswordField.setText(null);
}
break;
}
}
}
================================================
FILE: app/src/main/java/pub/devrel/easygoogle/sample/MainFragmentActivity.java
================================================
package pub.devrel.easygoogle.sample;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
import pub.devrel.easygoogle.Google;
import pub.devrel.easygoogle.gac.SignIn;
import pub.devrel.easygoogle.gcm.Messaging;
/**
* Activity for demonstrating the use of EasyGoogle in Fragments.
*/
public class MainFragmentActivity extends AppCompatActivity implements
Messaging.MessagingListener {
private static final String TAG = "MainFragmentActivity";
private Google mGoogle;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
mGoogle = new Google.Builder(this)
.enableMessaging(this, getString(R.string.gcm_defaultSenderId))
.build();
getSupportFragmentManager().beginTransaction()
.add(android.R.id.content, new EasyGoogleFragment())
.commit();
}
@Override
public void onStart() {
super.onStart();
mGoogle.getMessaging().subscribeTo("/topics/easygoogle-test");
}
@Override
public void onMessageReceived(String from, Bundle message) {
Log.d(TAG, "onMessageReceived:" + from);
}
/**
* Fragment that hosts Sign In.
*/
public static class EasyGoogleFragment extends Fragment implements
SignIn.SignInListener {
private static final String TAG = "EasyGoogleFragment";
private Google mGoogle;
public EasyGoogleFragment() {}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.d(TAG, "onActivityCreated");
mGoogle = new Google.Builder(getActivity())
.enableSignIn(this, null)
.build();
Log.d(TAG, "onActivityCreated:isSignedIn:" + isSignedIn());
}
@Override
public void onStart() {
super.onStart();
Log.d(TAG, "onStart");
Log.d(TAG, "onStart:isSignedIn:" + isSignedIn());
if (!isSignedIn()) {
mGoogle.getSignIn().signIn();
}
}
private boolean isSignedIn() {
// The GoogleApiClient is only created on onActivityCreated of GacFragment, which is
// not guaranteed to happen before or after this fragment is created
if (mGoogle.getGoogleApiClient() == null) {
Log.e(TAG, "isSignedIn: mGoogle.getGoogleApiClient() == null");
return false;
}
return (mGoogle.getSignIn().isSignedIn());
}
@Override
public void onSignedIn(GoogleSignInAccount account) {
Log.d(TAG, "onSignedIn:" + account);
}
@Override
public void onSignInFailed() {
Log.d(TAG, "onSignInFailed");
}
@Override
public void onSignedOut() {
Log.d(TAG, "onSignedOut");
}
}
}
================================================
FILE: app/src/main/java/pub/devrel/easygoogle/sample/MessagingService.java
================================================
package pub.devrel.easygoogle.sample;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import pub.devrel.easygoogle.gcm.EasyMessageService;
/**
* Background service to receive GCM events on behalf of the application.
*/
public class MessagingService extends EasyMessageService {
public static final String TAG = "MessagingService";
@Override
public void onMessageReceived(String from, Bundle data) {
Log.d(TAG, "onMessageReceived:" + from + ":" + data);
// If there is a running Activity that implements MessageListener, it should handle
// this message.
if (!forwardToListener(from, data)) {
// There is no active MessageListener to get this, I should fire a notification with
// a PendingIntent to an activity that can handle this
Log.d(TAG, "onMessageReceived: no active listeners");
PendingIntent pendingIntent = createMessageIntent(from, data, MainActivity.class);
Notification notif = new NotificationCompat.Builder(this)
.setContentTitle("Message from: " + from)
.setContentText(data.getString("message"))
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.build();
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, notif);
}
}
@Override
public void onNewToken(String token) {
Log.d(TAG, "onNewToken:" + token);
// When a new token is received, send it upstream so that the server knows about this
// client and can send downstream messages properly. This should happen infrequently.
String senderId = getString(R.string.gcm_defaultSenderId);
sendRegistrationMessage(senderId, token);
}
}
================================================
FILE: app/src/main/res/layout/activity_main.xml
================================================
================================================
FILE: app/src/main/res/values/dimens.xml
================================================
16dp16dp
================================================
FILE: app/src/main/res/values/strings.xml
================================================
easygooglepub.devrel.easygoogle.sampleapp.GCMGoogle Sign-inGoogle Cloud MessagingApp InvitesSmartLock for PasswordsSigned out.Signed in as: %s (%s)Sign in failed.Message from %s.Received invitation %s:%s.Sent %d invitations.Sending invitations failed.Credential retrieved.Credential retrieval failed.Sign OutSend MessageSend InvitesLoadSaveDeleteusernamepassword
================================================
FILE: app/src/main/res/values/styles.xml
================================================
================================================
FILE: app/src/main/res/values-w820dp/dimens.xml
================================================
64dp
================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.0'
classpath 'com.google.gms:google-services:1.5.0'
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.2'
}
}
allprojects {
repositories {
jcenter()
}
}
================================================
FILE: easygoogle/.gitignore
================================================
/build
================================================
FILE: easygoogle/bintray.gradle
================================================
apply plugin: 'com.jfrog.bintray'
apply from: 'constants.gradle'
group = mavenGroup
version = mavenVersion
bintray {
user = hasProperty('BINTRAY_USER') ? getProperty('BINTRAY_USER') : System.getenv('BINTRAY_USER')
key = hasProperty('BINTRAY_KEY') ? getProperty('BINTRAY_KEY') : System.getenv('BINTRAY_KEY')
configurations = [ 'archives' ]
pkg {
repo = projectName
name = mavenArtifactId
userOrg = bintrayOrg
licenses = [ 'Apache-2.0' ]
vcsUrl = "${githubUrl}.git"
version {
name = mavenVersion
released = new Date()
}
}
}
================================================
FILE: easygoogle/build.gradle
================================================
apply plugin: 'com.android.library'
apply from: 'maven.gradle'
apply from: 'bintray.gradle'
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
minSdkVersion 15
targetSdkVersion 22
versionCode 1
versionName "$mavenVersion"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile 'com.android.support:appcompat-v7:23.1.1'
compile 'com.google.android.gms:play-services-auth:8.3.0'
compile 'com.google.android.gms:play-services-appinvite:8.3.0'
compile 'com.google.android.gms:play-services-gcm:8.3.0'
}
================================================
FILE: easygoogle/constants.gradle
================================================
ext {
projectName = 'EasyGoogle'
projectDesc = 'A wrapper library for basic functions of Google Play Services APIs'
githubUrl = 'https://github.com/googlesamples/easygoogle'
mavenGroup = 'pub.devrel'
mavenArtifactId = 'easygoogle'
mavenVersion = '0.2.5'
bintrayOrg = 'easygoogle'
}
================================================
FILE: easygoogle/maven.gradle
================================================
apply plugin: 'com.github.dcendents.android-maven'
apply from: 'constants.gradle'
install {
repositories.mavenInstaller {
pom.project {
name projectName
description projectDesc
url githubUrl
inceptionYear '2015'
packaging 'aar'
groupId mavenGroup
artifactId mavenArtifactId
version mavenVersion
licenses {
license {
name 'The Apache Software License, Version 2.0'
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
distribution 'repo'
}
}
scm {
connection "${githubUrl}.git"
url githubUrl
}
developers {
developer {
name 'Google'
}
}
}
}
}
================================================
FILE: easygoogle/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /usr/local/Android/Sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: easygoogle/src/main/AndroidManifest.xml
================================================
================================================
FILE: easygoogle/src/main/java/pub/devrel/easygoogle/FragmentUtils.java
================================================
/*
* Copyright Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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 pub.devrel.easygoogle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;
/**
* Utility class for common Fragment operations.
*/
public class FragmentUtils extends Fragment {
private static final String TAG = FragmentUtils.class.getSimpleName();
/**
* Check if an Activity already has an instance of a particular Fragment/Tag. If so, return the
* existing instance. If it does not have one, add a new instance and return it.
* @param activity the FragmentActivity to host the Fragment.
* @param tag the Fragment tag, should be a unique string for each instance.
* @param instance an instance of the desired Fragment sub-class, to add if necessary.
* @param a class that extends Fragment.
* @return an instance of T which is added to the activity.
*/
public static T getOrCreate(FragmentActivity activity, String tag, T instance) {
// TODO(samstern): I'd like to avoid having to ask for an instance but I'd also like to avoid
// having to create an instance using reflection...
T result = null;
boolean shouldAdd = false;
FragmentManager fm = activity.getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
Fragment fragment = fm.findFragmentByTag(tag);
if (fragment == null) {
shouldAdd = true;
} else {
// TODO(samstern): how to be more confident about this cast?
Log.d(TAG, "Found fragment instance: " + tag);
result = (T) fragment;
}
if (shouldAdd) {
Log.d(TAG, "Adding new Fragment: " + tag);
// Use empty instance
result = instance;
ft.add(result, tag).disallowAddToBackStack().commit();
}
return result;
}
}
================================================
FILE: easygoogle/src/main/java/pub/devrel/easygoogle/Google.java
================================================
/*
* Copyright Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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 pub.devrel.easygoogle;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import com.google.android.gms.common.api.GoogleApiClient;
import pub.devrel.easygoogle.gac.AppInvites;
import pub.devrel.easygoogle.gac.GacFragment;
import pub.devrel.easygoogle.gac.SignIn;
import pub.devrel.easygoogle.gac.SmartLock;
import pub.devrel.easygoogle.gcm.Messaging;
import pub.devrel.easygoogle.gcm.MessagingFragment;
/**
* Google is the user-facing interface for all APIs, acts as a controller for a number of
* sub-fragments that the user should not interact with directly. All user interaction should
* be driven by listeners given to the Google.Builder.
*/
public class Google {
private static final String TAG = Google.class.getSimpleName();
// Tags for fragments that this class will control
private static final String TAG_GAC_FRAGMENT = "gac_fragment";
private static final String TAG_MESSAGING_FRAGMENT = "messaging_fragment";
// Fragment that holds the GoogleApiClient
private GacFragment mGacFragment;
private MessagingFragment mMessagingFragment;
public static class Builder {
private FragmentActivity mActivity;
private SignIn.SignInListener mSignInListener;
private String mServerClientId;
private Messaging.MessagingListener mMessagingListener;
private String mSenderId;
private AppInvites.AppInviteListener mAppInviteListener;
private SmartLock.SmartLockListener mSmartLockListener;
public Builder(FragmentActivity activity){
mActivity = activity;
}
/**
* Initialize {@link SignIn}.
* @param signInListener listener for sign in events,
* @return self, for chaining.
*/
public Builder enableSignIn(SignIn.SignInListener signInListener) {
mSignInListener = signInListener;
return this;
}
/**
* Initialize {@link SignIn}.
* @param signInListener listener for sign in events.
* @param serverClientId (optional) web client ID for obtaining ID tokens.
* @return self, for chaining.
*/
public Builder enableSignIn(SignIn.SignInListener signInListener, String serverClientId) {
mServerClientId = serverClientId;
mSignInListener = signInListener;
return this;
}
/**
* Initialize {@link Messaging}.
* @param listener listener for GCM events.
* @param senderId GCM sender Id.
* @return self, for chaining.
*/
public Builder enableMessaging(Messaging.MessagingListener listener, String senderId) {
mSenderId = senderId;
mMessagingListener = listener;
return this;
}
/**
* Initialize {@link AppInvites}.
* @param listener listener for app invites events.
* @return self, for chaining.
*/
public Builder enableAppInvites(AppInvites.AppInviteListener listener) {
mAppInviteListener = listener;
return this;
}
/**
* Initialize {@link SmartLock}.
* @param listener listener for SmartLock events.
* @return self, for chaining.
*/
public Builder enableSmartLock(SmartLock.SmartLockListener listener) {
mSmartLockListener = listener;
return this;
}
/**
* Build the {@link Google} instance for use with all enabled services,
* @return a Google instance.
*/
public Google build() {
Google google = new Google(mActivity);
if (mSignInListener != null) {
if(mServerClientId != null) {
google.mGacFragment.setServerClientId(mServerClientId);
}
google.mGacFragment.enableModule(SignIn.class, mSignInListener);
}
if (mSenderId != null) {
google.mMessagingFragment.setSenderId(mSenderId);
google.mMessagingFragment.setMessagingListener(mMessagingListener);
}
if (mAppInviteListener != null) {
google.mGacFragment.enableModule(AppInvites.class, mAppInviteListener);
}
if (mSmartLockListener != null) {
google.mGacFragment.enableModule(SmartLock.class, mSmartLockListener);
}
return google;
}
}
private Google(FragmentActivity activity) {
// Create the fragments first, then the shims.
mGacFragment = FragmentUtils.getOrCreate(activity, TAG_GAC_FRAGMENT, new GacFragment());
mMessagingFragment = FragmentUtils.getOrCreate(activity, TAG_MESSAGING_FRAGMENT, MessagingFragment.newInstance());
}
/**
* Get the underlying GoogleApiClient instance to access public methods. If GoogleApiClient is not
* properly created, there will be a warning in logcat.
* @return the underlying GoogleApiClient instance.
*/
public GoogleApiClient getGoogleApiClient() {
GoogleApiClient googleApiClient = mGacFragment.getGoogleApiClient();
if (googleApiClient == null) {
Log.w(TAG, "GoogleApiClient is not created, getGoogleApiClient() returning null.");
}
return googleApiClient;
}
/**
* Get the local {@link Messaging} instance to access public methods. If Messaging is not
* properly initialized, there will be a warning in logcat.
* @return a Messaging instance.
*/
public Messaging getMessaging() {
Messaging messaging = mMessagingFragment.getMessaging();
if (messaging == null) {
Log.w(TAG, "Messaging is not enabled, getMessaging() returning null.");
}
return messaging;
}
/**
* Get the local {@link SignIn} instance to access public methods. If SignIn is not
* properly initialized, there will be a warning in logcat.
* @return a SignIn instance.
*/
public SignIn getSignIn() {
SignIn signIn = mGacFragment.getModule(SignIn.class);
if (signIn == null) {
Log.w(TAG, "SignIn is not enabled, getSignIn() returning null.");
}
return signIn;
}
/**
* Get the local {@link AppInvites} instance to access public methods. If AppInvites is not
* properly initialized, there will be a warning in logcat.
* @return an AppInvites instance.
*/
public AppInvites getAppInvites() {
AppInvites appInvites = mGacFragment.getModule(AppInvites.class);
if (appInvites == null) {
Log.w(TAG, "AppInvites is not enabled, getAppInvites() returning null.");
}
return appInvites;
}
/**
* Get the local {@link SmartLock} instance to access public methods. If SmartLock is not
* properly initialized, there will be a warning in logcat.
* @return a SmartLock instance.
*/
public SmartLock getSmartLock() {
SmartLock smartLock = mGacFragment.getModule(SmartLock.class);
if (smartLock == null) {
Log.w(TAG, "SmartLock is not enabled, getSmartLock() returning null.");
}
return smartLock;
}
}
================================================
FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gac/AppInvites.java
================================================
/*
* Copyright Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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 pub.devrel.easygoogle.gac;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import com.google.android.gms.appinvite.AppInvite;
import com.google.android.gms.appinvite.AppInviteInvitation;
import com.google.android.gms.appinvite.AppInviteReferral;
import com.google.android.gms.common.api.Api;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.Scope;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import pub.devrel.easygoogle.R;
/**
* Interface to the App Invites API, which can be used to send Email and/or SMS invitations
* to a user's contacts. For more information visit: https://developers.google.com/app-invites/
*/
public class AppInvites extends GacModule {
/**
* Listener to be notified of asynchronous App Invite events, like invitation receipt
* or sending success.
*/
public interface AppInviteListener {
/**
* Called when the application has received an App Invite, either while running or through
* a Play Store install. Before this callback is invoked, the invitation will be marked
* as completed through the App Invites API.
* @param invitationId the unique ID of the invitation.
* @param deepLink the deep link data sent with the invitation,
*/
void onInvitationReceived(String invitationId, String deepLink);
/**
* The user has successfully invited one or more contacts.
* @param ids an array of unique IDs, one for each invitation sent by the user. The same
* id will be given to the recepient upon invitation receipt.
*/
void onInvitationsSent(String[] ids);
/**
* Sending invitations failed or the user canceled the operation.
*/
void onInvitationsFailed();
}
private static final String TAG = AppInvites.class.getSimpleName();
private static final int RC_INVITE = 9003;
private BroadcastReceiver mDeepLinkReceiver;
private Intent mCachedInvitationIntent;
protected AppInvites() {
// Instantiate local BroadcastReceiver for receiving broadcasts from
// AppInvitesReferralReceiver.
mDeepLinkReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// First, check if the Intent contains an AppInvite
if (AppInviteReferral.hasReferral(intent)) {
processReferralIntent(intent);
}
}
};
}
/**
* Launch the UI where the user can choose contacts and send invitations. The UI will be
* populated with the arguments of this method, however the user can choose to change the
* final invitation message. Success or failure of this call will be reported to
* {@link pub.devrel.easygoogle.gac.AppInvites.AppInviteListener}.
* @param title the title to display at the top of the invitation window. Cannot be
* overridden by the user.
* @param message the message to suggest as the body of the invitation, this will be editable
* by the sending user.
* @param deeplink a URI containing any information the receiving party will need to make use
* of the invitation, such as a coupon code or another identifier.
*/
public void sendInvitation(String title, String message, Uri deeplink) {
Intent intent = new AppInviteInvitation.IntentBuilder(title)
.setMessage(message)
.setDeepLink(deeplink)
.build();
getFragment().startActivityForResult(intent, RC_INVITE);
}
private void processReferralIntent(Intent intent) {
// Confirm receipt of the invitation
if (getFragment().isConnected()) {
updateInvitationStatus(intent);
} else {
Log.w(TAG, "GoogleAPIClient not connected, can't update invitation.");
mCachedInvitationIntent = intent;
}
// Notify the listener of the received invitation
String invitationId = AppInviteReferral.getInvitationId(intent);
String deepLink = AppInviteReferral.getDeepLink(intent);
getListener().onInvitationReceived(invitationId, deepLink);
}
private void updateInvitationStatus(Intent intent) {
// Extract invitation Id
String invitationId = AppInviteReferral.getInvitationId(intent);
// Update invitation installation status and also convert the invitation.
GoogleApiClient gac = getFragment().getGoogleApiClient();
if (AppInviteReferral.isOpenedFromPlayStore(intent)) {
AppInvite.AppInviteApi.updateInvitationOnInstall(gac, invitationId);
}
AppInvite.AppInviteApi.convertInvitation(gac, invitationId);
}
@Override
public void onStart() {
super.onStart();
// If app is already installed app and launched with deep link that matches
// DeepLinkActivity filter, then the referral info will be in the intent.
Intent launchIntent = getFragment().getActivity().getIntent();
if (AppInviteReferral.hasReferral(launchIntent)) {
processReferralIntent(launchIntent);
}
// Register the local BroadcastReceiver
IntentFilter intentFilter = new IntentFilter(
getFragment().getString(R.string.action_deep_link));
LocalBroadcastManager.getInstance(getFragment().getActivity()).registerReceiver(
mDeepLinkReceiver, intentFilter);
}
@Override
public void onStop() {
super.onStop();
if (mDeepLinkReceiver != null) {
// Unregister the local BroadcastReceiver
LocalBroadcastManager.getInstance(getFragment().getActivity()).unregisterReceiver(
mDeepLinkReceiver);
}
}
@Override
public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == RC_INVITE) {
if (resultCode == Activity.RESULT_OK) {
String[] ids = AppInviteInvitation.getInvitationIds(resultCode, data);
getListener().onInvitationsSent(ids);
} else {
getListener().onInvitationsFailed();
}
return true;
}
return false;
}
@Override
public List getApis() {
return Arrays.asList(new Api[]{
AppInvite.API
});
}
@Override
public List getScopes() {
return new ArrayList<>();
}
@Override
public void onConnected() {
super.onConnected();
// If there is an invitation waiting to be updated, do it now
if (mCachedInvitationIntent != null) {
updateInvitationStatus(mCachedInvitationIntent);
mCachedInvitationIntent = null;
}
}
}
================================================
FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gac/AppInvitesReferralReceiver.java
================================================
/*
* Copyright Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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 pub.devrel.easygoogle.gac;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.support.v4.content.LocalBroadcastManager;
import com.google.android.gms.appinvite.AppInviteReferral;
import pub.devrel.easygoogle.R;
/**
* Standard implementation of a Broadcast Receiver that is used to detect installation from
* the Play Store. When an app is installed from the Play Store, the Play Store broadcasts the
* install information some time during the app's first run. This receiver catches this
* broadcast and re-broadcasts the same information to the running application.
*/
public class AppInvitesReferralReceiver extends BroadcastReceiver {
public AppInvitesReferralReceiver() {}
@Override
public void onReceive(Context context, Intent intent) {
// Create deep link intent with correct action and add play store referral information
Intent deepLinkIntent = AppInviteReferral.addPlayStoreReferrerToIntent(intent,
new Intent(context.getString(R.string.action_deep_link)));
// Let any listeners know about the change
LocalBroadcastManager.getInstance(context).sendBroadcast(deepLinkIntent);
}
}
================================================
FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gac/GacFragment.java
================================================
/*
* Copyright Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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 pub.devrel.easygoogle.gac;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.api.Api;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.Scope;
import java.util.HashMap;
import java.util.Map;
import pub.devrel.easygoogle.R;
/**
* Fragment to manage the lifecycle of a GoogleApiClient in a generic way so that it can be
* used with different combinations of {@link GacModule} at runtime.
*/
public class GacFragment extends Fragment implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
private static final String TAG = GacFragment.class.getSimpleName();
private static final String KEY_IS_RESOLVING = "is_resolving";
private static final String KEY_SHOULD_RESOLVE = "should_resolve";
private String mServerClientId;
private GoogleApiClient mGoogleApiClient;
private Map, GacModule> mModules = new HashMap<>();
private boolean mIsResolving = false;
private boolean mShouldResolve = false;
private int mResolutionCode;
private void buildGoogleApiClient() {
Log.d(TAG, "buildGoogleApiClient: " + mModules);
// Can't build a GoogleApiClient with no APIs
if (mModules.size() == 0) {
Log.w(TAG, "No APIs, not building GoogleApiClient.");
return;
}
GoogleApiClient.Builder builder = new GoogleApiClient.Builder(getActivity())
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this);
for (GacModule> api : mModules.values()) {
for (Api apiObj : api.getApis()) {
// Add Api with options, if possible
Api.ApiOptions.HasOptions options = api.getOptionsFor(apiObj);
if (options != null) {
builder.addApi(apiObj, options);
} else {
builder.addApi(apiObj);
}
}
for (Scope scopeObj : api.getScopes()) {
builder.addScope(scopeObj);
}
}
mGoogleApiClient = builder.build();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.d(TAG, "onActivityResult:" + requestCode + ":" + resultCode + ":" + data);
// Give each API a chance to handle it
boolean handled = false;
for (GacModule module : mModules.values()) {
if (module.handleActivityResult(requestCode, resultCode, data)) {
handled = true;
mIsResolving = false;
break;
}
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
if (savedInstanceState != null) {
mIsResolving = savedInstanceState.getBoolean(KEY_IS_RESOLVING);
mShouldResolve = savedInstanceState.getBoolean(KEY_SHOULD_RESOLVE);
}
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.d(TAG, "onActivityCreated");
buildGoogleApiClient();
}
@Override
public void onStart() {
super.onStart();
if (mGoogleApiClient != null) {
mGoogleApiClient.connect();
}
for (GacModule module : mModules.values()) {
module.onStart();
}
}
@Override
public void onStop() {
super.onStop();
if (mGoogleApiClient != null) {
mGoogleApiClient.disconnect();
}
for (GacModule module : mModules.values()) {
module.onStop();
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(KEY_IS_RESOLVING, mIsResolving);
outState.putBoolean(KEY_SHOULD_RESOLVE, mShouldResolve);
}
@Override
public void onConnected(Bundle bundle) {
Log.d(TAG, "onConnected: " + bundle);
for (GacModule module : mModules.values()) {
module.onConnected();
}
}
@Override
public void onConnectionSuspended(int i) {
Log.d(TAG, "onConnectionSuspended: " + i);
}
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.d(TAG, "onConnectionFailed: " + connectionResult);
if (connectionResult.hasResolution()) {
for (GacModule module : mModules.values()) {
module.onResolvableFailure(connectionResult);
}
}
if (!mIsResolving && mShouldResolve) {
Log.d(TAG, "onConnectionFailed: resolving with code " + mResolutionCode);
if (connectionResult.hasResolution()) {
try {
connectionResult.startResolutionForResult(getActivity(), maskRequestCode(mResolutionCode));
mIsResolving = true;
} catch (IntentSender.SendIntentException e) {
Log.e(TAG, "Could not resolve " + connectionResult, e);
mIsResolving = false;
mGoogleApiClient.connect();
}
} else {
Log.e(TAG, "Error: no resolution:" + connectionResult.getErrorCode());
showErrorDialog(connectionResult);
for (GacModule module : mModules.values()) {
module.onUnresolvableFailure();
}
}
} else {
Log.d(TAG, String.format("Not resolving (isResolving, shouldResolve) = (%b, %b)",
mIsResolving, mShouldResolve));
}
}
private void showErrorDialog(ConnectionResult connectionResult) {
int errorCode = connectionResult.getErrorCode();
if (GooglePlayServicesUtil.isUserRecoverableError(errorCode)) {
// Show the default Google Play services error dialog which may still start an intent
// on our behalf if the user can resolve the issue.
GooglePlayServicesUtil.getErrorDialog(errorCode, getActivity(), mResolutionCode,
new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
mShouldResolve = false;
}
}).show();
} else {
// No default Google Play Services error, display a message to the user.
String errorString = getString(R.string.play_services_error_fmt, errorCode);
Toast.makeText(getActivity(), errorString, Toast.LENGTH_SHORT).show();
mShouldResolve = false;
}
}
/**
* This is a nasty hack. When calling startActivityForResult from a Fragment, the requestCode
* is tagged with the Fragment index and then the Activity (when super.onActivityResult is
* called) forwards the result to the calling fragment. However this does not happen for
* IntentSender, so we need to manually mask our request codes so that they come back to
* onActivityResult in the Fragment.
*
* @param requestCode the original request code to mask.
* @return the masked request code to use instead.
*/
protected int maskRequestCode(int requestCode) {
if ((requestCode & 0xffff0000) != 0) {
throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");
}
int fragmentIndex = getActivity().getSupportFragmentManager().getFragments().indexOf(this);
int maskedCode = requestCode + ((fragmentIndex + 1) << 16);
return maskedCode;
}
/**
* Determine if the GoogleApiClient exists and is connected with access to all requested APIs
* and Scopes.
* @return true if the GoogleApiClient is non-null and connected, false otherwise.
*/
public boolean isConnected() {
return (mGoogleApiClient != null && mGoogleApiClient.isConnected());
}
/**
* Determine if the Fragment should automatically resolve GoogleApiClient connection failures
* when possible. The default behavior is not to resolve connection failures.
* @param shouldResolve true if resolutions should be attempted until connection or
* unresolvable failure, false otherwise.
*/
public void setShouldResolve(boolean shouldResolve) {
Log.d(TAG, "setShouldResolve:" + shouldResolve);
mShouldResolve = shouldResolve;
}
/**
* Set the requestCode the Fragment should use when resolving a GoogleApiClient connection
* failure. This enables the {@link GacModule} to determine which calls to handleActivityResult
* it should handle and/or ignore.
* @param resolutionCode the resolution code to use for the next resolution, must be an integer
* and in the range 0 < x < 2^16.
*/
public void setResolutionCode(int resolutionCode) {
mResolutionCode = resolutionCode;
}
public void setServerClientId(String serverClientId) {
mServerClientId = serverClientId;
}
public String getServerClientId(){
return mServerClientId;
}
public GoogleApiClient getGoogleApiClient() {
return mGoogleApiClient;
}
public T getModule(Class clazz) {
return (T) mModules.get(clazz);
}
/**
* Enable an {@link GacModule} such as {@link SignIn} or {@link SmartLock}.
* @param clazz Class object the subclass of GacModule.
* @param listener the appropriate listener for the GacModule subclass.
* @param subclass of GacModule.
* @param type parameter of 'M' class.
*/
public , L> void enableModule(Class clazz, L listener) {
try {
M module = clazz.newInstance();
module.setFragment(this);
module.setListener(listener);
mModules.put(clazz, module);
} catch (java.lang.InstantiationException e) {
Log.e(TAG, "enableModule:InstantiationException", e);
} catch (IllegalAccessException e) {
Log.e(TAG, "enableModule:IllegalAccessExeption", e);
}
// Re-build GoogleApiClient, if necessary
if (getActivity() != null) {
Log.d(TAG, "enableModule: rebuilding GoogleApiClient");
buildGoogleApiClient();
}
}
}
================================================
FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gac/GacModule.java
================================================
/*
* Copyright Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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 pub.devrel.easygoogle.gac;
import android.content.Intent;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.Api;
import com.google.android.gms.common.api.Scope;
import java.util.List;
import pub.devrel.easygoogle.Google;
/**
* Simplified version of a GoogleApiClient "API". Declares the scopes and APIs it will need,
* reacts to onConnected events. Implementers should offer static methods that accept an
* {@link Google} as a parameter to represent very common API methods.
*/
public abstract class GacModule {
private GacFragment mFragment;
private T mListener;
public GacModule() {}
protected GacFragment getFragment() {
return mFragment;
}
protected void setFragment(GacFragment fragment) {
mFragment = fragment;
}
public abstract boolean handleActivityResult(int requestCode, int resultCode, Intent data);
public abstract List getApis();
public abstract List getScopes();
public Api.ApiOptions.HasOptions getOptionsFor(Api extends Api.ApiOptions> api) {
return null;
}
public void onConnected() {}
public void onResolvableFailure(ConnectionResult connectionResult) {}
public void onUnresolvableFailure() {}
public void onStart() {}
public void onStop() {}
public T getListener() {
return mListener;
}
public void setListener(T listener) {
mListener = listener;
}
}
================================================
FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gac/SignIn.java
================================================
/*
* Copyright Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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 pub.devrel.easygoogle.gac;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import com.google.android.gms.auth.api.Auth;
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.gms.auth.api.signin.GoogleSignInResult;
import com.google.android.gms.common.SignInButton;
import com.google.android.gms.common.api.Api;
import com.google.android.gms.common.api.OptionalPendingResult;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Scope;
import com.google.android.gms.common.api.Status;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Interface to the Google Sign In API, which can be used to determine the users identity. The
* default scopes "profile" and "email" are used to return basic information about the user
* (when possible) however this does not grant any authorization to use other Google APIs on
* behalf of the user. For more information visit:
* https://developers.google.com/identity/sign-in/android/
*
* When registering OAuth clients for Google Sign-In, Web server OAuth client registration is needed
* with android by requesting an ID token in your GoogleSignInOptions, and supplying the web client
* ID for your server. For more information visit:
* https://android-developers.blogspot.com/2016/03/registering-oauth-clients-for-google.html
*/
public class SignIn extends GacModule {
/**
* Listener to be notified of asynchronous Sign In events, like sign in or sign out,
*/
public interface SignInListener {
/**
* The user has been signed successfully.
* @param account basic information about the signed-in user like name and email.
*/
void onSignedIn(GoogleSignInAccount account);
/**
* The sign in process failed, either due to user cancellation or unresolvable errors. The
* app should display a sign-in button and wait for the user to initiate action before
* attempting sign-in again.
*/
void onSignInFailed();
/**
* The user has been signed out and access has been revoked.
*/
void onSignedOut();
}
private static final String TAG = SignIn.class.getSimpleName();
public static final int RC_SIGN_IN = 9001;
public SignIn() {}
@Override
public List getApis() {
return Arrays.asList(new Api[]{Auth.GOOGLE_SIGN_IN_API});
}
@Override
public Api.ApiOptions.HasOptions getOptionsFor(Api extends Api.ApiOptions> api) {
if (Auth.GOOGLE_SIGN_IN_API.equals(api)) {
GoogleSignInOptions.Builder googleSignInOptions = new GoogleSignInOptions.Builder(
GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail();
// Check server client id for OAuth, so GoogleSignInAccount.getIdToken(); is non-null
String serverClientId = getFragment().getServerClientId();
if(serverClientId != null){
googleSignInOptions.requestIdToken(serverClientId);
}
return googleSignInOptions.build();
} else {
return super.getOptionsFor(api);
}
}
@Override
public List getScopes() {
return Collections.emptyList();
}
@Override
public void onStart() {
super.onStart();
// Kick off silent sign-in process
Auth.GoogleSignInApi.silentSignIn(getFragment().getGoogleApiClient())
.setResultCallback(new ResultCallback() {
@Override
public void onResult(GoogleSignInResult googleSignInResult) {
if (googleSignInResult.isSuccess()) {
getListener().onSignedIn(googleSignInResult.getSignInAccount());
} else {
getListener().onSignInFailed();
}
}
});
}
@Override
public void onStop() {}
@Override
public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == RC_SIGN_IN) {
if (data != null) {
GoogleSignInResult gsr = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
if (gsr != null && gsr.isSuccess()) {
getListener().onSignedIn(gsr.getSignInAccount());
} else {
getListener().onSignInFailed();
}
}
return true;
}
return false;
}
/**
* Add a {@link SignInButton} to the current Activity/Fragment. When clicked, the button
* will automatically make a call to {@link SignIn#signIn()}.
* @param context the calling context.
* @param container a ViewGroup into which the SignInButton should be placed as the first child.
* @return the instantiated SignInButton, which can be customized further.
*/
public SignInButton createSignInButton(Context context, ViewGroup container) {
// Create SignInButton and configure style
SignInButton signInButton = new SignInButton(context);
signInButton.setSize(SignInButton.SIZE_STANDARD);
signInButton.setColorScheme(SignInButton.COLOR_DARK);
// Make it start sign-in on click
signInButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
signIn();
}
});
// Add to the layout, return reference to the button for user styling
container.addView(signInButton, 0);
return signInButton;
}
/**
* Get the currently signed in user as a GoogleSignInAccount.
* @return a {@link GoogleSignInAccount} or null.
*/
public GoogleSignInAccount getCurrentUser() {
OptionalPendingResult opr = Auth.GoogleSignInApi.silentSignIn(
getFragment().getGoogleApiClient());
if (opr.isDone()) {
return opr.get().getSignInAccount();
} else {
return null;
}
}
/**
* Convenience method to determine if a user is signed in (current user is not null).
* @return true if signed in, false otherwise.
*/
public boolean isSignedIn() {
return (getCurrentUser() != null);
}
/**
* Initiate the sign in process, resolving all possible errors (showing account picker, consent
* screen, etc). This operation may result in UI being displayed, results are returned to
* the {@link pub.devrel.easygoogle.gac.SignIn.SignInListener}.
*/
public void signIn() {
Log.d(TAG, "signIn");
Intent intent = Auth.GoogleSignInApi.getSignInIntent(getFragment().getGoogleApiClient());
getFragment().startActivityForResult(intent, RC_SIGN_IN);
}
/**
* Initiate the sign out and disconnect process. Results are returned to the
* {@link pub.devrel.easygoogle.gac.SignIn.SignInListener}. If the user is not already signed
* in or the sign out operation fails, no result will be returned.
*/
public void signOut() {
Log.d(TAG, "signOut");
final GacFragment fragment = getFragment();
if (!fragment.isConnected()) {
Log.w(TAG, "Can't sign out, not signed in!");
return;
}
Auth.GoogleSignInApi.revokeAccess(fragment.getGoogleApiClient()).setResultCallback(
new ResultCallback() {
@Override
public void onResult(Status status) {
if (status.isSuccess()) {
getListener().onSignedOut();
} else {
Log.w(TAG, "Could not sign out: " + status);
}
}
});
}
}
================================================
FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gac/SmartLock.java
================================================
package pub.devrel.easygoogle.gac;
import android.app.Activity;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.util.Log;
import com.google.android.gms.auth.api.Auth;
import com.google.android.gms.auth.api.credentials.Credential;
import com.google.android.gms.auth.api.credentials.CredentialPickerConfig;
import com.google.android.gms.auth.api.credentials.CredentialRequest;
import com.google.android.gms.auth.api.credentials.CredentialRequestResult;
import com.google.android.gms.common.api.Api;
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.common.api.ResolvingResultCallbacks;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Scope;
import com.google.android.gms.common.api.Status;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Interface to the SmartLock for Passwords API which can be used to save and retrieve
* id and password combinations on behalf of the user. SmartLock allows your users to
* enter their password once in Android or Chrome and be automatically signed in across
* all of their devices. For more information, visit:
* https://developers.google.com/identity/smartlock-passwords/android
*/
public class SmartLock extends GacModule {
/**
* Listener to be notified of asynchronous SmartLock events, like loading Credentials.
*/
public interface SmartLockListener {
/**
* A {@link Credential} has been successfully retrieved and the application
* should validate the id/password and attempt to sign the user in.
* @param credential the Credential chosen by the user.
*/
void onCredentialRetrieved(Credential credential);
/**
* There are Credentials available to load, but the app must display a picker
* dialog to allow the user to choose. See {@link #showCredentialPicker()}.
*/
void onShouldShowCredentialPicker();
/**
* There are no Credentials available to load, or the load operation failed or was
* canceled by the user.
*/
void onCredentialRetrievalFailed();
}
private static final String TAG = "SmartLock";
private static final int RC_READ = 9016;
private static final int RC_SAVE = 9017;
protected SmartLock() {}
@Override
public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == RC_READ) {
if (resultCode == Activity.RESULT_OK && data != null) {
Credential credential = data.getParcelableExtra(Credential.EXTRA_KEY);
getListener().onCredentialRetrieved(credential);
} else {
getListener().onCredentialRetrievalFailed();
}
return true;
}
if (requestCode == RC_SAVE) {
if (resultCode == Activity.RESULT_OK) {
Log.d(TAG, "RC_SAVE: Ok");
} else {
Log.d(TAG, "RC_SAVE: Failure");
}
return true;
}
return false;
}
@Override
public List getApis() {
return Arrays.asList(new Api[]{Auth.CREDENTIALS_API});
}
@Override
public List getScopes() {
return Collections.emptyList();
}
/**
* Begin the process of retrieving a {@link Credential} for the device user. This can have
* a few different results:
* 1) If the user has auto sign-in enabled and exactly one previously saved credential,
* {@link SmartLockListener#onCredentialRetrieved(Credential)} will be called and
* you can sign the user in immediately.
* 2) If the user has multiple saved credentials or one saved credential and has disabled
* auto sign-in, you will get the callback {@link SmartLockListener#onShouldShowCredentialPicker()}
* at which point you can choose to show the picker dialog to continue.
* 3) If the user has no saved credentials or cancels the operation, you will receive the
* {@link SmartLockListener#onCredentialRetrievalFailed()} callback.
*/
public void getCredentials() {
CredentialRequest request = buildCredentialRequest();
Auth.CredentialsApi.request(getFragment().getGoogleApiClient(), request)
.setResultCallback(new ResultCallback() {
@Override
public void onResult(CredentialRequestResult result) {
if (result.getStatus().isSuccess()) {
// Single credential, auto sign-in
Credential credential = result.getCredential();
getListener().onCredentialRetrieved(credential);
} else if (result.getStatus().hasResolution() &&
result.getStatus().getStatusCode() != CommonStatusCodes.SIGN_IN_REQUIRED) {
// Multiple credentials or auto-sign in disabled. If the status
// code is SIGN_IN_REQUIRED then it is a hint credential, which we
// do not want at this point.
getListener().onShouldShowCredentialPicker();
} else {
// Could not retrieve credentials
getListener().onCredentialRetrievalFailed();
}
}
});
}
/**
* Show the dialog allowing the user to choose a Credential. This method shoud only be called
* after you receive the {@link SmartLockListener#onShouldShowCredentialPicker()} callback.
*/
public void showCredentialPicker() {
CredentialRequest request = buildCredentialRequest();
Activity activity = getFragment().getActivity();
int maskedCode = getFragment().maskRequestCode(RC_READ);
Auth.CredentialsApi.request(getFragment().getGoogleApiClient(), request)
.setResultCallback(new ResolvingResultCallbacks(activity, maskedCode) {
@Override
public void onSuccess(CredentialRequestResult result) {
getListener().onCredentialRetrieved(result.getCredential());
}
@Override
public void onUnresolvableFailure(Status status) {
Log.e(TAG, "showCredentialPicker:onUnresolvableFailure:" + status);
}
});
}
/**
* Call this method if the user signs out of your application to disable auto sign-in on the
* next run. This prevents users from getting stuck in a loop between sign-out and auto sign-in.
*/
public void signOut() {
Auth.CredentialsApi.disableAutoSignIn(getFragment().getGoogleApiClient())
.setResultCallback(new ResultCallback() {
@Override
public void onResult(Status status) {
Log.d(TAG, "signOut:onResult:" + status);
}
});
}
/**
* Save a new Credential to the user's SmartLock. This will be retrievable by all instances
* of your application across devices so the user can avoid typing in his/her username and
* password in the future.
* @param id the id for the Credential, usually a username or email address.
* @param password the password for the Credential.
*/
public void save(@NonNull String id, String password) {
Credential credential = new Credential.Builder(id)
.setPassword(password)
.build();
// TODO(samstern): Should I notify the calling application on the success/failure of this
// operation. If so, also need to do it in handleActivityResult.
Activity activity = getFragment().getActivity();
int maskedCode = getFragment().maskRequestCode(RC_SAVE);
Auth.CredentialsApi.save(getFragment().getGoogleApiClient(), credential)
.setResultCallback(new ResolvingResultCallbacks(activity, maskedCode) {
@Override
public void onSuccess(Status status) {
Log.d(TAG, "save:onSuccess:" + status);
}
@Override
public void onUnresolvableFailure(Status status) {
Log.d(TAG, "save:onUnresolvableFailure:" + status);
}
});
}
/**
* Delete a saved Credential object from the SmartLock store.
* @param credential the Credential to delete.
*/
public void delete(Credential credential) {
Auth.CredentialsApi.delete(getFragment().getGoogleApiClient(), credential)
.setResultCallback(new ResultCallback() {
@Override
public void onResult(Status status) {
Log.d(TAG, "delete:onResult:" + status);
}
});
}
private CredentialRequest buildCredentialRequest() {
return new CredentialRequest.Builder()
.setCredentialPickerConfig(new CredentialPickerConfig.Builder()
.setShowAddAccountButton(false)
.setShowCancelButton(true)
.setForNewAccount(false)
.build())
.setPasswordLoginSupported(true)
.build();
}
}
================================================
FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gcm/EasyMessageService.java
================================================
/*
* Copyright Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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 pub.devrel.easygoogle.gcm;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import pub.devrel.easygoogle.R;
/**
* Base class for background GCM functions in EasyGoogle. Your application should declare
* a service that extends this class and has the following declaration:
*
*
*
*/
public abstract class EasyMessageService extends IntentService {
private static final String TAG = "EasyMessageService";
public static final String EXTRA_TOKEN = "token";
public static final String EXTRA_ACTION = "action";
public static final String EXTRA_FROM = "from";
public static final String ACTION_REGISTER = "register";
public EasyMessageService() {
super(TAG);
}
/**
* Called when the applications receives a new GCM message.
* @param from the sender's ID.
* @param data arbitrary message data (determined by sender).
*/
public abstract void onMessageReceived(String from, Bundle data);
/**
* Called when the application gets a new GCM ID Token. This should be sent to your server
* (if you have one) using an upstream GCM. See {@link #sendRegistrationMessage(String, String)}.
* @param token the GCM ID Token.
*/
public abstract void onNewToken(String token);
@Override
public void onHandleIntent(Intent intent) {
String action = intent.getAction();
if (getString(R.string.action_new_token).equals(action)) {
String token = intent.getStringExtra(EXTRA_TOKEN);
onNewToken(token);
}
if (getString(R.string.action_new_message).equals(action)) {
String from = intent.getStringExtra(EXTRA_FROM);
Bundle data = intent.getExtras();
data.remove(EXTRA_FROM);
onMessageReceived(from, data);
}
}
/**
* Forward message data to any active listeners. A listener is a running FragmentActivity that
* has enabled messaging on a {@link pub.devrel.easygoogle.Google} object. If no such Activity
* is in the foreground, this is a no-op.
* @param from the message sender's ID.
* @param data arbitrary message data (determined by sender).
* @return true if there is a running listener to receive the message, false otherwise.
*/
public boolean forwardToListener(String from, Bundle data) {
Intent msg = new Intent(MessagingFragment.MESSAGE_RECEIVED);
msg.putExtra(MessagingFragment.MESSAGE_ARG, data);
msg.putExtra(MessagingFragment.MESSAGE_FROM_FIELD, from);
return LocalBroadcastManager.getInstance(this).sendBroadcast(msg);
}
/**
* Create a {@code PendingIntent} from message data that can be used to populate a
* {@code Notficiation}.
* @param from the message sender's ID.
* @param data arbitrary messafe data (determined by sender).
* @param target the class to launch when the intent fires. This should be an Activity that is
* host to a {@code Google} object. When the intent fires, the
* {@code onMessageReceived(...)} callback will be fired.
* @return a PendingIntent to use with a Notification.
*/
public PendingIntent createMessageIntent(String from, Bundle data,
Class extends Messaging.MessagingListener> target) {
Intent intent = new Intent(this, target);
intent.setAction(MessagingFragment.MESSAGE_RECEIVED);
intent.putExtra(MessagingFragment.MESSAGE_FROM_FIELD, from);
intent.putExtra(MessagingFragment.MESSAGE_ARG, data);
PendingIntent pendingIntent =
PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
return pendingIntent;
}
/**
* Send an upstream GCM message with the following structure:
* {
* token: $TOKEN
* action: "register"
* }
* @param senderId your GCM sender Id.
* @param token a GCM token retrieved from {@code onNewToken}.
*/
public void sendRegistrationMessage(String senderId, String token) {
Bundle bundle = new Bundle();
bundle.putString(EXTRA_TOKEN, token);
bundle.putString(EXTRA_ACTION, ACTION_REGISTER);
MessagingFragment.send(this, senderId, bundle);
}
}
================================================
FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gcm/GCMUtils.java
================================================
/*
* Copyright Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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 pub.devrel.easygoogle.gcm;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
/**
* Utilities used by EasyGoogle GCM classes.
*/
public class GCMUtils {
private static final String TAG = "GCMUtils";
public static final String PREF_KEY_GCM_PERMISSION = "gcm_permission";
/**
* Find all services in the AndroidManifest with a given permission. This is useful when
* you want to start a particular service but don't have enough information to build
* an explicit intent (implicit intents stopped working in Android 5.0).
* @param context calling Context.
* @param permission the permission the service should have declared in the
* {@code android:permission} field.
* @return a list of {@code ComponentName} objects that can be used to create Intents.
*/
public static List findServices(Context context, String permission) {
PackageManager packageManager = context.getPackageManager();
String packageName = context.getPackageName();
Log.d(TAG, "Checking package: " + packageName);
// Find all services in the package
PackageInfo servicesInfo;
try {
servicesInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SERVICES);
} catch (PackageManager.NameNotFoundException e) {
throw new IllegalStateException("Could not get services for package " + packageName);
}
// Get components that have the permission
ArrayList results = new ArrayList<>();
ServiceInfo[] services = servicesInfo.services;
if (services != null) {
for (ServiceInfo service : services) {
if (permission.equals(service.permission)) {
ComponentName cn = new ComponentName(packageName, service.name);
results.add(cn);
}
}
}
return results;
}
}
================================================
FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gcm/IDListenerService.java
================================================
/*
* Copyright Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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 pub.devrel.easygoogle.gcm;
import android.content.Intent;
import android.util.Log;
import com.google.android.gms.iid.InstanceIDListenerService;
/**
* Trampoline service to launch {@link IDRegisterService} when a token refresh is needed.
*/
public class IDListenerService extends InstanceIDListenerService {
private static final String TAG = "IDListenerService";
public IDListenerService() {}
/**
* Called if InstanceID token is updated. This may occur if the security of
* the previous token had been compromised. This call is initiated by the
* InstanceID provider.
*/
@Override
public void onTokenRefresh() {
Log.d(TAG, "onTokenRefresh");
// TODO(samstern): I might not even need this class at all, it does not seem to do anything
// that I can't do directly in IDRegisterService (or vice versa).
Intent intent = new Intent(this, IDRegisterService.class);
startService(intent);
}
}
================================================
FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gcm/IDRegisterService.java
================================================
/*
* Copyright Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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 pub.devrel.easygoogle.gcm;
import android.app.IntentService;
import android.content.ComponentName;
import android.content.Intent;
import android.util.Log;
import com.google.android.gms.gcm.GoogleCloudMessaging;
import com.google.android.gms.iid.InstanceID;
import java.util.List;
import pub.devrel.easygoogle.R;
public class IDRegisterService extends IntentService {
public static final String TAG = "IDRegisterService";
public IDRegisterService() {
super(TAG);
}
@Override
protected void onHandleIntent(Intent intent) {
String senderId = intent.getStringExtra(MessagingFragment.SENDER_ID_ARG);
String gcmPermissionName = intent.getStringExtra(MessagingFragment.GCM_PERMISSION_ARG);
try {
// Initially this call goes out to the network to retrieve the token, subsequent calls
// are local.
InstanceID instanceID = InstanceID.getInstance(this);
String token = instanceID.getToken(senderId, GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
Log.i(TAG, "GCM Registration Token: " + token);
// Find any services that could handle this
List services = GCMUtils.findServices(this, gcmPermissionName);
// Notify the services of a new token
for (ComponentName cn : services) {
Log.d(TAG, "Launching service: " + cn);
Intent newTokenIntent = new Intent();
newTokenIntent.setComponent(cn);
newTokenIntent.setAction(getString(R.string.action_new_token));
newTokenIntent.putExtra(EasyMessageService.EXTRA_TOKEN, token);
startService(newTokenIntent);
}
} catch (Exception e) {
// If an exception happens while fetching the new token or updating our registration data
// on a third-party server, this ensures that we'll attempt the update at a later time.
Log.e(TAG, "Failed to complete token refresh", e);
}
}
}
================================================
FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gcm/MessageListenerService.java
================================================
/*
* Copyright Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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 pub.devrel.easygoogle.gcm;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import com.google.android.gms.gcm.GcmListenerService;
import java.util.List;
import pub.devrel.easygoogle.R;
/**
* Service to listen for incoming GCM messages.
*/
public class MessageListenerService extends GcmListenerService {
private static final String TAG = "MessageListenerService";
@Override
public void onMessageReceived(String from, Bundle data) {
Log.d(TAG, "onMessageReceived:" + from + ":" + data);
String gcmPermissionName = getGcmPermissionName();
if (gcmPermissionName == null) {
Log.w(TAG, "App has never run with messaging enabled, not initialized.");
return;
}
// Notify all services with the PERMISSION_EASY_GCM permission about this message
List components = GCMUtils.findServices(this, gcmPermissionName);
for (ComponentName cn : components) {
Log.d(TAG, "Launching: " + cn.toString());
Intent newMessageIntent = new Intent();
newMessageIntent.setComponent(cn);
newMessageIntent.setAction(getString(R.string.action_new_message));
newMessageIntent.putExtra(EasyMessageService.EXTRA_FROM, from);
newMessageIntent.putExtras(data);
startService(newMessageIntent);
}
}
private String getGcmPermissionName() {
return PreferenceManager.getDefaultSharedPreferences(this)
.getString(GCMUtils.PREF_KEY_GCM_PERMISSION, null);
}
}
================================================
FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gcm/MessageSenderService.java
================================================
/*
* Copyright Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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 pub.devrel.easygoogle.gcm;
import android.app.IntentService;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import com.google.android.gms.gcm.GoogleCloudMessaging;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
/**
* An {@link IntentService} for sending GCM upstream messages.
*/
public class MessageSenderService extends IntentService {
public static final String TAG = "MessageSenderService";
private static AtomicInteger sMessageId = new AtomicInteger();
public MessageSenderService() {
super("MessageSenderService");
}
@Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
String senderEmail = getSenderEmail(intent.getStringExtra(MessagingFragment.SENDER_ID_ARG));
Bundle data = intent.getBundleExtra(MessagingFragment.MESSAGE_ARG);
String id = Integer.toString(sMessageId.incrementAndGet());
Log.d(TAG, "Sending gcm message:" + senderEmail + ":" + data + ":" + id);
try {
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
gcm.send(senderEmail, id, data);
Log.d(TAG, "Sent!");
} catch (IOException e) {
Log.e(TAG, "Failed to send GCM Message.", e);
}
}
}
public static String getSenderEmail(String senderId) {
return senderId + "@gcm.googleapis.com";
}
}
================================================
FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gcm/Messaging.java
================================================
/*
* Copyright Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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 pub.devrel.easygoogle.gcm;
import android.os.Bundle;
/**
* Interface to the Google Cloud Messaging API, which can be used to deliver push notifications
* and send messages back to a server. For more information visit:
* https://developers.google.com/cloud-messaging/
*/
public class Messaging {
/**
* Listener to be notified of asynchronous GCM events, like message receipt.
*/
public interface MessagingListener {
/**
* Called when a downstream GCM message is received.
* @param from the sender's ID.
* @param message arbitrary message data included by the sender.
*/
void onMessageReceived(String from, Bundle message);
}
private MessagingFragment mFragment;
public Messaging(MessagingFragment fragment) {
mFragment = fragment;
}
/**
* Send an upstream GCM message with some data.
* @param bundle arbitrary key-value data to include in the message.
*/
public void send(Bundle bundle) {
mFragment.send(bundle);
}
/**
* Subscribe to a GCM topic.
* @see com.google.android.gms.gcm.GcmPubSub#subscribe(String, String, Bundle)
* @param topic topic to subscribe to.
*/
public void subscribeTo(String topic) {
mFragment.subscribeTo(topic);
}
/**
* Unsubscribe from a GCM topic.
* @see com.google.android.gms.gcm.GcmPubSub#unsubscribe(String, String)
* @param topic topic to unsubscribe from.
*/
public void unsubscribeFrom(String topic) {
mFragment.unsubscribeFrom(topic);
}
// TODO(afshar): remove or use unused methods
public void setSenderId(String senderId) {
mFragment.setSenderId(senderId);
mFragment.register();
}
}
================================================
FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gcm/MessagingFragment.java
================================================
/*
* Copyright Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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 pub.devrel.easygoogle.gcm;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import pub.devrel.easygoogle.R;
/**
* Fragment to manage communication with the various GCM services and deliver messages and
* other events to the host activity automatically.
*/
public class MessagingFragment extends Fragment {
private static final String TAG = "MessagingFragment";
public static final String SENDER_ID_ARG = "SENDER_ID";
public static final String GCM_PERMISSION_ARG = "GCM_PERMISSION";
public static final String MESSAGE_RECEIVED = "MESSAGE_RECEIVED";
public static final String MESSAGE_FROM_FIELD = "MESSAGE_FROM";
public static final String MESSAGE_ARG = "MESSAGE_ARG";
public static final String TOPIC_ARG = "TOPIC_ARG";
public static final String GCM_PERMISSION_RES_NAME = "gcm_permission";
private String mSenderId;
private Messaging mMessaging;
private Messaging.MessagingListener mListener;
private BroadcastReceiver mReceiver;
public static MessagingFragment newInstance() {
return new MessagingFragment();
}
protected static void send(Context context, String senderId, Bundle data) {
Intent intent = new Intent(context, MessageSenderService.class);
intent.putExtra(SENDER_ID_ARG, senderId);
intent.putExtra(MESSAGE_ARG, data);
intent.putExtra(GCM_PERMISSION_ARG, getGcmPermissionName(context));
context.startService(intent);
}
/**
* Get R.string.gcm_permission from the calling app's resources
* @param context calling app's context.
* @return the resource value for R.string.gcm_permission, throws IllegalArgumentException
* if this does not exist.
*/
private static String getGcmPermissionName(Context context) {
int gcmPermissionResourceId = context.getResources()
.getIdentifier(GCM_PERMISSION_RES_NAME, "string", context.getPackageName());
if (gcmPermissionResourceId == 0) {
throw new IllegalArgumentException(
"Error: must define " + GCM_PERMISSION_RES_NAME + " in strings.xml");
}
String gcmPermissionName = context.getString(gcmPermissionResourceId);
return gcmPermissionName;
}
public MessagingFragment() {
super();
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Sender ID
if (getArguments() != null) {
mSenderId = getArguments().getString(SENDER_ID_ARG);
} else {
Log.w(TAG, "getArguments() returned null, not setting senderId");
}
// Messaging permission store
PreferenceManager.getDefaultSharedPreferences(getActivity()).edit()
.putString(GCMUtils.PREF_KEY_GCM_PERMISSION, getGcmPermissionName(getActivity()))
.commit();
mMessaging = new Messaging(this);
if (mListener != null) {
// TODO(afshar): how often do we want to do this?
register();
}
}
@Override
public void onResume() {
super.onResume();
Intent intent = getActivity().getIntent();
if (intent != null && MESSAGE_RECEIVED.equals(intent.getAction())) {
parseMessageIntent(intent);
}
}
@Override
public void onStart() {
super.onStart();
// Register the local broadcast receiver
registerReceiver();
}
@Override
public void onStop() {
super.onStop();
// Unregister the local broadcast receiver
LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(mReceiver);
}
private void registerReceiver() {
if (mReceiver == null) {
mReceiver = new MessageBroadcastReceiver();
}
IntentFilter filter = new IntentFilter();
filter.addAction(MESSAGE_RECEIVED);
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(mReceiver, filter);
}
public void register() {
Intent intent = new Intent(getActivity(), IDRegisterService.class);
intent.putExtra(SENDER_ID_ARG, mSenderId);
intent.putExtra(GCM_PERMISSION_ARG, getGcmPermissionName(getActivity()));
getActivity().startService(intent);
}
public void setMessagingListener(Messaging.MessagingListener messagingListener) {
mListener = messagingListener;
}
public void setSenderId(String senderId) {
mSenderId = senderId;
}
private void parseMessageIntent(Intent intent) {
Bundle data = intent.getBundleExtra(MESSAGE_ARG);
String from = intent.getStringExtra(MESSAGE_FROM_FIELD);
onMessageReceived(from, data);
}
public void send(Bundle data) {
send(getActivity(), mSenderId, data);
}
public void subscribeTo(String topic) {
Intent intent = new Intent(getActivity(), PubSubService.class);
intent.setAction(getString(R.string.action_subscribe));
intent.putExtra(SENDER_ID_ARG, mSenderId);
intent.putExtra(TOPIC_ARG, topic);
getActivity().startService(intent);
}
public void unsubscribeFrom(String topic) {
Intent intent = new Intent(getActivity(), PubSubService.class);
intent.setAction(getString(R.string.action_unsubscribe));
intent.putExtra(SENDER_ID_ARG, mSenderId);
intent.putExtra(TOPIC_ARG, topic);
getActivity().startService(intent);
}
private void onMessageReceived(String from, Bundle data) {
Log.d(TAG, "onMessageReceived:" + from + ":" + data);
mListener.onMessageReceived(from, data);
}
public Messaging getMessaging() {
return mMessaging;
}
private class MessageBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
switch(intent.getAction()) {
case MESSAGE_RECEIVED:
parseMessageIntent(intent);
break;
}
}
}
}
================================================
FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gcm/PubSubService.java
================================================
package pub.devrel.easygoogle.gcm;
import android.app.IntentService;
import android.content.Intent;
import android.util.Log;
import com.google.android.gms.gcm.GcmPubSub;
import com.google.android.gms.gcm.GoogleCloudMessaging;
import com.google.android.gms.iid.InstanceID;
import java.io.IOException;
import pub.devrel.easygoogle.R;
public class PubSubService extends IntentService {
private static final String TAG = "PubSubService";
private GcmPubSub mGcmPubSub;
public PubSubService() {
super(TAG);
}
@Override
public void onCreate() {
super.onCreate();
mGcmPubSub = GcmPubSub.getInstance(this);
}
@Override
protected void onHandleIntent(Intent intent) {
String action = intent.getAction();
if (getString(R.string.action_subscribe).equals(action)) {
String topic = intent.getStringExtra(MessagingFragment.TOPIC_ARG);
String senderId = intent.getStringExtra(MessagingFragment.SENDER_ID_ARG);
Log.d(TAG, "Subscribing to:" + topic);
try {
mGcmPubSub.subscribe(getToken(senderId), topic, null);
} catch (IOException e) {
Log.e(TAG, "Failed to subscribe to " + topic, e);
}
}
if (getString(R.string.action_unsubscribe).equals(action)) {
String topic = intent.getStringExtra(MessagingFragment.TOPIC_ARG);
String senderId = intent.getStringExtra(MessagingFragment.SENDER_ID_ARG);
Log.d(TAG, "Unsubscribing from:" + topic);
try {
mGcmPubSub.unsubscribe(getToken(senderId), topic);
} catch (IOException e) {
Log.e(TAG, "Failed to unsubscribe from " + topic, e);
}
}
}
private String getToken(String senderId) throws IOException {
InstanceID instanceID = InstanceID.getInstance(this);
return instanceID.getToken(senderId, GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
}
}
================================================
FILE: easygoogle/src/main/res/values/strings.xml
================================================
easygoogleGoogle Play Services Error: %ipub.devrel.easygoogle.DEEP_LINK_ACTIONpub.devrel.easygoogle.NEW_TOKEN_ACTIONpub.devrel.easygoogle.NEW_MESSAGE_ACTIONpub.devrel.easygoogle.SUBSCRIBE_ACTIONpub.devrel.easygoogle.UNSUBSCRIBE_ACTION
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Mon Aug 03 15:12:08 PDT 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip
================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
================================================
FILE: gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: settings.gradle
================================================
include ':app', ':easygoogle'