├── .gitignore
├── LICENSE.md
├── Readme.txt
├── android-protips-location-clean.iml
├── app
├── app.iml
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── radioactiveyak
│ │ └── location_best_practices
│ │ ├── PlacesApplication.java
│ │ ├── PlacesBackupAgent.java
│ │ ├── PlacesConstants.java
│ │ ├── UI
│ │ ├── PlaceActivity.java
│ │ └── fragments
│ │ │ ├── CheckinFragment.java
│ │ │ ├── PlaceDetailFragment.java
│ │ │ └── PlaceListFragment.java
│ │ ├── content_providers
│ │ ├── PlaceDetailsContentProvider.java
│ │ ├── PlacesContentProvider.java
│ │ └── QueuedCheckinsContentProvider.java
│ │ ├── receivers
│ │ ├── BootReceiver.java
│ │ ├── ConnectivityChangedReceiver.java
│ │ ├── LocationChangedReceiver.java
│ │ ├── NewCheckinReceiver.java
│ │ ├── PassiveLocationChangedReceiver.java
│ │ └── PowerStateChangedReceiver.java
│ │ ├── services
│ │ ├── CheckinNotificationService.java
│ │ ├── EclairPlacesUpdateService.java
│ │ ├── PlaceCheckinService.java
│ │ ├── PlaceDetailsUpdateService.java
│ │ └── PlacesUpdateService.java
│ │ └── utils
│ │ ├── FroyoLocationUpdateRequester.java
│ │ ├── FroyoSharedPreferenceSaver.java
│ │ ├── GingerbreadLastLocationFinder.java
│ │ ├── GingerbreadLocationUpdateRequester.java
│ │ ├── GingerbreadSharedPreferenceSaver.java
│ │ ├── HoneycombStrictMode.java
│ │ ├── LegacyLastLocationFinder.java
│ │ ├── LegacyLocationUpdateRequester.java
│ │ ├── LegacySharedPreferenceSaver.java
│ │ ├── LegacyStrictMode.java
│ │ ├── PlatformSpecificImplementationFactory.java
│ │ └── base
│ │ ├── ILastLocationFinder.java
│ │ ├── IStrictMode.java
│ │ ├── LocationUpdateRequester.java
│ │ └── SharedPreferenceSaver.java
│ └── res
│ ├── anim-v11
│ ├── slide_in_left.xml
│ └── slide_out_right.xml
│ ├── drawable-hdpi
│ ├── icon.png
│ ├── powered_by_google_on_black.png
│ └── powered_by_google_on_white.png
│ ├── drawable-ldpi
│ ├── icon.png
│ ├── powered_by_google_on_black.png
│ └── powered_by_google_on_white.png
│ ├── drawable-mdpi
│ ├── icon.png
│ ├── powered_by_google_on_black.png
│ └── powered_by_google_on_white.png
│ ├── layout-port
│ └── main.xml
│ ├── layout-xlarge-port
│ └── main.xml
│ ├── layout
│ ├── checkin_box.xml
│ ├── main.xml
│ └── place_detail.xml
│ ├── menu
│ └── main_menu.xml
│ ├── values-v8
│ └── booleans.xml
│ └── values
│ ├── booleans.xml
│ └── strings.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ └── gradle-wrapper.properties
├── local.properties
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### Android template
3 | # Built application files
4 | *.apk
5 | *.ap_
6 |
7 | # Files for the ART/Dalvik VM
8 | *.dex
9 |
10 | # Java class files
11 | *.class
12 |
13 | # Generated files
14 | bin/
15 | gen/
16 | out/
17 |
18 | # Gradle files
19 | .gradle/
20 | build/
21 |
22 | # Local configuration file (sdk path, etc)
23 | local.properties
24 |
25 | # Proguard folder generated by Eclipse
26 | proguard/
27 |
28 | # Log Files
29 | *.log
30 |
31 | # Android Studio Navigation editor temp files
32 | .navigation/
33 |
34 | # Android Studio captures folder
35 | captures/
36 |
37 | # Intellij
38 | *.iml
39 | .idea/workspace.xml
40 |
41 | # Keystore files
42 | *.jks
43 |
44 | .gitignore
45 | .idea/
46 | app/build/
47 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/Readme.txt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | This project is using outdated Android APIs and is in the process of being updated.
18 |
19 | ** QUICK START GUIDE **
20 |
21 | 1) Make sure you've downloaded and installed the Android Compatibility Library:
22 | http://developer.android.com/sdk/compatibility-library.html
23 | 2) Obtain a Google Places API key from:
24 | http://code.google.com/apis/maps/documentation/places/#Limits
25 | And assign it to the MY_API_KEY static constant in PlacesConstants.java
26 | 3) Obtain a Backup Manager API key from:
27 | http://code.google.com/android/backup/signup.html
28 | And assign it to the backup_manager_key value in res/values/strings.xml
29 |
30 | ** About this Project **
31 |
32 | Project Home Page:
33 | https://github.com/retomeier/android-protips-location/
34 |
35 | Maintained by:
36 | Reto Meier
37 | http://www.twitter.com/retomeier
38 | http://blog.radioactiveyak.com
39 |
40 | Uses the Google Places API to mimic the core functionality of apps that use
41 | your current location to provide a list of nearby points of interest, allow you
42 | to drill down into the details, and then checkin / rate/ review.
43 |
44 | It is an open-source reference implementation of a location-based app that
45 | incorporates several tips, tricks, best practices, and cheats for creating
46 | high quality apps.
47 |
48 | Particular attention has been paid to reducing the time between opening an app
49 | and seeing an up-to-date list of nearby venues and providing a reasonable level
50 | of offline support.
51 |
52 | The code implements all of the location best-practices for reducing latency and
53 | battery consumption as detailed in my Google I/O 2011 session,
54 | Android Protips: Advanced Topics for Expert Android Developers:
55 | http://www.google.com/events/io/2011/sessions/android-protips-advanced-topics-for-expert-android-app-developers.html
56 |
--------------------------------------------------------------------------------
/android-protips-location-clean.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | generateDebugSources
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | android {
3 | compileSdkVersion 23
4 | buildToolsVersion "24.0.0"
5 |
6 | useLibrary 'org.apache.http.legacy'
7 |
8 | defaultConfig {
9 | applicationId "com.radioactiveyak.location_best_practices"
10 | minSdkVersion 4
11 | targetSdkVersion 11
12 | }
13 |
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile 'com.android.support:support-v4:24.0.0'
24 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/PlacesApplication.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices;
18 |
19 | import android.app.Application;
20 |
21 | import com.radioactiveyak.location_best_practices.utils.PlatformSpecificImplementationFactory;
22 | import com.radioactiveyak.location_best_practices.utils.base.IStrictMode;
23 |
24 | public class PlacesApplication extends Application {
25 |
26 | // TODO Please Insert your Google Places API into MY_API_KEY in PlacesConstants.java
27 | // TODO Insert your Backup Manager API into res/values/strings.xml : backup_manager_key
28 |
29 | @Override
30 | public final void onCreate() {
31 | super.onCreate();
32 |
33 | if (PlacesConstants.DEVELOPER_MODE) {
34 | IStrictMode strictMode = PlatformSpecificImplementationFactory.getStrictMode();
35 | if (strictMode != null)
36 | strictMode.enableStrictMode();
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/PlacesBackupAgent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices;
18 |
19 | import android.app.backup.BackupAgentHelper;
20 | import android.app.backup.SharedPreferencesBackupHelper;
21 |
22 | /**
23 | * A class that specifies which of the shared preferences you want to backup
24 | * to the Google Backup Service.
25 | */
26 | public class PlacesBackupAgent extends BackupAgentHelper {
27 | @Override
28 | public void onCreate() {
29 | SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this, PlacesConstants.SHARED_PREFERENCE_FILE);
30 | addHelper(PlacesConstants.SP_KEY_FOLLOW_LOCATION_CHANGES, helper);
31 | // TODO Add additional helpers for each of the preferences you want to backup.
32 | }
33 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/PlacesConstants.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices;
18 |
19 | import android.app.AlarmManager;
20 |
21 | public class PlacesConstants {
22 |
23 | /**
24 | * TODO **P1** You must put your Google Places API key here.
25 | * You can get your API key from:
26 | * {@link http://code.google.com/apis/maps/documentation/places/#Limits}
27 | */
28 | private static String MY_API_KEY = "";
29 |
30 | public static String PLACES_API_KEY = "&key=" + MY_API_KEY;
31 |
32 | /**
33 | * You'll need to modify these values to suit your own app.
34 | */
35 | // TODO Turn off when deploying your app.
36 | public static boolean DEVELOPER_MODE = true;
37 |
38 | // TODO Point these at your data sources.
39 | public static String PLACES_LIST_BASE_URI = "https://maps.googleapis.com/maps/api/place/search/xml?sensor=true";
40 | public static String PLACES_DETAIL_BASE_URI = "https://maps.googleapis.com/maps/api/place/details/xml?sensor=true&reference=";
41 | public static String PLACES_CHECKIN_URI = "https://maps.googleapis.com/maps/api/place/check-in/xml?sensor=true";
42 | public static String PLACES_CHECKIN_OK_STATUS = "OK";
43 |
44 | /**
45 | * These values control the user experience of your app. You should
46 | * modify them to provide the best experience based on how your
47 | * app will actually be used.
48 | * TODO Update these values for your app.
49 | */
50 | // The default search radius when searching for places nearby.
51 | public static int DEFAULT_RADIUS = 150;
52 | // The maximum distance the user should travel between location updates.
53 | public static int MAX_DISTANCE = DEFAULT_RADIUS/2;
54 | // The maximum time that should pass before the user gets a location update.
55 | public static long MAX_TIME = AlarmManager.INTERVAL_FIFTEEN_MINUTES;
56 |
57 | // You will generally want passive location updates to occur less frequently
58 | // than active updates. You need to balance location freshness with battery life.
59 | // The location update distance for passive updates.
60 | public static int PASSIVE_MAX_DISTANCE = MAX_DISTANCE;
61 | // The location update time for passive updates
62 | public static long PASSIVE_MAX_TIME = MAX_TIME;
63 | // Use the GPS (fine location provider) when the Activity is visible?
64 | public static boolean USE_GPS_WHEN_ACTIVITY_VISIBLE = true;
65 | //When the user exits via the back button, do you want to disable
66 | // passive background updates.
67 | public static boolean DISABLE_PASSIVE_LOCATION_WHEN_USER_EXIT = false;
68 |
69 | // Maximum latency before you force a cached detail page to be updated.
70 | public static long MAX_DETAILS_UPDATE_LATENCY = AlarmManager.INTERVAL_DAY;
71 |
72 | // Prefetching place details is useful but potentially expensive. The following
73 | // values lets you disable prefetching when on mobile data or low battery conditions.
74 | // Only prefetch on WIFI?
75 | public static boolean PREFETCH_ON_WIFI_ONLY = false;
76 | // Disable prefetching when battery is low?
77 | public static boolean DISABLE_PREFETCH_ON_LOW_BATTERY = true;
78 |
79 | // How long to wait before retrying failed checkins.
80 | public static long CHECKIN_RETRY_INTERVAL = AlarmManager.INTERVAL_FIFTEEN_MINUTES;
81 |
82 | // The maximum number of locations to prefetch for each update.
83 | public static int PREFETCH_LIMIT = 5;
84 |
85 |
86 | /**
87 | * These values are constants used for intents, exteas, and shared preferences.
88 | * You shouldn't need to modify them.
89 | */
90 | public static String SHARED_PREFERENCE_FILE = "SHARED_PREFERENCE_FILE";
91 | public static String SP_KEY_FOLLOW_LOCATION_CHANGES = "SP_KEY_FOLLOW_LOCATION_CHANGES";
92 | public static String SP_KEY_LAST_LIST_UPDATE_TIME = "SP_KEY_LAST_LIST_UPDATE_TIME";
93 | public static String SP_KEY_LAST_LIST_UPDATE_LAT = "SP_KEY_LAST_LIST_UPDATE_LAT";
94 | public static String SP_KEY_LAST_LIST_UPDATE_LNG = "SP_KEY_LAST_LIST_UPDATE_LNG";
95 | public static String SP_KEY_LAST_CHECKIN_ID = "SP_KEY_LAST_CHECKIN_ID";
96 | public static String SP_KEY_LAST_CHECKIN_TIMESTAMP = "SP_KEY_LAST_CHECKIN_TIMESTAMP";
97 | public static String SP_KEY_RUN_ONCE = "SP_KEY_RUN_ONCE";
98 |
99 | public static String EXTRA_KEY_REFERENCE = "reference";
100 | public static String EXTRA_KEY_ID = "id";
101 | public static String EXTRA_KEY_LOCATION = "location";
102 | public static String EXTRA_KEY_RADIUS = "radius";
103 | public static String EXTRA_KEY_TIME_STAMP = "time_stamp";
104 | public static String EXTRA_KEY_FORCEREFRESH = "force_refresh";
105 | public static String EXTRA_KEY_IN_BACKGROUND = "EXTRA_KEY_IN_BACKGROUND";
106 |
107 | public static String ARGUMENTS_KEY_REFERENCE = "reference";
108 | public static String ARGUMENTS_KEY_ID = "id";
109 |
110 | public static String NEW_CHECKIN_ACTION = "com.radioactiveyak.places.NEW_CHECKIN_ACTION";
111 | public static String RETRY_QUEUED_CHECKINS_ACTION = "com.radioactiveyak.places.retry_queued_checkins";
112 | public static String ACTIVE_LOCATION_UPDATE_PROVIDER_DISABLED = "com.radioactiveyak.places.active_location_update_provider_disabled";
113 |
114 | public static boolean SUPPORTS_GINGERBREAD = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD;
115 | public static boolean SUPPORTS_HONEYCOMB = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB;
116 | public static boolean SUPPORTS_FROYO = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.FROYO;
117 | public static boolean SUPPORTS_ECLAIR = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.ECLAIR;
118 |
119 | public static String CONSTRUCTED_LOCATION_PROVIDER = "CONSTRUCTED_LOCATION_PROVIDER";
120 |
121 | public static int CHECKIN_NOTIFICATION = 0;
122 | }
123 |
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/UI/fragments/CheckinFragment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.UI.fragments;
18 |
19 | import android.app.Activity;
20 | import android.database.Cursor;
21 | import android.os.Bundle;
22 | import android.os.Handler;
23 | import android.support.v4.app.Fragment;
24 | import android.support.v4.app.LoaderManager.LoaderCallbacks;
25 | import android.support.v4.content.CursorLoader;
26 | import android.support.v4.content.Loader;
27 | import android.view.LayoutInflater;
28 | import android.view.View;
29 | import android.view.ViewGroup;
30 | import android.widget.TextView;
31 |
32 | import com.radioactiveyak.location_best_practices.PlacesConstants;
33 | import com.radioactiveyak.location_best_practices.R;
34 | import com.radioactiveyak.location_best_practices.content_providers.PlaceDetailsContentProvider;
35 |
36 | // TODO Update this UI with details related to your checkin. That might include point / rewards
37 | // TODO Or incentives to review or rate the establishment.
38 |
39 | /**
40 | * UI Fragment to display which venue we are currently checked in to.
41 | */
42 | public class CheckinFragment extends Fragment implements LoaderCallbacks {
43 |
44 | /**
45 | * Factory that return a new instance of the {@link CheckinFragment}
46 | * populated with the details corresponding to the passed in venue
47 | * identifier.
48 | * @param id Identifier of the venue checked in to
49 | * @return A new CheckinFragment
50 | */
51 | public static CheckinFragment newInstance(String id) {
52 | CheckinFragment f = new CheckinFragment();
53 |
54 | // Supply id input as an argument.
55 | Bundle args = new Bundle();
56 | args.putString(PlacesConstants.ARGUMENTS_KEY_ID, id);
57 | f.setArguments(args);
58 |
59 | return f;
60 | }
61 |
62 | protected String placeId = null;
63 | protected Handler handler = new Handler();
64 | protected Activity activity;
65 | protected TextView checkPlaceNameTextView;
66 |
67 | public CheckinFragment() {
68 | super();
69 | }
70 |
71 | /**
72 | * Change the venue checked in to.
73 | * @param id Identifier of the venue checked in to
74 | */
75 | public void setPlaceId(String id) {
76 | // Update the place ID and restart the loader to update the UI.
77 | placeId = id;
78 | if (placeId != null)
79 | getLoaderManager().restartLoader(0, null, this);
80 | }
81 |
82 | public void onActivityCreated(Bundle savedInstanceState) {
83 | super.onActivityCreated(savedInstanceState);
84 | activity = getActivity();
85 |
86 | // Populate the UI by initiating the loader to retrieve the
87 | // details of the venue from the underlying Place Content Provider.
88 | if (placeId != null)
89 | getLoaderManager().initLoader(0, null, this);
90 | }
91 |
92 | @Override
93 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
94 | View view = inflater.inflate(R.layout.checkin_box, container, false);
95 | checkPlaceNameTextView = (TextView)view.findViewById(R.id.checkin_place_name);
96 |
97 | if (getArguments() != null)
98 | placeId = getArguments().getString(PlacesConstants.ARGUMENTS_KEY_ID);
99 |
100 | return view;
101 | }
102 |
103 | @Override
104 | public void onResume() {
105 | super.onResume();
106 | }
107 |
108 | /**
109 | * {@inheritDoc}
110 | * This loader queries the {@link PlaceContentProvider} to extract the name of
111 | * the venue that has been checked in to.
112 | */
113 | public Loader onCreateLoader(int id, Bundle args) {
114 | String[] projection = new String[] {PlaceDetailsContentProvider.KEY_NAME};
115 |
116 | String selection = PlaceDetailsContentProvider.KEY_ID + "='" + placeId + "'";
117 |
118 | return new CursorLoader(activity, PlaceDetailsContentProvider.CONTENT_URI,
119 | projection, selection, null, null);
120 | }
121 |
122 | /**
123 | * {@inheritDoc}
124 | * When this load has finished, update the UI with the name of the venue.
125 | */
126 | public void onLoadFinished(Loader loader, Cursor data) {
127 | if (data.moveToFirst()) {
128 | final String venueName = data.getString(data.getColumnIndex(PlaceDetailsContentProvider.KEY_NAME));
129 | handler.post(new Runnable () {
130 | public void run() {
131 | checkPlaceNameTextView.setText(venueName);
132 | }
133 | });
134 | }
135 | }
136 |
137 | /**
138 | * {@inheritDoc}
139 | */
140 | public void onLoaderReset(Loader loader) {
141 | handler.post(new Runnable () {
142 | public void run() {
143 | checkPlaceNameTextView.setText("");
144 | }
145 | });
146 | }
147 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/UI/fragments/PlaceDetailFragment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.UI.fragments;
18 |
19 | // TODO Create a richer UI to display places Details. This should include images,
20 | // TODO ratings, reviews, other people checked in here, etc.
21 |
22 | import android.app.Activity;
23 | import android.content.Context;
24 | import android.content.Intent;
25 | import android.content.SharedPreferences;
26 | import android.database.Cursor;
27 | import android.os.Bundle;
28 | import android.os.Handler;
29 | import android.support.v4.app.Fragment;
30 | import android.support.v4.app.LoaderManager.LoaderCallbacks;
31 | import android.support.v4.content.CursorLoader;
32 | import android.support.v4.content.Loader;
33 | import android.util.Log;
34 | import android.view.LayoutInflater;
35 | import android.view.View;
36 | import android.view.ViewGroup;
37 | import android.view.View.OnClickListener;
38 | import android.widget.Button;
39 | import android.widget.TextView;
40 |
41 | import com.radioactiveyak.location_best_practices.PlacesConstants;
42 | import com.radioactiveyak.location_best_practices.R;
43 | import com.radioactiveyak.location_best_practices.content_providers.PlaceDetailsContentProvider;
44 | import com.radioactiveyak.location_best_practices.services.PlaceCheckinService;
45 | import com.radioactiveyak.location_best_practices.services.PlaceDetailsUpdateService;
46 |
47 | /**
48 | * UI Fragment to display the details for a selected venue.
49 | */
50 | public class PlaceDetailFragment extends Fragment implements LoaderCallbacks {
51 |
52 | /**
53 | * Factory that produces a new {@link PlaceDetailFragment} populated with
54 | * details corresponding to the reference / ID of the venue passed in.
55 | * @param reference Venue Reference
56 | * @param id Venue Unique ID
57 | * @return {@link PlaceDetailFragment}
58 | */
59 | public static PlaceDetailFragment newInstance(String reference, String id) {
60 | PlaceDetailFragment f = new PlaceDetailFragment();
61 |
62 | // Supply reference and ID inputs as arguments.
63 | Bundle args = new Bundle();
64 | args.putString(PlacesConstants.ARGUMENTS_KEY_REFERENCE, reference);
65 | args.putString(PlacesConstants.ARGUMENTS_KEY_ID, id);
66 | f.setArguments(args);
67 |
68 | return f;
69 | }
70 |
71 | protected static String TAG = "PlaceDetailFragment";
72 |
73 | protected String placeReference = null;
74 | protected String placeId = null;
75 |
76 | protected Handler handler = new Handler();
77 | protected Activity activity;
78 | protected TextView nameTextView;
79 | protected TextView phoneTextView;
80 | protected TextView addressTextView;
81 | protected TextView ratingTextView;
82 | protected TextView urlTextView;
83 | protected Button checkinButton;
84 | protected TextView checkedInText;
85 |
86 | public PlaceDetailFragment() {
87 | super();
88 | }
89 |
90 | public void onActivityCreated(Bundle savedInstanceState) {
91 | super.onActivityCreated(savedInstanceState);
92 | activity = getActivity();
93 |
94 | // Query the PlacesDetails Content Provider using a Loader to find
95 | // the details for the selected venue.
96 | if (placeId != null)
97 | getLoaderManager().initLoader(0, null, this);
98 |
99 | // Query the Shared Preferences to find the ID of the last venue checked in to.
100 | SharedPreferences sp = activity.getSharedPreferences(PlacesConstants.SHARED_PREFERENCE_FILE, Context.MODE_PRIVATE);
101 | String lastCheckin = sp.getString(PlacesConstants.SP_KEY_LAST_CHECKIN_ID, null);
102 | if (lastCheckin != null )
103 | checkedIn(lastCheckin);
104 | }
105 |
106 | @Override
107 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
108 | View view = inflater.inflate(R.layout.place_detail, container, false);
109 | nameTextView = (TextView)view.findViewById(R.id.detail_name);
110 | phoneTextView = (TextView)view.findViewById(R.id.detail_phone);
111 | addressTextView = (TextView)view.findViewById(R.id.detail_address);
112 | ratingTextView = (TextView)view.findViewById(R.id.detail_rating);
113 | urlTextView = (TextView)view.findViewById(R.id.detail_url);
114 | checkinButton = (Button)view.findViewById(R.id.checkin_button);
115 | checkedInText = (TextView)view.findViewById(R.id.detail_checkin_text);
116 |
117 | checkinButton.setOnClickListener(checkinButtonOnClickListener);
118 |
119 | if (getArguments() != null) {
120 | placeReference = getArguments().getString(PlacesConstants.ARGUMENTS_KEY_REFERENCE);
121 | placeId = getArguments().getString(PlacesConstants.ARGUMENTS_KEY_ID);
122 | }
123 | return view;
124 | }
125 |
126 | @Override
127 | public void onResume() {
128 | super.onResume();
129 |
130 | // Always refresh the details on resume, but don't force
131 | // a refresh to minimize the network usage. Forced updates
132 | // are unnecessary as we force an update when a venue
133 | // is selected in the Place List Activity.
134 | if (placeReference != null && placeId != null)
135 | updatePlace(placeReference, placeId, false);
136 | }
137 |
138 | /**
139 | * Start the {@link PlaceDetailsUpdateService} to refresh the details for the
140 | * selected venue.
141 | * @param reference Reference
142 | * @param id Unique Identifier
143 | * @param forceUpdate Force an update
144 | */
145 | protected void updatePlace(String reference, String id, boolean forceUpdate) {
146 | if (placeReference != null && placeId != null) {
147 | // Start the PlaceDetailsUpdate Service to query the server for details
148 | // on the specified venue. A "forced update" will ignore the caching latency
149 | // rules and query the server.
150 | Intent updateServiceIntent = new Intent(activity, PlaceDetailsUpdateService.class);
151 | updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_REFERENCE, reference);
152 | updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_ID, id);
153 | updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_FORCEREFRESH, forceUpdate);
154 | activity.startService(updateServiceIntent);
155 | }
156 | }
157 |
158 | /**
159 | * {@inheritDoc}
160 | * Query the {@link PlaceDetailsContentProvider} for the Phone, Address, Rating, Reference, and Url
161 | * of the selected venue.
162 | * TODO Expand the projection to include any other details you are recording in the Place Detail Content Provider.
163 | */
164 | public Loader onCreateLoader(int id, Bundle args) {
165 | String[] projection = new String[] {PlaceDetailsContentProvider.KEY_NAME,
166 | PlaceDetailsContentProvider.KEY_PHONE,
167 | PlaceDetailsContentProvider.KEY_ADDRESS,
168 | PlaceDetailsContentProvider.KEY_RATING,
169 | PlaceDetailsContentProvider.KEY_REFERENCE,
170 | PlaceDetailsContentProvider.KEY_URL};
171 |
172 | String selection = PlaceDetailsContentProvider.KEY_ID + "='" + placeId + "'";
173 |
174 | return new CursorLoader(activity, PlaceDetailsContentProvider.CONTENT_URI,
175 | projection, selection, null, null);
176 | }
177 |
178 | /**
179 | * {@inheritDoc}
180 | * When the Loader has completed, schedule an update of the Fragment UI on the main application thread.
181 | */
182 | public void onLoadFinished(Loader loader, Cursor data) {
183 | if (data.moveToFirst()) {
184 | final String name = data.getString(data.getColumnIndex(PlaceDetailsContentProvider.KEY_NAME));
185 | final String phone = data.getString(data.getColumnIndex(PlaceDetailsContentProvider.KEY_PHONE));
186 | final String address = data.getString(data.getColumnIndex(PlaceDetailsContentProvider.KEY_ADDRESS));
187 | final String rating = data.getString(data.getColumnIndex(PlaceDetailsContentProvider.KEY_RATING));
188 | final String url = data.getString(data.getColumnIndex(PlaceDetailsContentProvider.KEY_URL));
189 |
190 | // If we don't have a place reference passed in, we need to look it up and update our details
191 | // accordingly.
192 | if (placeReference == null) {
193 | placeReference = data.getString(data.getColumnIndex(PlaceDetailsContentProvider.KEY_REFERENCE));
194 | updatePlace(placeReference, placeId, true);
195 | }
196 |
197 | handler.post(new Runnable () {
198 | public void run() {
199 | nameTextView.setText(name);
200 | phoneTextView.setText(phone);
201 | addressTextView.setText(address);
202 | ratingTextView.setText(rating);
203 | urlTextView.setText(url);
204 | }
205 | });
206 | }
207 | }
208 |
209 | /**
210 | * {@inheritDoc}
211 | */
212 | public void onLoaderReset(Loader loader) {
213 | handler.post(new Runnable () {
214 | public void run() {
215 | nameTextView.setText("");
216 | phoneTextView.setText("");
217 | addressTextView.setText("");
218 | ratingTextView.setText("");
219 | urlTextView.setText("");
220 | }
221 | });
222 | }
223 |
224 | /**
225 | * When the Checkin Button is clicked start the {@link PlaceCheckinService} to checkin.
226 | */
227 | protected OnClickListener checkinButtonOnClickListener = new OnClickListener() {
228 | public void onClick(View view) {
229 | // TODO Pass in additional parameters to your checkin / rating / review service as appropriate
230 | // TODO In some cases you may prefer to open a new Activity with checkin details before initiating the Service.
231 | Intent checkinServiceIntent = new Intent(getActivity(), PlaceCheckinService.class);
232 | checkinServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_REFERENCE, placeReference);
233 | checkinServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_ID, placeId);
234 | checkinServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_TIME_STAMP, System.currentTimeMillis());
235 | getActivity().startService(checkinServiceIntent);
236 | }
237 | };
238 |
239 | /**
240 | * Checks to see if the currently displayed venue is the last place checked in to.
241 | * IF it is, it disables the checkin button and update the UI accordingly.
242 | * @param id Checked-in place ID
243 | */
244 | public void checkedIn(String id) {
245 | if (placeId == null)
246 | Log.e(TAG, "Place ID = null");
247 | boolean checkedIn = id != null && placeId != null && placeId.equals(id);
248 | checkinButton.setEnabled(!checkedIn);
249 | checkedInText.setVisibility(checkedIn ? View.VISIBLE : View.INVISIBLE);
250 | }
251 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/UI/fragments/PlaceListFragment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.UI.fragments;
18 |
19 | import com.radioactiveyak.location_best_practices.PlacesConstants;
20 | import com.radioactiveyak.location_best_practices.UI.PlaceActivity;
21 | import com.radioactiveyak.location_best_practices.content_providers.PlacesContentProvider;
22 | import com.radioactiveyak.location_best_practices.services.PlaceDetailsUpdateService;
23 |
24 | import android.content.Intent;
25 | import android.database.Cursor;
26 | import android.os.Bundle;
27 | import android.support.v4.app.ListFragment;
28 | import android.support.v4.app.LoaderManager.LoaderCallbacks;
29 | import android.support.v4.content.CursorLoader;
30 | import android.support.v4.content.Loader;
31 | import android.support.v4.widget.SimpleCursorAdapter;
32 | import android.view.View;
33 | import android.widget.ListView;
34 |
35 | // TODO Update this UI to show a better list of available venues. This could include
36 | // TODO pictures, direction, more detailed text, etc. You will likely want to define
37 | // TODO your own List Item Layout.
38 |
39 | /**
40 | * UI Fragment to show a list of venues near to the users current location.
41 | */
42 | public class PlaceListFragment extends ListFragment implements LoaderCallbacks {
43 |
44 | protected Cursor cursor = null;
45 | protected SimpleCursorAdapter adapter;
46 | protected PlaceActivity activity;
47 |
48 | @Override
49 | public void onActivityCreated(Bundle savedInstanceState) {
50 | super.onActivityCreated(savedInstanceState);
51 |
52 | activity = (PlaceActivity)getActivity();
53 |
54 | // Create a new SimpleCursorAdapter that displays the name of each nearby
55 | // venue and the current distance to it.
56 | adapter = new SimpleCursorAdapter(
57 | activity,
58 | android.R.layout.two_line_list_item,
59 | cursor,
60 | new String[] {PlacesContentProvider.KEY_NAME, PlacesContentProvider.KEY_DISTANCE},
61 | new int[] {android.R.id.text1, android.R.id.text2},
62 | 0);
63 | // Allocate the adapter to the List displayed within this fragment.
64 | setListAdapter(adapter);
65 |
66 | // Populate the adapter / list using a Cursor Loader.
67 | getLoaderManager().initLoader(0, null, this);
68 | }
69 |
70 | /**
71 | * {@inheritDoc}
72 | * When a venue is clicked, fetch the details from your server and display the detail page.
73 | */
74 | @Override
75 | public void onListItemClick(ListView l, View v, int position, long theid) {
76 | super.onListItemClick(l, v, position, theid);
77 |
78 | // Find the ID and Reference of the selected venue.
79 | // These are needed to perform a lookup in our cache and the Google Places API server respectively.
80 | Cursor c = adapter.getCursor();
81 | c.moveToPosition(position);
82 | String reference = c.getString(c.getColumnIndex(PlacesContentProvider.KEY_REFERENCE));
83 | String id = c.getString(c.getColumnIndex(PlacesContentProvider.KEY_ID));
84 |
85 | // Initiate a lookup of the venue details usign the PlacesDetailsUpdateService.
86 | // Because this is a user initiated action (rather than a prefetch) we request
87 | // that the Service force a refresh.
88 | Intent serviceIntent = new Intent(activity, PlaceDetailsUpdateService.class);
89 | serviceIntent.putExtra(PlacesConstants.EXTRA_KEY_REFERENCE, reference);
90 | serviceIntent.putExtra(PlacesConstants.EXTRA_KEY_ID, id);
91 | serviceIntent.putExtra(PlacesConstants.EXTRA_KEY_FORCEREFRESH, true);
92 | activity.startService(serviceIntent);
93 |
94 | // Request the parent Activity display the venue detail UI.
95 | activity.selectDetail(reference, id);
96 | }
97 |
98 | /**
99 | * {@inheritDoc}
100 | * This loader will return the ID, Reference, Name, and Distance of all the venues
101 | * currently stored in the {@link PlacesContentProvider}.
102 | */
103 | public Loader onCreateLoader(int id, Bundle args) {
104 | String[] projection = new String[] {PlacesContentProvider.KEY_ID,PlacesContentProvider.KEY_NAME, PlacesContentProvider.KEY_DISTANCE, PlacesContentProvider.KEY_REFERENCE};
105 |
106 | return new CursorLoader(activity, PlacesContentProvider.CONTENT_URI,
107 | projection, null, null, null);
108 | }
109 |
110 | /**
111 | * {@inheritDoc}
112 | * When the loading has completed, assign the cursor to the adapter / list.
113 | */
114 | public void onLoadFinished(Loader loader, Cursor data) {
115 | adapter.swapCursor(data);
116 | }
117 |
118 | /**
119 | * {@inheritDoc}
120 | */
121 | public void onLoaderReset(Loader loader) {
122 | adapter.swapCursor(null);
123 | }
124 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/content_providers/PlaceDetailsContentProvider.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.content_providers;
18 |
19 | import android.content.ContentProvider;
20 | import android.content.ContentUris;
21 | import android.content.ContentValues;
22 | import android.content.Context;
23 | import android.content.UriMatcher;
24 | import android.database.Cursor;
25 | import android.database.SQLException;
26 | import android.database.sqlite.SQLiteDatabase;
27 | import android.database.sqlite.SQLiteException;
28 | import android.database.sqlite.SQLiteOpenHelper;
29 | import android.database.sqlite.SQLiteQueryBuilder;
30 | import android.database.sqlite.SQLiteDatabase.CursorFactory;
31 | import android.net.Uri;
32 | import android.text.TextUtils;
33 | import android.util.Log;
34 |
35 | /**
36 | * Content Provider and database for storing the details for
37 | * places whose details we've either viewed or prefetched.
38 | */
39 | public class PlaceDetailsContentProvider extends ContentProvider {
40 |
41 | private static final String TAG = "PlacesDetailsContentProvider";
42 |
43 | /** The underlying database */
44 | private SQLiteDatabase placesDB;
45 | private static final String DATABASE_NAME = "placedetails.db";
46 | private static final int DATABASE_VERSION = 3;
47 | private static final String PLACEDETAILS_TABLE = "placedetails";
48 |
49 | // TODO Replace the columns names and database creation SQL with values for your own app.
50 | // Column Names
51 | public static final String KEY_ID = "_id";
52 | public static final String KEY_NAME = "name";
53 | public static final String KEY_VICINITY = "vicinity";
54 | public static final String KEY_LOCATION_LAT = "latitude";
55 | public static final String KEY_LOCATION_LNG = "longitude";
56 | public static final String KEY_TYPES = "types";
57 | public static final String KEY_VIEWPORT = "viewport";
58 | public static final String KEY_ICON = "icon";
59 | public static final String KEY_REFERENCE = "reference";
60 | public static final String KEY_DISTANCE = "distance";
61 | public static final String KEY_PHONE = "phone";
62 | public static final String KEY_ADDRESS = "address";
63 | public static final String KEY_RATING = "rating";
64 | public static final String KEY_URL = "url";
65 | public static final String KEY_LAST_UPDATE_TIME = "lastupdatetime";
66 | public static final String KEY_FORCE_CACHE = "forcecache";
67 |
68 | // TODO Replace this URI with something unique to your own application.
69 | public static final Uri CONTENT_URI = Uri.parse("content://com.radioactiveyak.provider.placedetails/places");
70 |
71 | //Create the constants used to differentiate between the different URI requests.
72 | private static final int PLACES = 1;
73 | private static final int PLACE_ID = 2;
74 |
75 | //Allocate the UriMatcher object, where a URI ending in 'places' will
76 | //correspond to a request for all places, and 'places' with a trailing '/[Unique ID]' will represent a single place details row.
77 | private static final UriMatcher uriMatcher;
78 | static {
79 | uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
80 | uriMatcher.addURI("com.radioactiveyak.provider.placedetails", "places", PLACES);
81 | uriMatcher.addURI("com.radioactiveyak.provider.placedetails", "places/*", PLACE_ID);
82 | }
83 |
84 | @Override
85 | public boolean onCreate() {
86 | Context context = getContext();
87 |
88 | PlacesDatabaseHelper dbHelper = new PlacesDatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
89 | try {
90 | placesDB = dbHelper.getWritableDatabase();
91 | } catch (SQLiteException e) {
92 | placesDB = null;
93 | Log.e(TAG, "Database Opening exception");
94 | }
95 |
96 | return (placesDB == null) ? false : true;
97 | }
98 |
99 | @Override
100 | public String getType(Uri uri) {
101 | switch (uriMatcher.match(uri)) {
102 | case PLACES: return "vnd.android.cursor.dir/vnd.radioativeyak.placedetail";
103 | case PLACE_ID: return "vnd.android.cursor.item/vnd.radioactiveyak.placedetail";
104 | default: throw new IllegalArgumentException("Unsupported URI: " + uri);
105 | }
106 | }
107 |
108 |
109 | @Override
110 | public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sort) {
111 | SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
112 | qb.setTables(PLACEDETAILS_TABLE);
113 |
114 | // If this is a row query, limit the result set to the passed in row.
115 | switch (uriMatcher.match(uri)) {
116 | case PLACE_ID: qb.appendWhere(KEY_ID + "='" + uri.getPathSegments().get(1) + "'");
117 | break;
118 | default : break;
119 | }
120 |
121 | // If no sort order is specified sort by date / time
122 | String orderBy;
123 | if (TextUtils.isEmpty(sort)) {
124 | orderBy = KEY_DISTANCE + " ASC";
125 | } else {
126 | orderBy = sort;
127 | }
128 |
129 | // Apply the query to the underlying database.
130 | Cursor c = qb.query(placesDB,
131 | projection,
132 | selection, selectionArgs,
133 | null, null, orderBy);
134 |
135 | // Register the contexts ContentResolver to be notified if
136 | // the cursor result set changes.
137 | c.setNotificationUri(getContext().getContentResolver(), uri);
138 |
139 | // Return a cursor to the query result.
140 | return c;
141 | }
142 |
143 | @Override
144 | public Uri insert(Uri _uri, ContentValues _initialValues) {
145 | // Insert the new row, will return the row number if successful.
146 | long rowID = placesDB.insert(PLACEDETAILS_TABLE, "not_null", _initialValues);
147 |
148 | // Return a URI to the newly inserted row on success.
149 | if (rowID > 0) {
150 | Uri uri = ContentUris.withAppendedId(CONTENT_URI, rowID);
151 | getContext().getContentResolver().notifyChange(uri, null);
152 | return uri;
153 | }
154 | throw new SQLException("Failed to insert row into " + _uri);
155 | }
156 |
157 | @Override
158 | public int delete(Uri uri, String where, String[] whereArgs) {
159 | int count;
160 |
161 | switch (uriMatcher.match(uri)) {
162 | case PLACES:
163 | count = placesDB.delete(PLACEDETAILS_TABLE, where, whereArgs);
164 | break;
165 |
166 | case PLACE_ID:
167 | String segment = uri.getPathSegments().get(1);
168 | count = placesDB.delete(PLACEDETAILS_TABLE, KEY_ID + "="
169 | + segment
170 | + (!TextUtils.isEmpty(where) ? " AND ("
171 | + where + ')' : ""), whereArgs);
172 | break;
173 |
174 | default: throw new IllegalArgumentException("Unsupported URI: " + uri);
175 | }
176 |
177 | getContext().getContentResolver().notifyChange(uri, null);
178 | return count;
179 | }
180 |
181 | @Override
182 | public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
183 | int count;
184 | switch (uriMatcher.match(uri)) {
185 | case PLACES: count = placesDB.update(PLACEDETAILS_TABLE, values, where, whereArgs);
186 | break;
187 |
188 | case PLACE_ID: String segment = uri.getPathSegments().get(1);
189 | count = placesDB.update(PLACEDETAILS_TABLE, values, KEY_ID
190 | + "=" + segment
191 | + (!TextUtils.isEmpty(where) ? " AND ("
192 | + where + ')' : ""), whereArgs);
193 | break;
194 |
195 | default: throw new IllegalArgumentException("Unknown URI " + uri);
196 | }
197 |
198 | getContext().getContentResolver().notifyChange(uri, null);
199 | return count;
200 | }
201 |
202 | // Helper class for opening, creating, and managing database version control
203 | private static class PlacesDatabaseHelper extends SQLiteOpenHelper {
204 | private static final String DATABASE_CREATE =
205 | "create table " + PLACEDETAILS_TABLE + " ("
206 | + KEY_ID + " TEXT primary key, "
207 | + KEY_NAME + " TEXT, "
208 | + KEY_VICINITY + " TEXT, "
209 | + KEY_LOCATION_LAT + " FLOAT, "
210 | + KEY_LOCATION_LNG + " FLOAT, "
211 | + KEY_TYPES + " TEXT, "
212 | + KEY_VIEWPORT + " TEXT, "
213 | + KEY_ICON + " TEXT, "
214 | + KEY_REFERENCE + " TEXT, "
215 | + KEY_DISTANCE + " FLOAT, "
216 | + KEY_PHONE + " TEXT, "
217 | + KEY_ADDRESS + " TEXT, "
218 | + KEY_RATING + " FLOAT, "
219 | + KEY_URL + " TEXT, "
220 | + KEY_LAST_UPDATE_TIME + " LONG, "
221 | + KEY_FORCE_CACHE + " BOOLEAN); ";
222 |
223 | public PlacesDatabaseHelper(Context context, String name, CursorFactory factory, int version) {
224 | super(context, name, factory, version);
225 | }
226 |
227 | @Override
228 | public void onCreate(SQLiteDatabase db) {
229 | db.execSQL(DATABASE_CREATE);
230 | }
231 |
232 | @Override
233 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
234 | Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
235 | + newVersion + ", which will destroy all old data");
236 |
237 | db.execSQL("DROP TABLE IF EXISTS " + PLACEDETAILS_TABLE);
238 | onCreate(db);
239 | }
240 | }
241 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/content_providers/PlacesContentProvider.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.content_providers;
18 |
19 | import android.content.ContentProvider;
20 | import android.content.ContentUris;
21 | import android.content.ContentValues;
22 | import android.content.Context;
23 | import android.content.UriMatcher;
24 | import android.database.Cursor;
25 | import android.database.SQLException;
26 | import android.database.sqlite.SQLiteDatabase;
27 | import android.database.sqlite.SQLiteException;
28 | import android.database.sqlite.SQLiteOpenHelper;
29 | import android.database.sqlite.SQLiteQueryBuilder;
30 | import android.database.sqlite.SQLiteDatabase.CursorFactory;
31 | import android.net.Uri;
32 | import android.text.TextUtils;
33 | import android.util.Log;
34 |
35 | /**
36 | * Content Provider and database for storing the list of
37 | * places nearby our current location
38 | */
39 | public class PlacesContentProvider extends ContentProvider {
40 |
41 | /** The underlying database */
42 | private SQLiteDatabase placesDB;
43 |
44 | private static final String TAG = "PlacesContentProvider";
45 | private static final String DATABASE_NAME = "places.db";
46 | private static final int DATABASE_VERSION = 6;
47 | private static final String PLACES_TABLE = "places";
48 |
49 | // TODO Replace the columns names and database creation SQL with values for your own app.
50 | // Column Names
51 | public static final String KEY_ID = "_id";
52 | public static final String KEY_NAME = "name";
53 | public static final String KEY_VICINITY = "vicinity";
54 | public static final String KEY_LOCATION_LAT = "latitude";
55 | public static final String KEY_LOCATION_LNG = "longitude";
56 | public static final String KEY_TYPES = "types";
57 | public static final String KEY_VIEWPORT = "viewport";
58 | public static final String KEY_ICON = "icon";
59 | public static final String KEY_REFERENCE = "reference";
60 | public static final String KEY_DISTANCE = "distance";
61 | public static final String KEY_LAST_UPDATE_TIME = "lastupdatetime";
62 |
63 | // TODO Replace this URI with something unique to your own application.
64 | public static final Uri CONTENT_URI = Uri.parse("content://com.radioactiveyak.provider.places/places");
65 |
66 | //Create the constants used to differentiate between the different URI requests.
67 | private static final int PLACES = 1;
68 | private static final int PLACE_ID = 2;
69 |
70 | //Allocate the UriMatcher object, where a URI ending in 'places' will
71 | //correspond to a request for all places, and 'places' with a trailing '/[Unique ID]' will represent a single place details row.
72 | private static final UriMatcher uriMatcher;
73 | static {
74 | uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
75 | uriMatcher.addURI("com.radioactiveyak.provider.places", "places", PLACES);
76 | uriMatcher.addURI("com.radioactiveyak.provider.places", "places/*", PLACE_ID);
77 | }
78 |
79 | @Override
80 | public boolean onCreate() {
81 | Context context = getContext();
82 |
83 | PlacesDatabaseHelper dbHelper = new PlacesDatabaseHelper(context, DATABASE_NAME,
84 | null, DATABASE_VERSION);
85 | try {
86 | placesDB = dbHelper.getWritableDatabase();
87 | } catch (SQLiteException e) {
88 | placesDB = null;
89 | Log.d(TAG, "Database Opening exception");
90 | }
91 |
92 | return (placesDB == null) ? false : true;
93 | }
94 |
95 | @Override
96 | public String getType(Uri uri) {
97 | switch (uriMatcher.match(uri)) {
98 | case PLACES: return "vnd.android.cursor.dir/vnd.radioativeyak.place";
99 | case PLACE_ID: return "vnd.android.cursor.item/vnd.radioactiveyak.place";
100 | default: throw new IllegalArgumentException("Unsupported URI: " + uri);
101 | }
102 | }
103 |
104 |
105 | @Override
106 | public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sort) {
107 | SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
108 | qb.setTables(PLACES_TABLE);
109 |
110 | // If this is a row query, limit the result set to the passed in row.
111 | switch (uriMatcher.match(uri)) {
112 | case PLACE_ID: qb.appendWhere(KEY_ID + "=" + uri.getPathSegments().get(1));
113 | break;
114 | default : break;
115 | }
116 |
117 | // If no sort order is specified sort by date / time
118 | String orderBy;
119 | if (TextUtils.isEmpty(sort)) {
120 | orderBy = KEY_DISTANCE + " ASC";
121 | } else {
122 | orderBy = sort;
123 | }
124 |
125 | // Apply the query to the underlying database.
126 | Cursor c = qb.query(placesDB,
127 | projection,
128 | selection, selectionArgs,
129 | null, null, orderBy);
130 |
131 | // Register the contexts ContentResolver to be notified if
132 | // the cursor result set changes.
133 | c.setNotificationUri(getContext().getContentResolver(), uri);
134 |
135 | // Return a cursor to the query result.
136 | return c;
137 | }
138 |
139 | @Override
140 | public Uri insert(Uri _uri, ContentValues _initialValues) {
141 | // Insert the new row, will return the row number if successful.
142 | long rowID = placesDB.insert(PLACES_TABLE, "nullhack", _initialValues);
143 |
144 | // Return a URI to the newly inserted row on success.
145 | if (rowID > 0) {
146 | Uri uri = ContentUris.withAppendedId(CONTENT_URI, rowID);
147 | getContext().getContentResolver().notifyChange(uri, null);
148 | return uri;
149 | }
150 | throw new SQLException("Failed to insert row into " + _uri);
151 | }
152 |
153 | @Override
154 | public int delete(Uri uri, String where, String[] whereArgs) {
155 | int count;
156 |
157 | switch (uriMatcher.match(uri)) {
158 | case PLACES:
159 | count = placesDB.delete(PLACES_TABLE, where, whereArgs);
160 | break;
161 |
162 | case PLACE_ID:
163 | String segment = uri.getPathSegments().get(1);
164 | count = placesDB.delete(PLACES_TABLE, KEY_ID + "="
165 | + segment
166 | + (!TextUtils.isEmpty(where) ? " AND ("
167 | + where + ')' : ""), whereArgs);
168 | break;
169 |
170 | default: throw new IllegalArgumentException("Unsupported URI: " + uri);
171 | }
172 |
173 | getContext().getContentResolver().notifyChange(uri, null);
174 | return count;
175 | }
176 |
177 | @Override
178 | public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
179 | int count;
180 | switch (uriMatcher.match(uri)) {
181 | case PLACES: count = placesDB.update(PLACES_TABLE, values, where, whereArgs);
182 | break;
183 |
184 | case PLACE_ID: String segment = uri.getPathSegments().get(1);
185 | count = placesDB.update(PLACES_TABLE, values, KEY_ID
186 | + "=" + segment
187 | + (!TextUtils.isEmpty(where) ? " AND ("
188 | + where + ')' : ""), whereArgs);
189 | break;
190 |
191 | default: throw new IllegalArgumentException("Unknown URI " + uri);
192 | }
193 |
194 | getContext().getContentResolver().notifyChange(uri, null);
195 | return count;
196 | }
197 |
198 |
199 | // Helper class for opening, creating, and managing database version control
200 | private static class PlacesDatabaseHelper extends SQLiteOpenHelper {
201 | private static final String DATABASE_CREATE =
202 | "create table " + PLACES_TABLE + " ("
203 | + KEY_ID + " TEXT primary key, "
204 | + KEY_NAME + " TEXT, "
205 | + KEY_VICINITY + " TEXT, "
206 | + KEY_LOCATION_LAT + " FLOAT, "
207 | + KEY_LOCATION_LNG + " FLOAT, "
208 | + KEY_TYPES + " TEXT, "
209 | + KEY_VIEWPORT + " TEXT, "
210 | + KEY_ICON + " TEXT, "
211 | + KEY_REFERENCE + " TEXT, "
212 | + KEY_DISTANCE + " FLOAT, "
213 | + KEY_LAST_UPDATE_TIME + " LONG); ";
214 |
215 | public PlacesDatabaseHelper(Context context, String name, CursorFactory factory, int version) {
216 | super(context, name, factory, version);
217 | }
218 |
219 | @Override
220 | public void onCreate(SQLiteDatabase db) {
221 | db.execSQL(DATABASE_CREATE);
222 | }
223 |
224 | @Override
225 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
226 | Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
227 | + newVersion + ", which will destroy all old data");
228 |
229 | db.execSQL("DROP TABLE IF EXISTS " + PLACES_TABLE);
230 | onCreate(db);
231 | }
232 | }
233 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/content_providers/QueuedCheckinsContentProvider.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.content_providers;
18 |
19 | import android.content.ContentProvider;
20 | import android.content.ContentUris;
21 | import android.content.ContentValues;
22 | import android.content.Context;
23 | import android.content.UriMatcher;
24 | import android.database.Cursor;
25 | import android.database.SQLException;
26 | import android.database.sqlite.SQLiteDatabase;
27 | import android.database.sqlite.SQLiteException;
28 | import android.database.sqlite.SQLiteOpenHelper;
29 | import android.database.sqlite.SQLiteQueryBuilder;
30 | import android.database.sqlite.SQLiteDatabase.CursorFactory;
31 | import android.net.Uri;
32 | import android.text.TextUtils;
33 | import android.util.Log;
34 |
35 | /**
36 | * Content Provider and database for storing checkins which
37 | * have not yet successfully been reported to the server.
38 | * Checkins will be added be added and removed by the checkin Service.
39 | */
40 | public class QueuedCheckinsContentProvider extends ContentProvider {
41 | /** The underlying database */
42 | private SQLiteDatabase checkinsDB;
43 |
44 | private static final String TAG = "QueuedCheckinsContentProvider";
45 | private static final String DATABASE_NAME = "checkins.db";
46 | private static final int DATABASE_VERSION = 3;
47 | private static final String CHECKINS_TABLE = "checkins";
48 |
49 | // TODO Update the columns and SQL Create statement with the data you
50 | // TODO will be sending to your online checkin service.
51 | // Column Names
52 | public static final String KEY_REFERENCE = "_id";
53 | public static final String KEY_ID = "id";
54 | public static final String KEY_TIME_STAMP = "timestamp";
55 |
56 | public static final Uri CONTENT_URI = Uri.parse("content://com.radioactiveyak.provider.checkins/checkins");
57 |
58 | //Create the constants used to differentiate between the different URI requests.
59 | private static final int CHECKINS = 1;
60 | private static final int CHECKIN_ID = 2;
61 |
62 | //Allocate the UriMatcher object, where a URI ending in 'checkins' will
63 | //correspond to a request for all earthquakes, and 'checkins' with a trailing '/[Unique ID]' will represent a single earthquake row.
64 | private static final UriMatcher uriMatcher;
65 | static {
66 | uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
67 | uriMatcher.addURI("com.radioactiveyak.provider.checkins", "checkins", CHECKINS);
68 | uriMatcher.addURI("com.radioactiveyak.provider.checkins", "checkins/*", CHECKIN_ID);
69 | }
70 |
71 | @Override
72 | public boolean onCreate() {
73 | Context context = getContext();
74 |
75 | CheckinsDatabaseHelper dbHelper = new CheckinsDatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
76 | try {
77 | checkinsDB = dbHelper.getWritableDatabase();
78 | } catch (SQLiteException e) {
79 | checkinsDB = null;
80 | Log.d(TAG, "Database Opening exception");
81 | }
82 |
83 | return (checkinsDB == null) ? false : true;
84 | }
85 |
86 | @Override
87 | public String getType(Uri uri) {
88 | switch (uriMatcher.match(uri)) {
89 | case CHECKINS: return "vnd.android.cursor.dir/vnd.radioativeyak.checkin";
90 | case CHECKIN_ID: return "vnd.android.cursor.item/vnd.radioactiveyak.checkin";
91 | default: throw new IllegalArgumentException("Unsupported URI: " + uri);
92 | }
93 | }
94 |
95 |
96 | @Override
97 | public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sort) {
98 | SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
99 | qb.setTables(CHECKINS_TABLE);
100 |
101 | // If this is a row query, limit the result set to the passed in row.
102 | switch (uriMatcher.match(uri)) {
103 | case CHECKIN_ID: qb.appendWhere(KEY_REFERENCE + "=" + uri.getPathSegments().get(1));
104 | break;
105 | default : break;
106 | }
107 |
108 | // If no sort order is specified sort by date / time
109 | String orderBy;
110 | if (TextUtils.isEmpty(sort)) {
111 | orderBy = KEY_TIME_STAMP + " ASC";
112 | } else {
113 | orderBy = sort;
114 | }
115 |
116 | // Apply the query to the underlying database.
117 | Cursor c = qb.query(checkinsDB,
118 | projection,
119 | selection, selectionArgs,
120 | null, null, orderBy);
121 |
122 | // Register the contexts ContentResolver to be notified if
123 | // the cursor result set changes.
124 | c.setNotificationUri(getContext().getContentResolver(), uri);
125 |
126 | // Return a cursor to the query result.
127 | return c;
128 | }
129 |
130 | @Override
131 | public Uri insert(Uri _uri, ContentValues _initialValues) {
132 | // Insert the new row, will return the row number if successful.
133 | long rowID = checkinsDB.insert(CHECKINS_TABLE, "checkin", _initialValues);
134 |
135 | // Return a URI to the newly inserted row on success.
136 | if (rowID > 0) {
137 | Uri uri = ContentUris.withAppendedId(CONTENT_URI, rowID);
138 | getContext().getContentResolver().notifyChange(uri, null);
139 | return uri;
140 | }
141 | throw new SQLException("Failed to insert row into " + _uri);
142 | }
143 |
144 | @Override
145 | public int delete(Uri uri, String where, String[] whereArgs) {
146 | int count;
147 |
148 | switch (uriMatcher.match(uri)) {
149 | case CHECKINS:
150 | count = checkinsDB.delete(CHECKINS_TABLE, where, whereArgs);
151 | break;
152 |
153 | case CHECKIN_ID:
154 | String segment = uri.getPathSegments().get(1);
155 | count = checkinsDB.delete(CHECKINS_TABLE, KEY_REFERENCE + "="
156 | + segment
157 | + (!TextUtils.isEmpty(where) ? " AND ("
158 | + where + ')' : ""), whereArgs);
159 | break;
160 |
161 | default: throw new IllegalArgumentException("Unsupported URI: " + uri);
162 | }
163 |
164 | getContext().getContentResolver().notifyChange(uri, null);
165 | return count;
166 | }
167 |
168 | @Override
169 | public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
170 | int count;
171 | switch (uriMatcher.match(uri)) {
172 | case CHECKINS: count = checkinsDB.update(CHECKINS_TABLE, values, where, whereArgs);
173 | break;
174 |
175 | case CHECKIN_ID: String segment = uri.getPathSegments().get(1);
176 | count = checkinsDB.update(CHECKINS_TABLE, values, KEY_REFERENCE
177 | + "=" + segment
178 | + (!TextUtils.isEmpty(where) ? " AND ("
179 | + where + ')' : ""), whereArgs);
180 | break;
181 |
182 | default: throw new IllegalArgumentException("Unknown URI " + uri);
183 | }
184 |
185 | getContext().getContentResolver().notifyChange(uri, null);
186 | return count;
187 | }
188 |
189 | // Helper class for opening, creating, and managing database version control
190 | private static class CheckinsDatabaseHelper extends SQLiteOpenHelper {
191 | private static final String DATABASE_CREATE =
192 | "create table " + CHECKINS_TABLE + " ("
193 | + KEY_REFERENCE + " TEXT primary key, "
194 | + KEY_ID + " TEXT, "
195 | + KEY_TIME_STAMP + " LONG); ";
196 |
197 | public CheckinsDatabaseHelper(Context context, String name, CursorFactory factory, int version) {
198 | super(context, name, factory, version);
199 | }
200 |
201 | @Override
202 | public void onCreate(SQLiteDatabase db) {
203 | db.execSQL(DATABASE_CREATE);
204 | }
205 |
206 | @Override
207 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
208 | Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
209 | + newVersion + ", which will destroy all old data");
210 |
211 | db.execSQL("DROP TABLE IF EXISTS " + CHECKINS_TABLE);
212 | onCreate(db);
213 | }
214 | }
215 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/receivers/BootReceiver.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.receivers;
18 |
19 | import android.app.PendingIntent;
20 | import android.content.BroadcastReceiver;
21 | import android.content.Context;
22 | import android.content.Intent;
23 | import android.content.SharedPreferences;
24 | import android.location.LocationManager;
25 |
26 | import com.radioactiveyak.location_best_practices.PlacesConstants;
27 | import com.radioactiveyak.location_best_practices.utils.PlatformSpecificImplementationFactory;
28 | import com.radioactiveyak.location_best_practices.utils.base.LocationUpdateRequester;
29 |
30 | /**
31 | * This Receiver class is designed to listen for system boot.
32 | *
33 | * If the app has been run at least once, the passive location
34 | * updates should be enabled after a reboot.
35 | */
36 | public class BootReceiver extends BroadcastReceiver {
37 | @Override
38 | public void onReceive(Context context, Intent intent) {
39 | SharedPreferences prefs = context.getSharedPreferences(PlacesConstants.SHARED_PREFERENCE_FILE, Context.MODE_PRIVATE);
40 | boolean runOnce = prefs.getBoolean(PlacesConstants.SP_KEY_RUN_ONCE, false);
41 |
42 | if (runOnce) {
43 | LocationManager locationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
44 |
45 | // Instantiate a Location Update Requester class based on the available platform version.
46 | // This will be used to request location updates.
47 | LocationUpdateRequester locationUpdateRequester = PlatformSpecificImplementationFactory.getLocationUpdateRequester(locationManager);
48 |
49 | // Check the Shared Preferences to see if we are updating location changes.
50 | boolean followLocationChanges = prefs.getBoolean(PlacesConstants.SP_KEY_FOLLOW_LOCATION_CHANGES, true);
51 |
52 | if (followLocationChanges) {
53 | // Passive location updates from 3rd party apps when the Activity isn't visible.
54 | Intent passiveIntent = new Intent(context, PassiveLocationChangedReceiver.class);
55 | PendingIntent locationListenerPassivePendingIntent = PendingIntent.getActivity(context, 0, passiveIntent, PendingIntent.FLAG_UPDATE_CURRENT);
56 | locationUpdateRequester.requestPassiveLocationUpdates(PlacesConstants.PASSIVE_MAX_TIME, PlacesConstants.PASSIVE_MAX_DISTANCE, locationListenerPassivePendingIntent);
57 | }
58 | }
59 | }
60 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/receivers/ConnectivityChangedReceiver.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.receivers;
18 |
19 | import android.content.BroadcastReceiver;
20 | import android.content.ComponentName;
21 | import android.content.Context;
22 | import android.content.Intent;
23 | import android.content.pm.PackageManager;
24 | import android.net.ConnectivityManager;
25 | import android.net.NetworkInfo;
26 |
27 | import com.radioactiveyak.location_best_practices.services.PlaceCheckinService;
28 |
29 | /**
30 | * This Receiver class is designed to listen for changes in connectivity.
31 | *
32 | * When we lose connectivity the relevant Service classes will automatically
33 | * disable passive Location updates and queue pending checkins.
34 | *
35 | * This class will restart the checkin service to retry pending checkins
36 | * and re-enables passive location updates.
37 | */
38 | public class ConnectivityChangedReceiver extends BroadcastReceiver {
39 | @Override
40 | public void onReceive(Context context, Intent intent) {
41 | ConnectivityManager cm = (ConnectivityManager)context.
42 | getSystemService(Context.CONNECTIVITY_SERVICE);
43 |
44 | // Check if we are connected to an active data network.
45 | NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
46 | boolean isConnected = activeNetwork != null && activeNetwork.isConnectedOrConnecting();
47 |
48 | if (isConnected) {
49 | PackageManager pm = context.getPackageManager();
50 |
51 | ComponentName connectivityReceiver = new ComponentName(context, ConnectivityChangedReceiver.class);
52 | ComponentName locationReceiver = new ComponentName(context, LocationChangedReceiver.class);
53 | ComponentName passiveLocationReceiver = new ComponentName(context, PassiveLocationChangedReceiver.class);
54 |
55 | // The default state for this Receiver is disabled. it is only
56 | // enabled when a Service disables updates pending connectivity.
57 | pm.setComponentEnabledSetting(connectivityReceiver,
58 | PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
59 | PackageManager.DONT_KILL_APP);
60 |
61 | // The default state for the Location Receiver is enabled. it is only
62 | // disabled when a Service disables updates pending connectivity.
63 | pm.setComponentEnabledSetting(locationReceiver,
64 | PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
65 | PackageManager.DONT_KILL_APP);
66 |
67 | // The default state for the Location Receiver is enabled. it is only
68 | // disabled when a Service disables updates pending connectivity.
69 | pm.setComponentEnabledSetting(passiveLocationReceiver,
70 | PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
71 | PackageManager.DONT_KILL_APP);
72 |
73 | // Commit any queued checkins now that we have connectivity
74 | Intent checkinServiceIntent = new Intent(context, PlaceCheckinService.class);
75 | context.startService(checkinServiceIntent);
76 | }
77 | }
78 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/receivers/LocationChangedReceiver.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.receivers;
18 |
19 | import android.content.BroadcastReceiver;
20 | import android.content.Context;
21 | import android.content.Intent;
22 | import android.location.Location;
23 | import android.location.LocationManager;
24 | import android.util.Log;
25 |
26 | import com.radioactiveyak.location_best_practices.PlacesConstants;
27 | import com.radioactiveyak.location_best_practices.services.EclairPlacesUpdateService;
28 | import com.radioactiveyak.location_best_practices.services.PlacesUpdateService;
29 |
30 | /**
31 | * This Receiver class is used to listen for Broadcast Intents that announce
32 | * that a location change has occurred. This is used instead of a LocationListener
33 | * within an Activity is our only action is to start a service.
34 | */
35 | public class LocationChangedReceiver extends BroadcastReceiver {
36 |
37 | protected static String TAG = "LocationChangedReceiver";
38 |
39 | /**
40 | * When a new location is received, extract it from the Intent and use
41 | * it to start the Service used to update the list of nearby places.
42 | *
43 | * This is the Active receiver, used to receive Location updates when
44 | * the Activity is visible.
45 | */
46 | @Override
47 | public void onReceive(Context context, Intent intent) {
48 | String locationKey = LocationManager.KEY_LOCATION_CHANGED;
49 | String providerEnabledKey = LocationManager.KEY_PROVIDER_ENABLED;
50 | if (intent.hasExtra(providerEnabledKey)) {
51 | if (!intent.getBooleanExtra(providerEnabledKey, true)) {
52 | Intent providerDisabledIntent = new Intent(PlacesConstants.ACTIVE_LOCATION_UPDATE_PROVIDER_DISABLED);
53 | context.sendBroadcast(providerDisabledIntent);
54 | }
55 | }
56 | if (intent.hasExtra(locationKey)) {
57 | Location location = (Location)intent.getExtras().get(locationKey);
58 | Log.d(TAG, "Actively Updating place list");
59 | Intent updateServiceIntent = new Intent(context, PlacesConstants.SUPPORTS_ECLAIR ? EclairPlacesUpdateService.class : PlacesUpdateService.class);
60 | updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_LOCATION, location);
61 | updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_RADIUS, PlacesConstants.DEFAULT_RADIUS);
62 | updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_FORCEREFRESH, true);
63 | context.startService(updateServiceIntent);
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/receivers/NewCheckinReceiver.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.receivers;
18 |
19 | import com.radioactiveyak.location_best_practices.PlacesConstants;
20 | import com.radioactiveyak.location_best_practices.services.CheckinNotificationService;
21 |
22 | import android.content.BroadcastReceiver;
23 | import android.content.Context;
24 | import android.content.Intent;
25 |
26 | /**
27 | * Manifest Receiver that listens for broadcasts announcing a successful checkin.
28 | * This class starts the CheckinNotification Service that will trigger a notification
29 | * announcing the successful checkin. We don't want notifications for this app to
30 | * be announced while the app is running, so this receiver is disabled whenever the
31 | * main Activity is visible.
32 | */
33 | public class NewCheckinReceiver extends BroadcastReceiver {
34 |
35 | protected static String TAG = "NewCheckinReceiver";
36 |
37 | /**
38 | * When a successful checkin is announced, extract the unique ID of the place
39 | * that's been checked in to, and pass this value to the CheckinNotification Service
40 | * when you start it.
41 | */
42 | @Override
43 | public void onReceive(Context context, Intent intent) {
44 | String id = intent.getStringExtra(PlacesConstants.EXTRA_KEY_ID);
45 | if (id != null) {
46 | Intent serviceIntent = new Intent(context, CheckinNotificationService.class);
47 | serviceIntent.putExtra(PlacesConstants.EXTRA_KEY_ID, id);
48 | context.startService(serviceIntent);
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/receivers/PassiveLocationChangedReceiver.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.receivers;
18 |
19 | import com.radioactiveyak.location_best_practices.PlacesConstants;
20 | import com.radioactiveyak.location_best_practices.services.EclairPlacesUpdateService;
21 | import com.radioactiveyak.location_best_practices.services.PlacesUpdateService;
22 | import com.radioactiveyak.location_best_practices.utils.LegacyLastLocationFinder;
23 |
24 | import android.content.BroadcastReceiver;
25 | import android.content.Context;
26 | import android.content.Intent;
27 | import android.content.SharedPreferences;
28 | import android.location.Location;
29 | import android.location.LocationManager;
30 | import android.util.Log;
31 |
32 | /**
33 | * This Receiver class is used to listen for Broadcast Intents that announce
34 | * that a location change has occurred while this application isn't visible.
35 | *
36 | * Where possible, this is triggered by a Passive Location listener.
37 | */
38 | public class PassiveLocationChangedReceiver extends BroadcastReceiver {
39 |
40 | protected static String TAG = "PassiveLocationChangedReceiver";
41 |
42 | /**
43 | * When a new location is received, extract it from the Intent and use
44 | * it to start the Service used to update the list of nearby places.
45 | *
46 | * This is the Passive receiver, used to receive Location updates from
47 | * third party apps when the Activity is not visible.
48 | */
49 | @Override
50 | public void onReceive(Context context, Intent intent) {
51 | String key = LocationManager.KEY_LOCATION_CHANGED;
52 | Location location = null;
53 |
54 | if (intent.hasExtra(key)) {
55 | // This update came from Passive provider, so we can extract the location
56 | // directly.
57 | location = (Location)intent.getExtras().get(key);
58 | }
59 | else {
60 | // This update came from a recurring alarm. We need to determine if there
61 | // has been a more recent Location received than the last location we used.
62 |
63 | // Get the best last location detected from the providers.
64 | LegacyLastLocationFinder lastLocationFinder = new LegacyLastLocationFinder(context);
65 | location = lastLocationFinder.getLastBestLocation(PlacesConstants.MAX_DISTANCE, System.currentTimeMillis()-PlacesConstants.MAX_TIME);
66 | SharedPreferences prefs = context.getSharedPreferences(PlacesConstants.SHARED_PREFERENCE_FILE, Context.MODE_PRIVATE);
67 |
68 | // Get the last location we used to get a listing.
69 | long lastTime = prefs.getLong(PlacesConstants.SP_KEY_LAST_LIST_UPDATE_TIME, Long.MIN_VALUE);
70 | long lastLat = prefs.getLong(PlacesConstants.SP_KEY_LAST_LIST_UPDATE_LAT, Long.MIN_VALUE);
71 | long lastLng = prefs.getLong(PlacesConstants.SP_KEY_LAST_LIST_UPDATE_LNG, Long.MIN_VALUE);
72 | Location lastLocation = new Location(PlacesConstants.CONSTRUCTED_LOCATION_PROVIDER);
73 | lastLocation.setLatitude(lastLat);
74 | lastLocation.setLongitude(lastLng);
75 |
76 | // Check if the last location detected from the providers is either too soon, or too close to the last
77 | // value we used. If it is within those thresholds we set the location to null to prevent the update
78 | // Service being run unnecessarily (and spending battery on data transfers).
79 | if ((lastTime > System.currentTimeMillis()-PlacesConstants.MAX_TIME) ||
80 | (lastLocation.distanceTo(location) < PlacesConstants.MAX_DISTANCE))
81 | location = null;
82 | }
83 |
84 | // Start the Service used to find nearby points of interest based on the last detected location.
85 | if (location != null) {
86 | Log.d(TAG, "Passivly updating place list.");
87 | Intent updateServiceIntent = new Intent(context, PlacesConstants.SUPPORTS_ECLAIR ? EclairPlacesUpdateService.class : PlacesUpdateService.class);
88 | updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_LOCATION, location);
89 | updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_RADIUS, PlacesConstants.DEFAULT_RADIUS);
90 | updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_FORCEREFRESH, false);
91 | context.startService(updateServiceIntent);
92 | }
93 | }
94 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/receivers/PowerStateChangedReceiver.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.receivers;
18 |
19 | import android.content.BroadcastReceiver;
20 | import android.content.ComponentName;
21 | import android.content.Context;
22 | import android.content.Intent;
23 | import android.content.pm.PackageManager;
24 |
25 | /**
26 | * The manifest Receiver is used to detect changes in battery state.
27 | * When the system broadcasts a "Battery Low" warning we turn off
28 | * the passive location updates to conserve battery when the app is
29 | * in the background.
30 | *
31 | * When the system broadcasts "Battery OK" to indicate the battery
32 | * has returned to an okay state, the passive location updates are
33 | * resumed.
34 | */
35 | public class PowerStateChangedReceiver extends BroadcastReceiver {
36 | @Override
37 | public void onReceive(Context context, Intent intent) {
38 | boolean batteryLow = intent.getAction().equals(Intent.ACTION_BATTERY_LOW);
39 |
40 | PackageManager pm = context.getPackageManager();
41 | ComponentName passiveLocationReceiver = new ComponentName(context, PassiveLocationChangedReceiver.class);
42 |
43 | // Disable the passive location update receiver when the battery state is low.
44 | // Disabling the Receiver will prevent the app from initiating the background
45 | // downloads of nearby locations.
46 | pm.setComponentEnabledSetting(passiveLocationReceiver,
47 | batteryLow ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
48 | PackageManager.DONT_KILL_APP);
49 | }
50 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/services/CheckinNotificationService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.services;
18 |
19 | import android.app.IntentService;
20 | import android.app.Notification;
21 | import android.app.NotificationManager;
22 | import android.app.PendingIntent;
23 | import android.content.ContentResolver;
24 | import android.content.Context;
25 | import android.content.Intent;
26 | import android.database.Cursor;
27 | import android.net.Uri;
28 |
29 | import com.radioactiveyak.location_best_practices.PlacesConstants;
30 | import com.radioactiveyak.location_best_practices.R;
31 | import com.radioactiveyak.location_best_practices.UI.PlaceActivity;
32 | import com.radioactiveyak.location_best_practices.content_providers.PlaceDetailsContentProvider;
33 |
34 | /**
35 | * Service that handles background checkin notifications.
36 | * This Service will be started by the {@link NewCheckinReceiver}
37 | * when the Application isn't visible and trigger a Notification
38 | * telling the user that they have been checked in to a venue.
39 | * This typically happens if an earlier checkin has failed
40 | * (due to lack of connectivity, server error, etc.).
41 | *
42 | * If your app lets users post reviews / ratings / etc. This
43 | * Service can be used to notify them once they have been successfully
44 | * posted.
45 | *
46 | * TODO Update the Notification to display a richer payload.
47 | * TODO Create a variation of this Notification for Honeycomb+ devices.
48 | */
49 | public class CheckinNotificationService extends IntentService {
50 |
51 | protected static String TAG = "CheckinNotificationService";
52 |
53 | protected ContentResolver contentResolver;
54 | protected NotificationManager notificationManager;
55 | protected String[] projection;
56 |
57 | public CheckinNotificationService() {
58 | super(TAG);
59 | }
60 |
61 | @Override
62 | public void onCreate() {
63 | super.onCreate();
64 | contentResolver = getContentResolver();
65 | notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
66 |
67 | projection = new String[] {PlaceDetailsContentProvider.KEY_ID, PlaceDetailsContentProvider.KEY_NAME};
68 | }
69 |
70 | /**
71 | * {@inheritDoc}
72 | * Extract the name of the venue based on the ID specified in the broadcast Checkin Intent
73 | * and use it to display a Notification.
74 | */
75 | @Override
76 | protected void onHandleIntent(Intent intent) {
77 | String id = intent.getStringExtra(PlacesConstants.EXTRA_KEY_ID);
78 |
79 | // Query the PlaceDetailsContentProvider for the specified venue.
80 | Uri uri = Uri.withAppendedPath(PlaceDetailsContentProvider.CONTENT_URI, id);
81 | Cursor cursor = contentResolver.query(uri, projection, null, null, null);
82 |
83 | if (cursor.moveToFirst()) {
84 | // Construct a Pending Intent for the Notification. This will ensure that when
85 | // the notification is clicked, the application will open and the venue we've
86 | // checked in to will be displayed.
87 | Intent contentIntent = new Intent(this, PlaceActivity.class);
88 | contentIntent.putExtra(PlacesConstants.EXTRA_KEY_ID, id);
89 | PendingIntent contentPendingIntent = PendingIntent.getActivity(this, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT);
90 |
91 | // Construct the notification.
92 | String checkinText = getResources().getText(R.string.checkin_text).toString();
93 | String placeName = cursor.getString(cursor.getColumnIndex(PlaceDetailsContentProvider.KEY_NAME));
94 | String tickerText = checkinText + placeName;
95 | Notification notification = new Notification(R.drawable.icon, tickerText, System.currentTimeMillis());
96 | // TODO Update this with new API.
97 | // notification.setLatestEventInfo(this, checkinText, placeName, contentPendingIntent);
98 |
99 | // Trigger the notification.
100 | notificationManager.notify(PlacesConstants.CHECKIN_NOTIFICATION, notification);
101 | }
102 | }
103 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/services/EclairPlacesUpdateService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.services;
18 |
19 | /**
20 | * {@inheritDoc}
21 | * Extends the {@link PlacesUpdateService} to force Intent redelivery
22 | * on Eclaire+ devices (where this defaults to false).
23 | */
24 | public class EclairPlacesUpdateService extends PlacesUpdateService {
25 | @Override
26 | protected void setIntentRedeliveryMode(boolean enable) {
27 | setIntentRedelivery(true);
28 | }
29 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/services/PlaceCheckinService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.services;
18 |
19 | import java.io.IOException;
20 | import java.net.URI;
21 | import java.net.URISyntaxException;
22 | import java.net.URLEncoder;
23 | import java.util.ArrayList;
24 |
25 | // TODO Update this with new API
26 | import org.apache.http.HttpResponse;
27 | import org.apache.http.client.ClientProtocolException;
28 | import org.apache.http.client.HttpClient;
29 | import org.apache.http.client.methods.HttpPost;
30 | import org.apache.http.entity.StringEntity;
31 | import org.apache.http.impl.client.DefaultHttpClient;
32 |
33 | import android.app.AlarmManager;
34 | import android.app.IntentService;
35 | import android.app.PendingIntent;
36 | import android.content.ComponentName;
37 | import android.content.ContentResolver;
38 | import android.content.ContentValues;
39 | import android.content.Context;
40 | import android.content.Intent;
41 | import android.content.SharedPreferences;
42 | import android.content.SharedPreferences.Editor;
43 | import android.content.pm.PackageManager;
44 | import android.database.Cursor;
45 | import android.net.ConnectivityManager;
46 | import android.net.NetworkInfo;
47 | import android.util.Log;
48 |
49 | import com.radioactiveyak.location_best_practices.PlacesConstants;
50 | import com.radioactiveyak.location_best_practices.content_providers.QueuedCheckinsContentProvider;
51 | import com.radioactiveyak.location_best_practices.receivers.ConnectivityChangedReceiver;
52 | import com.radioactiveyak.location_best_practices.utils.PlatformSpecificImplementationFactory;
53 | import com.radioactiveyak.location_best_practices.utils.base.SharedPreferenceSaver;
54 |
55 | /**
56 | * Service that notifies the underlying web service to Checkin to the specified venue.
57 | * TODO Replace or augment with a Service that performs ratings / reviews / etc.
58 | */
59 | public class PlaceCheckinService extends IntentService {
60 |
61 | protected static String TAG = "PlaceCheckinService";
62 |
63 | protected ContentResolver contentResolver;
64 | protected ConnectivityManager cm;
65 | protected AlarmManager alarmManager;
66 | protected PendingIntent retryQueuedCheckinsPendingIntent;
67 | protected SharedPreferences sharedPreferences;
68 | protected Editor sharedPreferencesEditor;
69 | protected SharedPreferenceSaver sharedPreferenceSaver;
70 |
71 | public PlaceCheckinService() {
72 | super(TAG);
73 | }
74 |
75 | @Override
76 | public void onCreate() {
77 | super.onCreate();
78 | contentResolver = getContentResolver();
79 | cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
80 | alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
81 |
82 | sharedPreferences = getSharedPreferences(PlacesConstants.SHARED_PREFERENCE_FILE, Context.MODE_PRIVATE);
83 | sharedPreferencesEditor = sharedPreferences.edit();
84 | sharedPreferenceSaver = PlatformSpecificImplementationFactory.getSharedPreferenceSaver(this);
85 |
86 | Intent retryIntent = new Intent(PlacesConstants.RETRY_QUEUED_CHECKINS_ACTION);
87 | retryQueuedCheckinsPendingIntent = PendingIntent.getBroadcast(this, 0, retryIntent, PendingIntent.FLAG_UPDATE_CURRENT);
88 | }
89 |
90 | /**
91 | * {@inheritDoc}
92 | * Perform a checkin the specified venue. If the checkin fails, add it to the queue and
93 | * set an alarm to retry.
94 | *
95 | * Query the checkin queue to see if there are pending checkins to be retried.
96 | */
97 | @Override
98 | protected void onHandleIntent(Intent intent) {
99 | // Retrieve the details for the checkin to perform.
100 | String reference = intent.getStringExtra(PlacesConstants.EXTRA_KEY_REFERENCE);
101 | String id = intent.getStringExtra(PlacesConstants.EXTRA_KEY_ID);
102 | long timeStamp = intent.getLongExtra(PlacesConstants.EXTRA_KEY_TIME_STAMP, 0);
103 |
104 | // Check if we're running in the foreground, if not, check if
105 | // we have permission to do background updates.
106 | boolean backgroundAllowed = cm.getBackgroundDataSetting();
107 | boolean inBackground = sharedPreferences.getBoolean(PlacesConstants.EXTRA_KEY_IN_BACKGROUND, true);
108 |
109 | if (reference != null && !backgroundAllowed && inBackground) {
110 | addToQueue(timeStamp, reference, id);
111 | return;
112 | }
113 |
114 | // Check to see if we are connected to a data network.
115 | NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
116 | boolean isConnected = activeNetwork != null && activeNetwork.isConnectedOrConnecting();
117 |
118 | // If we're not connected then disable the retry Alarm, enable the Connectivity Changed Receiver
119 | // and add the new checkin directly to the queue. The Connectivity Changed Receiver will listen
120 | // for when we connect to a network and start this service to retry the checkins.
121 | if (!isConnected) {
122 | // No connection so no point triggering an alarm to retry until we're connected.
123 | alarmManager.cancel(retryQueuedCheckinsPendingIntent);
124 |
125 | // Enable the Connectivity Changed Receiver to listen for connection to a network
126 | // so we can commit the pending checkins.
127 | PackageManager pm = getPackageManager();
128 | ComponentName connectivityReceiver = new ComponentName(this, ConnectivityChangedReceiver.class);
129 | pm.setComponentEnabledSetting(connectivityReceiver,
130 | PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
131 | PackageManager.DONT_KILL_APP);
132 |
133 | // Add this checkin to the queue.
134 | addToQueue(timeStamp, reference, id);
135 | }
136 | else {
137 | // Execute the checkin. If it fails, add it to the retry queue.
138 | if (reference != null) {
139 | if (!checkin(timeStamp, reference, id))
140 | addToQueue(timeStamp, reference, id);
141 | }
142 |
143 | // Retry the queued checkins.
144 | ArrayList successfulCheckins = new ArrayList();
145 | Cursor queuedCheckins = contentResolver.query(QueuedCheckinsContentProvider.CONTENT_URI, null, null, null, null);
146 | try {
147 | // Retry each checkin.
148 | while (queuedCheckins.moveToNext()) {
149 | long queuedTimeStamp = queuedCheckins.getLong(queuedCheckins.getColumnIndex(QueuedCheckinsContentProvider.KEY_TIME_STAMP));
150 | String queuedReference = queuedCheckins.getString(queuedCheckins.getColumnIndex(QueuedCheckinsContentProvider.KEY_REFERENCE));
151 | String queuedId = queuedCheckins.getString(queuedCheckins.getColumnIndex(QueuedCheckinsContentProvider.KEY_ID));
152 | if (queuedReference == null || checkin(queuedTimeStamp, queuedReference, queuedId))
153 | successfulCheckins.add(queuedReference);
154 | }
155 |
156 | // Delete the queued checkins that were successful.
157 | if (successfulCheckins.size() > 0) {
158 | StringBuilder sb = new StringBuilder("("+QueuedCheckinsContentProvider.KEY_REFERENCE + "='" + successfulCheckins.get(0) + "'");
159 | for (int i = 1; i < successfulCheckins.size(); i++)
160 | sb.append(" OR " + QueuedCheckinsContentProvider.KEY_REFERENCE + " = '" + successfulCheckins.get(i) + "'");
161 | sb.append(")");
162 | int deleteCount = contentResolver.delete(QueuedCheckinsContentProvider.CONTENT_URI, sb.toString(), null);
163 | Log.d(TAG, "Deleted: " + deleteCount);
164 | }
165 |
166 | // If there are still queued checkins then set a non-waking alarm to retry them.
167 | queuedCheckins.requery();
168 | if (queuedCheckins.getCount() > 0) {
169 | long triggerAtTime = System.currentTimeMillis() + PlacesConstants.CHECKIN_RETRY_INTERVAL;
170 | alarmManager.set(AlarmManager.ELAPSED_REALTIME, triggerAtTime, retryQueuedCheckinsPendingIntent);
171 | }
172 | else
173 | alarmManager.cancel(retryQueuedCheckinsPendingIntent);
174 | }
175 | finally {
176 | queuedCheckins.close();
177 | }
178 | }
179 | }
180 |
181 | /**
182 | * Performs a checkin for the specified venue at the specified time.
183 | * TODO Add additional checkin / ratings / review details as appropriate for your service.
184 | * @param timeStamp Checkin timestamp
185 | * @param reference Checkin venue reference
186 | * @param id Checkin venue unique identifier
187 | * @return Successfully checked in
188 | */
189 | protected boolean checkin(long timeStamp, String reference, String id) {
190 | if (reference != null) {
191 | try {
192 | // Construct the URI required to perform a checkin.
193 | // TODO Replace this with the checkin URI for your own web service.
194 | URI uri = new URI(PlacesConstants.PLACES_CHECKIN_URI + PlacesConstants.PLACES_API_KEY);
195 |
196 | // Construct the payload
197 | // TODO Replace with your own payload
198 | StringBuilder postData = new StringBuilder("\n");
199 | postData.append(" ");
200 | postData.append(URLEncoder.encode(reference, "UTF-8"));
201 | postData.append("\n");
202 | postData.append("");
203 |
204 | // Construct and execute the HTTP Post
205 | HttpClient httpClient = new DefaultHttpClient();
206 | HttpPost httppost = new HttpPost(uri);
207 | httppost.setEntity(new StringEntity(postData.toString()));
208 | HttpResponse response = httpClient.execute(httppost);
209 |
210 | // If the post was successful, check if this is the newest checkin, and if so save it and broadcast
211 | // an Intent to notify the application.
212 | if (response.getStatusLine().getReasonPhrase().equals(PlacesConstants.PLACES_CHECKIN_OK_STATUS)) {
213 | long lastCheckin = sharedPreferences.getLong(PlacesConstants.SP_KEY_LAST_CHECKIN_TIMESTAMP, 0);
214 | if (timeStamp > lastCheckin) {
215 | sharedPreferencesEditor.putLong(PlacesConstants.SP_KEY_LAST_CHECKIN_TIMESTAMP, timeStamp);
216 | sharedPreferencesEditor.putString(PlacesConstants.SP_KEY_LAST_CHECKIN_ID, id);
217 | sharedPreferenceSaver.savePreferences(sharedPreferencesEditor, false);
218 | Intent intent = new Intent(PlacesConstants.NEW_CHECKIN_ACTION);
219 | intent.putExtra(PlacesConstants.EXTRA_KEY_ID, id);
220 | sendBroadcast(intent);
221 | }
222 | return true;
223 | }
224 | // If the checkin fails return false.
225 | else
226 | return false;
227 | } catch (ClientProtocolException e) {
228 | Log.e(TAG, e.getMessage());
229 | } catch (IOException e) {
230 | Log.e(TAG, e.getMessage());
231 | } catch (URISyntaxException e) {
232 | Log.e(TAG, e.getMessage());
233 | }
234 | }
235 | return false;
236 | }
237 |
238 | /**
239 | * Adds a checkin to the {@link QueuedCheckinsContentProvider} to be retried.
240 | * @param timeStamp Checkin timestamp
241 | * @param reference Checkin venue reference
242 | * @param id Checkin venue unique identifier
243 | * @return Successfully added to the queue
244 | */
245 | protected boolean addToQueue(long timeStamp, String reference, String id) {
246 | // Construct the new / updated row.
247 | ContentValues values = new ContentValues();
248 | values.put(QueuedCheckinsContentProvider.KEY_REFERENCE, reference);
249 | values.put(QueuedCheckinsContentProvider.KEY_ID, id);
250 | values.put(QueuedCheckinsContentProvider.KEY_TIME_STAMP, timeStamp);
251 |
252 | String where = QueuedCheckinsContentProvider.KEY_REFERENCE + " = '" + reference + "'";
253 |
254 | // Update the existing checkin for this venue or add the venue to the queue.
255 | try {
256 | if (contentResolver.update(QueuedCheckinsContentProvider.CONTENT_URI, values, where, null) == 0) {
257 | if (contentResolver.insert(QueuedCheckinsContentProvider.CONTENT_URI, values) != null)
258 | return true;
259 | return true;
260 | }
261 | }
262 | catch (Exception ex) {
263 | Log.e(TAG, "Queuing checkin for " + reference + " failed.");
264 | }
265 |
266 | return false;
267 | }
268 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/services/PlaceDetailsUpdateService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.services;
18 |
19 | import java.io.IOException;
20 | import java.io.InputStream;
21 | import java.net.HttpURLConnection;
22 | import java.net.MalformedURLException;
23 | import java.net.URL;
24 | import java.net.URLConnection;
25 |
26 | import javax.net.ssl.HttpsURLConnection;
27 |
28 | import org.xmlpull.v1.XmlPullParser;
29 | import org.xmlpull.v1.XmlPullParserException;
30 | import org.xmlpull.v1.XmlPullParserFactory;
31 |
32 | import android.app.IntentService;
33 | import android.content.ContentResolver;
34 | import android.content.ContentValues;
35 | import android.content.Context;
36 | import android.content.Intent;
37 | import android.content.SharedPreferences;
38 | import android.database.Cursor;
39 | import android.location.Location;
40 | import android.net.ConnectivityManager;
41 | import android.net.Uri;
42 | import android.util.Log;
43 |
44 | import com.radioactiveyak.location_best_practices.PlacesConstants;
45 | import com.radioactiveyak.location_best_practices.content_providers.PlaceDetailsContentProvider;
46 |
47 | /**
48 | * Service that queries the underlying web service to retrieve the full
49 | * details for the specified place / venue.
50 | * This Service is called by the {@link PlacesUpdateService} to prefetch details
51 | * for the nearby venues, or by the {@link PlacesActivity} and {@link PlaceDetailsFragment}
52 | * to retrieve the details for the selected place.
53 | * TODO Replace the URL and XML parsing to match the details available from your service.
54 | */
55 | public class PlaceDetailsUpdateService extends IntentService {
56 |
57 | protected static String TAG = "PlaceDetailsIntentService";
58 |
59 | protected ContentResolver contentResolver;
60 | protected String[] projection;
61 | protected SharedPreferences prefs;
62 | protected ConnectivityManager cm;
63 |
64 | public PlaceDetailsUpdateService() {
65 | super(TAG);
66 | }
67 |
68 | @Override
69 | public void onCreate() {
70 | super.onCreate();
71 | contentResolver = getContentResolver();
72 | projection = new String[] {PlaceDetailsContentProvider.KEY_LAST_UPDATE_TIME, PlaceDetailsContentProvider.KEY_FORCE_CACHE};
73 | cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
74 | prefs = getSharedPreferences(PlacesConstants.SHARED_PREFERENCE_FILE, Context.MODE_PRIVATE);
75 | }
76 |
77 | /**
78 | * {@inheritDoc}
79 | * Check to see if we already have these details, and if so, whether or not we should update them.
80 | * Initiates an update where appropriate.
81 | */
82 | @Override
83 | protected void onHandleIntent(Intent intent) {
84 | // Check if we're running in the foreground, if not, check if
85 | // we have permission to do background updates.
86 | boolean backgroundAllowed = cm.getBackgroundDataSetting();
87 | boolean inBackground = prefs.getBoolean(PlacesConstants.EXTRA_KEY_IN_BACKGROUND, true);
88 |
89 | if (!backgroundAllowed && inBackground) return;
90 |
91 | // Extract the identifiers for the place to refresh the detail for.
92 | String reference = intent.getStringExtra(PlacesConstants.EXTRA_KEY_REFERENCE);
93 | String id = intent.getStringExtra(PlacesConstants.EXTRA_KEY_ID);
94 |
95 | // If this is a forced refresh (typically because the details UI is being displayed
96 | // then do an update and force this into the cache.
97 | boolean forceCache = intent.getBooleanExtra(PlacesConstants.EXTRA_KEY_FORCEREFRESH, false);
98 | boolean doUpdate = id == null || forceCache;
99 |
100 | // Check to see if the latency since the last update is sufficient to perform a refresh.
101 | if (!doUpdate) {
102 | Uri uri = Uri.withAppendedPath(PlaceDetailsContentProvider.CONTENT_URI, id);
103 | Cursor cursor = contentResolver.query(uri, projection, null, null, null);
104 |
105 | try {
106 | doUpdate = true;
107 | if (cursor.moveToFirst()) {
108 | if (cursor.getLong(cursor.getColumnIndex(PlaceDetailsContentProvider.KEY_LAST_UPDATE_TIME)) > System.currentTimeMillis()-PlacesConstants.MAX_DETAILS_UPDATE_LATENCY)
109 | doUpdate = false;
110 | }
111 | }
112 | finally {
113 | cursor.close();
114 | }
115 | }
116 |
117 | // Hit the server for an update / refresh.
118 | if (doUpdate) {
119 | refreshPlaceDetails(reference, forceCache);
120 | }
121 | }
122 |
123 | /**
124 | * Request details for this place from the underlying web Service.
125 | * TODO Replace the URL and XML parsing with whatever is necessary for your service.
126 | * @param reference Reference
127 | * @param forceCache Force Cache
128 | */
129 | protected void refreshPlaceDetails(String reference, boolean forceCache) {
130 | URL url;
131 | try {
132 | // TODO Replace with your web service URL schema.
133 | String placesFeed = PlacesConstants.PLACES_DETAIL_BASE_URI + reference + PlacesConstants.PLACES_API_KEY;
134 |
135 | // Make the web query.
136 | url = new URL(placesFeed);
137 | URLConnection connection = url.openConnection();
138 | HttpsURLConnection httpConnection = (HttpsURLConnection)connection;
139 | int responseCode = httpConnection.getResponseCode();
140 |
141 | if (responseCode == HttpURLConnection.HTTP_OK) {
142 | InputStream in = httpConnection.getInputStream();
143 |
144 | // Extract the details from the returned feed.
145 | XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
146 | factory.setNamespaceAware(true);
147 | XmlPullParser xpp = factory.newPullParser();
148 |
149 | xpp.setInput(in, null);
150 | int eventType = xpp.getEventType();
151 | while (eventType != XmlPullParser.END_DOCUMENT) {
152 | if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("result")) {
153 | eventType = xpp.next();
154 | String id = "";
155 | String name = "";
156 | String vicinity = "";
157 | String types = "";
158 | String locationLat = "";
159 | String locationLng = "";
160 | String viewport = "";
161 | String icon = "";
162 | String phone = "";
163 | String address = "";
164 | float rating = 0;
165 | String placeurl = "";
166 |
167 | while (!(eventType == XmlPullParser.END_TAG && xpp.getName().equals("result"))) {
168 | if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("name"))
169 | name = xpp.nextText();
170 | else if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("vicinity"))
171 | vicinity = xpp.nextText();
172 | else if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("type"))
173 | types = types == "" ? xpp.nextText() : types + " " + xpp.nextText();
174 | else if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("lat"))
175 | locationLat = xpp.nextText();
176 | else if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("lng"))
177 | locationLng = xpp.nextText();
178 | else if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("icon"))
179 | icon = xpp.nextText();
180 | else if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("id"))
181 | id = xpp.nextText();
182 | else if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("formatted_phone_number"))
183 | phone = xpp.nextText();
184 | else if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("formatted_address"))
185 | address = xpp.nextText();
186 | else if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("url"))
187 | placeurl = xpp.nextText();
188 | else if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("rating"))
189 | rating = Float.parseFloat(xpp.nextText());
190 |
191 | eventType = xpp.next();
192 | }
193 | Location placeLocation = new Location("XML");
194 | try {
195 | placeLocation.setLatitude(Double.valueOf(locationLat));
196 | placeLocation.setLongitude(Double.valueOf(locationLng));
197 | }
198 | catch (NumberFormatException e) {
199 | Log.d(TAG, e.getMessage());
200 | }
201 | // Add the new place to the Content Provider
202 | addPlaceDetail(id, name, vicinity, types, placeLocation, viewport, icon, reference, phone, address, rating, placeurl, forceCache);
203 | }
204 | eventType = xpp.next();
205 | }
206 | }
207 | else
208 | Log.e(TAG, responseCode + ": " + httpConnection.getResponseMessage());
209 |
210 | } catch (MalformedURLException e) {
211 | Log.e(TAG, e.getMessage());
212 | } catch (IOException e) {
213 | Log.e(TAG, e.getMessage());
214 | } catch (XmlPullParserException e) {
215 | Log.e(TAG, e.getMessage());
216 | }
217 | finally {
218 | }
219 | }
220 |
221 | /**
222 | * Add details for a place / venue to the {@link PlaceDetailsContentProvider}.
223 | * TODO Update this with the details corresponding to what's available from your server.
224 | * @param id Unique identifier
225 | * @param name Name
226 | * @param vicinity Vicinity
227 | * @param types Types
228 | * @param location Location
229 | * @param viewport Viewport
230 | * @param icon Icon
231 | * @param reference Reference
232 | * @param phone Phone
233 | * @param address Address
234 | * @param rating Rating
235 | * @param url Url
236 | * @param forceCache Save to the persistent cache (do not delete)
237 | * @return Place detail has been updates or added
238 | */
239 | protected boolean addPlaceDetail(String id, String name, String vicinity, String types, Location location, String viewport, String icon, String reference, String phone, String address, float rating, String url, boolean forceCache) {
240 |
241 | // Construct the new row.
242 | ContentValues values = new ContentValues();
243 | values.put(PlaceDetailsContentProvider.KEY_ID, id);
244 | values.put(PlaceDetailsContentProvider.KEY_NAME, name);
245 | double lat = location.getLatitude();
246 | double lng = location.getLongitude();
247 | values.put(PlaceDetailsContentProvider.KEY_LOCATION_LAT, lat);
248 | values.put(PlaceDetailsContentProvider.KEY_LOCATION_LNG, lng);
249 | values.put(PlaceDetailsContentProvider.KEY_VICINITY, vicinity);
250 | values.put(PlaceDetailsContentProvider.KEY_TYPES, types);
251 | values.put(PlaceDetailsContentProvider.KEY_VIEWPORT, viewport);
252 | values.put(PlaceDetailsContentProvider.KEY_ICON, icon);
253 | values.put(PlaceDetailsContentProvider.KEY_REFERENCE, reference);
254 | values.put(PlaceDetailsContentProvider.KEY_PHONE, phone);
255 | values.put(PlaceDetailsContentProvider.KEY_ADDRESS, address);
256 | values.put(PlaceDetailsContentProvider.KEY_RATING, rating);
257 | values.put(PlaceDetailsContentProvider.KEY_URL, url);
258 | values.put(PlaceDetailsContentProvider.KEY_LAST_UPDATE_TIME, System.currentTimeMillis());
259 | values.put(PlaceDetailsContentProvider.KEY_FORCE_CACHE, forceCache);
260 |
261 | // Update the existing listing, or add a new listing.
262 | String where = PlaceDetailsContentProvider.KEY_ID + " = '" + id + "'";
263 | try {
264 | if (contentResolver.update(PlaceDetailsContentProvider.CONTENT_URI, values, where, null) == 0) {
265 | if (contentResolver.insert(PlaceDetailsContentProvider.CONTENT_URI, values) != null)
266 | return true;
267 | return true;
268 | }
269 | }
270 | catch (Exception ex) {
271 | Log.e(TAG, "Adding Detail for " + name + " failed.");
272 | }
273 |
274 | return false;
275 | }
276 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/services/PlacesUpdateService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.services;
18 |
19 | import java.io.IOException;
20 | import java.io.InputStream;
21 | import java.net.HttpURLConnection;
22 | import java.net.MalformedURLException;
23 | import java.net.URL;
24 | import java.net.URLConnection;
25 |
26 | import javax.net.ssl.HttpsURLConnection;
27 |
28 | import org.xmlpull.v1.XmlPullParser;
29 | import org.xmlpull.v1.XmlPullParserException;
30 | import org.xmlpull.v1.XmlPullParserFactory;
31 |
32 | import android.app.IntentService;
33 | import android.content.ComponentName;
34 | import android.content.ContentResolver;
35 | import android.content.ContentValues;
36 | import android.content.Context;
37 | import android.content.Intent;
38 | import android.content.IntentFilter;
39 | import android.content.SharedPreferences;
40 | import android.content.SharedPreferences.Editor;
41 | import android.content.pm.PackageManager;
42 | import android.location.Location;
43 | import android.net.ConnectivityManager;
44 | import android.net.NetworkInfo;
45 | import android.os.BatteryManager;
46 | import android.os.Bundle;
47 | import android.util.Log;
48 |
49 | import com.radioactiveyak.location_best_practices.PlacesConstants;
50 | import com.radioactiveyak.location_best_practices.content_providers.PlaceDetailsContentProvider;
51 | import com.radioactiveyak.location_best_practices.content_providers.PlacesContentProvider;
52 | import com.radioactiveyak.location_best_practices.receivers.ConnectivityChangedReceiver;
53 | import com.radioactiveyak.location_best_practices.receivers.LocationChangedReceiver;
54 | import com.radioactiveyak.location_best_practices.receivers.PassiveLocationChangedReceiver;
55 |
56 | /**
57 | * Service that requests a list of nearby locations from the underlying web service.
58 | * TODO Update the URL and XML parsing to correspond with your underlying web service.
59 | */
60 | public class PlacesUpdateService extends IntentService {
61 |
62 | protected static String TAG = "PlacesUpdateService";
63 |
64 | protected ContentResolver contentResolver;
65 | protected SharedPreferences prefs;
66 | protected Editor prefsEditor;
67 | protected ConnectivityManager cm;
68 | protected boolean lowBattery = false;
69 | protected boolean mobileData = false;
70 | protected int prefetchCount = 0;
71 |
72 | public PlacesUpdateService() {
73 | super(TAG);
74 | setIntentRedeliveryMode(false);
75 | }
76 |
77 | /**
78 | * Set the Intent Redelivery mode to true to ensure the Service starts "Sticky"
79 | * Defaults to "true" on legacy devices.
80 | */
81 | protected void setIntentRedeliveryMode(boolean enable) {}
82 |
83 | /**
84 | * Returns battery status. True if less than 10% remaining.
85 | * @param battery Battery Intent
86 | * @return Battery is low
87 | */
88 | protected boolean getIsLowBattery(Intent battery) {
89 | float pctLevel = (float)battery.getIntExtra(BatteryManager.EXTRA_LEVEL, 1) /
90 | battery.getIntExtra(BatteryManager.EXTRA_SCALE, 1);
91 | return pctLevel < 0.15;
92 | }
93 |
94 | @Override
95 | public void onCreate() {
96 | super.onCreate();
97 | cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
98 | contentResolver = getContentResolver();
99 | prefs = getSharedPreferences(PlacesConstants.SHARED_PREFERENCE_FILE, Context.MODE_PRIVATE);
100 | prefsEditor = prefs.edit();
101 | }
102 |
103 | /**
104 | * {@inheritDoc}
105 | * Checks the battery and connectivity state before removing stale venues
106 | * and initiating a server poll for new venues around the specified
107 | * location within the given radius.
108 | */
109 | @Override
110 | protected void onHandleIntent(Intent intent) {
111 | // Check if we're running in the foreground, if not, check if
112 | // we have permission to do background updates.
113 | boolean backgroundAllowed = cm.getBackgroundDataSetting();
114 | boolean inBackground = prefs.getBoolean(PlacesConstants.EXTRA_KEY_IN_BACKGROUND, true);
115 |
116 | if (!backgroundAllowed && inBackground) return;
117 |
118 | // Extract the location and radius around which to conduct our search.
119 | Location location = new Location(PlacesConstants.CONSTRUCTED_LOCATION_PROVIDER);
120 | int radius = PlacesConstants.DEFAULT_RADIUS;
121 |
122 | Bundle extras = intent.getExtras();
123 | if (intent.hasExtra(PlacesConstants.EXTRA_KEY_LOCATION)) {
124 | location = (Location)(extras.get(PlacesConstants.EXTRA_KEY_LOCATION));
125 | radius = extras.getInt(PlacesConstants.EXTRA_KEY_RADIUS, PlacesConstants.DEFAULT_RADIUS);
126 | }
127 |
128 | // Check if we're in a low battery situation.
129 | IntentFilter batIntentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
130 | Intent battery = registerReceiver(null, batIntentFilter);
131 | lowBattery = getIsLowBattery(battery);
132 |
133 | // Check if we're connected to a data network, and if so - if it's a mobile network.
134 | NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
135 | boolean isConnected = activeNetwork != null && activeNetwork.isConnectedOrConnecting();
136 | mobileData = activeNetwork != null && activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE;
137 |
138 | // If we're not connected, enable the connectivity receiver and disable the location receiver.
139 | // There's no point trying to poll the server for updates if we're not connected, and the
140 | // connectivity receiver will turn the location-based updates back on once we have a connection.
141 | if (!isConnected) {
142 | PackageManager pm = getPackageManager();
143 |
144 | ComponentName connectivityReceiver = new ComponentName(this, ConnectivityChangedReceiver.class);
145 | ComponentName locationReceiver = new ComponentName(this, LocationChangedReceiver.class);
146 | ComponentName passiveLocationReceiver = new ComponentName(this, PassiveLocationChangedReceiver.class);
147 |
148 | pm.setComponentEnabledSetting(connectivityReceiver,
149 | PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
150 | PackageManager.DONT_KILL_APP);
151 |
152 | pm.setComponentEnabledSetting(locationReceiver,
153 | PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
154 | PackageManager.DONT_KILL_APP);
155 |
156 | pm.setComponentEnabledSetting(passiveLocationReceiver,
157 | PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
158 | PackageManager.DONT_KILL_APP);
159 | }
160 | else {
161 | // If we are connected check to see if this is a forced update (typically triggered
162 | // when the location has changed).
163 | boolean doUpdate = intent.getBooleanExtra(PlacesConstants.EXTRA_KEY_FORCEREFRESH, false);
164 |
165 | // If it's not a forced update (for example from the Activity being restarted) then
166 | // check to see if we've moved far enough, or there's been a long enough delay since
167 | // the last update and if so, enforce a new update.
168 | if (!doUpdate) {
169 | // Retrieve the last update time and place.
170 | long lastTime = prefs.getLong(PlacesConstants.SP_KEY_LAST_LIST_UPDATE_TIME, Long.MIN_VALUE);
171 | long lastLat = prefs.getLong(PlacesConstants.SP_KEY_LAST_LIST_UPDATE_LAT, Long.MIN_VALUE);
172 | long lastLng = prefs.getLong(PlacesConstants.SP_KEY_LAST_LIST_UPDATE_LNG, Long.MIN_VALUE);
173 | Location lastLocation = new Location(PlacesConstants.CONSTRUCTED_LOCATION_PROVIDER);
174 | lastLocation.setLatitude(lastLat);
175 | lastLocation.setLongitude(lastLng);
176 |
177 | // If update time and distance bounds have been passed, do an update.
178 | if ((lastTime < System.currentTimeMillis()-PlacesConstants.MAX_TIME) ||
179 | (lastLocation.distanceTo(location) > PlacesConstants.MAX_DISTANCE))
180 | doUpdate = true;
181 | }
182 |
183 | if (doUpdate) {
184 | // Refresh the prefetch count for each new location.
185 | prefetchCount = 0;
186 | // Remove the old locations
187 | removeOldLocations(location, radius);
188 | // Hit the server for new venues for the current location.
189 | refreshPlaces(location, radius);
190 | }
191 | else
192 | Log.d(TAG, "Place List is fresh: Not refreshing");
193 |
194 | // Retry any queued checkins.
195 | Intent checkinServiceIntent = new Intent(this, PlaceCheckinService.class);
196 | startService(checkinServiceIntent);
197 | }
198 | Log.d(TAG, "Place List Download Service Complete");
199 | }
200 |
201 | /**
202 | * Polls the underlying service to return a list of places within the specified
203 | * radius of the specified Location.
204 | * @param location Location
205 | * @param radius Radius
206 | */
207 | protected void refreshPlaces(Location location, int radius) {
208 | // Log to see if we'll be prefetching the details page of each new place.
209 | if (mobileData) {
210 | Log.d(TAG, "Not prefetching due to being on mobile");
211 | } else if (lowBattery) {
212 | Log.d(TAG, "Not prefetching due to low battery");
213 | }
214 |
215 | long currentTime = System.currentTimeMillis();
216 | URL url;
217 |
218 | try {
219 | // TODO Replace this with a URI to your own service.
220 | String locationStr = location.getLatitude() + "," + location.getLongitude();
221 | String baseURI = PlacesConstants.PLACES_LIST_BASE_URI;
222 | String placesFeed = baseURI + "&location=" + locationStr + "&radius=" + radius + PlacesConstants.PLACES_API_KEY;
223 | url = new URL(placesFeed);
224 |
225 | // Open the connection
226 | URLConnection connection = url.openConnection();
227 | HttpsURLConnection httpConnection = (HttpsURLConnection)connection;
228 | int responseCode = httpConnection.getResponseCode();
229 |
230 | if (responseCode == HttpURLConnection.HTTP_OK) {
231 | // Use the XML Pull Parser to extract each nearby location.
232 | // TODO Replace the XML parsing to extract your own place list.
233 | InputStream in = httpConnection.getInputStream();
234 | XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
235 | factory.setNamespaceAware(true);
236 | XmlPullParser xpp = factory.newPullParser();
237 |
238 | xpp.setInput(in, null);
239 | int eventType = xpp.getEventType();
240 | while (eventType != XmlPullParser.END_DOCUMENT) {
241 | if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("result")) {
242 | eventType = xpp.next();
243 | String id = "";
244 | String name = "";
245 | String vicinity = "";
246 | String types = "";
247 | String locationLat = "";
248 | String locationLng = "";
249 | String viewport = "";
250 | String icon = "";
251 | String reference = "";
252 | while (!(eventType == XmlPullParser.END_TAG && xpp.getName().equals("result"))) {
253 | if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("name"))
254 | name = xpp.nextText();
255 | else if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("vicinity"))
256 | vicinity = xpp.nextText();
257 | else if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("type"))
258 | types = types == "" ? xpp.nextText() : types + " " + xpp.nextText();
259 | else if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("lat"))
260 | locationLat = xpp.nextText();
261 | else if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("lng"))
262 | locationLng = xpp.nextText();
263 | else if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("icon"))
264 | icon = xpp.nextText();
265 | else if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("reference"))
266 | reference = xpp.nextText();
267 | else if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("id"))
268 | id = xpp.nextText();
269 | eventType = xpp.next();
270 | }
271 | Location placeLocation = new Location(PlacesConstants.CONSTRUCTED_LOCATION_PROVIDER);
272 | placeLocation.setLatitude(Double.valueOf(locationLat));
273 | placeLocation.setLongitude(Double.valueOf(locationLng));
274 |
275 | // Add each new place to the Places Content Provider
276 | addPlace(location, id, name, vicinity, types, placeLocation, viewport, icon, reference, currentTime);
277 | }
278 | eventType = xpp.next();
279 | }
280 |
281 | // Remove places from the PlacesContentProviderlist that aren't from this updte.
282 | String where = PlaceDetailsContentProvider.KEY_LAST_UPDATE_TIME + " < " + currentTime;
283 | contentResolver.delete(PlacesContentProvider.CONTENT_URI, where, null);
284 |
285 | // Save the last update time and place to the Shared Preferences.
286 | prefsEditor.putLong(PlacesConstants.SP_KEY_LAST_LIST_UPDATE_LAT, (long) location.getLatitude());
287 | prefsEditor.putLong(PlacesConstants.SP_KEY_LAST_LIST_UPDATE_LNG, (long) location.getLongitude());
288 | prefsEditor.putLong(PlacesConstants.SP_KEY_LAST_LIST_UPDATE_TIME, System.currentTimeMillis());
289 | prefsEditor.commit();
290 | }
291 | else
292 | Log.e(TAG, responseCode + ": " + httpConnection.getResponseMessage());
293 |
294 | } catch (MalformedURLException e) {
295 | Log.e(TAG, e.getMessage());
296 | } catch (IOException e) {
297 | Log.e(TAG, e.getMessage());
298 | } catch (XmlPullParserException e) {
299 | Log.e(TAG, e.getMessage());
300 | }
301 | finally {
302 | }
303 | }
304 |
305 | /**
306 | * Adds the new place to the {@link PlacesContentProvider} using the values passed in.
307 | * TODO Update this method to accept and persist the place information your service provides.
308 | * @param currentLocation Current location
309 | * @param id Unique identifier
310 | * @param name Name
311 | * @param vicinity Vicinity
312 | * @param types Types
313 | * @param location Location
314 | * @param viewport Viewport
315 | * @param icon Icon
316 | * @param reference Reference
317 | * @param currentTime Current time
318 | * @return Successfully added
319 | */
320 | protected boolean addPlace(Location currentLocation, String id, String name, String vicinity, String types, Location location, String viewport, String icon, String reference, long currentTime) {
321 | // Contruct the Content Values
322 | ContentValues values = new ContentValues();
323 | values.put(PlacesContentProvider.KEY_ID, id);
324 | values.put(PlacesContentProvider.KEY_NAME, name);
325 | double lat = location.getLatitude();
326 | double lng = location.getLongitude();
327 | values.put(PlacesContentProvider.KEY_LOCATION_LAT, lat);
328 | values.put(PlacesContentProvider.KEY_LOCATION_LNG, lng);
329 | values.put(PlacesContentProvider.KEY_VICINITY, vicinity);
330 | values.put(PlacesContentProvider.KEY_TYPES, types);
331 | values.put(PlacesContentProvider.KEY_VIEWPORT, viewport);
332 | values.put(PlacesContentProvider.KEY_ICON, icon);
333 | values.put(PlacesContentProvider.KEY_REFERENCE, reference);
334 | values.put(PlacesContentProvider.KEY_LAST_UPDATE_TIME, currentTime);
335 |
336 | // Calculate the distance between the current location and the venue's location
337 | float distance = 0;
338 | if (currentLocation != null && location != null)
339 | distance = currentLocation.distanceTo(location);
340 | values.put(PlacesContentProvider.KEY_DISTANCE, distance);
341 |
342 | // Update or add the new place to the PlacesContentProvider
343 | String where = PlacesContentProvider.KEY_ID + " = '" + id + "'";
344 | boolean result = false;
345 | try {
346 | if (contentResolver.update(PlacesContentProvider.CONTENT_URI, values, where, null) == 0) {
347 | if (contentResolver.insert(PlacesContentProvider.CONTENT_URI, values) != null)
348 | result = true;
349 | }
350 | else
351 | result = true;
352 | }
353 | catch (Exception ex) {
354 | Log.e("PLACES", "Adding " + name + " failed.");
355 | }
356 |
357 | // If we haven't yet reached our prefetching limit, and we're either
358 | // on WiFi or don't have a WiFi-only prefetching restriction, and we
359 | // either don't have low batter or don't have a low battery prefetching
360 | // restriction, then prefetch the details for this newly added place.
361 | if ((prefetchCount < PlacesConstants.PREFETCH_LIMIT) &&
362 | (!PlacesConstants.PREFETCH_ON_WIFI_ONLY || !mobileData) &&
363 | (!PlacesConstants.DISABLE_PREFETCH_ON_LOW_BATTERY || !lowBattery)) {
364 | prefetchCount++;
365 |
366 | // Start the PlaceDetailsUpdateService to prefetch the details for this place.
367 | // As we're prefetching, don't force the refresh if we already have data.
368 | Intent updateServiceIntent = new Intent(this, PlaceDetailsUpdateService.class);
369 | updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_REFERENCE, reference);
370 | updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_ID, id);
371 | updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_FORCEREFRESH, false);
372 | startService(updateServiceIntent);
373 | }
374 |
375 | return result;
376 | }
377 |
378 | /**
379 | * Remove stale place detail records unless we've set the persistent cache flag to true.
380 | * This is typically the case where a place has actually been viewed rather than prefetched.
381 | * @param location Location
382 | * @param radius Radius
383 | */
384 | protected void removeOldLocations(Location location, int radius) {
385 | // Stale Detail Pages
386 | long minTime = System.currentTimeMillis()-PlacesConstants.MAX_DETAILS_UPDATE_LATENCY;
387 | String where = PlaceDetailsContentProvider.KEY_LAST_UPDATE_TIME + " < " + minTime + " AND " +
388 | PlaceDetailsContentProvider.KEY_FORCE_CACHE + " = 0";
389 | contentResolver.delete(PlaceDetailsContentProvider.CONTENT_URI, where, null);
390 | }
391 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/utils/FroyoLocationUpdateRequester.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.utils;
18 |
19 | import android.app.PendingIntent;
20 | import android.location.LocationManager;
21 |
22 | import com.radioactiveyak.location_best_practices.PlacesConstants;
23 | import com.radioactiveyak.location_best_practices.utils.base.LocationUpdateRequester;
24 |
25 | /**
26 | * Provides support for initiating active and passive location updates
27 | * optimized for the Froyo release. Includes use of the Passive Location Provider.
28 | *
29 | * Uses broadcast Intents to notify the app of location changes.
30 | */
31 | public class FroyoLocationUpdateRequester extends LocationUpdateRequester{
32 |
33 | public FroyoLocationUpdateRequester(LocationManager locationManager) {
34 | super(locationManager);
35 | }
36 |
37 | /**
38 | * {@inheritDoc}
39 | */
40 | @Override
41 | public void requestPassiveLocationUpdates(long minTime, long minDistance, PendingIntent pendingIntent) {
42 | // Froyo introduced the Passive Location Provider, which receives updates whenever a 3rd party app
43 | // receives location updates.
44 | locationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, PlacesConstants.MAX_TIME, PlacesConstants.MAX_DISTANCE, pendingIntent);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/utils/FroyoSharedPreferenceSaver.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.utils;
18 |
19 | import android.app.backup.BackupManager;
20 | import android.content.Context;
21 | import android.content.SharedPreferences;
22 | import android.content.SharedPreferences.Editor;
23 |
24 | /**
25 | * Save {@link SharedPreferences} and provide the option to notify
26 | * the BackupManager to initiate a backup.
27 | */
28 | public class FroyoSharedPreferenceSaver extends LegacySharedPreferenceSaver {
29 |
30 | protected BackupManager backupManager;
31 |
32 | public FroyoSharedPreferenceSaver(Context context) {
33 | super(context);
34 | backupManager = new BackupManager(context);
35 | }
36 |
37 | /**
38 | * {@inheritDoc}
39 | */
40 | @Override
41 | public void savePreferences(Editor editor, boolean backup) {
42 | editor.commit();
43 | backupManager.dataChanged();
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/utils/GingerbreadLastLocationFinder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.utils;
18 |
19 | import java.util.List;
20 |
21 | import android.app.PendingIntent;
22 | import android.content.BroadcastReceiver;
23 | import android.content.Context;
24 | import android.content.Intent;
25 | import android.content.IntentFilter;
26 | import android.location.Criteria;
27 | import android.location.Location;
28 | import android.location.LocationListener;
29 | import android.location.LocationManager;
30 |
31 | import com.radioactiveyak.location_best_practices.utils.base.ILastLocationFinder;
32 |
33 | /**
34 | * Optimized implementation of Last Location Finder for devices running Gingerbread
35 | * and above.
36 | *
37 | * This class let's you find the "best" (most accurate and timely) previously
38 | * detected location using whatever providers are available.
39 | *
40 | * Where a timely / accurate previous location is not detected it will
41 | * return the newest location (where one exists) and setup a oneshot
42 | * location update to find the current location.
43 | */
44 | public class GingerbreadLastLocationFinder implements ILastLocationFinder {
45 |
46 | protected static String TAG = "LastLocationFinder";
47 | protected static String SINGLE_LOCATION_UPDATE_ACTION = "com.radioactiveyak.places.SINGLE_LOCATION_UPDATE_ACTION";
48 |
49 | protected PendingIntent singleUpatePI;
50 | protected LocationListener locationListener;
51 | protected LocationManager locationManager;
52 | protected Context context;
53 | protected Criteria criteria;
54 |
55 | /**
56 | * Construct a new Gingerbread Last Location Finder.
57 | * @param context Context
58 | */
59 | public GingerbreadLastLocationFinder(Context context) {
60 | this.context = context;
61 | locationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
62 | // Coarse accuracy is specified here to get the fastest possible result.
63 | // The calling Activity will likely (or have already) request ongoing
64 | // updates using the Fine location provider.
65 | criteria = new Criteria();
66 | criteria.setAccuracy(Criteria.ACCURACY_LOW);
67 |
68 | // Construct the Pending Intent that will be broadcast by the oneshot
69 | // location update.
70 | Intent updateIntent = new Intent(SINGLE_LOCATION_UPDATE_ACTION);
71 | singleUpatePI = PendingIntent.getBroadcast(context, 0, updateIntent, PendingIntent.FLAG_UPDATE_CURRENT);
72 | }
73 |
74 | /**
75 | * Returns the most accurate and timely previously detected location.
76 | * Where the last result is beyond the specified maximum distance or
77 | * latency a one-off location update is returned via the {@link LocationListener}
78 | * specified in {@link setChangedLocationListener}.
79 | * @param minDistance Minimum distance before we require a location update.
80 | * @param minTime Minimum time required between location updates.
81 | * @return The most accurate and / or timely previously detected location.
82 | */
83 | public Location getLastBestLocation(int minDistance, long minTime) {
84 | Location bestResult = null;
85 | float bestAccuracy = Float.MAX_VALUE;
86 | long bestTime = Long.MIN_VALUE;
87 |
88 | // Iterate through all the providers on the system, keeping
89 | // note of the most accurate result within the acceptable time limit.
90 | // If no result is found within maxTime, return the newest Location.
91 | List matchingProviders = locationManager.getAllProviders();
92 | for (String provider: matchingProviders) {
93 | Location location = locationManager.getLastKnownLocation(provider);
94 | if (location != null) {
95 | float accuracy = location.getAccuracy();
96 | long time = location.getTime();
97 |
98 | if ((time > minTime && accuracy < bestAccuracy)) {
99 | bestResult = location;
100 | bestAccuracy = accuracy;
101 | bestTime = time;
102 | }
103 | else if (time < minTime && bestAccuracy == Float.MAX_VALUE && time > bestTime) {
104 | bestResult = location;
105 | bestTime = time;
106 | }
107 | }
108 | }
109 |
110 | // If the best result is beyond the allowed time limit, or the accuracy of the
111 | // best result is wider than the acceptable maximum distance, request a single update.
112 | // This check simply implements the same conditions we set when requesting regular
113 | // location updates every [minTime] and [minDistance].
114 | if (locationListener != null && (bestTime < minTime || bestAccuracy > minDistance)) {
115 | IntentFilter locIntentFilter = new IntentFilter(SINGLE_LOCATION_UPDATE_ACTION);
116 | context.registerReceiver(singleUpdateReceiver, locIntentFilter);
117 | locationManager.requestSingleUpdate(criteria, singleUpatePI);
118 | }
119 |
120 | return bestResult;
121 | }
122 |
123 | /**
124 | * This {@link BroadcastReceiver} listens for a single location
125 | * update before unregistering itself.
126 | * The oneshot location update is returned via the {@link LocationListener}
127 | * specified in {@link setChangedLocationListener}.
128 | */
129 | protected BroadcastReceiver singleUpdateReceiver = new BroadcastReceiver() {
130 | @Override
131 | public void onReceive(Context context, Intent intent) {
132 | context.unregisterReceiver(singleUpdateReceiver);
133 |
134 | String key = LocationManager.KEY_LOCATION_CHANGED;
135 | Location location = (Location)intent.getExtras().get(key);
136 |
137 | if (locationListener != null && location != null)
138 | locationListener.onLocationChanged(location);
139 |
140 | locationManager.removeUpdates(singleUpatePI);
141 | }
142 | };
143 |
144 | /**
145 | * {@inheritDoc}
146 | */
147 | public void setChangedLocationListener(LocationListener l) {
148 | locationListener = l;
149 | }
150 |
151 | /**
152 | * {@inheritDoc}
153 | */
154 | public void cancel() {
155 | locationManager.removeUpdates(singleUpatePI);
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/utils/GingerbreadLocationUpdateRequester.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.utils;
18 |
19 | import android.app.PendingIntent;
20 | import android.location.Criteria;
21 | import android.location.LocationManager;
22 |
23 | /**
24 | * Provides support for initiating active and passive location updates
25 | * optimized for the Gingerbread release. Includes use of the Passive Location Provider.
26 | *
27 | * Uses broadcast Intents to notify the app of location changes.
28 | */
29 | public class GingerbreadLocationUpdateRequester extends FroyoLocationUpdateRequester{
30 |
31 | public GingerbreadLocationUpdateRequester(LocationManager locationManager) {
32 | super(locationManager);
33 | }
34 |
35 | /**
36 | * {@inheritDoc}
37 | */
38 | @Override
39 | public void requestLocationUpdates(long minTime, long minDistance, Criteria criteria, PendingIntent pendingIntent) {
40 | // Gingerbread supports a location update request that accepts criteria directly.
41 | // Note that we aren't monitoring this provider to check if it becomes disabled - this is handled by the calling Activity.
42 | locationManager.requestLocationUpdates(minTime, minDistance, criteria, pendingIntent);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/utils/GingerbreadSharedPreferenceSaver.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.utils;
18 |
19 | import android.content.Context;
20 | import android.content.SharedPreferences;
21 |
22 | /**
23 | * Save {@link SharedPreferences} using the asynchronous apply method available
24 | * in Gingerbread, and provide the option to notify the BackupManager to
25 | * initiate a backup.
26 | */
27 | public class GingerbreadSharedPreferenceSaver extends FroyoSharedPreferenceSaver {
28 |
29 | public GingerbreadSharedPreferenceSaver(Context context) {
30 | super(context);
31 | }
32 |
33 | /**
34 | * {@inheritDoc}
35 | */
36 | @Override
37 | public void savePreferences(SharedPreferences.Editor editor, boolean backup) {
38 | editor.apply();
39 | backupManager.dataChanged();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/utils/HoneycombStrictMode.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.utils;
18 |
19 | import android.os.StrictMode;
20 |
21 | import com.radioactiveyak.location_best_practices.utils.base.IStrictMode;
22 |
23 | /**
24 | * Implementation that supports the Strict Mode functionality
25 | * available Honeycomb.
26 | */
27 | public class HoneycombStrictMode implements IStrictMode {
28 | protected static String TAG = "HoneycombStrictMode";
29 |
30 | /**
31 | * Enable {@link StrictMode}
32 | * TODO Set your preferred Strict Mode features.
33 | */
34 | public void enableStrictMode() {
35 | StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
36 | .detectDiskReads()
37 | .detectDiskWrites()
38 | .detectNetwork()
39 | .penaltyLog()
40 | .penaltyFlashScreen()
41 | .build());
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/utils/LegacyLastLocationFinder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.utils;
18 |
19 | import java.util.List;
20 |
21 | import android.content.Context;
22 | import android.location.Criteria;
23 | import android.location.Location;
24 | import android.location.LocationListener;
25 | import android.location.LocationManager;
26 | import android.os.Bundle;
27 | import android.util.Log;
28 |
29 | import com.radioactiveyak.location_best_practices.utils.base.ILastLocationFinder;
30 |
31 | /**
32 | * Legacy implementation of Last Location Finder for all Android platforms
33 | * down to Android 1.6.
34 | *
35 | * This class let's you find the "best" (most accurate and timely) previously
36 | * detected location using whatever providers are available.
37 | *
38 | * Where a timely / accurate previous location is not detected it will
39 | * return the newest location (where one exists) and setup a one-off
40 | * location update to find the current location.
41 | */
42 | public class LegacyLastLocationFinder implements ILastLocationFinder {
43 |
44 | protected static String TAG = "PreGingerbreadLastLocationFinder";
45 |
46 | protected LocationListener locationListener;
47 | protected LocationManager locationManager;
48 | protected Criteria criteria;
49 | protected Context context;
50 |
51 | /**
52 | * Construct a new Legacy Last Location Finder.
53 | * @param context Context
54 | */
55 | public LegacyLastLocationFinder(Context context) {
56 | locationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
57 | criteria = new Criteria();
58 | // Coarse accuracy is specified here to get the fastest possible result.
59 | // The calling Activity will likely (or have already) request ongoing
60 | // updates using the Fine location provider.
61 | criteria.setAccuracy(Criteria.ACCURACY_COARSE);
62 | this.context = context;
63 | }
64 |
65 |
66 | /**
67 | * Returns the most accurate and timely previously detected location.
68 | * Where the last result is beyond the specified maximum distance or
69 | * latency a one-off location update is returned via the {@link LocationListener}
70 | * specified in {@link setChangedLocationListener}.
71 | * @param minDistance Minimum distance before we require a location update.
72 | * @param minTime Minimum time required between location updates.
73 | * @return The most accurate and / or timely previously detected location.
74 | */
75 | public Location getLastBestLocation(int minDistance, long minTime) {
76 | Location bestResult = null;
77 | float bestAccuracy = Float.MAX_VALUE;
78 | long bestTime = Long.MAX_VALUE;
79 |
80 | // Iterate through all the providers on the system, keeping
81 | // note of the most accurate result within the acceptable time limit.
82 | // If no result is found within maxTime, return the newest Location.
83 | List matchingProviders = locationManager.getAllProviders();
84 | for (String provider: matchingProviders) {
85 | Location location = locationManager.getLastKnownLocation(provider);
86 | if (location != null) {
87 | float accuracy = location.getAccuracy();
88 | long time = location.getTime();
89 |
90 | if ((time < minTime && accuracy < bestAccuracy)) {
91 | bestResult = location;
92 | bestAccuracy = accuracy;
93 | bestTime = time;
94 | }
95 | else if (time > minTime && bestAccuracy == Float.MAX_VALUE && time < bestTime) {
96 | bestResult = location;
97 | bestTime = time;
98 | }
99 | }
100 | }
101 |
102 | // If the best result is beyond the allowed time limit, or the accuracy of the
103 | // best result is wider than the acceptable maximum distance, request a single update.
104 | // This check simply implements the same conditions we set when requesting regular
105 | // location updates every [minTime] and [minDistance].
106 | // Prior to Gingerbread "one-shot" updates weren't available, so we need to implement
107 | // this manually.
108 | if (locationListener != null && (bestTime > minTime || bestAccuracy > minDistance)) {
109 | String provider = locationManager.getBestProvider(criteria, true);
110 | if (provider != null)
111 | locationManager.requestLocationUpdates(provider, 0, 0, singeUpdateListener, context.getMainLooper());
112 | }
113 |
114 | return bestResult;
115 | }
116 |
117 | /**
118 | * This one-off {@link LocationListener} simply listens for a single location
119 | * update before unregistering itself.
120 | * The one-off location update is returned via the {@link LocationListener}
121 | * specified in {@link setChangedLocationListener}.
122 | */
123 | protected LocationListener singeUpdateListener = new LocationListener() {
124 | public void onLocationChanged(Location location) {
125 | Log.d(TAG, "Single Location Update Received: " + location.getLatitude() + "," + location.getLongitude());
126 | if (locationListener != null && location != null)
127 | locationListener.onLocationChanged(location);
128 | locationManager.removeUpdates(singeUpdateListener);
129 | }
130 |
131 | public void onStatusChanged(String provider, int status, Bundle extras) {}
132 | public void onProviderEnabled(String provider) {}
133 | public void onProviderDisabled(String provider) {}
134 | };
135 |
136 | /**
137 | * {@inheritDoc}
138 | */
139 | public void setChangedLocationListener(LocationListener l) {
140 | locationListener = l;
141 | }
142 |
143 | /**
144 | * {@inheritDoc}
145 | */
146 | public void cancel() {
147 | locationManager.removeUpdates(singeUpdateListener);
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/utils/LegacyLocationUpdateRequester.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.utils;
18 |
19 | import com.radioactiveyak.location_best_practices.PlacesConstants;
20 | import com.radioactiveyak.location_best_practices.utils.base.LocationUpdateRequester;
21 |
22 | import android.app.AlarmManager;
23 | import android.app.PendingIntent;
24 | import android.location.Criteria;
25 | import android.location.LocationManager;
26 |
27 | /**
28 | * Provides support for initiating active and passive location updates
29 | * for all Android platforms from Android 1.6.
30 | *
31 | * Uses broadcast Intents to notify the app of location changes.
32 | */
33 | public class LegacyLocationUpdateRequester extends LocationUpdateRequester{
34 |
35 | protected AlarmManager alarmManager;
36 |
37 | protected LegacyLocationUpdateRequester(LocationManager locationManager, AlarmManager alarmManager) {
38 | super(locationManager);
39 | this.alarmManager = alarmManager;
40 | }
41 |
42 | /**
43 | * {@inheritDoc}
44 | */
45 | @Override
46 | public void requestLocationUpdates(long minTime, long minDistance, Criteria criteria, PendingIntent pendingIntent) {
47 | // Prior to Gingerbread we needed to find the best provider manually.
48 | // Note that we aren't monitoring this provider to check if it becomes disabled - this is handled by the calling Activity.
49 | String provider = locationManager.getBestProvider(criteria, true);
50 | if (provider != null)
51 | locationManager.requestLocationUpdates(provider, minTime, minDistance, pendingIntent);
52 | }
53 |
54 | /**
55 | * {@inheritDoc}
56 | */
57 | @Override
58 | public void requestPassiveLocationUpdates(long minTime, long minDistance, PendingIntent pendingIntent) {
59 | // Pre-Froyo there was no Passive Location Provider, so instead we will set an inexact repeating, non-waking alarm
60 | // that will trigger once the minimum time between passive updates has expired. This is potentially more expensive
61 | // than simple passive alarms, however the Receiver will ensure we've transitioned beyond the minimum time and
62 | // distance before initiating a background nearby loction update.
63 | alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, System.currentTimeMillis()+PlacesConstants.MAX_TIME, PlacesConstants.MAX_TIME, pendingIntent);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/utils/LegacySharedPreferenceSaver.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.utils;
18 |
19 | import android.content.Context;
20 | import android.content.SharedPreferences;
21 | import android.content.SharedPreferences.Editor;
22 |
23 | import com.radioactiveyak.location_best_practices.utils.base.SharedPreferenceSaver;
24 |
25 | /**
26 | * Save {@link SharedPreferences} in a way compatible with Android 1.6.
27 | */
28 | public class LegacySharedPreferenceSaver extends SharedPreferenceSaver {
29 |
30 | public LegacySharedPreferenceSaver(Context context) {
31 | super(context);
32 | }
33 |
34 | /**
35 | * {@inheritDoc}
36 | */
37 | @Override
38 | public void savePreferences(Editor editor, boolean backup) {
39 | editor.commit();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/utils/LegacyStrictMode.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.utils;
18 |
19 | import com.radioactiveyak.location_best_practices.utils.base.IStrictMode;
20 |
21 | import android.os.StrictMode;
22 |
23 | /**
24 | * Implementation that supports the Strict Mode functionality
25 | * available for the first platform release that supported Strict Mode.
26 | */
27 | public class LegacyStrictMode implements IStrictMode {
28 |
29 | /**
30 | * Enable {@link StrictMode}
31 | * TODO Set your preferred Strict Mode features.
32 | */
33 | public void enableStrictMode() {
34 | StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
35 | .detectDiskReads()
36 | .detectDiskWrites()
37 | .detectNetwork()
38 | .penaltyLog()
39 | .build());
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/utils/PlatformSpecificImplementationFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.utils;
18 |
19 | import android.content.Context;
20 | import android.location.LocationManager;
21 |
22 | import com.radioactiveyak.location_best_practices.PlacesConstants;
23 | import com.radioactiveyak.location_best_practices.utils.base.ILastLocationFinder;
24 | import com.radioactiveyak.location_best_practices.utils.base.IStrictMode;
25 | import com.radioactiveyak.location_best_practices.utils.base.LocationUpdateRequester;
26 | import com.radioactiveyak.location_best_practices.utils.base.SharedPreferenceSaver;
27 |
28 | /**
29 | * Factory class to create the correct instances
30 | * of a variety of classes with platform specific
31 | * implementations.
32 | *
33 | */
34 | public class PlatformSpecificImplementationFactory {
35 |
36 | /**
37 | * Create a new LastLocationFinder instance
38 | * @param context Context
39 | * @return LastLocationFinder
40 | */
41 | public static ILastLocationFinder getLastLocationFinder(Context context) {
42 | return PlacesConstants.SUPPORTS_GINGERBREAD ? new GingerbreadLastLocationFinder(context) : new LegacyLastLocationFinder(context);
43 | }
44 |
45 | /**
46 | * Create a new StrictMode instance.
47 | * @return StrictMode
48 | */
49 | public static IStrictMode getStrictMode() {
50 | if (PlacesConstants.SUPPORTS_HONEYCOMB)
51 | return new HoneycombStrictMode();
52 | else if (PlacesConstants.SUPPORTS_GINGERBREAD)
53 | return new LegacyStrictMode();
54 | else
55 | return null;
56 | }
57 |
58 | /**
59 | * Create a new LocationUpdateRequester
60 | * @param locationManager Location Manager
61 | * @return LocationUpdateRequester
62 | */
63 | public static LocationUpdateRequester getLocationUpdateRequester(LocationManager locationManager) {
64 | return PlacesConstants.SUPPORTS_GINGERBREAD ? new GingerbreadLocationUpdateRequester(locationManager) : new FroyoLocationUpdateRequester(locationManager);
65 | }
66 |
67 | /**
68 | * Create a new SharedPreferenceSaver
69 | * @param context Context
70 | * @return SharedPreferenceSaver
71 | */
72 | public static SharedPreferenceSaver getSharedPreferenceSaver(Context context) {
73 | return PlacesConstants.SUPPORTS_GINGERBREAD ?
74 | new GingerbreadSharedPreferenceSaver(context) :
75 | PlacesConstants.SUPPORTS_FROYO ?
76 | new FroyoSharedPreferenceSaver(context) :
77 | new LegacySharedPreferenceSaver(context);
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/utils/base/ILastLocationFinder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.utils.base;
18 |
19 | import android.location.Location;
20 | import android.location.LocationListener;
21 |
22 | /**
23 | * Interface definition for a Last Location Finder.
24 | *
25 | * Classes that implement this interface must provide methods to
26 | * find the "best" (most accurate and timely) previously detected
27 | * location using whatever providers are available.
28 | *
29 | * Where a timely / accurate previous location is not detected, classes
30 | * should return the last location and create a one-shot update to find
31 | * the current location. The one-shot update should be returned via the
32 | * Location Listener passed in through setChangedLocationListener.
33 | */
34 | public interface ILastLocationFinder {
35 | /**
36 | * Find the most accurate and timely previously detected location
37 | * using all the location providers. Where the last result is beyond
38 | * the acceptable maximum distance or latency create a one-shot update
39 | * of the current location to be returned using the {@link LocationListener}
40 | * passed in through {@link setChangedLocationListener}
41 | * @param minDistance Minimum distance before we require a location update.
42 | * @param minTime Minimum time required between location updates.
43 | * @return The most accurate and / or timely previously detected location.
44 | */
45 | public Location getLastBestLocation(int minDistance, long minTime);
46 |
47 | /**
48 | * Set the {@link LocationListener} that may receive a one-shot current location update.
49 | * @param l LocationListener
50 | */
51 | public void setChangedLocationListener(LocationListener l);
52 |
53 | /**
54 | * Cancel the one-shot current location update.
55 | */
56 | public void cancel();
57 | }
58 |
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/utils/base/IStrictMode.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.utils.base;
18 |
19 | /**
20 | * This Interface definition allows you to create OS version-specific
21 | * implementations that offer the full Strict Mode functionality
22 | * available in each platform release.
23 | */
24 | public interface IStrictMode {
25 | /**
26 | * Enable {@link StrictMode} using whichever platform-specific flags you wish.
27 | */
28 | public void enableStrictMode();
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/utils/base/LocationUpdateRequester.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.utils.base;
18 |
19 | import android.app.PendingIntent;
20 | import android.location.Criteria;
21 | import android.location.LocationManager;
22 |
23 | /**
24 | * Abstract base class that can be extended to provide active and passive location updates
25 | * optimized for each platform release.
26 | *
27 | * Uses broadcast Intents to notify the app of location changes.
28 | */
29 | public abstract class LocationUpdateRequester {
30 |
31 | protected LocationManager locationManager;
32 |
33 | protected LocationUpdateRequester(LocationManager locationManager) {
34 | this.locationManager = locationManager;
35 | }
36 |
37 | /**
38 | * Request active location updates.
39 | * These updates will be triggered by a direct request from the Location Manager.
40 | * @param minTime Minimum time that should elapse between location update broadcasts.
41 | * @param minDistance Minimum distance that should have been moved between location update broadcasts.
42 | * @param criteria Criteria that define the Location Provider to use to detect the Location.
43 | * @param pendingIntent The Pending Intent to broadcast to notify the app of active location changes.
44 | */
45 | public void requestLocationUpdates(long minTime, long minDistance, Criteria criteria, PendingIntent pendingIntent) {}
46 |
47 | /**
48 | * Request passive location updates.
49 | * These updates will be triggered by locations received by 3rd party apps that have requested location updates.
50 | * The miniumim time and distance for passive updates will typically be longer than for active updates. The trick
51 | * is to balance the difference to minimize battery drain by maximize freshness.
52 | * @param minTime Minimum time that should elapse between location update broadcasts.
53 | * @param minDistance Minimum distance that should have been moved between location update broadcasts.
54 | * @param pendingIntent The Pending Intent to broadcast to notify the app of passive location changes.
55 | */
56 | public void requestPassiveLocationUpdates(long minTime, long minDistance, PendingIntent pendingIntent) {}
57 | }
58 |
--------------------------------------------------------------------------------
/app/src/main/java/com/radioactiveyak/location_best_practices/utils/base/SharedPreferenceSaver.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Google Inc.
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 |
17 | package com.radioactiveyak.location_best_practices.utils.base;
18 |
19 | import android.content.Context;
20 | import android.content.SharedPreferences;
21 |
22 | /**
23 | * Abstract base class that can be extended to provide classes that save
24 | * {@link SharedPreferences} in the most efficient way possible.
25 | * Decendent classes can optionally choose to backup some {@link SharedPreferences}
26 | * to the Google {@link BackupService} on platforms where this is available.
27 | */
28 | public abstract class SharedPreferenceSaver {
29 |
30 | protected Context context;
31 |
32 | protected SharedPreferenceSaver(Context context) {
33 | this.context = context;
34 | }
35 |
36 | /**
37 | * Save the Shared Preferences modified through the Editor object.
38 | * @param editor Shared Preferences Editor to commit.
39 | * @param backup Backup to the cloud if possible.
40 | */
41 | public void savePreferences(SharedPreferences.Editor editor, boolean backup) {}
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/res/anim-v11/slide_in_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/anim-v11/slide_out_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/retomeier/android-protips-location/6201f1e82dd0613a402bd2d655b1f9d05f717041/app/src/main/res/drawable-hdpi/icon.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/powered_by_google_on_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/retomeier/android-protips-location/6201f1e82dd0613a402bd2d655b1f9d05f717041/app/src/main/res/drawable-hdpi/powered_by_google_on_black.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/powered_by_google_on_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/retomeier/android-protips-location/6201f1e82dd0613a402bd2d655b1f9d05f717041/app/src/main/res/drawable-hdpi/powered_by_google_on_white.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-ldpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/retomeier/android-protips-location/6201f1e82dd0613a402bd2d655b1f9d05f717041/app/src/main/res/drawable-ldpi/icon.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-ldpi/powered_by_google_on_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/retomeier/android-protips-location/6201f1e82dd0613a402bd2d655b1f9d05f717041/app/src/main/res/drawable-ldpi/powered_by_google_on_black.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-ldpi/powered_by_google_on_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/retomeier/android-protips-location/6201f1e82dd0613a402bd2d655b1f9d05f717041/app/src/main/res/drawable-ldpi/powered_by_google_on_white.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/retomeier/android-protips-location/6201f1e82dd0613a402bd2d655b1f9d05f717041/app/src/main/res/drawable-mdpi/icon.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/powered_by_google_on_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/retomeier/android-protips-location/6201f1e82dd0613a402bd2d655b1f9d05f717041/app/src/main/res/drawable-mdpi/powered_by_google_on_black.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/powered_by_google_on_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/retomeier/android-protips-location/6201f1e82dd0613a402bd2d655b1f9d05f717041/app/src/main/res/drawable-mdpi/powered_by_google_on_white.png
--------------------------------------------------------------------------------
/app/src/main/res/layout-port/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
12 |
17 |
22 |
23 |
29 |
30 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/app/src/main/res/layout-xlarge-port/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
11 |
16 |
17 |
23 |
24 |
31 |
32 |
38 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/checkin_box.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
12 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
11 |
16 |
17 |
23 |
24 |
31 |
32 |
38 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/place_detail.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
13 |
21 |
26 |
32 |
37 |
42 |
47 |
48 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/main_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v8/booleans.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 | false
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/booleans.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | false
4 | true
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Location Best Practices
4 | Refresh
5 | Checked in to
6 | Checkin
7 |
8 |
9 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | jcenter()
5 | }
6 | dependencies {
7 | classpath 'com.android.tools.build:gradle:2.2.0-alpha6'
8 | }
9 | }
10 |
11 | allprojects {
12 | repositories {
13 | jcenter()
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx2048m
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
7 |
--------------------------------------------------------------------------------
/local.properties:
--------------------------------------------------------------------------------
1 | ## This file must *NOT* be checked into Version Control Systems,
2 | # as it contains information specific to your local configuration.
3 | #
4 | # Location of the SDK. This is only used by Gradle.
5 | #
6 | #Thu Jul 14 13:18:01 PDT 2016
7 | sdk.dir=/Users/retomeier/Library/Android/sdk
8 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------