Full Code of googlesamples/easygoogle for AI

master 6660e3f51b6b cached
48 files
136.6 KB
31.4k tokens
189 symbols
1 requests
Download .txt
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
![status: inactive](https://img.shields.io/badge/status-inactive-red.svg)

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
<string name="gcm_permission">your.unique.gcm.permission.name.here</string>
```

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
 <!-- This allows the app to receive GCM through EasyGoogle -->
 <service
     android:name=".MessagingService"
     android:enabled="true"
     android:exported="false"
     android:permission="your.unique.gcm.permission.name.here" />
 ```

Next, add the following permission to your `AndroidManifest.xml` file before the `application` tag,
replacing `<your-package-name>` with your Android package name:

```xml
    <permission android:name="<your-package-name>.permission.C2D_MESSAGE"
                android:protectionLevel="signature" />
    <uses-permission android:name="<your-package-name>.permission.C2D_MESSAGE" />
```

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
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="pub.devrel.easygoogle.sample">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">

        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:windowSoftInputMode="stateHidden">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:name=".MainFragmentActivity" />

        <!-- Receive GCM Messages and Tokens from EasyGoogle -->
        <service
            android:name=".MessagingService"
            android:enabled="true"
            android:exported="false"
            android:permission="@string/gcm_permission" />

    </application>

</manifest>


================================================
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
================================================
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context=".MainActivity">

        <!-- GCM -->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/title_gcm"
            android:textStyle="bold" />

        <TextView
            android:id="@+id/messaging_status"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <Button
            android:id="@+id/send_message_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/send_message" />

        <!-- Sign In -->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text="@string/title_google_sign_in"
            android:textStyle="bold" />

        <TextView
            android:id="@+id/sign_in_status"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/status_signed_out" />

        <LinearLayout
            android:id="@+id/layout_sign_in"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <!-- SignInButton will be injected here -->

            <Button
                android:id="@+id/sign_out_button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/sign_out" />
        </LinearLayout>

        <!-- App Invites -->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text="@string/title_app_invites"
            android:textStyle="bold" />

        <TextView
            android:id="@+id/app_invites_status"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <Button
            android:id="@+id/send_invites_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/send_invites" />

        <!-- SmartLock -->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text="@string/title_smart_lock"
            android:textStyle="bold" />

        <TextView
            android:id="@+id/smartlock_status"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <EditText
                android:id="@+id/field_smartlock_username"
                android:layout_width="150dp"
                android:layout_height="wrap_content"
                android:hint="@string/hint_username" />

            <EditText
                android:id="@+id/field_smartlock_password"
                android:layout_width="150dp"
                android:layout_height="wrap_content"
                android:hint="@string/hint_password"
                android:inputType="textPassword" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <Button
                android:id="@+id/button_smartlock_load"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/smartlock_load" />

            <Button
                android:id="@+id/button_smartlock_save"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/smartlock_save" />

            <Button
                android:id="@+id/button_smartlock_delete"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/smartlock_delete" />

        </LinearLayout>

    </LinearLayout>

</ScrollView>

================================================
FILE: app/src/main/res/values/dimens.xml
================================================
<resources>
    <!-- Default screen margins, per the Android Design guidelines. -->
    <dimen name="activity_horizontal_margin">16dp</dimen>
    <dimen name="activity_vertical_margin">16dp</dimen>
</resources>


================================================
FILE: app/src/main/res/values/strings.xml
================================================
<resources>
    <string name="app_name">easygoogle</string>

    <!-- IMPORTANT -->
    <!-- GCM permission string, this needs to be unique for every app -->
    <string name="gcm_permission">pub.devrel.easygoogle.sampleapp.GCM</string>

    <!-- Section titles -->
    <string name="title_google_sign_in">Google Sign-in</string>
    <string name="title_gcm">Google Cloud Messaging</string>
    <string name="title_app_invites">App Invites</string>
    <string name="title_smart_lock">SmartLock for Passwords</string>

    <!-- Status messages -->
    <string name="status_signed_out">Signed out.</string>
    <string name="status_signed_in_fmt" formatted="false">Signed in as: %s (%s)</string>
    <string name="status_sign_in_failed">Sign in failed.</string>
    <string name="status_message_fmt" formatted="false">Message from %s.</string>
    <string name="status_invitation_fmt" formatted="false">Received invitation %s:%s.</string>
    <string name="status_invitation_sent_fmt" formatted="false">Sent %d invitations.</string>
    <string name="status_invitation_failed">Sending invitations failed.</string>
    <string name="status_credential_retrieved">Credential retrieved.</string>
    <string name="status_credential_failed">Credential retrieval failed.</string>

    <!-- Button and field labels -->
    <string name="sign_out">Sign Out</string>
    <string name="send_message">Send Message</string>
    <string name="send_invites">Send Invites</string>
    <string name="smartlock_load">Load</string>
    <string name="smartlock_save">Save</string>
    <string name="smartlock_delete">Delete</string>
    <string name="hint_username">username</string>
    <string name="hint_password">password</string>
</resources>


================================================
FILE: app/src/main/res/values/styles.xml
================================================
<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
    </style>

</resources>


================================================
FILE: app/src/main/res/values-w820dp/dimens.xml
================================================
<resources>
    <!-- Example customization of dimensions originally defined in res/values/dimens.xml
         (such as screen margins) for screens with more than 820dp of available width. This
         would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
    <dimen name="activity_horizontal_margin">64dp</dimen>
</resources>


================================================
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
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="pub.devrel.easygoogle">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

    <application
        android:label="@string/app_name">

        <!-- AppInvites -->
        <receiver
            android:name=".gac.AppInvitesReferralReceiver"
            android:exported="true"
            tools:ignore="ExportedReceiver">
            <intent-filter>
                <action android:name="com.android.vending.INSTALL_REFERRER" />
            </intent-filter>
        </receiver>

        <!-- GCM -->
        <receiver
            android:name="com.google.android.gms.gcm.GcmReceiver"
            android:exported="true"
            android:permission="com.google.android.c2dm.permission.SEND">
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />

                <category android:name="com.example.gcm" />
            </intent-filter>
        </receiver>

        <service
            android:name=".gcm.MessageListenerService"
            android:enabled="true"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            </intent-filter>
        </service>

        <service
            android:name=".gcm.IDListenerService"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.android.gms.iid.InstanceID" />
            </intent-filter>
        </service>

        <service
            android:name=".gcm.IDRegisterService"
            android:exported="true"
            android:permission="com.google.android.c2dm.permission.SEND" />

        <service
            android:name=".gcm.MessageSenderService"
            android:exported="false" />

        <service
            android:name=".gcm.PubSubService"
            android:exported="false" />

    </application>
</manifest>


================================================
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 <T> a class that extends Fragment.
     * @return an instance of T which is added to the activity.
     */
    public static <T extends Fragment> 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 <code>GoogleApiClient</code> 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<AppInvites.AppInviteListener> {

    /**
     * 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<Api> getApis() {
        return Arrays.asList(new Api[]{
            AppInvite.API
        });
    }

    @Override
    public List<Scope> 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 <b>during</b> 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<Class<? extends GacModule>, 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> T getModule(Class<T> 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 <M> subclass of GacModule.
     * @param <L> type parameter of 'M' class.
     */
    public <M extends GacModule<L>, L> void enableModule(Class<M> 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<T> {

    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<Api> getApis();

    public abstract List<Scope> 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<SignIn.SignInListener> {

    /**
     * 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<Api> 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<Scope> getScopes() {
        return Collections.emptyList();
    }

    @Override
    public void onStart() {
        super.onStart();

        // Kick off silent sign-in process
        Auth.GoogleSignInApi.silentSignIn(getFragment().getGoogleApiClient())
                .setResultCallback(new ResultCallback<GoogleSignInResult>() {
                    @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<GoogleSignInResult> 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<Status>() {
                    @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<SmartLock.SmartLockListener> {

    /**
     * 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<Api> getApis() {
        return Arrays.asList(new Api[]{Auth.CREDENTIALS_API});
    }

    @Override
    public List<Scope> 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<CredentialRequestResult>() {
                    @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<CredentialRequestResult>(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<Status>() {
                    @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<Status>(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<Status>() {
                    @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:
 * <code>
 *              <service
 *                  android:name=".YourServiceClass"
 *                  android:enabled="true"
 *                  android:exported="false"
 *                  android:permission="pub.devrel.easygoogle.GCM" />
 * </code>
 */
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<ComponentName> 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<ComponentName> 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<ComponentName> 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<ComponentName> 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
================================================
<resources>
    <string name="app_name">easygoogle</string>
    <string name="play_services_error_fmt">Google Play Services Error: %i</string>
    <string name="action_deep_link">pub.devrel.easygoogle.DEEP_LINK_ACTION</string>
    <string name="action_new_token">pub.devrel.easygoogle.NEW_TOKEN_ACTION</string>
    <string name="action_new_message">pub.devrel.easygoogle.NEW_MESSAGE_ACTION</string>
    <string name="action_subscribe">pub.devrel.easygoogle.SUBSCRIBE_ACTION</string>
    <string name="action_unsubscribe">pub.devrel.easygoogle.UNSUBSCRIBE_ACTION</string>
</resources>


================================================
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'
Download .txt
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
Download .txt
SYMBOL INDEX (189 symbols across 20 files)

FILE: app/src/main/java/pub/devrel/easygoogle/sample/MainActivity.java
  class MainActivity (line 40) | public class MainActivity extends AppCompatActivity implements
    method onCreate (line 56) | @Override
    method onStart (line 87) | @Override
    method onSignedIn (line 95) | @Override
    method onSignedOut (line 102) | @Override
    method onSignInFailed (line 107) | @Override
    method onMessageReceived (line 112) | @Override
    method onInvitationReceived (line 118) | @Override
    method onInvitationsSent (line 124) | @Override
    method onInvitationsFailed (line 130) | @Override
    method onCredentialRetrieved (line 135) | @Override
    method onShouldShowCredentialPicker (line 143) | @Override
    method onCredentialRetrievalFailed (line 148) | @Override
    method onClick (line 155) | @Override

FILE: app/src/main/java/pub/devrel/easygoogle/sample/MainFragmentActivity.java
  class MainFragmentActivity (line 17) | public class MainFragmentActivity extends AppCompatActivity implements
    method onCreate (line 24) | @Override
    method onStart (line 38) | @Override
    method onMessageReceived (line 44) | @Override
    class EasyGoogleFragment (line 52) | public static class EasyGoogleFragment extends Fragment implements
      method EasyGoogleFragment (line 58) | public EasyGoogleFragment() {}
      method onActivityCreated (line 60) | @Override
      method onStart (line 72) | @Override
      method isSignedIn (line 83) | private boolean isSignedIn() {
      method onSignedIn (line 94) | @Override
      method onSignInFailed (line 99) | @Override
      method onSignedOut (line 104) | @Override

FILE: app/src/main/java/pub/devrel/easygoogle/sample/MessagingService.java
  class MessagingService (line 16) | public class MessagingService extends EasyMessageService {
    method onMessageReceived (line 20) | @Override
    method onNewToken (line 46) | @Override

FILE: easygoogle/src/main/java/pub/devrel/easygoogle/FragmentUtils.java
  class FragmentUtils (line 27) | public class FragmentUtils extends Fragment {
    method getOrCreate (line 40) | public static <T extends Fragment> T getOrCreate(FragmentActivity acti...

FILE: easygoogle/src/main/java/pub/devrel/easygoogle/Google.java
  class Google (line 35) | public class Google {
    class Builder (line 47) | public static class Builder {
      method Builder (line 61) | public Builder(FragmentActivity activity){
      method enableSignIn (line 70) | public Builder enableSignIn(SignIn.SignInListener signInListener) {
      method enableSignIn (line 81) | public Builder enableSignIn(SignIn.SignInListener signInListener, St...
      method enableMessaging (line 93) | public Builder enableMessaging(Messaging.MessagingListener listener,...
      method enableAppInvites (line 104) | public Builder enableAppInvites(AppInvites.AppInviteListener listene...
      method enableSmartLock (line 114) | public Builder enableSmartLock(SmartLock.SmartLockListener listener) {
      method build (line 123) | public Google build() {
    method Google (line 148) | private Google(FragmentActivity activity) {
    method getGoogleApiClient (line 159) | public GoogleApiClient getGoogleApiClient() {
    method getMessaging (line 173) | public Messaging getMessaging() {
    method getSignIn (line 187) | public SignIn getSignIn() {
    method getAppInvites (line 201) | public AppInvites getAppInvites() {
    method getSmartLock (line 215) | public SmartLock getSmartLock() {

FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gac/AppInvites.java
  class AppInvites (line 44) | public class AppInvites extends GacModule<AppInvites.AppInviteListener> {
    type AppInviteListener (line 50) | public interface AppInviteListener {
      method onInvitationReceived (line 59) | void onInvitationReceived(String invitationId, String deepLink);
      method onInvitationsSent (line 66) | void onInvitationsSent(String[] ids);
      method onInvitationsFailed (line 71) | void onInvitationsFailed();
    method AppInvites (line 80) | protected AppInvites() {
    method sendInvitation (line 106) | public void sendInvitation(String title, String message, Uri deeplink) {
    method processReferralIntent (line 115) | private void processReferralIntent(Intent intent) {
    method updateInvitationStatus (line 131) | private void updateInvitationStatus(Intent intent) {
    method onStart (line 144) | @Override
    method onStop (line 162) | @Override
    method handleActivityResult (line 173) | @Override
    method getApis (line 188) | @Override
    method getScopes (line 195) | @Override
    method onConnected (line 200) | @Override

FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gac/AppInvitesReferralReceiver.java
  class AppInvitesReferralReceiver (line 33) | public class AppInvitesReferralReceiver extends BroadcastReceiver {
    method AppInvitesReferralReceiver (line 35) | public AppInvitesReferralReceiver() {}
    method onReceive (line 37) | @Override

FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gac/GacFragment.java
  class GacFragment (line 41) | public class GacFragment extends Fragment implements
    method buildGoogleApiClient (line 59) | private void buildGoogleApiClient() {
    method onActivityResult (line 91) | @Override
    method onCreate (line 108) | @Override
    method onActivityCreated (line 119) | @Override
    method onStart (line 127) | @Override
    method onStop (line 139) | @Override
    method onSaveInstanceState (line 151) | @Override
    method onConnected (line 159) | @Override
    method onConnectionSuspended (line 168) | @Override
    method onConnectionFailed (line 173) | @Override
    method showErrorDialog (line 208) | private void showErrorDialog(ConnectionResult connectionResult) {
    method maskRequestCode (line 239) | protected int maskRequestCode(int requestCode) {
    method isConnected (line 255) | public boolean isConnected() {
    method setShouldResolve (line 265) | public void setShouldResolve(boolean shouldResolve) {
    method setResolutionCode (line 277) | public void setResolutionCode(int resolutionCode) {
    method setServerClientId (line 281) | public void setServerClientId(String serverClientId) {
    method getServerClientId (line 285) | public String getServerClientId(){
    method getGoogleApiClient (line 289) | public GoogleApiClient getGoogleApiClient() {
    method getModule (line 293) | public <T> T getModule(Class<T> clazz) {
    method enableModule (line 304) | public <M extends GacModule<L>, L> void enableModule(Class<M> clazz, L...

FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gac/GacModule.java
  class GacModule (line 33) | public abstract class GacModule<T> {
    method GacModule (line 38) | public GacModule() {}
    method getFragment (line 40) | protected GacFragment getFragment() {
    method setFragment (line 44) | protected void setFragment(GacFragment fragment) {
    method handleActivityResult (line 48) | public abstract boolean handleActivityResult(int requestCode, int resu...
    method getApis (line 50) | public abstract List<Api> getApis();
    method getScopes (line 52) | public abstract List<Scope> getScopes();
    method getOptionsFor (line 54) | public Api.ApiOptions.HasOptions getOptionsFor(Api<? extends Api.ApiOp...
    method onConnected (line 58) | public void onConnected() {}
    method onResolvableFailure (line 60) | public void onResolvableFailure(ConnectionResult connectionResult) {}
    method onUnresolvableFailure (line 62) | public void onUnresolvableFailure() {}
    method onStart (line 64) | public void onStart() {}
    method onStop (line 66) | public void onStop() {}
    method getListener (line 68) | public T getListener() {
    method setListener (line 72) | public void setListener(T listener) {

FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gac/SignIn.java
  class SignIn (line 51) | public class SignIn extends GacModule<SignIn.SignInListener> {
    type SignInListener (line 56) | public interface SignInListener {
      method onSignedIn (line 62) | void onSignedIn(GoogleSignInAccount account);
      method onSignInFailed (line 69) | void onSignInFailed();
      method onSignedOut (line 74) | void onSignedOut();
    method SignIn (line 80) | public SignIn() {}
    method getApis (line 82) | @Override
    method getOptionsFor (line 87) | @Override
    method getScopes (line 105) | @Override
    method onStart (line 110) | @Override
    method onStop (line 128) | @Override
    method handleActivityResult (line 131) | @Override
    method createSignInButton (line 156) | public SignInButton createSignInButton(Context context, ViewGroup cont...
    method getCurrentUser (line 180) | public GoogleSignInAccount getCurrentUser() {
    method isSignedIn (line 194) | public boolean isSignedIn() {
    method signIn (line 203) | public void signIn() {
    method signOut (line 215) | public void signOut() {

FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gac/SmartLock.java
  class SmartLock (line 31) | public class SmartLock extends GacModule<SmartLock.SmartLockListener> {
    type SmartLockListener (line 36) | public interface SmartLockListener {
      method onCredentialRetrieved (line 43) | void onCredentialRetrieved(Credential credential);
      method onShouldShowCredentialPicker (line 49) | void onShouldShowCredentialPicker();
      method onCredentialRetrievalFailed (line 55) | void onCredentialRetrievalFailed();
    method SmartLock (line 63) | protected SmartLock() {}
    method handleActivityResult (line 65) | @Override
    method getApis (line 91) | @Override
    method getScopes (line 96) | @Override
    method getCredentials (line 113) | public void getCredentials() {
    method showCredentialPicker (line 142) | public void showCredentialPicker() {
    method signOut (line 165) | public void signOut() {
    method save (line 182) | public void save(@NonNull String id, String password) {
    method delete (line 209) | public void delete(Credential credential) {
    method buildCredentialRequest (line 219) | private CredentialRequest buildCredentialRequest() {

FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gcm/EasyMessageService.java
  class EasyMessageService (line 38) | public abstract class EasyMessageService extends IntentService {
    method EasyMessageService (line 48) | public EasyMessageService() {
    method onMessageReceived (line 57) | public abstract void onMessageReceived(String from, Bundle data);
    method onNewToken (line 64) | public abstract void onNewToken(String token);
    method onHandleIntent (line 66) | @Override
    method forwardToListener (line 92) | public boolean forwardToListener(String from, Bundle data) {
    method createMessageIntent (line 110) | public PendingIntent createMessageIntent(String from, Bundle data,
    method sendRegistrationMessage (line 132) | public void sendRegistrationMessage(String senderId, String token) {

FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gcm/GCMUtils.java
  class GCMUtils (line 31) | public class GCMUtils {
    method findServices (line 45) | public static List<ComponentName> findServices(Context context, String...

FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gcm/IDListenerService.java
  class IDListenerService (line 26) | public class IDListenerService extends InstanceIDListenerService {
    method IDListenerService (line 29) | public IDListenerService() {}
    method onTokenRefresh (line 36) | @Override

FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gcm/IDRegisterService.java
  class IDRegisterService (line 30) | public class IDRegisterService extends IntentService {
    method IDRegisterService (line 34) | public IDRegisterService() {
    method onHandleIntent (line 38) | @Override

FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gcm/MessageListenerService.java
  class MessageListenerService (line 33) | public class MessageListenerService extends GcmListenerService {
    method onMessageReceived (line 37) | @Override
    method getGcmPermissionName (line 62) | private String getGcmPermissionName() {

FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gcm/MessageSenderService.java
  class MessageSenderService (line 32) | public class MessageSenderService extends IntentService {
    method MessageSenderService (line 38) | public MessageSenderService() {
    method onHandleIntent (line 42) | @Override
    method getSenderEmail (line 60) | public static String getSenderEmail(String senderId) {

FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gcm/Messaging.java
  class Messaging (line 26) | public class Messaging {
    type MessagingListener (line 31) | public interface MessagingListener {
      method onMessageReceived (line 38) | void onMessageReceived(String from, Bundle message);
    method Messaging (line 43) | public Messaging(MessagingFragment fragment) {
    method send (line 51) | public void send(Bundle bundle) {
    method subscribeTo (line 60) | public void subscribeTo(String topic) {
    method unsubscribeFrom (line 69) | public void unsubscribeFrom(String topic) {
    method setSenderId (line 74) | public void setSenderId(String senderId) {

FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gcm/MessagingFragment.java
  class MessagingFragment (line 34) | public class MessagingFragment extends Fragment {
    method newInstance (line 51) | public static MessagingFragment newInstance() {
    method send (line 55) | protected static void send(Context context, String senderId, Bundle da...
    method getGcmPermissionName (line 69) | private static String getGcmPermissionName(Context context) {
    method MessagingFragment (line 81) | public MessagingFragment() {
    method onActivityCreated (line 85) | @Override
    method onResume (line 108) | @Override
    method onStart (line 118) | @Override
    method onStop (line 126) | @Override
    method registerReceiver (line 134) | private void registerReceiver() {
    method register (line 144) | public void register() {
    method setMessagingListener (line 151) | public void setMessagingListener(Messaging.MessagingListener messaging...
    method setSenderId (line 155) | public void setSenderId(String senderId) {
    method parseMessageIntent (line 159) | private void parseMessageIntent(Intent intent) {
    method send (line 165) | public void send(Bundle data) {
    method subscribeTo (line 169) | public void subscribeTo(String topic) {
    method unsubscribeFrom (line 177) | public void unsubscribeFrom(String topic) {
    method onMessageReceived (line 185) | private void onMessageReceived(String from, Bundle data) {
    method getMessaging (line 190) | public Messaging getMessaging() {
    class MessageBroadcastReceiver (line 194) | private class MessageBroadcastReceiver extends BroadcastReceiver {
      method onReceive (line 196) | @Override

FILE: easygoogle/src/main/java/pub/devrel/easygoogle/gcm/PubSubService.java
  class PubSubService (line 15) | public class PubSubService extends IntentService {
    method PubSubService (line 21) | public PubSubService() {
    method onCreate (line 25) | @Override
    method onHandleIntent (line 31) | @Override
    method getToken (line 60) | private String getToken(String senderId) throws IOException {
Condensed preview — 48 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (148K chars).
[
  {
    "path": ".gitignore",
    "chars": 150,
    "preview": ".gradle\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.DS_Store\n/build\n.idea/\n*.iml\nbuild/\n/local.properties\n/"
  },
  {
    "path": "LICENSE",
    "chars": 10273,
    "preview": "Apache License\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AN"
  },
  {
    "path": "README.md",
    "chars": 10013,
    "preview": "# EasyGoogle\n\n## Project status\n![status: inactive](https://img.shields.io/badge/status-inactive-red.svg)\n\nThis project "
  },
  {
    "path": "RELEASENOTES.md",
    "chars": 471,
    "preview": "# 0.2.1\n\n  * Added new module for SmartLock for Passwords.\n\n# 0.2.0\n\n  * Upgraded to Google Play Services version 8.3.0."
  },
  {
    "path": "app/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "app/build.gradle",
    "chars": 754,
    "preview": "apply plugin: 'com.android.application'\napply plugin: 'com.google.gms.google-services'\n\n\nandroid {\n    compileSdkVersion"
  },
  {
    "path": "app/proguard-rules.pro",
    "chars": 653,
    "preview": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /u"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "chars": 1076,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package="
  },
  {
    "path": "app/src/main/java/pub/devrel/easygoogle/sample/MainActivity.java",
    "chars": 7672,
    "preview": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n "
  },
  {
    "path": "app/src/main/java/pub/devrel/easygoogle/sample/MainFragmentActivity.java",
    "chars": 3191,
    "preview": "package pub.devrel.easygoogle.sample;\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android."
  },
  {
    "path": "app/src/main/java/pub/devrel/easygoogle/sample/MessagingService.java",
    "chars": 2163,
    "preview": "package pub.devrel.easygoogle.sample;\n\nimport android.app.Notification;\nimport android.app.NotificationManager;\nimport a"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "chars": 5032,
    "preview": "<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "chars": 211,
    "preview": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "chars": 1728,
    "preview": "<resources>\n    <string name=\"app_name\">easygoogle</string>\n\n    <!-- IMPORTANT -->\n    <!-- GCM permission string, this"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "chars": 194,
    "preview": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar"
  },
  {
    "path": "app/src/main/res/values-w820dp/dimens.xml",
    "chars": 358,
    "preview": "<resources>\n    <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n         (such as s"
  },
  {
    "path": "build.gradle",
    "chars": 503,
    "preview": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    r"
  },
  {
    "path": "easygoogle/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "easygoogle/bintray.gradle",
    "chars": 627,
    "preview": "apply plugin: 'com.jfrog.bintray'\napply from: 'constants.gradle'\n\ngroup = mavenGroup\nversion = mavenVersion\n\nbintray {\n "
  },
  {
    "path": "easygoogle/build.gradle",
    "chars": 740,
    "preview": "apply plugin: 'com.android.library'\napply from: 'maven.gradle'\napply from: 'bintray.gradle'\n\nandroid {\n    compileSdkVer"
  },
  {
    "path": "easygoogle/constants.gradle",
    "chars": 313,
    "preview": "ext {\n    projectName = 'EasyGoogle'\n    projectDesc = 'A wrapper library for basic functions of Google Play Services AP"
  },
  {
    "path": "easygoogle/maven.gradle",
    "chars": 914,
    "preview": "apply plugin: 'com.github.dcendents.android-maven'\napply from: 'constants.gradle'\n\ninstall {\n    repositories.mavenInsta"
  },
  {
    "path": "easygoogle/proguard-rules.pro",
    "chars": 653,
    "preview": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /u"
  },
  {
    "path": "easygoogle/src/main/AndroidManifest.xml",
    "chars": 2276,
    "preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n"
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/FragmentUtils.java",
    "chars": 2627,
    "preview": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n "
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/Google.java",
    "chars": 7905,
    "preview": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n "
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gac/AppInvites.java",
    "chars": 7858,
    "preview": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n "
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gac/AppInvitesReferralReceiver.java",
    "chars": 1860,
    "preview": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n "
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gac/GacFragment.java",
    "chars": 11597,
    "preview": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n "
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gac/GacModule.java",
    "chars": 2102,
    "preview": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n "
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gac/SignIn.java",
    "chars": 8793,
    "preview": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n "
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gac/SmartLock.java",
    "chars": 9754,
    "preview": "package pub.devrel.easygoogle.gac;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.support.a"
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gcm/EasyMessageService.java",
    "chars": 5305,
    "preview": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n "
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gcm/GCMUtils.java",
    "chars": 2803,
    "preview": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n "
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gcm/IDListenerService.java",
    "chars": 1595,
    "preview": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n "
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gcm/IDRegisterService.java",
    "chars": 2667,
    "preview": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n "
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gcm/MessageListenerService.java",
    "chars": 2299,
    "preview": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n "
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gcm/MessageSenderService.java",
    "chars": 2116,
    "preview": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n "
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gcm/Messaging.java",
    "chars": 2389,
    "preview": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n "
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gcm/MessagingFragment.java",
    "chars": 7004,
    "preview": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n "
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gcm/PubSubService.java",
    "chars": 2016,
    "preview": "package pub.devrel.easygoogle.gcm;\n\nimport android.app.IntentService;\nimport android.content.Intent;\nimport android.util"
  },
  {
    "path": "easygoogle/src/main/res/values/strings.xml",
    "chars": 584,
    "preview": "<resources>\n    <string name=\"app_name\">easygoogle</string>\n    <string name=\"play_services_error_fmt\">Google Play Servi"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "chars": 230,
    "preview": "#Mon Aug 03 15:12:08 PDT 2015\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
  },
  {
    "path": "gradle.properties",
    "chars": 855,
    "preview": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will o"
  },
  {
    "path": "gradlew",
    "chars": 5080,
    "preview": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start "
  },
  {
    "path": "gradlew.bat",
    "chars": 2404,
    "preview": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@r"
  },
  {
    "path": "settings.gradle",
    "chars": 30,
    "preview": "include ':app', ':easygoogle'\n"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the googlesamples/easygoogle GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 48 files (136.6 KB), approximately 31.4k tokens, and a symbol index with 189 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!