├── .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 |
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 |