[
  {
    "path": ".gitignore",
    "content": ".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/captures\ngoogle-services.json\n"
  },
  {
    "path": "LICENSE",
    "content": "Apache License\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n\"License\" shall mean the terms and conditions for use, reproduction, and\ndistribution as defined by Sections 1 through 9 of this document.\n\n\"Licensor\" shall mean the copyright owner or entity authorized by the copyright\nowner that is granting the License.\n\n\"Legal Entity\" shall mean the union of the acting entity and all other entities\nthat control, are controlled by, or are under common control with that entity.\nFor the purposes of this definition, \"control\" means (i) the power, direct or\nindirect, to cause the direction or management of such entity, whether by\ncontract or otherwise, or (ii) ownership of fifty percent (50%) or more of the\noutstanding shares, or (iii) beneficial ownership of such entity.\n\n\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising\npermissions granted by this License.\n\n\"Source\" form shall mean the preferred form for making modifications, including\nbut not limited to software source code, documentation source, and configuration\nfiles.\n\n\"Object\" form shall mean any form resulting from mechanical transformation or\ntranslation of a Source form, including but not limited to compiled object code,\ngenerated documentation, and conversions to other media types.\n\n\"Work\" shall mean the work of authorship, whether in Source or Object form, made\navailable under the License, as indicated by a copyright notice that is included\nin or attached to the work (an example is provided in the Appendix below).\n\n\"Derivative Works\" shall mean any work, whether in Source or Object form, that\nis based on (or derived from) the Work and for which the editorial revisions,\nannotations, elaborations, or other modifications represent, as a whole, an\noriginal work of authorship. For the purposes of this License, Derivative Works\nshall not include works that remain separable from, or merely link (or bind by\nname) to the interfaces of, the Work and Derivative Works thereof.\n\n\"Contribution\" shall mean any work of authorship, including the original version\nof the Work and any modifications or additions to that Work or Derivative Works\nthereof, that is intentionally submitted to Licensor for inclusion in the Work\nby the copyright owner or by an individual or Legal Entity authorized to submit\non behalf of the copyright owner. For the purposes of this definition,\n\"submitted\" means any form of electronic, verbal, or written communication sent\nto the Licensor or its representatives, including but not limited to\ncommunication on electronic mailing lists, source code control systems, and\nissue tracking systems that are managed by, or on behalf of, the Licensor for\nthe purpose of discussing and improving the Work, but excluding communication\nthat is conspicuously marked or otherwise designated in writing by the copyright\nowner as \"Not a Contribution.\"\n\n\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf\nof whom a Contribution has been received by Licensor and subsequently\nincorporated within the Work.\n\n2. Grant of Copyright License.\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable copyright license to reproduce, prepare Derivative Works of,\npublicly display, publicly perform, sublicense, and distribute the Work and such\nDerivative Works in Source or Object form.\n\n3. Grant of Patent License.\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable (except as stated in this section) patent license to make, have\nmade, use, offer to sell, sell, import, and otherwise transfer the Work, where\nsuch license applies only to those patent claims licensable by such Contributor\nthat are necessarily infringed by their Contribution(s) alone or by combination\nof their Contribution(s) with the Work to which such Contribution(s) was\nsubmitted. If You institute patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that the Work or a\nContribution incorporated within the Work constitutes direct or contributory\npatent infringement, then any patent licenses granted to You under this License\nfor that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution.\n\nYou may reproduce and distribute copies of the Work or Derivative Works thereof\nin any medium, with or without modifications, and in Source or Object form,\nprovided that You meet the following conditions:\n\nYou must give any other recipients of the Work or Derivative Works a copy of\nthis License; and\nYou must cause any modified files to carry prominent notices stating that You\nchanged the files; and\nYou must retain, in the Source form of any Derivative Works that You distribute,\nall copyright, patent, trademark, and attribution notices from the Source form\nof the Work, excluding those notices that do not pertain to any part of the\nDerivative Works; and\nIf the Work includes a \"NOTICE\" text file as part of its distribution, then any\nDerivative Works that You distribute must include a readable copy of the\nattribution notices contained within such NOTICE file, excluding those notices\nthat do not pertain to any part of the Derivative Works, in at least one of the\nfollowing places: within a NOTICE text file distributed as part of the\nDerivative Works; within the Source form or documentation, if provided along\nwith the Derivative Works; or, within a display generated by the Derivative\nWorks, if and wherever such third-party notices normally appear. The contents of\nthe NOTICE file are for informational purposes only and do not modify the\nLicense. You may add Your own attribution notices within Derivative Works that\nYou distribute, alongside or as an addendum to the NOTICE text from the Work,\nprovided that such additional attribution notices cannot be construed as\nmodifying the License.\nYou may add Your own copyright statement to Your modifications and may provide\nadditional or different license terms and conditions for use, reproduction, or\ndistribution of Your modifications, or for any such Derivative Works as a whole,\nprovided Your use, reproduction, and distribution of the Work otherwise complies\nwith the conditions stated in this License.\n\n5. Submission of Contributions.\n\nUnless You explicitly state otherwise, any Contribution intentionally submitted\nfor inclusion in the Work by You to the Licensor shall be under the terms and\nconditions of this License, without any additional terms or conditions.\nNotwithstanding the above, nothing herein shall supersede or modify the terms of\nany separate license agreement you may have executed with Licensor regarding\nsuch Contributions.\n\n6. Trademarks.\n\nThis License does not grant permission to use the trade names, trademarks,\nservice marks, or product names of the Licensor, except as required for\nreasonable and customary use in describing the origin of the Work and\nreproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty.\n\nUnless required by applicable law or agreed to in writing, Licensor provides the\nWork (and each Contributor provides its Contributions) on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,\nincluding, without limitation, any warranties or conditions of TITLE,\nNON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are\nsolely responsible for determining the appropriateness of using or\nredistributing the Work and assume any risks associated with Your exercise of\npermissions under this License.\n\n8. Limitation of Liability.\n\nIn no event and under no legal theory, whether in tort (including negligence),\ncontract, or otherwise, unless required by applicable law (such as deliberate\nand grossly negligent acts) or agreed to in writing, shall any Contributor be\nliable to You for damages, including any direct, indirect, special, incidental,\nor consequential damages of any character arising as a result of this License or\nout of the use or inability to use the Work (including but not limited to\ndamages for loss of goodwill, work stoppage, computer failure or malfunction, or\nany and all other commercial damages or losses), even if such Contributor has\nbeen advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability.\n\nWhile redistributing the Work or Derivative Works thereof, You may choose to\noffer, and charge a fee for, acceptance of support, warranty, indemnity, or\nother liability obligations and/or rights consistent with this License. However,\nin accepting such obligations, You may act only on Your own behalf and on Your\nsole responsibility, not on behalf of any other Contributor, and only if You\nagree to indemnify, defend, and hold each Contributor harmless for any liability\nincurred by, or claims asserted against, such Contributor by reason of your\naccepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work\n\nTo apply the Apache License to your work, attach the following boilerplate\nnotice, with the fields enclosed by brackets \"[]\" replaced with your own\nidentifying information. (Don't include the brackets!) The text should be\nenclosed in the appropriate comment syntax for the file format. We also\nrecommend that a file or class name and description of purpose be included on\nthe same \"printed page\" as the copyright notice for easier identification within\nthird-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# EasyGoogle\n\n## Project status\n![status: inactive](https://img.shields.io/badge/status-inactive-red.svg)\n\nThis project is no longer actively maintained, and remains here as an archive of this work.\n\nEasyGoogle was created as an experimental improvement to the developer experience for certain\nGoogle APIs. In the time since its creation, most of these APIs have improved (and many in ways\nthat resemble EasyGoogle). For this reason it no longer makes sense to maintain this library.\nThank you to everyone who submitted issues or gave feedback, your thoughts influenced API\ndevelopment at Google.\n\n## Introduction\n\nEasyGoogle is a wrapper library to simplify basic integrations with Google Play\nServices.  The library wraps the following APIs (for now):\n\n  * [Google Sign-In](https://developers.google.com/identity/sign-in/android/)\n  * [Google Cloud Messaging](https://developers.google.com/cloud-messaging/)\n  * [Google App Invites](https://developers.google.com/app-invites/)\n  * [Google SmartLock for Passwords](https://developers.google.com/identity/smartlock-passwords/android/)\n\n## Installation\nEasyGoogle is installed by adding the following dependency to your\n`build.gradle` file:\n\n    dependencies {\n      compile 'pub.devrel:easygoogle:0.2.5+'\n    }\n\n## Usage\n\n### Enabling Services\nBefore you begin, visit [this page](https://developers.google.com/mobile/add)\nto select Google services and add them to your Android app.  Make sure to enable any services\nyou plan to use and follow all of the steps, including modifying your `build.gradle` files\nto enable the `google-services` plugin.\n\nOnce you have a `google-services.json` file in the proper place you can proceed to use\nEasyGoogle.\n\n### Basic\nEasyGoogle makes use of `Fragments` to manage the lifecycle of the\n`GoogleApiClient`, so any Activity which uses EasyGoogle must extend\n`FragmentActivity`.\n\nAll interaction with EasyGoogle is through the `Google` class, which is\ninstantiated like this:\n\n```java\npublic class MainActivity extends AppCompatActivity {\n\n  private Google mGoogle;\n\n  @Override\n  protected void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    setContentView(R.layout.activity_main);\n\n    mGoogle = new Google.Builder(this).build();\n  }\n\n}\n```\n\nOf course, instantiating a `Google` object like this won't do anything at all,\nyou need to enable features individually.\n\n### Sign-In\nTo enable Google Sign-In, call the appropriate method on `Google.Builder` and\nimplement the `SignIn.SignInListener` interface:\n\n```java\n public class MainActivity extends AppCompatActivity implements\n   SignIn.SignInListener {\n\n   private Google mGoogle;\n\n   @Override\n   protected void onCreate(Bundle savedInstanceState) {\n     super.onCreate(savedInstanceState);\n     setContentView(R.layout.activity_main);\n\n     // This is optional, pass 'null' if no ID token is required.\n     String serverClientId = getString(R.string.default_web_client_id);\n\n     mGoogle = new Google.Builder(this)\n       .enableSignIn(this, serverClientId)\n       .build();\n   }\n\n   @Override\n   public void onSignedIn(GoogleSignInAccount account) {\n       // Sign in was successful.\n   }\n\n   @Override\n   public void onSignedOut() {\n       // Sign out was successful.\n   }\n\n   @Override\n   public void onSignInFailed() {\n       // Sign in failed for some reason and should not be attempted again\n       // unless the user requests it.\n   }\n\n }\n ```\n\nThen, use the `SignIn` object from `mGoogle.getSignIn()` to access API methods\nlike `SignIn#getCurrentUser()`, `SignIn#signIn`, and `SignIn#signOut`.\n\n### Cloud Messaging\nTo enable Cloud Messaging, you will have to implement a simple `Service` in your application.\n\nFirst, pick a unique permission name and make the following string resource in your `strings.xml` file.\nIt is important to pick a unique name:\n\n```xml\n<string name=\"gcm_permission\">your.unique.gcm.permission.name.here</string>\n```\n\nNext, add the following to your `AndroidManifest.xml` inside the `application` tag, making sure\nthat the value of the `android:permission` element is the same value you specified in your\n`strings.xml` file above:\n\n```xml\n <!-- This allows the app to receive GCM through EasyGoogle -->\n <service\n     android:name=\".MessagingService\"\n     android:enabled=\"true\"\n     android:exported=\"false\"\n     android:permission=\"your.unique.gcm.permission.name.here\" />\n ```\n\nNext, add the following permission to your `AndroidManifest.xml` file before the `application` tag,\nreplacing `<your-package-name>` with your Android package name:\n\n```xml\n    <permission android:name=\"<your-package-name>.permission.C2D_MESSAGE\"\n                android:protectionLevel=\"signature\" />\n    <uses-permission android:name=\"<your-package-name>.permission.C2D_MESSAGE\" />\n```\n\nThen implement a class called `MessagingService` that extends `EasyMessageService`. Below is\none example of such a class:\n\n```java\npublic class MessagingService extends EasyMessageService {\n\n  @Override\n  public void onMessageReceived(String from, Bundle data) {\n      // If there is a running Activity that implements MessageListener, it should handle\n      // this message.\n      if (!forwardToListener(from, data)) {\n          // There is no active MessageListener to get this, I should fire a notification with\n          // a PendingIntent to an activity that can handle this.\n          PendingIntent pendingIntent = createMessageIntent(from, data, MainActivity.class);\n          Notification notif = new NotificationCompat.Builder(this)\n                  .setContentTitle(\"Message from: \" + from)\n                  .setContentText(data.getString(\"message\"))\n                  .setSmallIcon(R.mipmap.ic_launcher)\n                  .setContentIntent(pendingIntent)\n                  .setAutoCancel(true)\n                  .build();\n\n          NotificationManager notificationManager =\n                  (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);\n          notificationManager.notify(0, notif);\n      }\n  }\n\n  @Override\n  public void onNewToken(String token) {\n      // Send a registration message to the server with our new token\n      String senderId = getString(R.string.gcm_defaultSenderId);\n      sendRegistrationMessage(senderId, token);\n  }\n}\n```\n\nNote the use of the helper methods `forwardToListener` and `createMessageIntent`, which make\nit easier for you to either launch an Activity or create a Notification to handle the message.\n\nThe `forwardToListener` method checks to see if there is an Activity that implements\n`Messaging.MessagingListener` in the foreground.  If there is, it sends the GCM message to the\nActivity to be handled.  To implement `Messaging.MessagingListener`, call the appropriate\nmethod on `Google.Builder` in your `Activity` and implement the interface:\n\n```java\npublic class MainActivity extends AppCompatActivity implements\n  Messaging.MessagingListener {\n\n  private Google mGoogle;\n\n  @Override\n  protected void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    setContentView(R.layout.activity_main);\n\n    mGoogle = new Google.Builder(this)\n      .enableMessaging(this, getString(R.string.gcm_defaultSenderId))\n      .build();\n  }\n\n  @Override\n  public void onMessageReceived(String from, Bundle message) {\n      // GCM message received.\n  }\n}\n```\n\nThen, use the `Messaging` object from `mGoogle.getMessaging()` to access API\nmethodslike `Messaging#send`.\n\n### App Invites\nTo enable App Invites, call the appropriate method on `Google.Builder` and\nimplement the `AppInvites.AppInviteListener` interface:\n\n```java\npublic class MainActivity extends AppCompatActivity implements\n  AppInvites.AppInviteListener {\n\n  private Google mGoogle;\n\n  @Override\n  protected void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    setContentView(R.layout.activity_main);\n\n    mGoogle = new Google.Builder(this)\n      .enableAppInvites(this)\n      .build();\n  }\n\n  @Override\n  public void onInvitationReceived(String invitationId, String deepLink) {\n      // Invitation recieved in the app.\n  }\n\n  @Override\n  public void onInvitationsSent(String[] ids) {\n      // The user selected contacts and invitations sent successfully.\n  }\n\n  @Override\n  public void onInvitationsFailed() {\n      // The user either canceled sending invitations or they failed to\n      // send due to some configuration error.\n  }\n\n}\n```\n\nThen, use the `AppInvites` object from `mGoogle.getAppInvites()` to access API\nmethods like `AppInvites#sendInvitation`.\n\n### SmartLock for Passwords\nTo enable Smart Lock for Passwords, call the appropriate method on `Google.Builder` and\nimplement the `SmartLock.SmartLockListener` interface:\n\n```java\npublic class MainActivity extends AppCompatActivity implements\n  SmartLock.SmartLockListener {\n\n  private Google mGoogle;\n\n  @Override\n  protected void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    setContentView(R.layout.activity_main);\n\n    mGoogle = new Google.Builder(this)\n      .enableSmartLock(this)\n      .build();\n  }\n\n  @Override\n  public void onCredentialRetrieved(Credential credential) {\n    // Successfully retrieved a Credential for the current device user.\n  }\n\n  @Override\n  public void onShouldShowCredentialPicker() {\n    // In order to retrieve a Credential, the app must show the picker dialog\n    // using the SmartLock#showCredentialPicker() method.\n  }\n\n  @Override\n  public void onCredentialRetrievalFailed() {\n    // The user has no stored credentials, or the retrieval operation failed or\n    // was canceled.\n  }\n\n}\n```\n\nThen, use the `SmartLock` object from `mGoogle.getSmartLock()` to access API\nmethods like `SmartLock#getCredentials()` and `SmartLock#save()`.\n\n### Advanced Usage\nIf you would like to perform some action using one of the enabled Google\nservices but it is not properly wrapped by the EasyGoogle library, just call\n`Google#getGoogleApiClient()` to get access to the underlying `GoogleApiClient`\nheld by the `Google` object.\n"
  },
  {
    "path": "RELEASENOTES.md",
    "content": "# 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.\n  * Upgraded gradle plugin version, target SDK version, and appcompat version.\n  * Updated SignIn to use new `GoogleSignIn` API and added the method `SignIn#getCurrentUser`.\n  * Removed dependency on `play-services-plus` and replaced all instances of `Person` with\n    `GoogleSignInAccount` (breaking interface change).\n\n# 0.1.0\n\n  * Initial Release\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "app/build.gradle",
    "content": "apply plugin: 'com.android.application'\napply plugin: 'com.google.gms.google-services'\n\n\nandroid {\n    compileSdkVersion 23\n    buildToolsVersion \"23.0.2\"\n\n    defaultConfig {\n        applicationId \"pub.devrel.easygoogle.sample\"\n        minSdkVersion 15\n        targetSdkVersion 23\n        versionCode 1\n        versionName \"1.0\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\nrepositories {\n    mavenLocal()\n}\n\ndependencies {\n    compile fileTree(dir: 'libs', include: ['*.jar'])\n    compile 'com.android.support:appcompat-v7:23.1.1'\n\n    // compile 'pub.devrel:easygoogle:+'\n    compile project(':easygoogle')\n}\n"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /usr/local/Android/Sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"pub.devrel.easygoogle.sample\">\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:theme=\"@style/AppTheme\">\n\n        <activity\n            android:name=\".MainActivity\"\n            android:label=\"@string/app_name\"\n            android:windowSoftInputMode=\"stateHidden\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n\n        <activity\n            android:name=\".MainFragmentActivity\" />\n\n        <!-- Receive GCM Messages and Tokens from EasyGoogle -->\n        <service\n            android:name=\".MessagingService\"\n            android:enabled=\"true\"\n            android:exported=\"false\"\n            android:permission=\"@string/gcm_permission\" />\n\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "app/src/main/java/pub/devrel/easygoogle/sample/MainActivity.java",
    "content": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pub.devrel.easygoogle.sample;\n\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.support.v7.app.AppCompatActivity;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.EditText;\nimport android.widget.TextView;\n\nimport com.google.android.gms.auth.api.credentials.Credential;\nimport com.google.android.gms.auth.api.signin.GoogleSignInAccount;\n\nimport pub.devrel.easygoogle.Google;\nimport pub.devrel.easygoogle.gac.AppInvites;\nimport pub.devrel.easygoogle.gac.SignIn;\nimport pub.devrel.easygoogle.gac.SmartLock;\nimport pub.devrel.easygoogle.gcm.Messaging;\n\n/**\n * Simple Activity demonstrating how to use the EasyGoogle library to quickly integrate\n * Sign-In, App Invites, Google Cloud Messaging, and SmartLock for Passwords.\n */\npublic class MainActivity extends AppCompatActivity implements\n        SignIn.SignInListener,\n        Messaging.MessagingListener,\n        AppInvites.AppInviteListener,\n        SmartLock.SmartLockListener,\n        View.OnClickListener {\n\n    public static String TAG = \"sample.MainActivity\";\n\n    private Google mGoogle;\n\n    // SmartLock data/fields\n    private Credential mCredential;\n    private EditText mUsernameField;\n    private EditText mPasswordField;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n\n        // Initialize the Google object with access to Cloud Messaging, Sign-In, and App Invites.\n        // All APIs are accessed through the Google object and the result of asynchronous operations\n        // are returned through API-specific listener classes like {@link SignIn.SignInListener}.\n        mGoogle = new Google.Builder(this)\n                .enableMessaging(this, getString(R.string.gcm_defaultSenderId))\n                .enableSignIn(this, null)\n                .enableAppInvites(this)\n                .enableSmartLock(this)\n                .build();\n\n        // Inject sign-in button, automatically configured to initiate sign-in when clicked.\n        mGoogle.getSignIn().createSignInButton(this, (ViewGroup) findViewById(R.id.layout_sign_in));\n\n        // Click listeners for buttons\n        findViewById(R.id.send_message_button).setOnClickListener(this);\n        findViewById(R.id.sign_out_button).setOnClickListener(this);\n        findViewById(R.id.send_invites_button).setOnClickListener(this);\n        findViewById(R.id.button_smartlock_load).setOnClickListener(this);\n        findViewById(R.id.button_smartlock_save).setOnClickListener(this);\n        findViewById(R.id.button_smartlock_delete).setOnClickListener(this);\n\n        // Other views\n        mUsernameField = ((EditText) findViewById(R.id.field_smartlock_username));\n        mPasswordField = ((EditText) findViewById(R.id.field_smartlock_password));\n    }\n\n    @Override\n    public void onStart() {\n        super.onStart();\n\n        // Subscribe to the \"easygoogle\" topic\n        mGoogle.getMessaging().subscribeTo(\"/topics/easygoogle\");\n    }\n\n    @Override\n    public void onSignedIn(GoogleSignInAccount account) {\n        Log.d(TAG, \"onSignedIn:\" + account.getEmail());\n        ((TextView) findViewById(R.id.sign_in_status)).setText(\n                getString(R.string.status_signed_in_fmt, account.getDisplayName(), account.getEmail()));\n    }\n\n    @Override\n    public void onSignedOut() {\n        ((TextView) findViewById(R.id.sign_in_status)).setText(R.string.status_signed_out);\n    }\n\n    @Override\n    public void onSignInFailed() {\n        ((TextView) findViewById(R.id.sign_in_status)).setText(R.string.status_sign_in_failed);\n    }\n\n    @Override\n    public void onMessageReceived(String from, Bundle message) {\n        ((TextView) findViewById(R.id.messaging_status)).setText(\n                getString(R.string.status_message_fmt, from));\n    }\n\n    @Override\n    public void onInvitationReceived(String invitationId, String deepLink) {\n        ((TextView) findViewById(R.id.app_invites_status)).setText(\n                getString(R.string.status_invitation_fmt, invitationId, deepLink));\n    }\n\n    @Override\n    public void onInvitationsSent(String[] ids) {\n        ((TextView) findViewById(R.id.app_invites_status)).setText(\n                getString(R.string.status_invitation_sent_fmt, ids.length));\n    }\n\n    @Override\n    public void onInvitationsFailed() {\n        ((TextView) findViewById(R.id.app_invites_status)).setText(R.string.status_invitation_failed);\n    }\n\n    @Override\n    public void onCredentialRetrieved(Credential credential) {\n        ((TextView) findViewById(R.id.smartlock_status)).setText(R.string.status_credential_retrieved);\n        mCredential = credential;\n        mUsernameField.setText(credential.getId());\n        mPasswordField.setText(credential.getPassword());\n    }\n\n    @Override\n    public void onShouldShowCredentialPicker() {\n        mGoogle.getSmartLock().showCredentialPicker();\n    }\n\n    @Override\n    public void onCredentialRetrievalFailed() {\n        ((TextView) findViewById(R.id.smartlock_status)).setText(R.string.status_credential_failed);\n        mUsernameField.setText(null);\n        mPasswordField.setText(null);\n    }\n\n    @Override\n    public void onClick(View v) {\n        switch (v.getId()) {\n            case R.id.sign_out_button:\n                // Sign out with Google\n                mGoogle.getSignIn().signOut();\n                break;\n            case R.id.send_message_button:\n                // Send a GCM message\n                Bundle b = new Bundle();\n                b.putString(\"message\", \"I am a banana!\");\n                mGoogle.getMessaging().send(b);\n                break;\n            case R.id.send_invites_button:\n                // Send App Invites\n                mGoogle.getAppInvites().sendInvitation(\n                        \"Title\", \"Message\", Uri.parse(\"http://example.com/id/12345\"));\n                break;\n            case R.id.button_smartlock_load:\n                // Begin loading Credentials\n                mGoogle.getSmartLock().getCredentials();\n                break;\n            case R.id.button_smartlock_save:\n                // Save Credential\n                String id = mUsernameField.getText().toString();\n                String password = mPasswordField.getText().toString();\n                mGoogle.getSmartLock().save(id, password);\n\n                ((TextView) findViewById(R.id.smartlock_status)).setText(null);\n                mUsernameField.setText(null);\n                mPasswordField.setText(null);\n                break;\n            case R.id.button_smartlock_delete:\n                // Delete Credential and clear fields\n                if (mCredential != null) {\n                    mGoogle.getSmartLock().delete(mCredential);\n\n                    mCredential = null;\n                    ((TextView) findViewById(R.id.smartlock_status)).setText(null);\n                    mUsernameField.setText(null);\n                    mPasswordField.setText(null);\n                }\n                break;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/pub/devrel/easygoogle/sample/MainFragmentActivity.java",
    "content": "package pub.devrel.easygoogle.sample;\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v7.app.AppCompatActivity;\nimport android.util.Log;\n\nimport com.google.android.gms.auth.api.signin.GoogleSignInAccount;\n\nimport pub.devrel.easygoogle.Google;\nimport pub.devrel.easygoogle.gac.SignIn;\nimport pub.devrel.easygoogle.gcm.Messaging;\n\n/**\n * Activity for demonstrating the use of EasyGoogle in Fragments.\n */\npublic class MainFragmentActivity extends AppCompatActivity implements\n        Messaging.MessagingListener {\n\n    private static final String TAG = \"MainFragmentActivity\";\n\n    private Google mGoogle;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        Log.d(TAG, \"onCreate\");\n\n        mGoogle = new Google.Builder(this)\n                .enableMessaging(this, getString(R.string.gcm_defaultSenderId))\n                .build();\n\n        getSupportFragmentManager().beginTransaction()\n                .add(android.R.id.content, new EasyGoogleFragment())\n                .commit();\n    }\n\n    @Override\n    public void onStart() {\n        super.onStart();\n        mGoogle.getMessaging().subscribeTo(\"/topics/easygoogle-test\");\n    }\n\n    @Override\n    public void onMessageReceived(String from, Bundle message) {\n        Log.d(TAG, \"onMessageReceived:\" + from);\n    }\n\n    /**\n     * Fragment that hosts Sign In.\n     */\n    public static class EasyGoogleFragment extends Fragment implements\n            SignIn.SignInListener {\n\n        private static final String TAG = \"EasyGoogleFragment\";\n        private Google mGoogle;\n\n        public EasyGoogleFragment() {}\n\n        @Override\n        public void onActivityCreated(Bundle savedInstanceState) {\n            super.onActivityCreated(savedInstanceState);\n            Log.d(TAG, \"onActivityCreated\");\n\n            mGoogle = new Google.Builder(getActivity())\n                    .enableSignIn(this, null)\n                    .build();\n\n            Log.d(TAG, \"onActivityCreated:isSignedIn:\" + isSignedIn());\n        }\n\n        @Override\n        public void onStart() {\n            super.onStart();\n            Log.d(TAG, \"onStart\");\n            Log.d(TAG, \"onStart:isSignedIn:\" + isSignedIn());\n\n            if (!isSignedIn()) {\n                mGoogle.getSignIn().signIn();\n            }\n        }\n\n        private boolean isSignedIn() {\n            // The GoogleApiClient is only created on onActivityCreated of GacFragment, which is\n            // not guaranteed to happen before or after this fragment is created\n            if (mGoogle.getGoogleApiClient() == null) {\n                Log.e(TAG, \"isSignedIn: mGoogle.getGoogleApiClient() == null\");\n                return false;\n            }\n\n            return (mGoogle.getSignIn().isSignedIn());\n        }\n\n        @Override\n        public void onSignedIn(GoogleSignInAccount account) {\n            Log.d(TAG, \"onSignedIn:\" + account);\n        }\n\n        @Override\n        public void onSignInFailed() {\n            Log.d(TAG, \"onSignInFailed\");\n        }\n\n        @Override\n        public void onSignedOut() {\n            Log.d(TAG, \"onSignedOut\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/pub/devrel/easygoogle/sample/MessagingService.java",
    "content": "package pub.devrel.easygoogle.sample;\n\nimport android.app.Notification;\nimport android.app.NotificationManager;\nimport android.app.PendingIntent;\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.support.v4.app.NotificationCompat;\nimport android.util.Log;\n\nimport pub.devrel.easygoogle.gcm.EasyMessageService;\n\n/**\n * Background service to receive GCM events on behalf of the application.\n */\npublic class MessagingService extends EasyMessageService {\n\n    public static final String TAG = \"MessagingService\";\n\n    @Override\n    public void onMessageReceived(String from, Bundle data) {\n        Log.d(TAG, \"onMessageReceived:\" + from + \":\" + data);\n\n        // If there is a running Activity that implements MessageListener, it should handle\n        // this message.\n        if (!forwardToListener(from, data)) {\n            // There is no active MessageListener to get this, I should fire a notification with\n            // a PendingIntent to an activity that can handle this\n            Log.d(TAG, \"onMessageReceived: no active listeners\");\n\n            PendingIntent pendingIntent = createMessageIntent(from, data, MainActivity.class);\n            Notification notif = new NotificationCompat.Builder(this)\n                    .setContentTitle(\"Message from: \" + from)\n                    .setContentText(data.getString(\"message\"))\n                    .setSmallIcon(R.mipmap.ic_launcher)\n                    .setContentIntent(pendingIntent)\n                    .setAutoCancel(true)\n                    .build();\n\n            NotificationManager notificationManager =\n                    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);\n            notificationManager.notify(0, notif);\n        }\n    }\n\n    @Override\n    public void onNewToken(String token) {\n        Log.d(TAG, \"onNewToken:\" + token);\n\n        // When a new token is received, send it upstream so that the server knows about this\n        // client and can send downstream messages properly. This should happen infrequently.\n        String senderId = getString(R.string.gcm_defaultSenderId);\n        sendRegistrationMessage(senderId, token);\n    }\n}\n"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "content": "<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        android:paddingBottom=\"@dimen/activity_vertical_margin\"\n        android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n        android:paddingRight=\"@dimen/activity_horizontal_margin\"\n        android:paddingTop=\"@dimen/activity_vertical_margin\"\n        tools:context=\".MainActivity\">\n\n        <!-- GCM -->\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/title_gcm\"\n            android:textStyle=\"bold\" />\n\n        <TextView\n            android:id=\"@+id/messaging_status\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\" />\n\n        <Button\n            android:id=\"@+id/send_message_button\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/send_message\" />\n\n        <!-- Sign In -->\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"20dp\"\n            android:text=\"@string/title_google_sign_in\"\n            android:textStyle=\"bold\" />\n\n        <TextView\n            android:id=\"@+id/sign_in_status\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/status_signed_out\" />\n\n        <LinearLayout\n            android:id=\"@+id/layout_sign_in\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"horizontal\">\n\n            <!-- SignInButton will be injected here -->\n\n            <Button\n                android:id=\"@+id/sign_out_button\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/sign_out\" />\n        </LinearLayout>\n\n        <!-- App Invites -->\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"20dp\"\n            android:text=\"@string/title_app_invites\"\n            android:textStyle=\"bold\" />\n\n        <TextView\n            android:id=\"@+id/app_invites_status\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\" />\n\n        <Button\n            android:id=\"@+id/send_invites_button\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/send_invites\" />\n\n        <!-- SmartLock -->\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"20dp\"\n            android:text=\"@string/title_smart_lock\"\n            android:textStyle=\"bold\" />\n\n        <TextView\n            android:id=\"@+id/smartlock_status\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\" />\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"horizontal\">\n\n            <EditText\n                android:id=\"@+id/field_smartlock_username\"\n                android:layout_width=\"150dp\"\n                android:layout_height=\"wrap_content\"\n                android:hint=\"@string/hint_username\" />\n\n            <EditText\n                android:id=\"@+id/field_smartlock_password\"\n                android:layout_width=\"150dp\"\n                android:layout_height=\"wrap_content\"\n                android:hint=\"@string/hint_password\"\n                android:inputType=\"textPassword\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"horizontal\">\n\n            <Button\n                android:id=\"@+id/button_smartlock_load\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/smartlock_load\" />\n\n            <Button\n                android:id=\"@+id/button_smartlock_save\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/smartlock_save\" />\n\n            <Button\n                android:id=\"@+id/button_smartlock_delete\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/smartlock_delete\" />\n\n        </LinearLayout>\n\n    </LinearLayout>\n\n</ScrollView>"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">easygoogle</string>\n\n    <!-- IMPORTANT -->\n    <!-- GCM permission string, this needs to be unique for every app -->\n    <string name=\"gcm_permission\">pub.devrel.easygoogle.sampleapp.GCM</string>\n\n    <!-- Section titles -->\n    <string name=\"title_google_sign_in\">Google Sign-in</string>\n    <string name=\"title_gcm\">Google Cloud Messaging</string>\n    <string name=\"title_app_invites\">App Invites</string>\n    <string name=\"title_smart_lock\">SmartLock for Passwords</string>\n\n    <!-- Status messages -->\n    <string name=\"status_signed_out\">Signed out.</string>\n    <string name=\"status_signed_in_fmt\" formatted=\"false\">Signed in as: %s (%s)</string>\n    <string name=\"status_sign_in_failed\">Sign in failed.</string>\n    <string name=\"status_message_fmt\" formatted=\"false\">Message from %s.</string>\n    <string name=\"status_invitation_fmt\" formatted=\"false\">Received invitation %s:%s.</string>\n    <string name=\"status_invitation_sent_fmt\" formatted=\"false\">Sent %d invitations.</string>\n    <string name=\"status_invitation_failed\">Sending invitations failed.</string>\n    <string name=\"status_credential_retrieved\">Credential retrieved.</string>\n    <string name=\"status_credential_failed\">Credential retrieval failed.</string>\n\n    <!-- Button and field labels -->\n    <string name=\"sign_out\">Sign Out</string>\n    <string name=\"send_message\">Send Message</string>\n    <string name=\"send_invites\">Send Invites</string>\n    <string name=\"smartlock_load\">Load</string>\n    <string name=\"smartlock_save\">Save</string>\n    <string name=\"smartlock_delete\">Delete</string>\n    <string name=\"hint_username\">username</string>\n    <string name=\"hint_password\">password</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <!-- Customize your theme here. -->\n    </style>\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-w820dp/dimens.xml",
    "content": "<resources>\n    <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n         (such as screen margins) for screens with more than 820dp of available width. This\n         would include 7\" and 10\" devices in landscape (~960dp and ~1280dp respectively). -->\n    <dimen name=\"activity_horizontal_margin\">64dp</dimen>\n</resources>\n"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    repositories {\n        jcenter()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:1.3.0'\n        classpath 'com.google.gms:google-services:1.5.0'\n        classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3'\n        classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.2'\n    }\n}\n\nallprojects {\n    repositories {\n        jcenter()\n    }\n}\n"
  },
  {
    "path": "easygoogle/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "easygoogle/bintray.gradle",
    "content": "apply plugin: 'com.jfrog.bintray'\napply from: 'constants.gradle'\n\ngroup = mavenGroup\nversion = mavenVersion\n\nbintray {\n    user = hasProperty('BINTRAY_USER') ? getProperty('BINTRAY_USER') : System.getenv('BINTRAY_USER')\n    key = hasProperty('BINTRAY_KEY') ? getProperty('BINTRAY_KEY') : System.getenv('BINTRAY_KEY')\n\n    configurations = [ 'archives' ]\n\n    pkg {\n        repo = projectName\n        name = mavenArtifactId\n        userOrg = bintrayOrg\n        licenses = [ 'Apache-2.0' ]\n        vcsUrl = \"${githubUrl}.git\"\n\n        version {\n            name = mavenVersion\n            released = new Date()\n        }\n    }\n}\n"
  },
  {
    "path": "easygoogle/build.gradle",
    "content": "apply plugin: 'com.android.library'\napply from: 'maven.gradle'\napply from: 'bintray.gradle'\n\nandroid {\n    compileSdkVersion 23\n    buildToolsVersion \"23.0.2\"\n\n    defaultConfig {\n        minSdkVersion 15\n        targetSdkVersion 22\n        versionCode 1\n        versionName \"$mavenVersion\"\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\ndependencies {\n    compile 'com.android.support:appcompat-v7:23.1.1'\n    compile 'com.google.android.gms:play-services-auth:8.3.0'\n    compile 'com.google.android.gms:play-services-appinvite:8.3.0'\n    compile 'com.google.android.gms:play-services-gcm:8.3.0'\n}\n"
  },
  {
    "path": "easygoogle/constants.gradle",
    "content": "ext {\n    projectName = 'EasyGoogle'\n    projectDesc = 'A wrapper library for basic functions of Google Play Services APIs'\n\n    githubUrl = 'https://github.com/googlesamples/easygoogle'\n\n    mavenGroup = 'pub.devrel'\n    mavenArtifactId = 'easygoogle'\n    mavenVersion = '0.2.5'\n\n    bintrayOrg = 'easygoogle'\n}\n"
  },
  {
    "path": "easygoogle/maven.gradle",
    "content": "apply plugin: 'com.github.dcendents.android-maven'\napply from: 'constants.gradle'\n\ninstall {\n    repositories.mavenInstaller {\n        pom.project {\n            name projectName\n            description projectDesc\n            url githubUrl\n            inceptionYear '2015'\n\n            packaging 'aar'\n            groupId mavenGroup\n            artifactId mavenArtifactId\n            version mavenVersion\n\n            licenses {\n                license {\n                    name 'The Apache Software License, Version 2.0'\n                    url 'http://www.apache.org/licenses/LICENSE-2.0.txt'\n                    distribution 'repo'\n                }\n            }\n            scm {\n                connection \"${githubUrl}.git\"\n                url githubUrl\n\n            }\n            developers {\n                developer {\n                    name 'Google'\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "easygoogle/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /usr/local/Android/Sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "easygoogle/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"pub.devrel.easygoogle\">\n\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission android:name=\"android.permission.GET_ACCOUNTS\" />\n    <uses-permission android:name=\"android.permission.WAKE_LOCK\" />\n    <uses-permission android:name=\"com.google.android.c2dm.permission.RECEIVE\" />\n\n    <application\n        android:label=\"@string/app_name\">\n\n        <!-- AppInvites -->\n        <receiver\n            android:name=\".gac.AppInvitesReferralReceiver\"\n            android:exported=\"true\"\n            tools:ignore=\"ExportedReceiver\">\n            <intent-filter>\n                <action android:name=\"com.android.vending.INSTALL_REFERRER\" />\n            </intent-filter>\n        </receiver>\n\n        <!-- GCM -->\n        <receiver\n            android:name=\"com.google.android.gms.gcm.GcmReceiver\"\n            android:exported=\"true\"\n            android:permission=\"com.google.android.c2dm.permission.SEND\">\n            <intent-filter>\n                <action android:name=\"com.google.android.c2dm.intent.RECEIVE\" />\n\n                <category android:name=\"com.example.gcm\" />\n            </intent-filter>\n        </receiver>\n\n        <service\n            android:name=\".gcm.MessageListenerService\"\n            android:enabled=\"true\"\n            android:exported=\"false\">\n            <intent-filter>\n                <action android:name=\"com.google.android.c2dm.intent.RECEIVE\" />\n            </intent-filter>\n        </service>\n\n        <service\n            android:name=\".gcm.IDListenerService\"\n            android:exported=\"false\">\n            <intent-filter>\n                <action android:name=\"com.google.android.gms.iid.InstanceID\" />\n            </intent-filter>\n        </service>\n\n        <service\n            android:name=\".gcm.IDRegisterService\"\n            android:exported=\"true\"\n            android:permission=\"com.google.android.c2dm.permission.SEND\" />\n\n        <service\n            android:name=\".gcm.MessageSenderService\"\n            android:exported=\"false\" />\n\n        <service\n            android:name=\".gcm.PubSubService\"\n            android:exported=\"false\" />\n\n    </application>\n</manifest>\n"
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/FragmentUtils.java",
    "content": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pub.devrel.easygoogle;\n\nimport android.support.v4.app.Fragment;\nimport android.support.v4.app.FragmentActivity;\nimport android.support.v4.app.FragmentManager;\nimport android.support.v4.app.FragmentTransaction;\nimport android.util.Log;\n\n/**\n * Utility class for common Fragment operations.\n */\npublic class FragmentUtils extends Fragment {\n\n    private static final String TAG = FragmentUtils.class.getSimpleName();\n\n    /**\n     * Check if an Activity already has an instance of a particular Fragment/Tag.  If so, return the\n     * existing instance.  If it does not have one, add a new instance and return it.\n     * @param activity the FragmentActivity to host the Fragment.\n     * @param tag the Fragment tag, should be a unique string for each instance.\n     * @param instance an instance of the desired Fragment sub-class, to add if necessary.\n     * @param <T> a class that extends Fragment.\n     * @return an instance of T which is added to the activity.\n     */\n    public static <T extends Fragment> T getOrCreate(FragmentActivity activity, String tag, T instance) {\n        // TODO(samstern): I'd like to avoid having to ask for an instance but I'd also like to avoid\n        //                 having to create an instance using reflection...\n\n        T result = null;\n        boolean shouldAdd = false;\n\n        FragmentManager fm = activity.getSupportFragmentManager();\n        FragmentTransaction ft = fm.beginTransaction();\n\n        Fragment fragment = fm.findFragmentByTag(tag);\n        if (fragment == null) {\n            shouldAdd = true;\n        } else {\n            // TODO(samstern): how to be more confident about this cast?\n            Log.d(TAG, \"Found fragment instance: \" + tag);\n            result = (T) fragment;\n        }\n\n        if (shouldAdd) {\n            Log.d(TAG, \"Adding new Fragment: \" + tag);\n\n            // Use empty instance\n            result = instance;\n            ft.add(result, tag).disallowAddToBackStack().commit();\n        }\n\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/Google.java",
    "content": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pub.devrel.easygoogle;\n\nimport android.support.v4.app.FragmentActivity;\nimport android.util.Log;\n\nimport com.google.android.gms.common.api.GoogleApiClient;\n\nimport pub.devrel.easygoogle.gac.AppInvites;\nimport pub.devrel.easygoogle.gac.GacFragment;\nimport pub.devrel.easygoogle.gac.SignIn;\nimport pub.devrel.easygoogle.gac.SmartLock;\nimport pub.devrel.easygoogle.gcm.Messaging;\nimport pub.devrel.easygoogle.gcm.MessagingFragment;\n\n/**\n * Google is the user-facing interface for all APIs, acts as a controller for a number of\n * sub-fragments that the user should not interact with directly. All user interaction should\n * be driven by listeners given to the Google.Builder.\n */\npublic class Google {\n\n    private static final String TAG = Google.class.getSimpleName();\n\n    // Tags for fragments that this class will control\n    private static final String TAG_GAC_FRAGMENT = \"gac_fragment\";\n    private static final String TAG_MESSAGING_FRAGMENT = \"messaging_fragment\";\n\n    // Fragment that holds the GoogleApiClient\n    private GacFragment mGacFragment;\n    private MessagingFragment mMessagingFragment;\n\n    public static class Builder {\n\n        private FragmentActivity mActivity;\n\n        private SignIn.SignInListener mSignInListener;\n        private String mServerClientId;\n\n        private Messaging.MessagingListener mMessagingListener;\n        private String mSenderId;\n\n        private AppInvites.AppInviteListener mAppInviteListener;\n\n        private SmartLock.SmartLockListener mSmartLockListener;\n\n        public Builder(FragmentActivity activity){\n            mActivity = activity;\n        }\n\n        /**\n         * Initialize {@link SignIn}.\n         * @param signInListener listener for sign in events,\n         * @return self, for chaining.\n         */\n        public Builder enableSignIn(SignIn.SignInListener signInListener) {\n            mSignInListener = signInListener;\n            return this;\n        }\n\n        /**\n         * Initialize {@link SignIn}.\n         * @param signInListener listener for sign in events.\n         * @param serverClientId (optional) web client ID for obtaining ID tokens.\n         * @return self, for chaining.\n         */\n        public Builder enableSignIn(SignIn.SignInListener signInListener, String serverClientId) {\n            mServerClientId = serverClientId;\n            mSignInListener = signInListener;\n            return this;\n        }\n\n        /**\n         * Initialize {@link Messaging}.\n         * @param listener listener for GCM events.\n         * @param senderId GCM sender Id.\n         * @return self, for chaining.\n         */\n        public Builder enableMessaging(Messaging.MessagingListener listener, String senderId) {\n            mSenderId = senderId;\n            mMessagingListener = listener;\n            return this;\n        }\n\n        /**\n         * Initialize {@link AppInvites}.\n         * @param listener listener for app invites events.\n         * @return self, for chaining.\n         */\n        public Builder enableAppInvites(AppInvites.AppInviteListener listener) {\n            mAppInviteListener = listener;\n            return this;\n        }\n\n        /**\n         * Initialize {@link SmartLock}.\n         * @param listener listener for SmartLock events.\n         * @return self, for chaining.\n         */\n        public Builder enableSmartLock(SmartLock.SmartLockListener listener) {\n            mSmartLockListener = listener;\n            return this;\n        }\n\n        /**\n         * Build the {@link Google} instance for use with all enabled services,\n         * @return a Google instance.\n         */\n        public Google build() {\n            Google google = new Google(mActivity);\n            if (mSignInListener != null) {\n                if(mServerClientId != null) {\n                    google.mGacFragment.setServerClientId(mServerClientId);\n                }\n                google.mGacFragment.enableModule(SignIn.class, mSignInListener);\n            }\n\n            if (mSenderId != null) {\n                google.mMessagingFragment.setSenderId(mSenderId);\n                google.mMessagingFragment.setMessagingListener(mMessagingListener);\n            }\n\n            if (mAppInviteListener != null) {\n                google.mGacFragment.enableModule(AppInvites.class, mAppInviteListener);\n            }\n\n            if (mSmartLockListener != null) {\n                google.mGacFragment.enableModule(SmartLock.class, mSmartLockListener);\n            }\n            return google;\n        }\n    }\n\n    private Google(FragmentActivity activity) {\n        // Create the fragments first, then the shims.\n        mGacFragment = FragmentUtils.getOrCreate(activity, TAG_GAC_FRAGMENT, new GacFragment());\n        mMessagingFragment =  FragmentUtils.getOrCreate(activity, TAG_MESSAGING_FRAGMENT, MessagingFragment.newInstance());\n    }\n\n  /**\n     * Get the underlying <code>GoogleApiClient</code> instance to access public methods. If GoogleApiClient is not\n     * properly created, there will be a warning in logcat.\n     * @return the underlying GoogleApiClient instance.\n     */\n    public GoogleApiClient getGoogleApiClient() {\n        GoogleApiClient googleApiClient = mGacFragment.getGoogleApiClient();\n        if (googleApiClient == null) {\n            Log.w(TAG, \"GoogleApiClient is not created, getGoogleApiClient() returning null.\");\n        }\n\n        return googleApiClient;\n    }\n\n    /**\n     * Get the local {@link Messaging} instance to access public methods. If Messaging is not\n     * properly initialized, there will be a warning in logcat.\n     * @return a Messaging instance.\n     */\n    public Messaging getMessaging() {\n        Messaging messaging = mMessagingFragment.getMessaging();\n        if (messaging == null) {\n            Log.w(TAG, \"Messaging is not enabled, getMessaging() returning null.\");\n        }\n\n        return messaging;\n    }\n\n    /**\n     * Get the local {@link SignIn} instance to access public methods. If SignIn is not\n     * properly initialized, there will be a warning in logcat.\n     * @return a SignIn instance.\n     */\n    public SignIn getSignIn() {\n        SignIn signIn = mGacFragment.getModule(SignIn.class);\n        if (signIn == null) {\n            Log.w(TAG, \"SignIn is not enabled, getSignIn() returning null.\");\n        }\n\n        return signIn;\n    }\n\n    /**\n     * Get the local {@link AppInvites} instance to access public methods. If AppInvites is not\n     * properly initialized, there will be a warning in logcat.\n     * @return an AppInvites instance.\n     */\n    public AppInvites getAppInvites() {\n        AppInvites appInvites = mGacFragment.getModule(AppInvites.class);\n        if (appInvites == null) {\n            Log.w(TAG, \"AppInvites is not enabled, getAppInvites() returning null.\");\n        }\n\n        return appInvites;\n    }\n\n    /**\n     * Get the local {@link SmartLock} instance to access public methods. If SmartLock is not\n     * properly initialized, there will be a warning in logcat.\n     * @return a SmartLock instance.\n     */\n    public SmartLock getSmartLock() {\n        SmartLock smartLock = mGacFragment.getModule(SmartLock.class);\n        if (smartLock == null) {\n            Log.w(TAG, \"SmartLock is not enabled, getSmartLock() returning null.\");\n        }\n\n        return smartLock;\n    }\n}\n"
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gac/AppInvites.java",
    "content": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pub.devrel.easygoogle.gac;\n\nimport android.app.Activity;\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.net.Uri;\nimport android.support.v4.content.LocalBroadcastManager;\nimport android.util.Log;\n\nimport com.google.android.gms.appinvite.AppInvite;\nimport com.google.android.gms.appinvite.AppInviteInvitation;\nimport com.google.android.gms.appinvite.AppInviteReferral;\nimport com.google.android.gms.common.api.Api;\nimport com.google.android.gms.common.api.GoogleApiClient;\nimport com.google.android.gms.common.api.Scope;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport pub.devrel.easygoogle.R;\n\n/**\n * Interface to the App Invites API, which can be used to send Email and/or SMS invitations\n * to a user's contacts.  For more information visit: https://developers.google.com/app-invites/\n */\npublic class AppInvites extends GacModule<AppInvites.AppInviteListener> {\n\n    /**\n     * Listener to be notified of asynchronous App Invite events, like invitation receipt\n     * or sending success.\n     */\n    public interface AppInviteListener {\n\n        /**\n         * Called when the application has received an App Invite, either while running or through\n         * a Play Store install.  Before this callback is invoked, the invitation will be marked\n         * as completed through the App Invites API.\n         * @param invitationId the unique ID of the invitation.\n         * @param deepLink the deep link data sent with the invitation,\n         */\n        void onInvitationReceived(String invitationId, String deepLink);\n\n        /**\n         * The user has successfully invited one or more contacts.\n         * @param ids an array of unique IDs, one for each invitation sent by the user. The same\n         *            id will be given to the recepient upon invitation receipt.\n         */\n        void onInvitationsSent(String[] ids);\n\n        /**\n         * Sending invitations failed or the user canceled the operation.\n         */\n        void onInvitationsFailed();\n    }\n\n    private static final String TAG = AppInvites.class.getSimpleName();\n    private static final int RC_INVITE = 9003;\n\n    private BroadcastReceiver mDeepLinkReceiver;\n    private Intent mCachedInvitationIntent;\n\n    protected AppInvites() {\n        // Instantiate local BroadcastReceiver for receiving broadcasts from\n        // AppInvitesReferralReceiver.\n        mDeepLinkReceiver = new BroadcastReceiver() {\n            @Override\n            public void onReceive(Context context, Intent intent) {\n                // First, check if the Intent contains an AppInvite\n                if (AppInviteReferral.hasReferral(intent)) {\n                   processReferralIntent(intent);\n                }\n            }\n        };\n    }\n\n    /**\n     * Launch the UI where the user can choose contacts and send invitations. The UI will be\n     * populated with the arguments of this method, however the user can choose to change the\n     * final invitation message. Success or failure of this call will be reported to\n     * {@link pub.devrel.easygoogle.gac.AppInvites.AppInviteListener}.\n     * @param title the title to display at the top of the invitation window. Cannot be\n     *              overridden by the user.\n     * @param message the message to suggest as the body of the invitation, this will be editable\n     *                by the sending user.\n     * @param deeplink a URI containing any information the receiving party will need to make use\n     *                 of the invitation, such as a coupon code or another identifier.\n     */\n    public void sendInvitation(String title, String message, Uri deeplink) {\n        Intent intent = new AppInviteInvitation.IntentBuilder(title)\n                .setMessage(message)\n                .setDeepLink(deeplink)\n                .build();\n\n        getFragment().startActivityForResult(intent, RC_INVITE);\n    }\n\n    private void processReferralIntent(Intent intent) {\n        // Confirm receipt of the invitation\n        if (getFragment().isConnected()) {\n            updateInvitationStatus(intent);\n        } else {\n            Log.w(TAG, \"GoogleAPIClient not connected, can't update invitation.\");\n            mCachedInvitationIntent = intent;\n        }\n\n\n        // Notify the listener of the received invitation\n        String invitationId = AppInviteReferral.getInvitationId(intent);\n        String deepLink = AppInviteReferral.getDeepLink(intent);\n        getListener().onInvitationReceived(invitationId, deepLink);\n    }\n\n    private void updateInvitationStatus(Intent intent) {\n        // Extract invitation Id\n        String invitationId = AppInviteReferral.getInvitationId(intent);\n\n        // Update invitation installation status and also convert the invitation.\n        GoogleApiClient gac = getFragment().getGoogleApiClient();\n        if (AppInviteReferral.isOpenedFromPlayStore(intent)) {\n            AppInvite.AppInviteApi.updateInvitationOnInstall(gac, invitationId);\n        }\n\n        AppInvite.AppInviteApi.convertInvitation(gac, invitationId);\n    }\n\n    @Override\n    public void onStart() {\n        super.onStart();\n\n        // If app is already installed app and launched with deep link that matches\n        // DeepLinkActivity filter, then the referral info will be in the intent.\n        Intent launchIntent = getFragment().getActivity().getIntent();\n        if (AppInviteReferral.hasReferral(launchIntent)) {\n            processReferralIntent(launchIntent);\n        }\n\n        // Register the local BroadcastReceiver\n        IntentFilter intentFilter = new IntentFilter(\n                getFragment().getString(R.string.action_deep_link));\n        LocalBroadcastManager.getInstance(getFragment().getActivity()).registerReceiver(\n                mDeepLinkReceiver, intentFilter);\n    }\n\n    @Override\n    public void onStop() {\n        super.onStop();\n\n        if (mDeepLinkReceiver != null) {\n            // Unregister the local BroadcastReceiver\n            LocalBroadcastManager.getInstance(getFragment().getActivity()).unregisterReceiver(\n                    mDeepLinkReceiver);\n        }\n    }\n\n    @Override\n    public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {\n        if (requestCode == RC_INVITE) {\n            if (resultCode == Activity.RESULT_OK) {\n                String[] ids = AppInviteInvitation.getInvitationIds(resultCode, data);\n                getListener().onInvitationsSent(ids);\n            } else {\n                getListener().onInvitationsFailed();\n            }\n\n            return true;\n        }\n        return false;\n    }\n\n    @Override\n    public List<Api> getApis() {\n        return Arrays.asList(new Api[]{\n            AppInvite.API\n        });\n    }\n\n    @Override\n    public List<Scope> getScopes() {\n        return new ArrayList<>();\n    }\n\n    @Override\n    public void onConnected() {\n        super.onConnected();\n\n        // If there is an invitation waiting to be updated, do it now\n        if (mCachedInvitationIntent != null) {\n            updateInvitationStatus(mCachedInvitationIntent);\n            mCachedInvitationIntent = null;\n        }\n    }\n}\n"
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gac/AppInvitesReferralReceiver.java",
    "content": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pub.devrel.easygoogle.gac;\n\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.support.v4.content.LocalBroadcastManager;\n\nimport com.google.android.gms.appinvite.AppInviteReferral;\n\nimport pub.devrel.easygoogle.R;\n\n/**\n * Standard implementation of a Broadcast Receiver that is used to detect installation from\n * the Play Store. When an app is installed from the Play Store, the Play Store broadcasts the\n * install information some time <b>during</b> the app's first run. This receiver catches this\n * broadcast and re-broadcasts the same information to the running application.\n */\npublic class AppInvitesReferralReceiver extends BroadcastReceiver {\n\n    public AppInvitesReferralReceiver() {}\n\n    @Override\n    public void onReceive(Context context, Intent intent) {\n        // Create deep link intent with correct action and add play store referral information\n        Intent deepLinkIntent = AppInviteReferral.addPlayStoreReferrerToIntent(intent,\n                new Intent(context.getString(R.string.action_deep_link)));\n\n        // Let any listeners know about the change\n        LocalBroadcastManager.getInstance(context).sendBroadcast(deepLinkIntent);\n    }\n\n}\n"
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gac/GacFragment.java",
    "content": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pub.devrel.easygoogle.gac;\n\nimport android.content.DialogInterface;\nimport android.content.Intent;\nimport android.content.IntentSender;\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.util.Log;\nimport android.widget.Toast;\n\nimport com.google.android.gms.common.ConnectionResult;\nimport com.google.android.gms.common.GooglePlayServicesUtil;\nimport com.google.android.gms.common.api.Api;\nimport com.google.android.gms.common.api.GoogleApiClient;\nimport com.google.android.gms.common.api.Scope;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport pub.devrel.easygoogle.R;\n\n/**\n * Fragment to manage the lifecycle of a GoogleApiClient in a generic way so that it can be\n * used with different combinations of {@link GacModule} at runtime.\n */\npublic class GacFragment extends Fragment implements\n        GoogleApiClient.ConnectionCallbacks,\n        GoogleApiClient.OnConnectionFailedListener {\n\n    private static final String TAG = GacFragment.class.getSimpleName();\n\n    private static final String KEY_IS_RESOLVING = \"is_resolving\";\n    private static final String KEY_SHOULD_RESOLVE = \"should_resolve\";\n\n    private String mServerClientId;\n\n    private GoogleApiClient mGoogleApiClient;\n    private Map<Class<? extends GacModule>, GacModule> mModules = new HashMap<>();\n\n    private boolean mIsResolving = false;\n    private boolean mShouldResolve = false;\n    private int mResolutionCode;\n\n    private void buildGoogleApiClient() {\n        Log.d(TAG, \"buildGoogleApiClient: \" + mModules);\n\n        // Can't build a GoogleApiClient with no APIs\n        if (mModules.size() == 0) {\n            Log.w(TAG, \"No APIs, not building GoogleApiClient.\");\n            return;\n        }\n\n        GoogleApiClient.Builder builder = new GoogleApiClient.Builder(getActivity())\n                .addConnectionCallbacks(this)\n                .addOnConnectionFailedListener(this);\n\n        for (GacModule<?> api : mModules.values()) {\n            for (Api apiObj : api.getApis()) {\n                // Add Api with options, if possible\n                Api.ApiOptions.HasOptions options = api.getOptionsFor(apiObj);\n                if (options != null) {\n                    builder.addApi(apiObj, options);\n                } else {\n                    builder.addApi(apiObj);\n                }\n            }\n\n            for (Scope scopeObj : api.getScopes()) {\n                builder.addScope(scopeObj);\n            }\n        }\n\n        mGoogleApiClient = builder.build();\n    }\n\n    @Override\n    public void onActivityResult(int requestCode, int resultCode, Intent data) {\n        super.onActivityResult(requestCode, resultCode, data);\n        Log.d(TAG, \"onActivityResult:\" + requestCode + \":\" + resultCode + \":\" + data);\n\n        // Give each API a chance to handle it\n        boolean handled = false;\n        for (GacModule module : mModules.values()) {\n            if (module.handleActivityResult(requestCode, resultCode, data)) {\n                handled = true;\n\n                mIsResolving = false;\n                break;\n            }\n        }\n    }\n\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        Log.d(TAG, \"onCreate\");\n\n        if (savedInstanceState != null) {\n            mIsResolving = savedInstanceState.getBoolean(KEY_IS_RESOLVING);\n            mShouldResolve = savedInstanceState.getBoolean(KEY_SHOULD_RESOLVE);\n        }\n    }\n\n    @Override\n    public void onActivityCreated(Bundle savedInstanceState) {\n        super.onActivityCreated(savedInstanceState);\n        Log.d(TAG, \"onActivityCreated\");\n\n        buildGoogleApiClient();\n    }\n\n    @Override\n    public void onStart() {\n        super.onStart();\n        if (mGoogleApiClient != null) {\n            mGoogleApiClient.connect();\n        }\n\n        for (GacModule module : mModules.values()) {\n            module.onStart();\n        }\n    }\n\n    @Override\n    public void onStop() {\n        super.onStop();\n        if (mGoogleApiClient != null) {\n            mGoogleApiClient.disconnect();\n        }\n\n        for (GacModule module : mModules.values()) {\n            module.onStop();\n        }\n    }\n\n    @Override\n    public void onSaveInstanceState(Bundle outState) {\n        super.onSaveInstanceState(outState);\n\n        outState.putBoolean(KEY_IS_RESOLVING, mIsResolving);\n        outState.putBoolean(KEY_SHOULD_RESOLVE, mShouldResolve);\n    }\n\n    @Override\n    public void onConnected(Bundle bundle) {\n        Log.d(TAG, \"onConnected: \" + bundle);\n\n        for (GacModule module : mModules.values()) {\n            module.onConnected();\n        }\n    }\n\n    @Override\n    public void onConnectionSuspended(int i) {\n        Log.d(TAG, \"onConnectionSuspended: \" + i);\n    }\n\n    @Override\n    public void onConnectionFailed(ConnectionResult connectionResult) {\n        Log.d(TAG, \"onConnectionFailed: \" + connectionResult);\n\n        if (connectionResult.hasResolution()) {\n            for (GacModule module : mModules.values()) {\n                module.onResolvableFailure(connectionResult);\n            }\n        }\n\n        if (!mIsResolving && mShouldResolve) {\n            Log.d(TAG, \"onConnectionFailed: resolving with code \" + mResolutionCode);\n            if (connectionResult.hasResolution()) {\n                try {\n                    connectionResult.startResolutionForResult(getActivity(), maskRequestCode(mResolutionCode));\n                    mIsResolving = true;\n                } catch (IntentSender.SendIntentException e) {\n                    Log.e(TAG, \"Could not resolve \" + connectionResult, e);\n                    mIsResolving = false;\n                    mGoogleApiClient.connect();\n                }\n            } else {\n                Log.e(TAG, \"Error: no resolution:\" + connectionResult.getErrorCode());\n                showErrorDialog(connectionResult);\n\n                for (GacModule module : mModules.values()) {\n                    module.onUnresolvableFailure();\n                }\n            }\n        } else {\n            Log.d(TAG, String.format(\"Not resolving (isResolving, shouldResolve) = (%b, %b)\",\n                    mIsResolving, mShouldResolve));\n        }\n    }\n\n    private void showErrorDialog(ConnectionResult connectionResult) {\n        int errorCode = connectionResult.getErrorCode();\n\n        if (GooglePlayServicesUtil.isUserRecoverableError(errorCode)) {\n            // Show the default Google Play services error dialog which may still start an intent\n            // on our behalf if the user can resolve the issue.\n            GooglePlayServicesUtil.getErrorDialog(errorCode, getActivity(), mResolutionCode,\n                    new DialogInterface.OnCancelListener() {\n                        @Override\n                        public void onCancel(DialogInterface dialog) {\n                            mShouldResolve = false;\n                        }\n                    }).show();\n        } else {\n            // No default Google Play Services error, display a message to the user.\n            String errorString = getString(R.string.play_services_error_fmt, errorCode);\n            Toast.makeText(getActivity(), errorString, Toast.LENGTH_SHORT).show();\n            mShouldResolve = false;\n        }\n    }\n\n    /**\n     * This is a nasty hack. When calling startActivityForResult from a Fragment, the requestCode\n     * is tagged with the Fragment index and then the Activity (when super.onActivityResult is\n     * called) forwards the result to the calling fragment.  However this does not happen for\n     * IntentSender, so we need to manually mask our request codes so that they come back to\n     * onActivityResult in the Fragment.\n     *\n     * @param requestCode the original request code to mask.\n     * @return the masked request code to use instead.\n     */\n    protected int maskRequestCode(int requestCode) {\n        if ((requestCode & 0xffff0000) != 0) {\n            throw new IllegalArgumentException(\"Can only use lower 16 bits for requestCode\");\n        }\n\n        int fragmentIndex = getActivity().getSupportFragmentManager().getFragments().indexOf(this);\n        int maskedCode = requestCode + ((fragmentIndex + 1) << 16);\n\n        return maskedCode;\n    }\n\n    /**\n     * Determine if the GoogleApiClient exists and is connected with access to all requested APIs\n     * and Scopes.\n     * @return true if the GoogleApiClient is non-null and connected, false otherwise.\n     */\n    public boolean isConnected() {\n        return (mGoogleApiClient != null && mGoogleApiClient.isConnected());\n    }\n\n    /**\n     * Determine if the Fragment should automatically resolve GoogleApiClient connection failures\n     * when possible. The default behavior is not to resolve connection failures.\n     * @param shouldResolve true if resolutions should be attempted until connection or\n     *                      unresolvable failure, false otherwise.\n     */\n    public void setShouldResolve(boolean shouldResolve) {\n        Log.d(TAG, \"setShouldResolve:\" + shouldResolve);\n        mShouldResolve = shouldResolve;\n    }\n\n    /**\n     * Set the requestCode the Fragment should use when resolving a GoogleApiClient connection\n     * failure. This enables the {@link GacModule} to determine which calls to handleActivityResult\n     * it should handle and/or ignore.\n     * @param resolutionCode the resolution code to use for the next resolution, must be an integer\n     *                       and in the range 0 < x < 2^16.\n     */\n    public void setResolutionCode(int resolutionCode) {\n        mResolutionCode = resolutionCode;\n    }\n\n    public void setServerClientId(String serverClientId) {\n        mServerClientId = serverClientId;\n    }\n\n    public String getServerClientId(){\n        return mServerClientId;\n    }\n\n    public GoogleApiClient getGoogleApiClient() {\n        return mGoogleApiClient;\n    }\n\n    public <T> T getModule(Class<T> clazz) {\n        return (T) mModules.get(clazz);\n    }\n\n    /**\n     * Enable an {@link GacModule} such as {@link SignIn} or {@link SmartLock}.\n     * @param clazz Class object the subclass of GacModule.\n     * @param listener the appropriate listener for the GacModule subclass.\n     * @param <M> subclass of GacModule.\n     * @param <L> type parameter of 'M' class.\n     */\n    public <M extends GacModule<L>, L> void enableModule(Class<M> clazz, L listener) {\n        try {\n            M module = clazz.newInstance();\n            module.setFragment(this);\n            module.setListener(listener);\n            mModules.put(clazz, module);\n        } catch (java.lang.InstantiationException e) {\n            Log.e(TAG, \"enableModule:InstantiationException\", e);\n        } catch (IllegalAccessException e) {\n            Log.e(TAG, \"enableModule:IllegalAccessExeption\", e);\n        }\n\n        // Re-build GoogleApiClient, if necessary\n        if (getActivity() != null) {\n            Log.d(TAG, \"enableModule: rebuilding GoogleApiClient\");\n            buildGoogleApiClient();\n        }\n    }\n}\n"
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gac/GacModule.java",
    "content": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pub.devrel.easygoogle.gac;\n\nimport android.content.Intent;\n\nimport com.google.android.gms.common.ConnectionResult;\nimport com.google.android.gms.common.api.Api;\nimport com.google.android.gms.common.api.Scope;\n\nimport java.util.List;\n\nimport pub.devrel.easygoogle.Google;\n\n/**\n * Simplified version of a GoogleApiClient \"API\".  Declares the scopes and APIs it will need,\n * reacts to onConnected events.  Implementers should offer static methods that accept an\n * {@link Google} as a parameter to represent very common API methods.\n */\npublic abstract class GacModule<T> {\n\n    private GacFragment mFragment;\n    private T mListener;\n\n    public GacModule() {}\n\n    protected GacFragment getFragment() {\n        return mFragment;\n    }\n\n    protected void setFragment(GacFragment fragment) {\n        mFragment = fragment;\n    }\n\n    public abstract boolean handleActivityResult(int requestCode, int resultCode, Intent data);\n\n    public abstract List<Api> getApis();\n\n    public abstract List<Scope> getScopes();\n\n    public Api.ApiOptions.HasOptions getOptionsFor(Api<? extends Api.ApiOptions> api) {\n        return null;\n    }\n\n    public void onConnected() {}\n\n    public void onResolvableFailure(ConnectionResult connectionResult) {}\n\n    public void onUnresolvableFailure() {}\n\n    public void onStart() {}\n\n    public void onStop() {}\n\n    public T getListener() {\n        return mListener;\n    }\n\n    public void setListener(T listener) {\n        mListener = listener;\n    }\n}\n"
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gac/SignIn.java",
    "content": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pub.devrel.easygoogle.gac;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.google.android.gms.auth.api.Auth;\nimport com.google.android.gms.auth.api.signin.GoogleSignInAccount;\nimport com.google.android.gms.auth.api.signin.GoogleSignInOptions;\nimport com.google.android.gms.auth.api.signin.GoogleSignInResult;\nimport com.google.android.gms.common.SignInButton;\nimport com.google.android.gms.common.api.Api;\nimport com.google.android.gms.common.api.OptionalPendingResult;\nimport com.google.android.gms.common.api.ResultCallback;\nimport com.google.android.gms.common.api.Scope;\nimport com.google.android.gms.common.api.Status;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * Interface to the Google Sign In API, which can be used to determine the users identity. The\n * default scopes \"profile\" and \"email\" are used to return basic information about the user\n * (when possible) however this does not grant any authorization to use other Google APIs on\n * behalf of the user. For more information visit:\n * https://developers.google.com/identity/sign-in/android/\n *\n * When registering OAuth clients for Google Sign-In, Web server OAuth client registration is needed\n * with android by requesting an ID token in your GoogleSignInOptions, and supplying the web client\n * ID for your server. For more information visit:\n * https://android-developers.blogspot.com/2016/03/registering-oauth-clients-for-google.html\n */\npublic class SignIn extends GacModule<SignIn.SignInListener> {\n\n    /**\n     * Listener to be notified of asynchronous Sign In events, like sign in or sign out,\n     */\n    public interface SignInListener {\n\n        /**\n         * The user has been signed successfully.\n         * @param account basic information about the signed-in user like name and email.\n         */\n        void onSignedIn(GoogleSignInAccount account);\n\n        /**\n         * The sign in process failed, either due to user cancellation or unresolvable errors. The\n         * app should display a sign-in button and wait for the user to initiate action before\n         * attempting sign-in again.\n         */\n        void onSignInFailed();\n\n        /**\n         * The user has been signed out and access has been revoked.\n         */\n        void onSignedOut();\n    }\n\n    private static final String TAG = SignIn.class.getSimpleName();\n    public static final int RC_SIGN_IN = 9001;\n\n    public SignIn() {}\n\n    @Override\n    public List<Api> getApis() {\n        return Arrays.asList(new Api[]{Auth.GOOGLE_SIGN_IN_API});\n    }\n\n    @Override\n    public Api.ApiOptions.HasOptions getOptionsFor(Api<? extends Api.ApiOptions> api) {\n        if (Auth.GOOGLE_SIGN_IN_API.equals(api)) {\n            GoogleSignInOptions.Builder googleSignInOptions =  new GoogleSignInOptions.Builder(\n                    GoogleSignInOptions.DEFAULT_SIGN_IN)\n                    .requestEmail();\n\n            // Check server client id for OAuth, so GoogleSignInAccount.getIdToken(); is non-null\n            String serverClientId = getFragment().getServerClientId();\n            if(serverClientId != null){\n                googleSignInOptions.requestIdToken(serverClientId);\n            }\n            return googleSignInOptions.build();\n        } else {\n            return super.getOptionsFor(api);\n        }\n    }\n\n    @Override\n    public List<Scope> getScopes() {\n        return Collections.emptyList();\n    }\n\n    @Override\n    public void onStart() {\n        super.onStart();\n\n        // Kick off silent sign-in process\n        Auth.GoogleSignInApi.silentSignIn(getFragment().getGoogleApiClient())\n                .setResultCallback(new ResultCallback<GoogleSignInResult>() {\n                    @Override\n                    public void onResult(GoogleSignInResult googleSignInResult) {\n                        if (googleSignInResult.isSuccess()) {\n                            getListener().onSignedIn(googleSignInResult.getSignInAccount());\n                        } else {\n                            getListener().onSignInFailed();\n                        }\n                    }\n                });\n    }\n\n    @Override\n    public void onStop() {}\n\n    @Override\n    public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {\n        if (requestCode == RC_SIGN_IN) {\n            if (data != null) {\n                GoogleSignInResult gsr = Auth.GoogleSignInApi.getSignInResultFromIntent(data);\n                if (gsr != null && gsr.isSuccess()) {\n                    getListener().onSignedIn(gsr.getSignInAccount());\n                } else {\n                    getListener().onSignInFailed();\n                }\n            }\n\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * Add a {@link SignInButton} to the current Activity/Fragment. When clicked, the button\n     * will automatically make a call to {@link SignIn#signIn()}.\n     * @param context the calling context.\n     * @param container a ViewGroup into which the SignInButton should be placed as the first child.\n     * @return the instantiated SignInButton, which can be customized further.\n     */\n    public SignInButton createSignInButton(Context context, ViewGroup container) {\n\n        // Create SignInButton and configure style\n        SignInButton signInButton = new SignInButton(context);\n        signInButton.setSize(SignInButton.SIZE_STANDARD);\n        signInButton.setColorScheme(SignInButton.COLOR_DARK);\n\n        // Make it start sign-in on click\n        signInButton.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                signIn();\n            }\n        });\n\n        // Add to the layout, return reference to the button for user styling\n        container.addView(signInButton, 0);\n        return signInButton;\n    }\n\n    /**\n     * Get the currently signed in user as a GoogleSignInAccount.\n     * @return a {@link GoogleSignInAccount} or null.\n     */\n    public GoogleSignInAccount getCurrentUser() {\n        OptionalPendingResult<GoogleSignInResult> opr = Auth.GoogleSignInApi.silentSignIn(\n                getFragment().getGoogleApiClient());\n        if (opr.isDone()) {\n            return opr.get().getSignInAccount();\n        } else {\n            return null;\n        }\n    }\n\n    /**\n     * Convenience method to determine if a user is signed in (current user is not null).\n     * @return true if signed in, false otherwise.\n     */\n    public boolean isSignedIn() {\n        return (getCurrentUser() != null);\n    }\n\n    /**\n     * Initiate the sign in process, resolving all possible errors (showing account picker, consent\n     * screen, etc). This operation may result in UI being displayed, results are returned to\n     * the {@link pub.devrel.easygoogle.gac.SignIn.SignInListener}.\n     */\n    public void signIn() {\n        Log.d(TAG, \"signIn\");\n        Intent intent = Auth.GoogleSignInApi.getSignInIntent(getFragment().getGoogleApiClient());\n        getFragment().startActivityForResult(intent, RC_SIGN_IN);\n    }\n\n\n    /**\n     * Initiate the sign out and disconnect process. Results are returned to the\n     * {@link pub.devrel.easygoogle.gac.SignIn.SignInListener}. If the user is not already signed\n     * in or the sign out operation fails, no result will be returned.\n     */\n    public void signOut() {\n        Log.d(TAG, \"signOut\");\n        final GacFragment fragment = getFragment();\n        if (!fragment.isConnected()) {\n            Log.w(TAG, \"Can't sign out, not signed in!\");\n            return;\n        }\n\n        Auth.GoogleSignInApi.revokeAccess(fragment.getGoogleApiClient()).setResultCallback(\n                new ResultCallback<Status>() {\n                    @Override\n                    public void onResult(Status status) {\n                        if (status.isSuccess()) {\n                            getListener().onSignedOut();\n                        } else {\n                            Log.w(TAG, \"Could not sign out: \" + status);\n                        }\n                    }\n                });\n    }\n}\n"
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gac/SmartLock.java",
    "content": "package pub.devrel.easygoogle.gac;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.support.annotation.NonNull;\nimport android.util.Log;\n\nimport com.google.android.gms.auth.api.Auth;\nimport com.google.android.gms.auth.api.credentials.Credential;\nimport com.google.android.gms.auth.api.credentials.CredentialPickerConfig;\nimport com.google.android.gms.auth.api.credentials.CredentialRequest;\nimport com.google.android.gms.auth.api.credentials.CredentialRequestResult;\nimport com.google.android.gms.common.api.Api;\nimport com.google.android.gms.common.api.CommonStatusCodes;\nimport com.google.android.gms.common.api.ResolvingResultCallbacks;\nimport com.google.android.gms.common.api.ResultCallback;\nimport com.google.android.gms.common.api.Scope;\nimport com.google.android.gms.common.api.Status;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * Interface to the SmartLock for Passwords API which can be used to save and retrieve\n * id and password combinations on behalf of the user.  SmartLock allows your users to\n * enter their password once in Android or Chrome and be automatically signed in across\n * all of their devices. For more information, visit:\n * https://developers.google.com/identity/smartlock-passwords/android\n */\npublic class SmartLock extends GacModule<SmartLock.SmartLockListener> {\n\n    /**\n     * Listener to be notified of asynchronous SmartLock events, like loading Credentials.\n     */\n    public interface SmartLockListener {\n\n        /**\n         * A {@link Credential} has been successfully retrieved and the application\n         * should validate the id/password and attempt to sign the user in.\n         * @param credential the Credential chosen by the user.\n         */\n        void onCredentialRetrieved(Credential credential);\n\n        /**\n         * There are Credentials available to load, but the app must display a picker\n         * dialog to allow the user to choose. See {@link #showCredentialPicker()}.\n         */\n        void onShouldShowCredentialPicker();\n\n        /**\n         * There are no Credentials available to load, or the load operation failed or was\n         * canceled by the user.\n         */\n        void onCredentialRetrievalFailed();\n\n    }\n\n    private static final String TAG = \"SmartLock\";\n    private static final int RC_READ = 9016;\n    private static final int RC_SAVE = 9017;\n\n    protected SmartLock() {}\n\n    @Override\n    public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {\n        if (requestCode == RC_READ) {\n            if (resultCode == Activity.RESULT_OK && data != null) {\n                Credential credential = data.getParcelableExtra(Credential.EXTRA_KEY);\n                getListener().onCredentialRetrieved(credential);\n            } else {\n                getListener().onCredentialRetrievalFailed();\n            }\n\n            return true;\n        }\n\n        if (requestCode == RC_SAVE) {\n            if (resultCode == Activity.RESULT_OK) {\n                Log.d(TAG, \"RC_SAVE: Ok\");\n            } else {\n                Log.d(TAG, \"RC_SAVE: Failure\");\n            }\n\n            return true;\n        }\n\n        return false;\n    }\n\n    @Override\n    public List<Api> getApis() {\n        return Arrays.asList(new Api[]{Auth.CREDENTIALS_API});\n    }\n\n    @Override\n    public List<Scope> getScopes() {\n        return Collections.emptyList();\n    }\n\n    /**\n     * Begin the process of retrieving a {@link Credential} for the device user. This can have\n     * a few different results:\n     *   1) If the user has auto sign-in enabled and exactly one previously saved credential,\n     *      {@link SmartLockListener#onCredentialRetrieved(Credential)} will be called and\n     *      you can sign the user in immediately.\n     *   2) If the user has multiple saved credentials or one saved credential and has disabled\n     *      auto sign-in, you will get the callback {@link SmartLockListener#onShouldShowCredentialPicker()}\n     *      at which point you can choose to show the picker dialog to continue.\n     *   3) If the user has no saved credentials or cancels the operation, you will receive the\n     *      {@link SmartLockListener#onCredentialRetrievalFailed()} callback.\n     */\n    public void getCredentials() {\n        CredentialRequest request = buildCredentialRequest();\n\n        Auth.CredentialsApi.request(getFragment().getGoogleApiClient(), request)\n                .setResultCallback(new ResultCallback<CredentialRequestResult>() {\n                    @Override\n                    public void onResult(CredentialRequestResult result) {\n                        if (result.getStatus().isSuccess()) {\n                            // Single credential, auto sign-in\n                            Credential credential = result.getCredential();\n                            getListener().onCredentialRetrieved(credential);\n                        } else if (result.getStatus().hasResolution() &&\n                                result.getStatus().getStatusCode() != CommonStatusCodes.SIGN_IN_REQUIRED) {\n                            // Multiple credentials or auto-sign in disabled.  If the status\n                            // code is SIGN_IN_REQUIRED then it is a hint credential, which we\n                            // do not want at this point.\n                            getListener().onShouldShowCredentialPicker();\n                        } else {\n                            // Could not retrieve credentials\n                            getListener().onCredentialRetrievalFailed();\n                        }\n                    }\n                });\n    }\n\n    /**\n     * Show the dialog allowing the user to choose a Credential. This method shoud only be called\n     * after you receive the {@link SmartLockListener#onShouldShowCredentialPicker()} callback.\n     */\n    public void showCredentialPicker() {\n        CredentialRequest request = buildCredentialRequest();\n        Activity activity = getFragment().getActivity();\n        int maskedCode = getFragment().maskRequestCode(RC_READ);\n\n        Auth.CredentialsApi.request(getFragment().getGoogleApiClient(), request)\n                .setResultCallback(new ResolvingResultCallbacks<CredentialRequestResult>(activity, maskedCode) {\n                    @Override\n                    public void onSuccess(CredentialRequestResult result) {\n                        getListener().onCredentialRetrieved(result.getCredential());\n                    }\n\n                    @Override\n                    public void onUnresolvableFailure(Status status) {\n                        Log.e(TAG, \"showCredentialPicker:onUnresolvableFailure:\" + status);\n                    }\n                });\n    }\n\n    /**\n     * Call this method if the user signs out of your application to disable auto sign-in on the\n     * next run. This prevents users from getting stuck in a loop between sign-out and auto sign-in.\n     */\n    public void signOut() {\n        Auth.CredentialsApi.disableAutoSignIn(getFragment().getGoogleApiClient())\n                .setResultCallback(new ResultCallback<Status>() {\n                    @Override\n                    public void onResult(Status status) {\n                        Log.d(TAG, \"signOut:onResult:\" + status);\n                    }\n                });\n    }\n\n    /**\n     * Save a new Credential to the user's SmartLock. This will be retrievable by all instances\n     * of your application across devices so the user can avoid typing in his/her username and\n     * password in the future.\n     * @param id the id for the Credential, usually a username or email address.\n     * @param password the password for the Credential.\n     */\n    public void save(@NonNull String id, String password) {\n        Credential credential = new Credential.Builder(id)\n                .setPassword(password)\n                .build();\n\n        // TODO(samstern): Should I notify the calling application on the success/failure of this\n        //                 operation. If so, also need to do it in handleActivityResult.\n        Activity activity = getFragment().getActivity();\n        int maskedCode = getFragment().maskRequestCode(RC_SAVE);\n        Auth.CredentialsApi.save(getFragment().getGoogleApiClient(), credential)\n                .setResultCallback(new ResolvingResultCallbacks<Status>(activity, maskedCode) {\n                    @Override\n                    public void onSuccess(Status status) {\n                        Log.d(TAG, \"save:onSuccess:\" + status);\n                    }\n\n                    @Override\n                    public void onUnresolvableFailure(Status status) {\n                        Log.d(TAG, \"save:onUnresolvableFailure:\" + status);\n                    }\n                });\n    }\n\n    /**\n     * Delete a saved Credential object from the SmartLock store.\n     * @param credential the Credential to delete.\n     */\n    public void delete(Credential credential) {\n        Auth.CredentialsApi.delete(getFragment().getGoogleApiClient(), credential)\n                .setResultCallback(new ResultCallback<Status>() {\n                    @Override\n                    public void onResult(Status status) {\n                        Log.d(TAG, \"delete:onResult:\" + status);\n                    }\n                });\n    }\n\n    private CredentialRequest buildCredentialRequest() {\n        return new CredentialRequest.Builder()\n                .setCredentialPickerConfig(new CredentialPickerConfig.Builder()\n                    .setShowAddAccountButton(false)\n                    .setShowCancelButton(true)\n                    .setForNewAccount(false)\n                    .build())\n                .setPasswordLoginSupported(true)\n                .build();\n    }\n}\n"
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gcm/EasyMessageService.java",
    "content": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pub.devrel.easygoogle.gcm;\n\nimport android.app.IntentService;\nimport android.app.PendingIntent;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.support.v4.content.LocalBroadcastManager;\n\nimport pub.devrel.easygoogle.R;\n\n\n/**\n * Base class for background GCM functions in EasyGoogle. Your application should declare\n * a service that extends this class and has the following declaration:\n * <code>\n *              <service\n *                  android:name=\".YourServiceClass\"\n *                  android:enabled=\"true\"\n *                  android:exported=\"false\"\n *                  android:permission=\"pub.devrel.easygoogle.GCM\" />\n * </code>\n */\npublic abstract class EasyMessageService extends IntentService {\n\n    private static final String TAG = \"EasyMessageService\";\n\n    public static final String EXTRA_TOKEN = \"token\";\n    public static final String EXTRA_ACTION = \"action\";\n    public static final String EXTRA_FROM = \"from\";\n\n    public static final String ACTION_REGISTER = \"register\";\n\n    public EasyMessageService() {\n        super(TAG);\n    }\n\n    /**\n     * Called when the applications receives a new GCM message.\n     * @param from the sender's ID.\n     * @param data arbitrary message data (determined by sender).\n     */\n    public abstract void onMessageReceived(String from, Bundle data);\n\n    /**\n     * Called when the application gets a new GCM ID Token.  This should be sent to your server\n     * (if you have one) using an upstream GCM. See {@link #sendRegistrationMessage(String, String)}.\n     * @param token the GCM ID Token.\n     */\n    public abstract void onNewToken(String token);\n\n    @Override\n    public void onHandleIntent(Intent intent) {\n        String action = intent.getAction();\n        if (getString(R.string.action_new_token).equals(action)) {\n            String token = intent.getStringExtra(EXTRA_TOKEN);\n\n            onNewToken(token);\n        }\n\n        if (getString(R.string.action_new_message).equals(action)) {\n            String from = intent.getStringExtra(EXTRA_FROM);\n            Bundle data = intent.getExtras();\n            data.remove(EXTRA_FROM);\n\n            onMessageReceived(from, data);\n        }\n    }\n\n    /**\n     * Forward message data to any active listeners. A listener is a running FragmentActivity that\n     * has enabled messaging on a {@link pub.devrel.easygoogle.Google} object.  If no such Activity\n     * is in the foreground, this is a no-op.\n     * @param from the message sender's ID.\n     * @param data arbitrary message data (determined by sender).\n     * @return true if there is a running listener to receive the message, false otherwise.\n     */\n    public boolean forwardToListener(String from, Bundle data) {\n        Intent msg = new Intent(MessagingFragment.MESSAGE_RECEIVED);\n        msg.putExtra(MessagingFragment.MESSAGE_ARG, data);\n        msg.putExtra(MessagingFragment.MESSAGE_FROM_FIELD, from);\n\n        return LocalBroadcastManager.getInstance(this).sendBroadcast(msg);\n    }\n\n    /**\n     * Create a {@code PendingIntent} from message data that can be used to populate a\n     * {@code Notficiation}.\n     * @param from the message sender's ID.\n     * @param data arbitrary messafe data (determined by sender).\n     * @param target the class to launch when the intent fires. This should be an Activity that is\n     *               host to a {@code Google} object. When the intent fires, the\n     *               {@code onMessageReceived(...)} callback will be fired.\n     * @return a PendingIntent to use with a Notification.\n     */\n    public PendingIntent createMessageIntent(String from, Bundle data,\n                                             Class<? extends Messaging.MessagingListener> target) {\n\n        Intent intent = new Intent(this, target);\n        intent.setAction(MessagingFragment.MESSAGE_RECEIVED);\n        intent.putExtra(MessagingFragment.MESSAGE_FROM_FIELD, from);\n        intent.putExtra(MessagingFragment.MESSAGE_ARG, data);\n        PendingIntent pendingIntent =\n                PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);\n\n        return pendingIntent;\n    }\n\n    /**\n     * Send an upstream GCM message with the following structure:\n     * {\n     *     token: $TOKEN\n     *     action: \"register\"\n     * }\n     * @param senderId your GCM sender Id.\n     * @param token a GCM token retrieved from {@code onNewToken}.\n     */\n    public void sendRegistrationMessage(String senderId, String token) {\n        Bundle bundle = new Bundle();\n        bundle.putString(EXTRA_TOKEN, token);\n        bundle.putString(EXTRA_ACTION, ACTION_REGISTER);\n        MessagingFragment.send(this, senderId, bundle);\n    }\n}\n"
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gcm/GCMUtils.java",
    "content": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pub.devrel.easygoogle.gcm;\n\nimport android.content.ComponentName;\nimport android.content.Context;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\nimport android.content.pm.ServiceInfo;\nimport android.util.Log;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Utilities used by EasyGoogle GCM classes.\n */\npublic class GCMUtils {\n\n    private static final String TAG = \"GCMUtils\";\n    public static final String PREF_KEY_GCM_PERMISSION = \"gcm_permission\";\n\n    /**\n     * Find all services in the AndroidManifest with a given permission. This is useful when\n     * you want to start a particular service but don't have enough information to build\n     * an explicit intent (implicit intents stopped working in Android 5.0).\n     * @param context calling Context.\n     * @param permission the permission the service should have declared in the\n     *                   {@code android:permission} field.\n     * @return a list of {@code ComponentName} objects that can be used to create Intents.\n     */\n    public static List<ComponentName> findServices(Context context, String permission) {\n        PackageManager packageManager = context.getPackageManager();\n        String packageName = context.getPackageName();\n        Log.d(TAG, \"Checking package: \" + packageName);\n\n        // Find all services in the package\n        PackageInfo servicesInfo;\n        try {\n            servicesInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SERVICES);\n        } catch (PackageManager.NameNotFoundException e) {\n            throw new IllegalStateException(\"Could not get services for package \" + packageName);\n        }\n\n        // Get components that have the permission\n        ArrayList<ComponentName> results = new ArrayList<>();\n        ServiceInfo[] services = servicesInfo.services;\n        if (services != null) {\n            for (ServiceInfo service : services) {\n                if (permission.equals(service.permission)) {\n                    ComponentName cn = new ComponentName(packageName, service.name);\n                    results.add(cn);\n                }\n            }\n        }\n\n        return results;\n    }\n}\n"
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gcm/IDListenerService.java",
    "content": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pub.devrel.easygoogle.gcm;\n\nimport android.content.Intent;\nimport android.util.Log;\n\nimport com.google.android.gms.iid.InstanceIDListenerService;\n\n/**\n * Trampoline service to launch {@link IDRegisterService} when a token refresh is needed.\n */\npublic class IDListenerService extends InstanceIDListenerService {\n    private static final String TAG = \"IDListenerService\";\n\n    public IDListenerService() {}\n\n    /**\n     * Called if InstanceID token is updated. This may occur if the security of\n     * the previous token had been compromised. This call is initiated by the\n     * InstanceID provider.\n     */\n    @Override\n    public void onTokenRefresh() {\n        Log.d(TAG, \"onTokenRefresh\");\n\n        // TODO(samstern): I might not even need this class at all, it does not seem to do anything\n        // that I can't do directly in IDRegisterService (or vice versa).\n        Intent intent = new Intent(this, IDRegisterService.class);\n        startService(intent);\n    }\n}\n"
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gcm/IDRegisterService.java",
    "content": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pub.devrel.easygoogle.gcm;\n\nimport android.app.IntentService;\nimport android.content.ComponentName;\nimport android.content.Intent;\nimport android.util.Log;\n\nimport com.google.android.gms.gcm.GoogleCloudMessaging;\nimport com.google.android.gms.iid.InstanceID;\n\nimport java.util.List;\n\nimport pub.devrel.easygoogle.R;\n\npublic class IDRegisterService extends IntentService {\n\n    public static final String TAG = \"IDRegisterService\";\n\n    public IDRegisterService() {\n        super(TAG);\n    }\n\n    @Override\n    protected void onHandleIntent(Intent intent) {\n        String senderId = intent.getStringExtra(MessagingFragment.SENDER_ID_ARG);\n        String gcmPermissionName = intent.getStringExtra(MessagingFragment.GCM_PERMISSION_ARG);\n\n        try {\n            // Initially this call goes out to the network to retrieve the token, subsequent calls\n            // are local.\n            InstanceID instanceID = InstanceID.getInstance(this);\n            String token = instanceID.getToken(senderId, GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);\n            Log.i(TAG, \"GCM Registration Token: \" + token);\n\n            // Find any services that could handle this\n            List<ComponentName> services = GCMUtils.findServices(this, gcmPermissionName);\n\n            // Notify the services of a new token\n            for (ComponentName cn : services) {\n                Log.d(TAG, \"Launching service: \" + cn);\n\n                Intent newTokenIntent = new Intent();\n                newTokenIntent.setComponent(cn);\n                newTokenIntent.setAction(getString(R.string.action_new_token));\n                newTokenIntent.putExtra(EasyMessageService.EXTRA_TOKEN, token);\n\n                startService(newTokenIntent);\n            }\n        } catch (Exception e) {\n            // If an exception happens while fetching the new token or updating our registration data\n            // on a third-party server, this ensures that we'll attempt the update at a later time.\n            Log.e(TAG, \"Failed to complete token refresh\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gcm/MessageListenerService.java",
    "content": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pub.devrel.easygoogle.gcm;\n\nimport android.content.ComponentName;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.preference.PreferenceManager;\nimport android.util.Log;\n\nimport com.google.android.gms.gcm.GcmListenerService;\n\nimport java.util.List;\n\nimport pub.devrel.easygoogle.R;\n\n/**\n * Service to listen for incoming GCM messages.\n */\npublic class MessageListenerService extends GcmListenerService {\n\n    private static final String TAG = \"MessageListenerService\";\n\n    @Override\n    public void onMessageReceived(String from, Bundle data) {\n        Log.d(TAG, \"onMessageReceived:\" + from + \":\" + data);\n\n        String gcmPermissionName = getGcmPermissionName();\n        if (gcmPermissionName == null) {\n            Log.w(TAG, \"App has never run with messaging enabled, not initialized.\");\n            return;\n        }\n\n        // Notify all services with the PERMISSION_EASY_GCM permission about this message\n        List<ComponentName> components = GCMUtils.findServices(this, gcmPermissionName);\n        for (ComponentName cn : components) {\n            Log.d(TAG, \"Launching: \" + cn.toString());\n\n            Intent newMessageIntent = new Intent();\n            newMessageIntent.setComponent(cn);\n            newMessageIntent.setAction(getString(R.string.action_new_message));\n            newMessageIntent.putExtra(EasyMessageService.EXTRA_FROM, from);\n            newMessageIntent.putExtras(data);\n\n            startService(newMessageIntent);\n        }\n    }\n\n    private String getGcmPermissionName() {\n        return PreferenceManager.getDefaultSharedPreferences(this)\n                .getString(GCMUtils.PREF_KEY_GCM_PERMISSION, null);\n    }\n}\n"
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gcm/MessageSenderService.java",
    "content": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pub.devrel.easygoogle.gcm;\n\nimport android.app.IntentService;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.util.Log;\n\nimport com.google.android.gms.gcm.GoogleCloudMessaging;\n\nimport java.io.IOException;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n\n/**\n * An {@link IntentService} for sending GCM upstream messages.\n */\npublic class MessageSenderService extends IntentService {\n\n    public static final String TAG = \"MessageSenderService\";\n\n    private static AtomicInteger sMessageId = new AtomicInteger();\n\n    public MessageSenderService() {\n        super(\"MessageSenderService\");\n    }\n\n    @Override\n    protected void onHandleIntent(Intent intent) {\n        if (intent != null) {\n            String senderEmail = getSenderEmail(intent.getStringExtra(MessagingFragment.SENDER_ID_ARG));\n            Bundle data = intent.getBundleExtra(MessagingFragment.MESSAGE_ARG);\n            String id = Integer.toString(sMessageId.incrementAndGet());\n            Log.d(TAG, \"Sending gcm message:\" + senderEmail + \":\" + data + \":\" + id);\n\n            try {\n                GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);\n                gcm.send(senderEmail, id, data);\n                Log.d(TAG, \"Sent!\");\n            } catch (IOException e) {\n                Log.e(TAG, \"Failed to send GCM Message.\", e);\n            }\n        }\n    }\n\n    public static String getSenderEmail(String senderId) {\n        return senderId + \"@gcm.googleapis.com\";\n    }\n}\n"
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gcm/Messaging.java",
    "content": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pub.devrel.easygoogle.gcm;\n\nimport android.os.Bundle;\n\n\n/**\n * Interface to the Google Cloud Messaging API, which can be used to deliver push notifications\n * and send messages back to a server. For more information visit:\n * https://developers.google.com/cloud-messaging/\n */\npublic class Messaging {\n\n    /**\n     * Listener to be notified of asynchronous GCM events, like message receipt.\n     */\n    public interface MessagingListener {\n\n        /**\n         * Called when a downstream GCM message is received.\n         * @param from the sender's ID.\n         * @param message arbitrary message data included by the sender.\n         */\n        void onMessageReceived(String from, Bundle message);\n    }\n\n    private MessagingFragment mFragment;\n\n    public Messaging(MessagingFragment fragment) {\n        mFragment = fragment;\n    }\n\n    /**\n     * Send an upstream GCM message with some data.\n     * @param bundle arbitrary key-value data to include in the message.\n     */\n    public void send(Bundle bundle) {\n        mFragment.send(bundle);\n    }\n\n    /**\n     * Subscribe to a GCM topic.\n     * @see com.google.android.gms.gcm.GcmPubSub#subscribe(String, String, Bundle)\n     * @param topic topic to subscribe to.\n     */\n    public void subscribeTo(String topic) {\n        mFragment.subscribeTo(topic);\n    }\n\n    /**\n     * Unsubscribe from a GCM topic.\n     * @see com.google.android.gms.gcm.GcmPubSub#unsubscribe(String, String)\n     * @param topic topic to unsubscribe from.\n     */\n    public void unsubscribeFrom(String topic) {\n        mFragment.unsubscribeFrom(topic);\n    }\n\n    // TODO(afshar): remove or use unused methods\n    public void setSenderId(String senderId) {\n        mFragment.setSenderId(senderId);\n        mFragment.register();\n    }\n}\n"
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gcm/MessagingFragment.java",
    "content": "/*\n * Copyright Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pub.devrel.easygoogle.gcm;\n\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.os.Bundle;\nimport android.preference.PreferenceManager;\nimport android.support.v4.app.Fragment;\nimport android.support.v4.content.LocalBroadcastManager;\nimport android.util.Log;\n\nimport pub.devrel.easygoogle.R;\n\n/**\n * Fragment to manage communication with the various GCM services and deliver messages and\n * other events to the host activity automatically.\n */\npublic class MessagingFragment extends Fragment {\n    private static final String TAG = \"MessagingFragment\";\n\n    public static final String SENDER_ID_ARG = \"SENDER_ID\";\n    public static final String GCM_PERMISSION_ARG = \"GCM_PERMISSION\";\n    public static final String MESSAGE_RECEIVED = \"MESSAGE_RECEIVED\";\n    public static final String MESSAGE_FROM_FIELD = \"MESSAGE_FROM\";\n    public static final String MESSAGE_ARG = \"MESSAGE_ARG\";\n    public static final String TOPIC_ARG = \"TOPIC_ARG\";\n\n    public static final String GCM_PERMISSION_RES_NAME = \"gcm_permission\";\n\n    private String mSenderId;\n    private Messaging mMessaging;\n    private Messaging.MessagingListener mListener;\n    private BroadcastReceiver mReceiver;\n\n    public static MessagingFragment newInstance() {\n        return new MessagingFragment();\n    }\n\n    protected static void send(Context context, String senderId, Bundle data) {\n        Intent intent = new Intent(context, MessageSenderService.class);\n        intent.putExtra(SENDER_ID_ARG, senderId);\n        intent.putExtra(MESSAGE_ARG, data);\n        intent.putExtra(GCM_PERMISSION_ARG, getGcmPermissionName(context));\n        context.startService(intent);\n    }\n\n    /**\n     * Get R.string.gcm_permission from the calling app's resources\n     * @param context calling app's context.\n     * @return the resource value for R.string.gcm_permission, throws IllegalArgumentException\n     * if this does not exist.\n     */\n    private static String getGcmPermissionName(Context context) {\n        int gcmPermissionResourceId = context.getResources()\n                .getIdentifier(GCM_PERMISSION_RES_NAME, \"string\", context.getPackageName());\n        if (gcmPermissionResourceId == 0) {\n            throw new IllegalArgumentException(\n                    \"Error: must define \" + GCM_PERMISSION_RES_NAME + \" in strings.xml\");\n        }\n\n        String gcmPermissionName = context.getString(gcmPermissionResourceId);\n        return gcmPermissionName;\n    }\n\n    public MessagingFragment() {\n        super();\n    }\n\n    @Override\n    public void onActivityCreated(Bundle savedInstanceState) {\n        super.onActivityCreated(savedInstanceState);\n\n        // Sender ID\n        if (getArguments() != null) {\n            mSenderId = getArguments().getString(SENDER_ID_ARG);\n        } else {\n            Log.w(TAG, \"getArguments() returned null, not setting senderId\");\n        }\n\n        // Messaging permission store\n        PreferenceManager.getDefaultSharedPreferences(getActivity()).edit()\n                .putString(GCMUtils.PREF_KEY_GCM_PERMISSION, getGcmPermissionName(getActivity()))\n                .commit();\n\n        mMessaging = new Messaging(this);\n        if (mListener != null) {\n            // TODO(afshar): how often do we want to do this?\n            register();\n        }\n    }\n\n    @Override\n    public void onResume() {\n        super.onResume();\n\n        Intent intent = getActivity().getIntent();\n        if (intent != null && MESSAGE_RECEIVED.equals(intent.getAction())) {\n            parseMessageIntent(intent);\n        }\n    }\n\n    @Override\n    public void onStart() {\n        super.onStart();\n\n        // Register the local broadcast receiver\n        registerReceiver();\n    }\n\n    @Override\n    public void onStop() {\n        super.onStop();\n\n        // Unregister the local broadcast receiver\n        LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(mReceiver);\n    }\n\n    private void registerReceiver() {\n        if (mReceiver == null) {\n            mReceiver = new MessageBroadcastReceiver();\n        }\n\n        IntentFilter filter = new IntentFilter();\n        filter.addAction(MESSAGE_RECEIVED);\n        LocalBroadcastManager.getInstance(getActivity()).registerReceiver(mReceiver, filter);\n    }\n\n    public void register() {\n        Intent intent = new Intent(getActivity(), IDRegisterService.class);\n        intent.putExtra(SENDER_ID_ARG, mSenderId);\n        intent.putExtra(GCM_PERMISSION_ARG, getGcmPermissionName(getActivity()));\n        getActivity().startService(intent);\n    }\n\n    public void setMessagingListener(Messaging.MessagingListener messagingListener) {\n        mListener = messagingListener;\n    }\n\n    public void setSenderId(String senderId) {\n        mSenderId = senderId;\n    }\n\n    private void parseMessageIntent(Intent intent) {\n        Bundle data = intent.getBundleExtra(MESSAGE_ARG);\n        String from = intent.getStringExtra(MESSAGE_FROM_FIELD);\n        onMessageReceived(from, data);\n    }\n\n    public void send(Bundle data) {\n        send(getActivity(), mSenderId, data);\n    }\n\n    public void subscribeTo(String topic) {\n        Intent intent = new Intent(getActivity(), PubSubService.class);\n        intent.setAction(getString(R.string.action_subscribe));\n        intent.putExtra(SENDER_ID_ARG, mSenderId);\n        intent.putExtra(TOPIC_ARG, topic);\n        getActivity().startService(intent);\n    }\n\n    public void unsubscribeFrom(String topic) {\n        Intent intent = new Intent(getActivity(), PubSubService.class);\n        intent.setAction(getString(R.string.action_unsubscribe));\n        intent.putExtra(SENDER_ID_ARG, mSenderId);\n        intent.putExtra(TOPIC_ARG, topic);\n        getActivity().startService(intent);\n    }\n\n    private void onMessageReceived(String from, Bundle data) {\n        Log.d(TAG, \"onMessageReceived:\" + from + \":\" + data);\n        mListener.onMessageReceived(from, data);\n    }\n\n    public Messaging getMessaging() {\n        return mMessaging;\n    }\n\n    private class MessageBroadcastReceiver extends BroadcastReceiver {\n\n        @Override\n        public void onReceive(Context context, Intent intent) {\n            switch(intent.getAction()) {\n                case MESSAGE_RECEIVED:\n                    parseMessageIntent(intent);\n                    break;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "easygoogle/src/main/java/pub/devrel/easygoogle/gcm/PubSubService.java",
    "content": "package pub.devrel.easygoogle.gcm;\n\nimport android.app.IntentService;\nimport android.content.Intent;\nimport android.util.Log;\n\nimport com.google.android.gms.gcm.GcmPubSub;\nimport com.google.android.gms.gcm.GoogleCloudMessaging;\nimport com.google.android.gms.iid.InstanceID;\n\nimport java.io.IOException;\n\nimport pub.devrel.easygoogle.R;\n\npublic class PubSubService extends IntentService {\n\n    private static final String TAG = \"PubSubService\";\n\n    private GcmPubSub mGcmPubSub;\n\n    public PubSubService() {\n        super(TAG);\n    }\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        mGcmPubSub = GcmPubSub.getInstance(this);\n    }\n\n    @Override\n    protected void onHandleIntent(Intent intent) {\n        String action = intent.getAction();\n\n        if (getString(R.string.action_subscribe).equals(action)) {\n            String topic = intent.getStringExtra(MessagingFragment.TOPIC_ARG);\n            String senderId = intent.getStringExtra(MessagingFragment.SENDER_ID_ARG);\n\n            Log.d(TAG, \"Subscribing to:\" + topic);\n            try {\n                mGcmPubSub.subscribe(getToken(senderId), topic, null);\n            } catch (IOException e) {\n                Log.e(TAG, \"Failed to subscribe to \" + topic, e);\n            }\n        }\n\n        if (getString(R.string.action_unsubscribe).equals(action)) {\n            String topic = intent.getStringExtra(MessagingFragment.TOPIC_ARG);\n            String senderId = intent.getStringExtra(MessagingFragment.SENDER_ID_ARG);\n\n            Log.d(TAG, \"Unsubscribing from:\" + topic);\n            try {\n                mGcmPubSub.unsubscribe(getToken(senderId), topic);\n            } catch (IOException e) {\n                Log.e(TAG, \"Failed to unsubscribe from \" + topic, e);\n            }\n        }\n    }\n\n    private String getToken(String senderId) throws IOException {\n        InstanceID instanceID = InstanceID.getInstance(this);\n        return instanceID.getToken(senderId, GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);\n    }\n}\n"
  },
  {
    "path": "easygoogle/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">easygoogle</string>\n    <string name=\"play_services_error_fmt\">Google Play Services Error: %i</string>\n    <string name=\"action_deep_link\">pub.devrel.easygoogle.DEEP_LINK_ACTION</string>\n    <string name=\"action_new_token\">pub.devrel.easygoogle.NEW_TOKEN_ACTION</string>\n    <string name=\"action_new_message\">pub.devrel.easygoogle.NEW_MESSAGE_ACTION</string>\n    <string name=\"action_subscribe\">pub.devrel.easygoogle.SUBSCRIBE_ACTION</string>\n    <string name=\"action_unsubscribe\">pub.devrel.easygoogle.UNSUBSCRIBE_ACTION</string>\n</resources>\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Mon Aug 03 15:12:08 PDT 2015\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-2.4-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\n# Default value: -Xmx10248m -XX:MaxPermSize=256m\n# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8\n\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# For Cygwin, ensure paths are in UNIX format before anything is touched.\nif $cygwin ; then\n    [ -n \"$JAVA_HOME\" ] && JAVA_HOME=`cygpath --unix \"$JAVA_HOME\"`\nfi\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >&-\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >&-\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windowz variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\ngoto execute\r\n\r\n:4NT_args\r\n@rem Get arguments from the 4NT Shell from JP Software\r\nset CMD_LINE_ARGS=%$\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "settings.gradle",
    "content": "include ':app', ':easygoogle'\n"
  }
]