├── .gitignore ├── LICENSE-2.0.txt ├── README.md ├── build.gradle ├── gcm_demo ├── .gitignore ├── build.gradle ├── proguard-rules.txt └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── grokkingandroid │ │ └── sampleapp │ │ └── samples │ │ ├── BaseConstants.java │ │ ├── SampleBaseActivity.java │ │ ├── SampleBaseFragment.java │ │ ├── about │ │ └── AboutFragment.java │ │ ├── description │ │ ├── DescriptionActivity.java │ │ ├── DescriptionDelegate.java │ │ └── DescriptionFragment.java │ │ ├── gcm │ │ ├── BaseActivity.java │ │ ├── Constants.java │ │ ├── DemoBaseFragment.java │ │ ├── GCMDemoActivity.java │ │ ├── GcmBroadcastReceiver.java │ │ ├── GcmDemoFragment.java │ │ └── GcmIntentService.java │ │ └── util │ │ └── DateUtils.java │ └── res │ ├── drawable-hdpi-v11 │ └── ic_stat_collections_cloud.png │ ├── drawable-hdpi │ ├── ic_action_add.png │ ├── ic_action_add_inverse.png │ ├── ic_action_document_inverse.png │ ├── ic_action_gamepad.png │ ├── ic_action_info.png │ ├── ic_action_info_inverse.png │ ├── ic_action_reload.png │ ├── ic_action_reload_inverse.png │ ├── ic_action_search.png │ ├── ic_action_search_inverse.png │ ├── ic_launcher.png │ ├── ic_stat_collections_cloud.png │ └── now_bg_shadow.9.png │ ├── drawable-ldpi │ ├── ic_action_add.png │ ├── ic_action_add_inverse.png │ ├── ic_action_document_inverse.png │ ├── ic_action_info.png │ ├── ic_action_info_inverse.png │ ├── ic_action_reload.png │ ├── ic_action_reload_inverse.png │ ├── ic_action_search.png │ ├── ic_action_search_inverse.png │ └── ic_launcher.png │ ├── drawable-mdpi-v11 │ └── ic_stat_collections_cloud.png │ ├── drawable-mdpi │ ├── ic_action_add.png │ ├── ic_action_add_inverse.png │ ├── ic_action_document_inverse.png │ ├── ic_action_gamepad.png │ ├── ic_action_info.png │ ├── ic_action_info_inverse.png │ ├── ic_action_reload.png │ ├── ic_action_reload_inverse.png │ ├── ic_action_search.png │ ├── ic_action_search_inverse.png │ ├── ic_launcher.png │ └── ic_stat_collections_cloud.png │ ├── drawable-xhdpi-v11 │ └── ic_stat_collections_cloud.png │ ├── drawable-xhdpi │ ├── grey_frame_on_white.9.png │ ├── ic_action_add.png │ ├── ic_action_add_inverse.png │ ├── ic_action_document_inverse.png │ ├── ic_action_gamepad.png │ ├── ic_action_info.png │ ├── ic_action_info_inverse.png │ ├── ic_action_reload.png │ ├── ic_action_reload_inverse.png │ ├── ic_action_search.png │ ├── ic_action_search_inverse.png │ ├── ic_launcher.png │ └── ic_stat_collections_cloud.png │ ├── drawable-xxhdpi-v11 │ └── ic_stat_collections_cloud.png │ ├── drawable-xxhdpi │ ├── grey_frame_on_white.9.png │ ├── ic_launcher.png │ └── ic_stat_collections_cloud.png │ ├── drawable-xxxhdpi │ └── ic_launcher.png │ ├── drawable │ └── .gitkeep │ ├── layout │ ├── activity_fragment_container.xml │ ├── activity_fragment_container_long.xml │ ├── activity_fragment_container_wide.xml │ ├── container_content_gcm_demo.xml │ ├── container_content_no_play_services.xml │ ├── fragment_about.xml │ ├── fragment_demo_base.xml │ ├── fragment_demo_description.xml │ ├── include_demo_description.xml │ ├── single_blog_link.xml │ └── single_library_layout.xml │ ├── menu │ └── menu_basesampleactivity.xml │ ├── values-sw600dp │ └── dimens.xml │ ├── values-sw720dp-land │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── gradle.properties └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | #Android generated 2 | bin 3 | gen 4 | lint.xml 5 | lint 6 | 7 | #Eclipse 8 | .project 9 | .classpath 10 | .settings 11 | .checkstyle 12 | 13 | #IntelliJ IDEA 14 | .idea 15 | *.iml 16 | *.ipr 17 | *.iws 18 | classes 19 | gen-external-apklibs 20 | 21 | #gradle 22 | .gradle 23 | local.properties 24 | gradlew 25 | gradlew.bat 26 | gradle/ 27 | build/ 28 | 29 | #vi 30 | *.swp 31 | 32 | #other editors 33 | *.bak 34 | 35 | #Maven 36 | target 37 | release.properties 38 | pom.xml.* 39 | 40 | #Ant 41 | build.xml 42 | ant.properties 43 | local.properties 44 | proguard.cfg 45 | proguard-project.txt 46 | 47 | #Other 48 | .DS_Store 49 | Thumbs.db 50 | tmp 51 | *.tgz 52 | *.lock 53 | *.lck 54 | gcm_strings.xml 55 | -------------------------------------------------------------------------------- /LICENSE-2.0.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Readme 2 | This is a sample project to showcase Google Cloud Messaging using upstream messaging. 3 | The project was originally created for a talk I held at the [Dutch Android User Group](www.dutchaug.org) 4 | meeting in Utrecht on January, 16th 2014. If you ever have the possibility to 5 | visit a meeting of the Dutch AUG, I stringly recommend to do so. It has been a gorgeous evening! 6 | 7 | 8 | ##Libraries used 9 | This demo makes of [EventBus](https://github.com/greenrobot/EventBus), [Google Play Services](https://developer.android.com/google/play-services/index.html), the Support Library, and ActionBarCompat. Thus you have to add those to your project: 10 | 11 | dependencies { 12 | compile 'com.google.android.gms:play-services:4.0.30' 13 | compile 'com.android.support:appcompat-v7:+' 14 | compile 'com.android.support:support-v4:19.0.0' 15 | compile 'de.greenrobot:eventbus:2.2.0' 16 | compile 'de.keyboardsurfer.android.widget:crouton:1.8.2' 17 | } 18 | 19 | 20 | ##Most important classes 21 | All important classes are within the `com.grokkingandroid.sampleapp.samples.gcm` package. 22 | 23 | The initial starting acvtivity is `GcmDemoActivity.java`. Most logic though is contained 24 | in `GcmDemoFragments`. 25 | 26 | The BroadcastRecevier `GcmBroadcastReceiver` just passes the intent through to the 27 | IntentService `GcmIntentService` which handles calls from Google's cloud. The 28 | `GcmIntentService` is also used by the `GcmDemoFragment` for all upstream calls. 29 | 30 | 31 | Not all sources of the demo might have appropriate license annotation. Nevertheless: The license for the code 32 | used in this demo is the Apache Software License. You can find the license in the `LICENSE-2.0.txt` file at the root of the project. 33 | 34 | 35 | ##Credentials 36 | **To run this project you need a GCM-project number.** You can read more about it on the 37 | [Getting Started page of Google's documentation](http://developer.android.com/google/gcm/gs.html). 38 | 39 | I suggest to put the project number into a separate resource file named `gcm_strings.xml`. This file is already excluded 40 | from the repository by the current `.gitignore` file. 41 | 42 | Keep in mind: For using the newer features of Google Cloud Messaging (upstream messaging / user notifications) 43 | **you need to apply first**. See for example the note at the top of the [http://developer.android.com/google/gcm/notifications.html](User Notification page). 44 | You can find a current link there as well. 45 | 46 | ##Relevant Blogposts on [Grokking Android](http://www.grokkingandroid.com/) 47 | Right now I haven't finished the blog post about GCM. It's in the making and should be up pretty soon. 48 | 49 | 50 | ##Warning: Generated codebase 51 | 52 | **Please note:** I have created the core of this sample using a custom generator. 53 | I'm going to publish many samples when writing blog posts and want to keep my work 54 | for those as focused as possible. The generator helps me achieve this even if it 55 | means that some code might seem a bit odd. I always refer to the relevant classes 56 | at the beginning of the README file. 57 | 58 | **tl;dr:** Do not copy without thinking - should go without saying, but it's better to mention it once more! 59 | 60 | 61 | ##Developed by 62 | 63 | *Wolfram Rittmeyer* - You can contact me via: 64 | 65 | * [Grokking Android (my blog)](http://www.grokkingandroid.com) 66 | 67 | * [Google+](https://plus.google.com/+WolframRittmeyer) 68 | 69 | * [Twitter](https://twitter.com/RittmeyerW) 70 | 71 | 72 | ##Thanks 73 | A big thanks to the organizers of the meetup in Utrecht. It was a great evening with good presentations 74 | and interesting conversations in the breaks! 75 | 76 | Also thanks to all my readers and blog commenters. Your feedback helps me to stay on track and to go on 77 | with projects like this. Without my blog (and thus you) I probably woouln't have been invited in the first place :-) 78 | 79 | Special thanks also to the Android crowd on G+. You're an awesome crowd. I have gained many insights 80 | by reading posts there, by following links to blog posts or by discussions on G+! And it was great to meet some of you 81 | in Utrecht. 82 | 83 | And finally: This readme was created using [dillinger.io](http://dillinger.io). Thanks for this service. 84 | 85 | 86 | ##License 87 | Copyright 2014 Wolfram Rittmeyer 88 | 89 | Licensed under the Apache License, Version 2.0 (the "License"); 90 | you may not use this file except in compliance with the License. 91 | You may obtain a copy of the License at 92 | 93 | http://www.apache.org/licenses/LICENSE-2.0 94 | 95 | Unless required by applicable law or agreed to in writing, software 96 | distributed under the License is distributed on an "AS IS" BASIS, 97 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 98 | See the License for the specific language governing permissions and 99 | limitations under the License. 100 | 101 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | mavenCentral() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:0.7.+' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | mavenCentral() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /gcm_demo/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /gcm_demo/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'android' 2 | 3 | android { 4 | compileSdkVersion 19 5 | buildToolsVersion '19.0.0' 6 | 7 | defaultConfig { 8 | minSdkVersion 8 9 | targetSdkVersion 19 10 | versionCode 1 11 | versionName "1.0" 12 | } 13 | buildTypes { 14 | release { 15 | runProguard false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | compile 'com.google.android.gms:play-services:4.0.30' 23 | compile 'com.android.support:appcompat-v7:+' 24 | compile 'com.android.support:support-v4:19.0.0' 25 | compile 'de.greenrobot:eventbus:2.2.0' 26 | compile 'de.keyboardsurfer.android.widget:crouton:1.8.2' 27 | } 28 | 29 | repositories { 30 | mavenCentral() 31 | } -------------------------------------------------------------------------------- /gcm_demo/proguard-rules.txt: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /opt/apps/android-studio/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the ProGuard 5 | # include property in project.properties. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} -------------------------------------------------------------------------------- /gcm_demo/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 21 | 22 | 25 | 26 | 27 | 28 | 29 | 30 | 32 | 33 | 34 | 39 | 42 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 59 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /gcm_demo/src/main/java/com/grokkingandroid/sampleapp/samples/BaseConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Wolfram Rittmeyer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.grokkingandroid.sampleapp.samples; 17 | 18 | public interface BaseConstants { 19 | 20 | // keys for DescriptionDelegate 21 | String KEY_LINK_TARGETS_ID = "key_linkTargetsId"; 22 | String KEY_LINK_TEXTS_ID = "key_linkTextsId"; 23 | String KEY_DESCRIPTION_ID = "key_descriptionId"; 24 | String KEY_HOME_CLASS = "keyHomeClass"; 25 | String KEY_LIB_TITLES_ID = "keyLibTitlesId"; 26 | String KEY_LIB_DESCRIPTIONS_ID = "keyLibDescriptionsId"; 27 | String KEY_APP_TITLE_ID = "keyAppTitleId"; 28 | String KEY_COPYRIGHT_YEAR_ID = "keyCopyrightYearId"; 29 | String KEY_REPOSITORY_LINK_ID = "keyRepositoryLinkId"; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /gcm_demo/src/main/java/com/grokkingandroid/sampleapp/samples/SampleBaseActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Wolfram Rittmeyer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.grokkingandroid.sampleapp.samples; 17 | 18 | import android.content.Intent; 19 | import android.os.Bundle; 20 | import android.support.v4.app.DialogFragment; 21 | import android.support.v4.app.NavUtils; 22 | import android.support.v7.app.ActionBar; 23 | import android.support.v7.app.ActionBarActivity; 24 | import android.view.Menu; 25 | import android.view.MenuItem; 26 | 27 | import com.grokkingandroid.sampleapp.samples.about.AboutFragment; 28 | import com.grokkingandroid.sampleapp.samples.description.DescriptionActivity; 29 | import com.grokkingandroid.sampleapp.samples.gcm.R; 30 | 31 | /** 32 | * The base for all sample activities. Samples have to create a subclass 33 | * which provides the sample specific menu (the very least every sample 34 | * app has to provide is an about and a description menu item). 35 | * 36 | * @author Wolfram Rittmeyer 37 | * 38 | */ 39 | public abstract class SampleBaseActivity extends ActionBarActivity { 40 | 41 | private SampleBaseFragment mDemoFragment; 42 | 43 | @Override 44 | protected void onCreate(Bundle icicle) { 45 | super.onCreate(icicle); 46 | } 47 | 48 | @Override 49 | public boolean onCreateOptionsMenu(Menu menu) { 50 | getMenuInflater().inflate(R.menu.menu_basesampleactivity, menu); 51 | return true; 52 | } 53 | 54 | @Override 55 | public boolean onOptionsItemSelected(MenuItem item) { 56 | int itemId = item.getItemId(); 57 | if (itemId == android.R.id.home) { 58 | ActionBar ab = getSupportActionBar(); 59 | if (ab != null && 60 | (ab.getDisplayOptions() & ActionBar.DISPLAY_SHOW_HOME) == ActionBar.DISPLAY_SHOW_HOME && 61 | (ab.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) == ActionBar.DISPLAY_HOME_AS_UP) { 62 | // This ID represents the Home or Up button. In the case of this 63 | // activity, the Up button is shown. Use NavUtils to allow users 64 | // to navigate up one level in the application structure. For 65 | // more details, see the Navigation pattern on Android Design: 66 | // 67 | // http://developer.android.com/design/patterns/navigation.html#up-vs-back 68 | // 69 | // Keep in mind: The Activity may, or may not 70 | // have an the up navigation enabled 71 | NavUtils.navigateUpTo(this, 72 | new Intent(this, getSampleHomeActivity())); 73 | } 74 | return true; 75 | } else if (itemId == R.id.about) { 76 | showAboutDialog(); 77 | return true; 78 | } else if (itemId == R.id.menu_description) { 79 | showDescription(); 80 | return true; 81 | } 82 | return super.onOptionsItemSelected(item); 83 | } 84 | 85 | private void showAboutDialog() { 86 | int[] resIds = new int[]{ 87 | getLibTitlesArrayId(), 88 | getLibDescriptionsArrayId(), 89 | getAboutTextId(), 90 | getAppTitleResId(), 91 | getCopyrightYearResId(), 92 | getRepositoryLinkResId()}; 93 | DialogFragment newFragment = AboutFragment.newInstance(resIds, getAddDefaultLibs()); 94 | newFragment.show(getSupportFragmentManager(), "dialog"); 95 | } 96 | 97 | private void showDescription() { 98 | Intent descIntent = new Intent(this, DescriptionActivity.class); 99 | descIntent.putExtra(BaseConstants.KEY_DESCRIPTION_ID, getDescriptionTextId()); 100 | descIntent.putExtra(BaseConstants.KEY_LINK_TEXTS_ID, getLinkTextsArrayId()); 101 | descIntent.putExtra(BaseConstants.KEY_LINK_TARGETS_ID, getLinkTextsArrayId()); 102 | descIntent.putExtra(BaseConstants.KEY_HOME_CLASS, getSampleHomeActivity()); 103 | descIntent.putExtra(BaseConstants.KEY_LIB_TITLES_ID, getLibTitlesArrayId()); 104 | descIntent.putExtra(BaseConstants.KEY_LIB_DESCRIPTIONS_ID, getLibDescriptionsArrayId()); 105 | descIntent.putExtra(BaseConstants.KEY_APP_TITLE_ID, getAppTitleResId()); 106 | descIntent.putExtra(BaseConstants.KEY_REPOSITORY_LINK_ID, getRepositoryLinkResId()); 107 | descIntent.putExtra(BaseConstants.KEY_COPYRIGHT_YEAR_ID, getCopyrightYearResId()); 108 | startActivity(descIntent); 109 | } 110 | 111 | protected abstract Class getSampleHomeActivity(); 112 | protected abstract int getDescriptionTextId(); 113 | protected abstract int getLinkTextsArrayId(); 114 | protected abstract int getLinkTargetsArrayId(); 115 | protected abstract int getAppTitleResId(); 116 | protected abstract int getCopyrightYearResId(); 117 | protected abstract int getRepositoryLinkResId(); 118 | /** 119 | * The resource id of the array containing the library titles. 120 | * If the demo needs no additional library, this method must 121 | * return -1. 122 | */ 123 | protected abstract int getLibTitlesArrayId(); 124 | /** 125 | * The resource id of the array containing the library descriptions. 126 | * If the demo needs no additional library, this method must 127 | * return -1. 128 | */ 129 | protected abstract int getLibDescriptionsArrayId(); 130 | 131 | /** 132 | * Returns the resource id of the usual about text. 133 | * If the common text isn't suitable 134 | * for a demo the demo, your specific subclass 135 | * has to override this method. 136 | * Make sure that any other id, that you return, 137 | * is able to deal with four substitution strings. 138 | */ 139 | protected int getAboutTextId() { 140 | return R.string.common_about_text; 141 | } 142 | 143 | /** 144 | * Returns whether the default libs should be included 145 | * in the about fragment. Unless you override this 146 | * method to return false the default libs will be shown. 147 | * 148 | * As of now the default libs are Android's support library 149 | * and the ActionBarCompat library. 150 | */ 151 | protected boolean getAddDefaultLibs() { 152 | return true; 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /gcm_demo/src/main/java/com/grokkingandroid/sampleapp/samples/SampleBaseFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Wolfram Rittmeyer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.grokkingandroid.sampleapp.samples; 17 | 18 | import android.annotation.SuppressLint; 19 | import android.os.Build; 20 | import android.os.Bundle; 21 | import android.support.v4.app.Fragment; 22 | import android.text.Html; 23 | import android.text.Spanned; 24 | import android.text.method.LinkMovementMethod; 25 | import android.view.LayoutInflater; 26 | import android.view.Menu; 27 | import android.view.MenuInflater; 28 | import android.view.View; 29 | import android.view.ViewGroup; 30 | import android.view.ViewGroup.LayoutParams; 31 | import android.widget.LinearLayout; 32 | import android.widget.TextView; 33 | 34 | import com.grokkingandroid.sampleapp.samples.gcm.R; 35 | 36 | public abstract class SampleBaseFragment extends Fragment { 37 | 38 | @Override 39 | public void onCreate(Bundle savedInstanceState) { 40 | super.onCreate(savedInstanceState); 41 | setHasOptionsMenu(true); 42 | } 43 | 44 | @SuppressLint("NewApi") 45 | protected void showLinks(ViewGroup container) { 46 | String[] linkTexts = getLinkTexts(); 47 | String[] linkTargets = getLinkTargets(); 48 | StringBuilder link = null; 49 | for (int i = 0; i < linkTexts.length; i++) { 50 | // TODO: Use an inflater 51 | TextView tv = new TextView(getActivity()); 52 | LinearLayout.LayoutParams params = null; 53 | params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 54 | if (i != linkTexts.length -1) { 55 | int marginBottom = getResources().getDimensionPixelSize(R.dimen.linkSpacing); 56 | params.setMargins(0, 0, 0, marginBottom); 57 | } 58 | tv.setLayoutParams(params); 59 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 60 | tv.setTextIsSelectable(true); 61 | } 62 | tv.setTextAppearance(getActivity(), android.R.style.TextAppearance_Medium); 63 | link = new StringBuilder(100); 64 | link.append(""); 67 | link.append(linkTexts[i]); 68 | link.append(""); 69 | Spanned spannedLink = Html.fromHtml(link.toString()); 70 | tv.setText(spannedLink); 71 | tv.setMovementMethod(LinkMovementMethod.getInstance()); 72 | container.addView(tv); 73 | } 74 | } 75 | 76 | @Override 77 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 78 | Bundle savedInstanceState) { 79 | View rootView = inflater.inflate(R.layout.fragment_demo_base, 80 | container, false); 81 | if (isFragmentWithInlineDescription()) { 82 | TextView description = (TextView) rootView 83 | .findViewById(R.id.demoapp_fragment_description); 84 | Spanned descSpannable= Html.fromHtml(getResources().getString(getDescriptionTextId())); 85 | description.setText(descSpannable); 86 | description.setMovementMethod(LinkMovementMethod.getInstance()); 87 | ViewGroup linkContainer = (ViewGroup)rootView.findViewById(R.id.container_demo_blog_links); 88 | showLinks(linkContainer); 89 | linkContainer.invalidate(); 90 | } 91 | else { 92 | rootView.findViewById(R.id.container_demo_description).setVisibility(View.GONE); 93 | rootView.findViewById(R.id.container_demo_blog_links).setVisibility(View.GONE); 94 | } 95 | 96 | // delegate to subclass for content view 97 | ViewGroup contentContainer = 98 | (ViewGroup)rootView.findViewById(R.id.container_demo_content); 99 | onCreateContentView(inflater, contentContainer, savedInstanceState); 100 | return rootView; 101 | } 102 | 103 | public abstract String[] getLinkTexts(); 104 | 105 | public abstract String[] getLinkTargets(); 106 | 107 | public void onCreateContentView(LayoutInflater inflater, 108 | ViewGroup container, Bundle savedInstanceState) { 109 | //TODO: temporary solution; 110 | // either generate it or do it in a better way 111 | // works for now, though 112 | container.setVisibility(View.GONE); 113 | } 114 | 115 | /** 116 | * Fragments without any visibile content should return true. In this 117 | * case the description will be presented inline. Otherwise 118 | * the description will be displayed after the user has selected 119 | * the corresponding menu item in the action bar. 120 | *

121 | * By default this method returns false. 122 | */ 123 | public boolean isFragmentWithInlineDescription() { 124 | return false; 125 | } 126 | 127 | public abstract int getDescriptionTextId(); 128 | 129 | public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 130 | if (isFragmentWithInlineDescription()) { 131 | menu.findItem(R.id.menu_description).setVisible(false); 132 | } 133 | addFragmentSpecificMenu(menu, inflater); 134 | } 135 | 136 | protected abstract void addFragmentSpecificMenu(Menu menu, MenuInflater inflater); 137 | 138 | } -------------------------------------------------------------------------------- /gcm_demo/src/main/java/com/grokkingandroid/sampleapp/samples/about/AboutFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Wolfram Rittmeyer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.grokkingandroid.sampleapp.samples.about; 17 | 18 | import android.content.res.Resources; 19 | import android.os.Bundle; 20 | import android.support.v4.app.DialogFragment; 21 | import android.text.Html; 22 | import android.text.Spanned; 23 | import android.text.method.LinkMovementMethod; 24 | import android.view.LayoutInflater; 25 | import android.view.View; 26 | import android.view.ViewGroup; 27 | import android.widget.TextView; 28 | 29 | import com.grokkingandroid.sampleapp.samples.gcm.R; 30 | 31 | 32 | public class AboutFragment extends DialogFragment { 33 | 34 | private static final String KEY_RESOURCE_IDS = "keyResIds"; 35 | private static final String KEY_ADD_DEFAULT_LIBS = "keyAddDefaultLibs"; 36 | 37 | @Override 38 | public void onCreate(Bundle savedInstanceState) { 39 | super.onCreate(savedInstanceState); 40 | } 41 | 42 | @Override 43 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 44 | Bundle savedInstanceState) { 45 | Resources res = getResources(); 46 | if (getArguments() == null) { 47 | throw new IllegalStateException("Arguments bundle must not be empty"); 48 | } 49 | int[] resIds = getArguments().getIntArray(KEY_RESOURCE_IDS); 50 | getDialog().setTitle(res.getString(R.string.about)); 51 | View view = inflater.inflate(R.layout.fragment_about, container, false); 52 | ViewGroup libParent = (ViewGroup)view.findViewById(R.id.about_container); 53 | 54 | String[] libTitles = null; 55 | String[] libDescriptions = null; 56 | if (resIds[0] != -1) { 57 | libTitles = res.getStringArray(resIds[0]); 58 | libDescriptions = res.getStringArray(resIds[1]); 59 | } 60 | if (getArguments().getBoolean(KEY_ADD_DEFAULT_LIBS, true)) { 61 | if (resIds[0] == -1) { 62 | libTitles = res.getStringArray(R.array.grokkingandroidsample_about_titles); 63 | libDescriptions = res.getStringArray(R.array.grokkingandroidsample_about_contents); 64 | } 65 | else { 66 | String[] defaultTitles = res.getStringArray(R.array.grokkingandroidsample_about_titles); 67 | String[] target = new String[defaultTitles.length + libTitles.length]; 68 | System.arraycopy(libTitles, 0, target, 0, libTitles.length); 69 | System.arraycopy(defaultTitles, 0, target, libTitles.length, defaultTitles.length); 70 | libTitles = target; 71 | String[] defaultDescriptions = res.getStringArray(R.array.grokkingandroidsample_about_contents); 72 | target = new String[defaultDescriptions.length + libTitles.length]; 73 | System.arraycopy(libDescriptions, 0, target, 0, libDescriptions.length); 74 | System.arraycopy(defaultDescriptions, 0, target, libDescriptions.length, defaultDescriptions.length); 75 | libDescriptions = target; 76 | } 77 | } 78 | String libraryPlural = res.getQuantityString(R.plurals.plural_libraries, libTitles.length); 79 | String appTitle = res.getString(resIds[3]); 80 | String copyrightYear = res.getString(resIds[4]); 81 | String repositoryLink = res.getString(resIds[5]); 82 | String aboutText = res.getString(resIds[2], appTitle, copyrightYear, repositoryLink, libraryPlural); 83 | Spanned spannedAboutText = Html.fromHtml(aboutText); 84 | TextView aboutTv = (TextView)libParent.findViewById(R.id.about_text); 85 | aboutTv.setText(spannedAboutText); 86 | aboutTv.setMovementMethod(LinkMovementMethod.getInstance()); 87 | 88 | if (libTitles != null) { 89 | for (int i = 0; i < libTitles.length; i++) { 90 | View libContainer = inflater.inflate(R.layout.single_library_layout, libParent, false); 91 | TextView currLibTitle = (TextView)libContainer.findViewById(R.id.library_title); 92 | currLibTitle.setText(libTitles[i]); 93 | TextView currLibDesc = (TextView)libContainer.findViewById(R.id.library_text); 94 | Spanned spanned = Html.fromHtml(libDescriptions[i]); 95 | currLibDesc.setText(spanned); 96 | currLibDesc.setMovementMethod(LinkMovementMethod.getInstance()); 97 | libParent.addView(libContainer); 98 | } 99 | } 100 | return view; 101 | } 102 | 103 | public static AboutFragment newInstance(int[] resourceIds, boolean useDefaultLibs) { 104 | AboutFragment f = new AboutFragment(); 105 | Bundle bundle = new Bundle(); 106 | bundle.putIntArray(KEY_RESOURCE_IDS, resourceIds); 107 | bundle.putBoolean(KEY_ADD_DEFAULT_LIBS, useDefaultLibs); 108 | f.setArguments(bundle); 109 | return f; 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /gcm_demo/src/main/java/com/grokkingandroid/sampleapp/samples/description/DescriptionActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Wolfram Rittmeyer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.grokkingandroid.sampleapp.samples.description; 17 | 18 | import android.os.Bundle; 19 | import android.view.Menu; 20 | 21 | import com.grokkingandroid.sampleapp.samples.BaseConstants; 22 | import com.grokkingandroid.sampleapp.samples.SampleBaseActivity; 23 | 24 | import com.grokkingandroid.sampleapp.samples.gcm.R; 25 | 26 | 27 | public class DescriptionActivity extends SampleBaseActivity { 28 | 29 | public static final String EXTRA_DESC_INSTANCE = "extraDescInstance"; 30 | 31 | private int mLibTitlesId; 32 | private int mLibDescriptionsId; 33 | private Class mHomeActivityClass; 34 | private int mAppTitleId; 35 | private int mCopyrightYearId; 36 | private int mRepositoryLinkid; 37 | 38 | @Override 39 | protected void onCreate(Bundle savedInstanceState) { 40 | super.onCreate(savedInstanceState); 41 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 42 | Bundle extras = getIntent().getExtras(); 43 | int descId = extras.getInt(BaseConstants.KEY_DESCRIPTION_ID); 44 | int linkTextsId = extras.getInt(BaseConstants.KEY_LINK_TEXTS_ID); 45 | int linkTargetsId = extras.getInt(BaseConstants.KEY_LINK_TARGETS_ID); 46 | mLibTitlesId = extras.getInt(BaseConstants.KEY_LIB_TITLES_ID); 47 | mLibDescriptionsId = extras.getInt(BaseConstants.KEY_LIB_DESCRIPTIONS_ID); 48 | mHomeActivityClass = (Class)extras.getSerializable(BaseConstants.KEY_HOME_CLASS); 49 | mAppTitleId = extras.getInt(BaseConstants.KEY_APP_TITLE_ID); 50 | mRepositoryLinkid = extras.getInt(BaseConstants.KEY_REPOSITORY_LINK_ID); 51 | mCopyrightYearId = extras.getInt(BaseConstants.KEY_COPYRIGHT_YEAR_ID); 52 | setContentView(R.layout.activity_fragment_container); 53 | if (savedInstanceState == null) { 54 | DescriptionFragment fragment = DescriptionFragment.newInstance(descId, linkTextsId, linkTargetsId); 55 | getSupportFragmentManager() 56 | .beginTransaction() 57 | .add(R.id.demo_fragment_container, fragment) 58 | .commit(); 59 | } 60 | } 61 | 62 | @Override 63 | public boolean onCreateOptionsMenu(Menu menu) { 64 | boolean res = super.onCreateOptionsMenu(menu); 65 | menu.findItem(R.id.menu_description).setVisible(false); 66 | return res; 67 | } 68 | 69 | 70 | 71 | protected Class getSampleHomeActivity() { 72 | return mHomeActivityClass; 73 | } 74 | 75 | @Override 76 | protected int getDescriptionTextId() { 77 | return 0; // irrelevant 78 | } 79 | 80 | @Override 81 | protected int getLinkTextsArrayId() { 82 | return 0; // irrelevant 83 | } 84 | 85 | @Override 86 | protected int getLinkTargetsArrayId() { 87 | return 0; // irrelevant 88 | } 89 | 90 | @Override 91 | protected int getLibTitlesArrayId() { 92 | return mLibTitlesId; 93 | } 94 | 95 | @Override 96 | protected int getLibDescriptionsArrayId() { 97 | return mLibDescriptionsId; 98 | } 99 | 100 | @Override 101 | protected int getAppTitleResId() { 102 | return mAppTitleId; 103 | } 104 | 105 | @Override 106 | protected int getCopyrightYearResId() { 107 | return mCopyrightYearId; 108 | } 109 | 110 | @Override 111 | protected int getRepositoryLinkResId() { 112 | return mRepositoryLinkid; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /gcm_demo/src/main/java/com/grokkingandroid/sampleapp/samples/description/DescriptionDelegate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Wolfram Rittmeyer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.grokkingandroid.sampleapp.samples.description; 17 | 18 | import android.annotation.SuppressLint; 19 | import android.content.res.Resources; 20 | import android.text.Html; 21 | import android.text.Spanned; 22 | import android.text.method.LinkMovementMethod; 23 | import android.view.LayoutInflater; 24 | import android.view.ViewGroup; 25 | import android.widget.TextView; 26 | 27 | import com.grokkingandroid.sampleapp.samples.gcm.R; 28 | 29 | 30 | public class DescriptionDelegate { 31 | 32 | private ViewGroup mLinkContainer; 33 | 34 | @SuppressLint("NewApi") 35 | void showLinks(ViewGroup container, LayoutInflater inflater, 36 | String[] linkTexts, String[] linkTargets) { 37 | StringBuilder link = null; 38 | for (int i = 0; i < linkTexts.length; i++) { 39 | final TextView tv = (TextView)inflater.inflate(R.layout.single_blog_link, container, false);//new TextView(getSherlockActivity()); 40 | link = new StringBuilder(100); 41 | link.append(""); 44 | link.append(linkTexts[i]); 45 | link.append(""); 46 | Spanned spannedLink = Html.fromHtml(link.toString()); 47 | tv.setText(spannedLink); 48 | tv.setMovementMethod(LinkMovementMethod.getInstance()); 49 | container.addView(tv); 50 | } 51 | } 52 | 53 | public void addDescriptionAndLinks(LayoutInflater inflater, ViewGroup rootView, 54 | Resources resources, int descriptionId, 55 | int linkTextsId, int linkTargetsId) { 56 | TextView description = (TextView) rootView 57 | .findViewById(R.id.demoapp_fragment_description); 58 | Spanned descSpannable= Html.fromHtml(resources.getString(descriptionId)); 59 | description.setText(descSpannable); 60 | description.setMovementMethod(LinkMovementMethod.getInstance()); 61 | mLinkContainer = 62 | (ViewGroup)rootView.findViewById(R.id.container_demo_blog_links); 63 | String[] linkTexts = resources.getStringArray(linkTextsId); 64 | String[] linkTargets = resources.getStringArray(linkTargetsId); 65 | showLinks(mLinkContainer, inflater, linkTexts, linkTargets); 66 | } 67 | 68 | void clearFocusOnLinks() { 69 | // since LinkMovementMethod doesn't highlight 70 | // the link correctly, I've used a workaround. 71 | // I set the attribute android:selectAllOnFocus="true" 72 | // for the TextView in the single_blog_link.xml file. 73 | // when returnning from the browser, I have to 74 | // remove the focus. This is done here: 75 | if (mLinkContainer != null) { 76 | int count = mLinkContainer.getChildCount(); 77 | for (int i = 0; i < count; i++) { 78 | mLinkContainer.getChildAt(i).clearFocus(); 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /gcm_demo/src/main/java/com/grokkingandroid/sampleapp/samples/description/DescriptionFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Wolfram Rittmeyer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.grokkingandroid.sampleapp.samples.description; 17 | 18 | import android.os.Bundle; 19 | import android.support.v4.app.Fragment; 20 | import android.view.LayoutInflater; 21 | import android.view.View; 22 | import android.view.ViewGroup; 23 | 24 | import com.grokkingandroid.sampleapp.samples.BaseConstants; 25 | import com.grokkingandroid.sampleapp.samples.gcm.R; 26 | 27 | 28 | public class DescriptionFragment extends Fragment { 29 | 30 | private DescriptionDelegate mDescDelegate; 31 | 32 | public static DescriptionFragment newInstance(int descriptionId, 33 | int linkTextsId, int linkTargetsId) { 34 | DescriptionFragment f = new DescriptionFragment(); 35 | Bundle bundle = new Bundle(); 36 | bundle.putInt(BaseConstants.KEY_LINK_TARGETS_ID, linkTargetsId); 37 | bundle.putInt(BaseConstants.KEY_LINK_TEXTS_ID, linkTextsId); 38 | bundle.putInt(BaseConstants.KEY_DESCRIPTION_ID, descriptionId); 39 | f.setArguments(bundle); 40 | return f; 41 | } 42 | 43 | @Override 44 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 45 | Bundle savedInstanceState) { 46 | ViewGroup rootView = (ViewGroup)inflater.inflate(R.layout.fragment_demo_description, 47 | container, false); 48 | mDescDelegate = new DescriptionDelegate(); 49 | mDescDelegate.addDescriptionAndLinks(inflater, 50 | rootView, 51 | getResources(), 52 | getArguments().getInt(BaseConstants.KEY_DESCRIPTION_ID), 53 | getArguments().getInt(BaseConstants.KEY_LINK_TEXTS_ID), 54 | getArguments().getInt(BaseConstants.KEY_LINK_TARGETS_ID) 55 | ); 56 | return rootView; 57 | } 58 | 59 | @Override 60 | public void onResume() { 61 | super.onResume(); 62 | if (mDescDelegate != null) { 63 | mDescDelegate.clearFocusOnLinks(); 64 | } 65 | } 66 | 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /gcm_demo/src/main/java/com/grokkingandroid/sampleapp/samples/gcm/BaseActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Wolfram Rittmeyer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.grokkingandroid.sampleapp.samples.gcm; 17 | 18 | import com.grokkingandroid.sampleapp.samples.SampleBaseActivity; 19 | 20 | 21 | 22 | public abstract class BaseActivity extends SampleBaseActivity { 23 | 24 | } 25 | -------------------------------------------------------------------------------- /gcm_demo/src/main/java/com/grokkingandroid/sampleapp/samples/gcm/Constants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Wolfram Rittmeyer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.grokkingandroid.sampleapp.samples.gcm; 17 | 18 | public interface Constants { 19 | 20 | String PROJECT_ID = "219125488842"; 21 | 22 | String KEY_STATE = "keyState"; 23 | String KEY_REG_ID = "keyRegId"; 24 | String KEY_MSG_ID = "keyMsgId"; 25 | String KEY_ACCOUNT = "keyAccount"; 26 | String KEY_MESSAGE_TXT = "keyMessageTxt"; 27 | String KEY_EVENT_TYPE = "keyEventbusType"; 28 | 29 | String ACTION = "action"; 30 | // very simply notification handling :-) 31 | int NOTIFICATION_NR = 10; 32 | 33 | long GCM_DEFAULT_TTL = 2 * 24 * 60 * 60 * 1000; // two days 34 | 35 | 36 | String PACKAGE = "com.grokkingandroid.sampleapp.samples.gcm"; 37 | // actions for server interaction 38 | String ACTION_REGISTER = PACKAGE + ".REGISTER"; 39 | String ACTION_UNREGISTER = PACKAGE + ".UNREGISTER"; 40 | String ACTION_ECHO = PACKAGE + ".ECHO"; 41 | 42 | // action for notification intent 43 | String NOTIFICATION_ACTION = PACKAGE + ".NOTIFICATION"; 44 | 45 | String DEFAULT_USER = "fakeUser"; 46 | 47 | enum EventbusMessageType { 48 | REGISTRATION_FAILED, REGISTRATION_SUCCEEDED, UNREGISTRATION_SUCCEEDED, UNREGISTRATION_FAILED; 49 | } 50 | 51 | enum State { 52 | REGISTERED, UNREGISTERED; 53 | } 54 | 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /gcm_demo/src/main/java/com/grokkingandroid/sampleapp/samples/gcm/DemoBaseFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Wolfram Rittmeyer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.grokkingandroid.sampleapp.samples.gcm; 17 | 18 | import com.grokkingandroid.sampleapp.samples.SampleBaseFragment; 19 | 20 | public abstract class DemoBaseFragment extends SampleBaseFragment { 21 | 22 | } 23 | -------------------------------------------------------------------------------- /gcm_demo/src/main/java/com/grokkingandroid/sampleapp/samples/gcm/GCMDemoActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Wolfram Rittmeyer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.grokkingandroid.sampleapp.samples.gcm; 17 | 18 | import de.keyboardsurfer.android.widget.crouton.Crouton; 19 | import de.keyboardsurfer.android.widget.crouton.Style; 20 | import android.app.NotificationManager; 21 | import android.content.Context; 22 | import android.content.Intent; 23 | import android.os.Bundle; 24 | 25 | public class GCMDemoActivity extends BaseActivity { 26 | 27 | private static final String KEY_SPINNER_POS = "keySpinnerPos"; 28 | 29 | private int mCurrSpinnerPos = 0; 30 | 31 | @Override 32 | public void onCreate(Bundle icicle) { 33 | super.onCreate(icicle); 34 | setContentView(R.layout.activity_fragment_container); 35 | 36 | if (icicle == null) { 37 | DemoBaseFragment fragment = GcmDemoFragment.newInstance(); 38 | getSupportFragmentManager().beginTransaction() 39 | .replace(R.id.demo_fragment_container, fragment).commit(); 40 | } 41 | 42 | final Intent intent = getIntent(); 43 | String msg = intent.getStringExtra(Constants.KEY_MESSAGE_TXT); 44 | if (msg != null) { 45 | final NotificationManager manager = 46 | (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 47 | manager.cancel(Constants.NOTIFICATION_NR); 48 | String msgTxt = getString(R.string.msg_received, msg); 49 | Crouton.showText(this, msgTxt, Style.INFO); 50 | } 51 | } 52 | 53 | @Override 54 | protected void onDestroy() { 55 | super.onDestroy(); 56 | // do not forget to clean up, if necessary 57 | } 58 | 59 | @Override 60 | protected void onSaveInstanceState(Bundle outState) { 61 | super.onSaveInstanceState(outState); 62 | outState.putInt(KEY_SPINNER_POS, mCurrSpinnerPos); 63 | } 64 | 65 | @Override 66 | protected Class getSampleHomeActivity() { 67 | return GCMDemoActivity.class; 68 | } 69 | 70 | @Override 71 | protected int getDescriptionTextId() { 72 | return R.string.gcm_demo_demo_desc; 73 | } 74 | 75 | @Override 76 | protected int getLinkTextsArrayId() { 77 | return R.array.gcm_demo_link_texts; 78 | } 79 | 80 | @Override 81 | protected int getLinkTargetsArrayId() { 82 | return R.array.gcm_demo_link_targets; 83 | } 84 | 85 | @Override 86 | protected int getAppTitleResId() { 87 | return R.string.gcm_demo_app_name; 88 | } 89 | 90 | @Override 91 | protected int getCopyrightYearResId() { 92 | return R.string.gcm_demo_copyright; 93 | } 94 | 95 | @Override 96 | protected int getRepositoryLinkResId() { 97 | return R.string.gcm_demo_repo_link; 98 | } 99 | 100 | @Override 101 | protected int getLibTitlesArrayId() { 102 | return -1; 103 | } 104 | 105 | @Override 106 | protected int getLibDescriptionsArrayId() { 107 | return -1; 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /gcm_demo/src/main/java/com/grokkingandroid/sampleapp/samples/gcm/GcmBroadcastReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Wolfram Rittmeyer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.grokkingandroid.sampleapp.samples.gcm; 17 | 18 | import android.app.Activity; 19 | import android.content.ComponentName; 20 | import android.content.Context; 21 | import android.content.Intent; 22 | import android.support.v4.content.WakefulBroadcastReceiver; 23 | 24 | /** 25 | * See http://developer.android.com/google/gcm/client.html 26 | */ 27 | public class GcmBroadcastReceiver extends WakefulBroadcastReceiver { 28 | @Override 29 | public void onReceive(Context context, Intent intent) { 30 | // Explicitly specify that GcmIntentService will handle the intent. 31 | ComponentName comp = new ComponentName(context.getPackageName(), 32 | GcmIntentService.class.getName()); 33 | // Start the service, keeping the device awake while it is launching. 34 | startWakefulService(context, (intent.setComponent(comp))); 35 | setResultCode(Activity.RESULT_OK); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /gcm_demo/src/main/java/com/grokkingandroid/sampleapp/samples/gcm/GcmDemoFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Wolfram Rittmeyer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.grokkingandroid.sampleapp.samples.gcm; 17 | 18 | import android.accounts.AccountManager; 19 | import android.annotation.TargetApi; 20 | import android.app.Activity; 21 | import android.content.Intent; 22 | import android.content.SharedPreferences; 23 | import android.os.Build; 24 | import android.os.Bundle; 25 | import android.preference.PreferenceManager; 26 | import android.text.TextUtils; 27 | import android.util.Log; 28 | import android.view.LayoutInflater; 29 | import android.view.Menu; 30 | import android.view.MenuInflater; 31 | import android.view.View; 32 | import android.view.ViewGroup; 33 | import android.widget.Button; 34 | import android.widget.TextView; 35 | 36 | import com.google.android.gms.common.ConnectionResult; 37 | import com.google.android.gms.common.GooglePlayServicesUtil; 38 | import com.grokkingandroid.sampleapp.samples.gcm.Constants.EventbusMessageType; 39 | import com.grokkingandroid.sampleapp.samples.gcm.Constants.State; 40 | 41 | import de.greenrobot.event.EventBus; 42 | import de.keyboardsurfer.android.widget.crouton.Crouton; 43 | import de.keyboardsurfer.android.widget.crouton.Style; 44 | 45 | public class GcmDemoFragment extends DemoBaseFragment implements 46 | View.OnClickListener { 47 | 48 | private static final int RC_RES_REQUEST = 100; 49 | private static final int RC_SELECT_ACCOUNT = 200; 50 | private Button mBtnRegister; 51 | private Button mBtnMessage; 52 | private TextView mTxtAccountName; 53 | private TextView mTxtRegId; 54 | private TextView mTxtMsg; 55 | private State mState = State.UNREGISTERED; 56 | 57 | public static GcmDemoFragment newInstance() { 58 | return new GcmDemoFragment(); 59 | } 60 | 61 | @Override 62 | public void onCreateContentView(LayoutInflater inflater, 63 | ViewGroup container, Bundle savedInstanceState) { 64 | Log.v("grokking", "onCreateContentView " + container); 65 | if (!checkPlayServices()) { 66 | inflater.inflate( 67 | R.layout.container_content_no_play_services, container, true); 68 | return; 69 | } 70 | // get current state from prefs 71 | mState = getCurrState(); 72 | 73 | View root = inflater.inflate(R.layout.container_content_gcm_demo, container, true); 74 | mBtnRegister = (Button) root.findViewById(R.id.btn_register); 75 | mBtnRegister.setOnClickListener(this); 76 | 77 | mTxtRegId = (TextView)root.findViewById(R.id.txt_reg_id); 78 | mBtnMessage = (Button) root.findViewById(R.id.btn_send_message); 79 | if (mState != State.REGISTERED) { 80 | mBtnMessage.setEnabled(false); 81 | } 82 | else { 83 | if (mState == State.REGISTERED) { 84 | mBtnRegister.setText(R.string.btn_unregister); 85 | mTxtRegId.setText(getRegId()); 86 | } 87 | mBtnMessage.setOnClickListener(this); 88 | } 89 | 90 | mTxtMsg = (TextView)root.findViewById(R.id.txt_message); 91 | mTxtAccountName = (TextView)root.findViewById(R.id.txt_user_account); 92 | Button btnSelectAccount = (Button)root.findViewById(R.id.btn_select_account); 93 | btnSelectAccount.setOnClickListener(this); 94 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 95 | btnSelectAccount.setVisibility(View.GONE); 96 | } 97 | Log.v("grokking", "onCreateContentView"); 98 | } 99 | 100 | private State getCurrState() { 101 | SharedPreferences prefs = PreferenceManager 102 | .getDefaultSharedPreferences(getActivity()); 103 | int stateAsInt = prefs.getInt(Constants.KEY_STATE, 104 | State.UNREGISTERED.ordinal()); 105 | return State.values()[stateAsInt]; 106 | } 107 | 108 | private String getRegId() { 109 | SharedPreferences prefs = PreferenceManager 110 | .getDefaultSharedPreferences(getActivity()); 111 | return prefs.getString(Constants.KEY_REG_ID, null); 112 | } 113 | 114 | @Override 115 | public void onPause() { 116 | super.onPause(); 117 | EventBus.getDefault().unregister(this); 118 | } 119 | 120 | @Override 121 | public void onResume() { 122 | super.onResume(); 123 | EventBus.getDefault().register(this); 124 | } 125 | 126 | @Override 127 | public String[] getLinkTexts() { 128 | return getResources().getStringArray(R.array.gcm_demo_link_texts); 129 | } 130 | 131 | @Override 132 | public String[] getLinkTargets() { 133 | return getResources().getStringArray(R.array.gcm_demo_link_targets); 134 | } 135 | 136 | @Override 137 | public int getDescriptionTextId() { 138 | return R.string.gcm_demo_demo_desc; 139 | } 140 | 141 | @Override 142 | protected void addFragmentSpecificMenu(Menu menu, MenuInflater inflater) { 143 | } 144 | 145 | @Override 146 | public void onClick(View view) { 147 | Log.v("grokkingandroid", "onClick: " + view.getId()); 148 | if (view.getId() == R.id.btn_register) { 149 | mBtnRegister.setEnabled(false); 150 | mBtnMessage.setEnabled(false); 151 | switch (mState) { 152 | case REGISTERED: 153 | unregisterDevice(); 154 | break; 155 | case UNREGISTERED: 156 | registerDevice(); 157 | break; 158 | default: 159 | Log.e("grokkingandroid", "click event on register button while it should be deactiviated"); 160 | break; 161 | } 162 | } else if (view.getId() == R.id.btn_send_message) { 163 | sendMessage(); 164 | } else if (view.getId() == R.id.btn_select_account) { 165 | startAccountSelector(); 166 | } 167 | } 168 | 169 | @TargetApi(value=Build.VERSION_CODES.ICE_CREAM_SANDWICH) 170 | private void startAccountSelector() { 171 | Intent selectAccount = 172 | AccountManager. 173 | newChooseAccountIntent( 174 | null, 175 | null, 176 | new String[]{"com.google"}, 177 | false, 178 | null, 179 | null, 180 | null, 181 | null); 182 | startActivityForResult(selectAccount, RC_SELECT_ACCOUNT); 183 | } 184 | 185 | private void registerDevice() { 186 | Intent regIntent = new Intent(getActivity(), GcmIntentService.class); 187 | if (!TextUtils.isEmpty(mTxtAccountName.getText())) { 188 | regIntent.putExtra(Constants.KEY_ACCOUNT, mTxtAccountName.getText().toString()); 189 | } 190 | else { 191 | regIntent.putExtra(Constants.KEY_ACCOUNT, Constants.DEFAULT_USER); 192 | } 193 | regIntent.setAction(Constants.ACTION_REGISTER); 194 | getActivity().startService(regIntent); 195 | } 196 | 197 | private void unregisterDevice() { 198 | Intent regIntent = new Intent(getActivity(), GcmIntentService.class); 199 | regIntent.setAction(Constants.ACTION_UNREGISTER); 200 | getActivity().startService(regIntent); 201 | } 202 | 203 | private void sendMessage() { 204 | Intent msgIntent = new Intent(getActivity(), GcmIntentService.class); 205 | msgIntent.setAction(Constants.ACTION_ECHO); 206 | String msg; 207 | if (!TextUtils.isEmpty(mTxtMsg.getText())) { 208 | msg = mTxtMsg.getText().toString(); 209 | mTxtMsg.setText(""); 210 | } 211 | else { 212 | msg = getActivity().getString(R.string.no_message); 213 | } 214 | String msgTxt = getString(R.string.msg_sent, msg); 215 | Crouton.showText(getActivity(), msgTxt, Style.INFO); 216 | msgIntent.putExtra(Constants.KEY_MESSAGE_TXT, msg); 217 | getActivity().startService(msgIntent); 218 | } 219 | 220 | /** 221 | * EventBus messages. 222 | */ 223 | public void onEventMainThread(Bundle bundle) { 224 | int typeOrdinal = bundle.getInt(Constants.KEY_EVENT_TYPE); 225 | EventbusMessageType type = EventbusMessageType.values()[typeOrdinal]; 226 | switch (type) { 227 | case REGISTRATION_FAILED: 228 | mBtnRegister.setEnabled(true); 229 | break; 230 | case REGISTRATION_SUCCEEDED: 231 | mBtnRegister.setText(R.string.btn_unregister); 232 | mBtnRegister.setEnabled(true); 233 | mBtnMessage.setEnabled(true); 234 | mTxtRegId.setText(bundle.getString(Constants.KEY_REG_ID)); 235 | mState = State.REGISTERED; 236 | break; 237 | case UNREGISTRATION_FAILED: 238 | mBtnRegister.setEnabled(true); 239 | mBtnMessage.setEnabled(true); 240 | break; 241 | case UNREGISTRATION_SUCCEEDED: 242 | mBtnRegister.setText(R.string.btn_register); 243 | mBtnRegister.setEnabled(true); 244 | mTxtRegId.setText(""); 245 | mState = State.UNREGISTERED; 246 | break; 247 | } 248 | } 249 | 250 | @Override 251 | public void onActivityResult(int requestCode, int resultCode, Intent data) { 252 | super.onActivityResult(requestCode, resultCode, data); 253 | if (requestCode == RC_SELECT_ACCOUNT) { 254 | if (resultCode == Activity.RESULT_OK) { 255 | String accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME); 256 | mTxtAccountName.setText(accountName); 257 | } 258 | else { 259 | Log.v("grokkingandroid", "couldn't select account: " + resultCode); 260 | } 261 | } 262 | } 263 | 264 | // taken more or less verbatim from the documentation: 265 | // 266 | /** 267 | * Check the device to make sure it has the Google Play Services APK. If it 268 | * doesn't, display a dialog that allows users to download the APK from the 269 | * Google Play Store or enable it in the device's system settings. 270 | */ 271 | private boolean checkPlayServices() { 272 | int resultCode = GooglePlayServicesUtil 273 | .isGooglePlayServicesAvailable(getActivity()); 274 | if (resultCode != ConnectionResult.SUCCESS) { 275 | if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) { 276 | GooglePlayServicesUtil.getErrorDialog(resultCode, getActivity(), 277 | RC_RES_REQUEST).show(); 278 | } else { 279 | Log.i("grokkingandroid", "This device is not supported."); 280 | } 281 | return false; 282 | } 283 | return true; 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /gcm_demo/src/main/java/com/grokkingandroid/sampleapp/samples/gcm/GcmIntentService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Wolfram Rittmeyer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.grokkingandroid.sampleapp.samples.gcm; 17 | 18 | import java.io.IOException; 19 | 20 | import android.app.IntentService; 21 | import android.app.NotificationManager; 22 | import android.app.PendingIntent; 23 | import android.content.Context; 24 | import android.content.Intent; 25 | import android.content.SharedPreferences; 26 | import android.content.SharedPreferences.Editor; 27 | import android.os.Bundle; 28 | import android.os.SystemClock; 29 | import android.preference.PreferenceManager; 30 | import android.support.v4.app.NotificationCompat; 31 | import android.text.TextUtils; 32 | import android.util.Log; 33 | 34 | import com.google.android.gms.gcm.GoogleCloudMessaging; 35 | import com.grokkingandroid.sampleapp.samples.gcm.Constants.EventbusMessageType; 36 | import com.grokkingandroid.sampleapp.samples.gcm.Constants.State; 37 | 38 | import de.greenrobot.event.EventBus; 39 | 40 | public class GcmIntentService extends IntentService { 41 | 42 | private NotificationManager mNotificationManager; 43 | private String mSenderId = null; 44 | 45 | public GcmIntentService() { 46 | super("GcmIntentService"); 47 | } 48 | 49 | @Override 50 | protected void onHandleIntent(Intent intent) { 51 | mSenderId = getResources().getString(R.string.gcm_project_id); 52 | GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this); 53 | 54 | // action handling for actions of the activity 55 | String action = intent.getAction(); 56 | Log.v("grokkingandroid", "action: " + action); 57 | if (action.equals(Constants.ACTION_REGISTER)) { 58 | register(gcm, intent); 59 | } else if (action.equals(Constants.ACTION_UNREGISTER)) { 60 | unregister(gcm, intent); 61 | } else if (action.equals(Constants.ACTION_ECHO)) { 62 | sendMessage(gcm, intent); 63 | } 64 | 65 | // handling of stuff as described on 66 | // http://developer.android.com/google/gcm/client.html 67 | try { 68 | Bundle extras = intent.getExtras(); 69 | // The getMessageType() intent parameter must be the intent you 70 | // received in your BroadcastReceiver. 71 | String messageType = gcm.getMessageType(intent); 72 | 73 | if (extras != null && !extras.isEmpty()) { // has effect of 74 | // unparcelling Bundle 75 | /* 76 | * Filter messages based on message type. Since it is likely that 77 | * GCM will be extended in the future with new message types, just 78 | * ignore any message types you're not interested in, or that you 79 | * don't recognize. 80 | */ 81 | if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR 82 | .equals(messageType)) { 83 | sendNotification("Send error: " + extras.toString()); 84 | } else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED 85 | .equals(messageType)) { 86 | sendNotification("Deleted messages on server: " 87 | + extras.toString()); 88 | // If it's a regular GCM message, do some work. 89 | } else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE 90 | .equals(messageType)) { 91 | // Post notification of received message. 92 | String msg = extras.getString("message"); 93 | if (TextUtils.isEmpty(msg)) { 94 | msg = "empty message"; 95 | } 96 | sendNotification(msg); 97 | Log.i("grokkingandroid", "Received: " + extras.toString() 98 | + ", sent: " + msg); 99 | } 100 | } 101 | } finally { 102 | // Release the wake lock provided by the WakefulBroadcastReceiver. 103 | GcmBroadcastReceiver.completeWakefulIntent(intent); 104 | } 105 | } 106 | 107 | private void unregister(GoogleCloudMessaging gcm, Intent intent) { 108 | try { 109 | Log.v("grokingandroid", "about to unregister..."); 110 | gcm.unregister(); 111 | Log.v("grokkingandroid", "device unregistered"); 112 | 113 | // Persist the regID - no need to register again. 114 | removeRegistrationId(); 115 | Bundle bundle = new Bundle(); 116 | bundle.putInt(Constants.KEY_EVENT_TYPE, 117 | EventbusMessageType.UNREGISTRATION_SUCCEEDED.ordinal()); 118 | EventBus.getDefault().post(bundle); 119 | } catch (IOException e) { 120 | // If there is an error, don't just keep trying to register. 121 | // Require the user to click a button again, or perform 122 | // exponential back-off. 123 | 124 | // I simply notify the user: 125 | Bundle bundle = new Bundle(); 126 | bundle.putInt(Constants.KEY_EVENT_TYPE, 127 | EventbusMessageType.UNREGISTRATION_FAILED.ordinal()); 128 | EventBus.getDefault().post(bundle); 129 | Log.e("grokkingandroid", "Unregistration failed", e); 130 | } 131 | } 132 | 133 | private void register(GoogleCloudMessaging gcm, Intent intent) { 134 | try { 135 | Log.v("grokingandroid", "about to register..."); 136 | String regid = gcm.register(mSenderId); 137 | Log.v("grokkingandroid", "device registered: " + regid); 138 | 139 | String account = intent.getStringExtra(Constants.KEY_ACCOUNT); 140 | sendRegistrationIdToBackend(gcm, regid, account); 141 | 142 | // Persist the regID - no need to register again. 143 | storeRegistrationId(regid); 144 | Bundle bundle = new Bundle(); 145 | bundle.putInt(Constants.KEY_EVENT_TYPE, 146 | EventbusMessageType.REGISTRATION_SUCCEEDED.ordinal()); 147 | bundle.putString(Constants.KEY_REG_ID, regid); 148 | EventBus.getDefault().post(bundle); 149 | } catch (IOException e) { 150 | // If there is an error, don't just keep trying to register. 151 | // Require the user to click a button again, or perform 152 | // exponential back-off. 153 | 154 | // I simply notify the user: 155 | Bundle bundle = new Bundle(); 156 | bundle.putInt(Constants.KEY_EVENT_TYPE, 157 | EventbusMessageType.REGISTRATION_FAILED.ordinal()); 158 | EventBus.getDefault().post(bundle); 159 | Log.e("grokkingandroid", "Registration failed", e); 160 | } 161 | } 162 | 163 | private void storeRegistrationId(String regId) { 164 | final SharedPreferences prefs = PreferenceManager 165 | .getDefaultSharedPreferences(this); 166 | Log.i("grokkingandroid", "Saving regId to prefs: " + regId); 167 | SharedPreferences.Editor editor = prefs.edit(); 168 | editor.putString(Constants.KEY_REG_ID, regId); 169 | editor.putInt(Constants.KEY_STATE, State.REGISTERED.ordinal()); 170 | editor.commit(); 171 | } 172 | 173 | private void removeRegistrationId() { 174 | final SharedPreferences prefs = PreferenceManager 175 | .getDefaultSharedPreferences(this); 176 | Log.i("grokkingandroid", "Removing regId from prefs"); 177 | SharedPreferences.Editor editor = prefs.edit(); 178 | editor.remove(Constants.KEY_REG_ID); 179 | editor.putInt(Constants.KEY_STATE, State.UNREGISTERED.ordinal()); 180 | editor.commit(); 181 | } 182 | 183 | private void sendRegistrationIdToBackend(GoogleCloudMessaging gcm, 184 | String regId, String account) { 185 | try { 186 | Bundle data = new Bundle(); 187 | // the name is used for keeping track of user notifications 188 | // if you use the same name everywhere, the notifications will 189 | // be cancelled 190 | data.putString("account", account); 191 | data.putString("action", Constants.ACTION_REGISTER); 192 | String msgId = Integer.toString(getNextMsgId()); 193 | gcm.send(mSenderId + "@gcm.googleapis.com", msgId, 194 | Constants.GCM_DEFAULT_TTL, data); 195 | Log.v("grokkingandroid", "regId sent: " + regId); 196 | } catch (IOException e) { 197 | Log.e("grokkingandroid", 198 | "IOException while sending registration to backend...", e); 199 | } 200 | } 201 | 202 | private void sendMessage(GoogleCloudMessaging gcm, Intent intent) { 203 | try { 204 | String msg = intent.getStringExtra(Constants.KEY_MESSAGE_TXT); 205 | Bundle data = new Bundle(); 206 | data.putString(Constants.ACTION, Constants.ACTION_ECHO); 207 | data.putString("message", msg); 208 | String id = Integer.toString(getNextMsgId()); 209 | gcm.send(mSenderId + "@gcm.googleapis.com", id, data); 210 | Log.v("grokkingandroid", "sent message: " + msg); 211 | } catch (IOException e) { 212 | Log.e("grokkingandroid", "Error while sending a message", e); 213 | } 214 | } 215 | 216 | // Put the message into a notification and post it. 217 | // This is just one simple example of what you might choose to do with 218 | // a GCM message. 219 | private void sendNotification(String msg) { 220 | mNotificationManager = (NotificationManager) this 221 | .getSystemService(Context.NOTIFICATION_SERVICE); 222 | 223 | Intent notificationIntent = new Intent(this, GCMDemoActivity.class); 224 | notificationIntent.setAction(Constants.NOTIFICATION_ACTION); 225 | notificationIntent.putExtra(Constants.KEY_MESSAGE_TXT, msg); 226 | notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 227 | notificationIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 228 | PendingIntent contentIntent = PendingIntent.getActivity(this, 0, 229 | notificationIntent, 0); 230 | 231 | NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this) 232 | .setSmallIcon(R.drawable.ic_stat_collections_cloud) 233 | .setContentTitle("GCM Notification") 234 | .setStyle(new NotificationCompat.BigTextStyle().bigText(msg)) 235 | .setContentText(msg); 236 | 237 | mBuilder.setContentIntent(contentIntent); 238 | mNotificationManager.notify(Constants.NOTIFICATION_NR, mBuilder.build()); 239 | } 240 | 241 | private int getNextMsgId() { 242 | SharedPreferences prefs = getPrefs(); 243 | int id = prefs.getInt(Constants.KEY_MSG_ID, 0); 244 | Editor editor = prefs.edit(); 245 | editor.putInt(Constants.KEY_MSG_ID, ++id); 246 | editor.commit(); 247 | return id; 248 | } 249 | 250 | private SharedPreferences getPrefs() { 251 | return PreferenceManager.getDefaultSharedPreferences(this); 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /gcm_demo/src/main/java/com/grokkingandroid/sampleapp/samples/util/DateUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Wolfram Rittmeyer 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.grokkingandroid.sampleapp.samples.util; 17 | 18 | import java.util.Date; 19 | 20 | public class DateUtils { 21 | 22 | public static String formatDateTime(String formatStr, Date date) { 23 | if (date == null) { 24 | return null; 25 | } 26 | return android.text.format.DateFormat.format(formatStr, date).toString(); 27 | } 28 | 29 | public static String formatDateTimeForIO(Date date) { 30 | return formatDateTime("yyyy-MM-dd_hh-mm-ss", date); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-hdpi-v11/ic_stat_collections_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-hdpi-v11/ic_stat_collections_cloud.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-hdpi/ic_action_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-hdpi/ic_action_add.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-hdpi/ic_action_add_inverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-hdpi/ic_action_add_inverse.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-hdpi/ic_action_document_inverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-hdpi/ic_action_document_inverse.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-hdpi/ic_action_gamepad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-hdpi/ic_action_gamepad.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-hdpi/ic_action_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-hdpi/ic_action_info.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-hdpi/ic_action_info_inverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-hdpi/ic_action_info_inverse.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-hdpi/ic_action_reload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-hdpi/ic_action_reload.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-hdpi/ic_action_reload_inverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-hdpi/ic_action_reload_inverse.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-hdpi/ic_action_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-hdpi/ic_action_search.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-hdpi/ic_action_search_inverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-hdpi/ic_action_search_inverse.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-hdpi/ic_stat_collections_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-hdpi/ic_stat_collections_cloud.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-hdpi/now_bg_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-hdpi/now_bg_shadow.9.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-ldpi/ic_action_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-ldpi/ic_action_add.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-ldpi/ic_action_add_inverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-ldpi/ic_action_add_inverse.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-ldpi/ic_action_document_inverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-ldpi/ic_action_document_inverse.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-ldpi/ic_action_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-ldpi/ic_action_info.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-ldpi/ic_action_info_inverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-ldpi/ic_action_info_inverse.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-ldpi/ic_action_reload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-ldpi/ic_action_reload.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-ldpi/ic_action_reload_inverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-ldpi/ic_action_reload_inverse.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-ldpi/ic_action_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-ldpi/ic_action_search.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-ldpi/ic_action_search_inverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-ldpi/ic_action_search_inverse.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-ldpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-ldpi/ic_launcher.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-mdpi-v11/ic_stat_collections_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-mdpi-v11/ic_stat_collections_cloud.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-mdpi/ic_action_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-mdpi/ic_action_add.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-mdpi/ic_action_add_inverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-mdpi/ic_action_add_inverse.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-mdpi/ic_action_document_inverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-mdpi/ic_action_document_inverse.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-mdpi/ic_action_gamepad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-mdpi/ic_action_gamepad.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-mdpi/ic_action_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-mdpi/ic_action_info.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-mdpi/ic_action_info_inverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-mdpi/ic_action_info_inverse.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-mdpi/ic_action_reload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-mdpi/ic_action_reload.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-mdpi/ic_action_reload_inverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-mdpi/ic_action_reload_inverse.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-mdpi/ic_action_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-mdpi/ic_action_search.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-mdpi/ic_action_search_inverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-mdpi/ic_action_search_inverse.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-mdpi/ic_stat_collections_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-mdpi/ic_stat_collections_cloud.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-xhdpi-v11/ic_stat_collections_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-xhdpi-v11/ic_stat_collections_cloud.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-xhdpi/grey_frame_on_white.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-xhdpi/grey_frame_on_white.9.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-xhdpi/ic_action_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-xhdpi/ic_action_add.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-xhdpi/ic_action_add_inverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-xhdpi/ic_action_add_inverse.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-xhdpi/ic_action_document_inverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-xhdpi/ic_action_document_inverse.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-xhdpi/ic_action_gamepad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-xhdpi/ic_action_gamepad.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-xhdpi/ic_action_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-xhdpi/ic_action_info.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-xhdpi/ic_action_info_inverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-xhdpi/ic_action_info_inverse.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-xhdpi/ic_action_reload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-xhdpi/ic_action_reload.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-xhdpi/ic_action_reload_inverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-xhdpi/ic_action_reload_inverse.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-xhdpi/ic_action_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-xhdpi/ic_action_search.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-xhdpi/ic_action_search_inverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-xhdpi/ic_action_search_inverse.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-xhdpi/ic_stat_collections_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-xhdpi/ic_stat_collections_cloud.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-xxhdpi-v11/ic_stat_collections_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-xxhdpi-v11/ic_stat_collections_cloud.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-xxhdpi/grey_frame_on_white.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-xxhdpi/grey_frame_on_white.9.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-xxhdpi/ic_stat_collections_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-xxhdpi/ic_stat_collections_cloud.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /gcm_demo/src/main/res/drawable/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/writtmeyer/gcm_sample/92ef3871672c219d2cbd2694fdbf3922de039c68/gcm_demo/src/main/res/drawable/.gitkeep -------------------------------------------------------------------------------- /gcm_demo/src/main/res/layout/activity_fragment_container.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 21 | 22 | -------------------------------------------------------------------------------- /gcm_demo/src/main/res/layout/activity_fragment_container_long.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 21 | 22 | -------------------------------------------------------------------------------- /gcm_demo/src/main/res/layout/activity_fragment_container_wide.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 21 | 22 | -------------------------------------------------------------------------------- /gcm_demo/src/main/res/layout/container_content_gcm_demo.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 21 | 22 | 31 | 32 | 37 | 38 | 44 | 45 |