├── .gitignore
├── ic_launcher-web.png
├── libs
├── gson-2.2.4.jar
├── android-support-v4.jar
└── libGoogleAnalyticsServices.jar
├── res
├── drawable-hdpi
│ ├── ic_launcher.png
│ ├── ic_notification.png
│ ├── current_location_dark.png
│ └── current_location_light.png
├── drawable-mdpi
│ ├── ic_launcher.png
│ ├── ic_notification.png
│ ├── current_location_dark.png
│ └── current_location_light.png
├── drawable-xhdpi
│ ├── ic_launcher.png
│ ├── ic_notification.png
│ ├── current_location_dark.png
│ └── current_location_light.png
├── drawable-xxhdpi
│ ├── ic_launcher.png
│ ├── ic_notification.png
│ ├── current_location_dark.png
│ └── current_location_light.png
├── values
│ ├── attrs.xml
│ ├── dimens.xml
│ ├── analytics.xml
│ ├── styles.xml
│ ├── settings.xml
│ └── strings.xml
├── values-sw600dp
│ └── dimens.xml
├── values-sw720dp-land
│ └── dimens.xml
├── values-v14
│ └── styles.xml
├── layout
│ ├── dialog_loadlocation.xml
│ ├── dialog_savelocation.xml
│ └── activity_main.xml
├── xml
│ └── settings.xml
└── menu
│ └── main.xml
├── src
└── com
│ └── vellut
│ └── geoalarm
│ ├── GeoAlarmBootServiceStarter.java
│ ├── AlarmWakeUpBroadcastReceiver.java
│ ├── ReceiveTransitionsBroadcastReceiver.java
│ ├── SavedLocation.java
│ ├── io
│ ├── LatLngBoundsSerializer.java
│ └── LatLngBoundsDeserializer.java
│ ├── GeoAlarmBootService.java
│ ├── GeoAlarmUtils.java
│ ├── SettingsActivity.java
│ ├── ReceiveTransitionsIntentService.java
│ ├── AlarmWakeUpService.java
│ ├── GeoAlarm.java
│ ├── SwipeDismissListViewTouchListener.java
│ └── MainActivity.java
├── project.properties
├── proguard-project.txt
├── MIT-LICENSE.txt
├── README.md
├── AndroidManifest.xml
├── icon_notification.svg
└── icon_app.svg
/.gitignore:
--------------------------------------------------------------------------------
1 | .settings
2 | .project
3 | .classpath
4 | /gen
5 | /bin
6 | .DS_Store
7 |
--------------------------------------------------------------------------------
/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gvellut/MapAlarmist/HEAD/ic_launcher-web.png
--------------------------------------------------------------------------------
/libs/gson-2.2.4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gvellut/MapAlarmist/HEAD/libs/gson-2.2.4.jar
--------------------------------------------------------------------------------
/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gvellut/MapAlarmist/HEAD/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gvellut/MapAlarmist/HEAD/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gvellut/MapAlarmist/HEAD/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gvellut/MapAlarmist/HEAD/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/libs/libGoogleAnalyticsServices.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gvellut/MapAlarmist/HEAD/libs/libGoogleAnalyticsServices.jar
--------------------------------------------------------------------------------
/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gvellut/MapAlarmist/HEAD/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gvellut/MapAlarmist/HEAD/res/drawable-hdpi/ic_notification.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gvellut/MapAlarmist/HEAD/res/drawable-mdpi/ic_notification.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gvellut/MapAlarmist/HEAD/res/drawable-xhdpi/ic_notification.png
--------------------------------------------------------------------------------
/res/drawable-xxhdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gvellut/MapAlarmist/HEAD/res/drawable-xxhdpi/ic_notification.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/current_location_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gvellut/MapAlarmist/HEAD/res/drawable-hdpi/current_location_dark.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/current_location_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gvellut/MapAlarmist/HEAD/res/drawable-hdpi/current_location_light.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/current_location_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gvellut/MapAlarmist/HEAD/res/drawable-mdpi/current_location_dark.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/current_location_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gvellut/MapAlarmist/HEAD/res/drawable-mdpi/current_location_light.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/current_location_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gvellut/MapAlarmist/HEAD/res/drawable-xhdpi/current_location_dark.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/current_location_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gvellut/MapAlarmist/HEAD/res/drawable-xhdpi/current_location_light.png
--------------------------------------------------------------------------------
/res/drawable-xxhdpi/current_location_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gvellut/MapAlarmist/HEAD/res/drawable-xxhdpi/current_location_dark.png
--------------------------------------------------------------------------------
/res/drawable-xxhdpi/current_location_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gvellut/MapAlarmist/HEAD/res/drawable-xxhdpi/current_location_light.png
--------------------------------------------------------------------------------
/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/res/values-sw600dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16dp
5 | 16dp
6 |
7 |
8 |
--------------------------------------------------------------------------------
/res/values/analytics.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | UA-46221369-1
5 | true
6 | true
7 |
8 |
--------------------------------------------------------------------------------
/res/values-sw720dp-land/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | 128dp
8 |
9 |
10 |
--------------------------------------------------------------------------------
/res/values-v14/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/res/layout/dialog_loadlocation.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
11 |
12 |
--------------------------------------------------------------------------------
/src/com/vellut/geoalarm/GeoAlarmBootServiceStarter.java:
--------------------------------------------------------------------------------
1 | package com.vellut.geoalarm;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 |
7 | public class GeoAlarmBootServiceStarter extends BroadcastReceiver {
8 |
9 | @Override
10 | public void onReceive(Context context, Intent intent) {
11 | Intent i = new Intent();
12 | i.setClass(context, GeoAlarmBootService.class);
13 | context.startService(i);
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/res/xml/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
--------------------------------------------------------------------------------
/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
10 |
11 |
12 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-19
15 | android.library.reference.1=../google-play-services_lib
16 |
--------------------------------------------------------------------------------
/src/com/vellut/geoalarm/AlarmWakeUpBroadcastReceiver.java:
--------------------------------------------------------------------------------
1 | package com.vellut.geoalarm;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.support.v4.content.WakefulBroadcastReceiver;
6 | import android.util.Log;
7 |
8 | public class AlarmWakeUpBroadcastReceiver extends WakefulBroadcastReceiver {
9 |
10 | @Override
11 | public void onReceive(Context context, Intent intent) {
12 | // Do nothing
13 | Log.d(GeoAlarmUtils.APPTAG, "Received Alarm Notification");
14 | Intent launchService = new Intent();
15 | launchService.setClass(context, AlarmWakeUpService.class);
16 | startWakefulService(context, launchService);
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/com/vellut/geoalarm/ReceiveTransitionsBroadcastReceiver.java:
--------------------------------------------------------------------------------
1 | package com.vellut.geoalarm;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.support.v4.content.WakefulBroadcastReceiver;
6 | import android.util.Log;
7 |
8 | public class ReceiveTransitionsBroadcastReceiver extends
9 | WakefulBroadcastReceiver {
10 |
11 | @Override
12 | public void onReceive(Context context, Intent intent) {
13 | Intent service = new Intent(context, ReceiveTransitionsIntentService.class);
14 | Log.d(GeoAlarmUtils.APPTAG, "in ReceiveTransBrodcastReceiver");
15 | service.putExtras(intent);
16 | startWakefulService(context, service);
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/com/vellut/geoalarm/SavedLocation.java:
--------------------------------------------------------------------------------
1 | package com.vellut.geoalarm;
2 |
3 | import com.google.android.gms.maps.model.LatLngBounds;
4 |
5 | public class SavedLocation implements Comparable {
6 | public String description;
7 | public LatLngBounds zone;
8 |
9 | public SavedLocation() {
10 | }
11 |
12 | public SavedLocation(String description, LatLngBounds zone) {
13 | this.description = description;
14 | this.zone = zone;
15 | }
16 |
17 | @Override
18 | public String toString() {
19 | return description;
20 | }
21 |
22 | @Override
23 | public int compareTo(SavedLocation another) {
24 | return (this.description.compareTo(another.description));
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/res/layout/dialog_savelocation.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
17 |
18 |
--------------------------------------------------------------------------------
/res/values/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Low Power
5 | Balanced Power / Accuracy
6 | High Accuracy (GPS)
7 |
8 |
9 |
10 | @string/lowpower
11 | @string/balancedpowacc
12 | @string/highaccuracy
13 |
14 |
15 | @string/balancedpowacc
16 |
17 | lowpower
18 | balancedpowacc
19 | highaccuracy
20 |
21 |
--------------------------------------------------------------------------------
/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/src/com/vellut/geoalarm/io/LatLngBoundsSerializer.java:
--------------------------------------------------------------------------------
1 | package com.vellut.geoalarm.io;
2 |
3 | import java.lang.reflect.Type;
4 |
5 |
6 | import com.google.android.gms.maps.model.LatLngBounds;
7 | import com.google.gson.JsonArray;
8 | import com.google.gson.JsonElement;
9 | import com.google.gson.JsonPrimitive;
10 | import com.google.gson.JsonSerializationContext;
11 | import com.google.gson.JsonSerializer;
12 |
13 | public class LatLngBoundsSerializer implements JsonSerializer {
14 |
15 | @Override
16 | public JsonElement serialize(LatLngBounds zone, Type arg1,
17 | JsonSerializationContext arg2) {
18 | JsonArray arr = new JsonArray();
19 | arr.add(new JsonPrimitive(zone.southwest.latitude));
20 | arr.add(new JsonPrimitive(zone.southwest.longitude));
21 | arr.add(new JsonPrimitive(zone.northeast.latitude));
22 | arr.add(new JsonPrimitive(zone.northeast.longitude));
23 | return arr;
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
29 |
--------------------------------------------------------------------------------
/src/com/vellut/geoalarm/io/LatLngBoundsDeserializer.java:
--------------------------------------------------------------------------------
1 | package com.vellut.geoalarm.io;
2 |
3 | import java.lang.reflect.Type;
4 |
5 | import com.google.android.gms.maps.model.LatLng;
6 | import com.google.android.gms.maps.model.LatLngBounds;
7 | import com.google.gson.JsonArray;
8 | import com.google.gson.JsonDeserializationContext;
9 | import com.google.gson.JsonDeserializer;
10 | import com.google.gson.JsonElement;
11 | import com.google.gson.JsonParseException;
12 |
13 | public class LatLngBoundsDeserializer implements JsonDeserializer{
14 |
15 | @Override
16 | public LatLngBounds deserialize(JsonElement element, Type arg1,
17 | JsonDeserializationContext arg2) throws JsonParseException {
18 | JsonArray arr = element.getAsJsonArray();
19 | LatLng southwest = new LatLng(arr.get(0).getAsDouble(), arr.get(1).getAsDouble());
20 | LatLng northeast = new LatLng(arr.get(2).getAsDouble(), arr.get(3).getAsDouble());
21 | LatLngBounds zone = new LatLngBounds(southwest, northeast);
22 | return zone;
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/MIT-LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright 2013 Guilhem Vellut
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## About
2 | Map Alarmist is a simple location-based alarm app. It is distributed in the Google Play store: https://play.google.com/store/apps/details?id=com.vellut.geoalarm
3 | It is my first Android project and I used it to learn some aspects of the platform.
4 |
5 | It includes examples of usage for the following:
6 | - Google Maps (using the Google Play Services)
7 | - Location Client (using the Google Play Services), with Location Updates and Location Geofencing
8 | - Launching broadcast receiver at boot
9 | - Using the Alarm Manager to wake up the device
10 | - Swipe to delete in a ListView
11 | - Preference screen
12 | - Saving and reading to/from SharedPreferences
13 | - Displaying the Alarm Picker dialog
14 |
15 | ## Using the app
16 | Use the map to select the area where the alarm should be triggered then select a sound and whether the phone should vibrate. Flip the switch at the bottom to set the alarm. Once inside the area, the phone will notify you. This application works even when another app is open or when the phone is sleeping.
17 |
18 | Additional features include:
19 | - Save preferred locations for quick retrieval
20 | - Choose the location technique: Precise location using GPS, battery saving mode or a balanced accuracy/power technique.
21 |
22 | ## Compiling the app
23 | Import the code into the Eclise ADT using the "Import... > Existing Android Code into Workspace" tool.
24 |
25 | ## Running
26 | Generate a Google API key (used for Google Maps) for your debug and release signing keys. Replace the key inside the AndroidManifest.xml file.
27 | Replace the AdMob tracking key inside the res/layout/activity_main.xml file
28 | Create a Google Analytics configuration for the app and replace the tracking key inside the res/values/analytics.xml file
29 |
30 |
31 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Map Alarmist
5 | Choose ringtone
6 | Vibrate
7 | Select ringtone for alarm
8 | Position
9 | Click to return to app
10 | Map Alarmist has been triggered
11 | Geofencing Error: %d
12 | Welcome to Map Alarmist.\n\nUse the map to select the area where the alarm should be triggered and select a sound.\n\nFlip the switch at the bottom to set the alarm.
13 | OK
14 | Cancel
15 | OFF
16 | ON
17 | Load location
18 | Save location
19 | Location description
20 | Location saved
21 | Enter a description for the location
22 | Load location
23 | Save location
24 | Settings
25 | Unable to obtain location
26 |
27 | Location technique
28 |
29 |
--------------------------------------------------------------------------------
/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
22 |
23 |
29 |
30 |
35 |
36 |
43 |
44 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/src/com/vellut/geoalarm/GeoAlarmBootService.java:
--------------------------------------------------------------------------------
1 | package com.vellut.geoalarm;
2 |
3 | import android.app.Service;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.os.IBinder;
7 | import android.util.Log;
8 |
9 | import com.google.android.gms.common.ConnectionResult;
10 | import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks;
11 | import com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener;
12 | import com.google.android.gms.location.LocationClient;
13 | import com.google.android.gms.location.LocationClient.OnAddGeofencesResultListener;
14 |
15 | public class GeoAlarmBootService extends Service implements
16 | ConnectionCallbacks, OnConnectionFailedListener,
17 | OnAddGeofencesResultListener {
18 |
19 | private GeoAlarm geoAlarm;
20 | private LocationClient locationClient;
21 |
22 | @Override
23 | public int onStartCommand(Intent intent, int flags, int startId) {
24 | geoAlarm = new GeoAlarm();
25 | geoAlarm.restorePreferences(this);
26 | if (geoAlarm.isAlarmOn) {
27 | locationClient = new LocationClient(this, this, this);
28 | locationClient.connect();
29 | } else {
30 | stopSelf();
31 | }
32 |
33 | return START_STICKY;
34 | }
35 |
36 | @Override
37 | public IBinder onBind(Intent arg0) {
38 | // unsupported
39 | return null;
40 | }
41 |
42 | @Override
43 | public void onConnected(Bundle dataBundle) {
44 | Log.d(GeoAlarmUtils.APPTAG, "Connected to Location Services in service");
45 | // setup alarm
46 | try {
47 | geoAlarm.setAlarm(this, locationClient, this);
48 | geoAlarm.savePreferences(this);
49 | } catch (Exception e) {
50 | Log.e(GeoAlarmUtils.APPTAG, "Error setting alarm", e);
51 |
52 | }
53 | }
54 |
55 | @Override
56 | public void onDisconnected() {
57 | stopSelf();
58 | }
59 |
60 | @Override
61 | public void onConnectionFailed(ConnectionResult connectionResult) {
62 | stopSelf();
63 | }
64 |
65 | @Override
66 | public void onAddGeofencesResult(int statusCode, String[] geofenceRequestIds) {
67 |
68 | Log.e(GeoAlarmUtils.APPTAG, "Connected to Location Services in service");
69 | stopSelf();
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/src/com/vellut/geoalarm/GeoAlarmUtils.java:
--------------------------------------------------------------------------------
1 | package com.vellut.geoalarm;
2 |
3 | import android.content.Context;
4 |
5 | import com.google.analytics.tracking.android.EasyTracker;
6 | import com.google.analytics.tracking.android.MapBuilder;
7 |
8 | public class GeoAlarmUtils {
9 | public static final String APPTAG = "GeoAlarm";
10 |
11 | public static final String GEOFENCE_REQUEST_ID = "GeoAlarm";
12 |
13 | public static final String ACTION_STOP_ALARM = "StopAlarm";
14 |
15 | public static final int GEOFENCE_NOTIFICATION_ID = 1;
16 |
17 | public static final String EXTRA_USE_VIBRATE = "UseVibrate";
18 | public static final String EXTRA_RINGTONE_URI = "RingtoneUri";
19 | public static final String EXTRA_ALARM_SET_TIME = "AlarmSetTime";
20 |
21 | public static final long MIN_DTIME = 10000;
22 |
23 | public final static int RINGTONE_PICKER_REQUEST_CODE = 1;
24 | public final static int CONNECTION_FAILURE_RESOLUTION_REQUEST = 2;
25 |
26 | public final static String PREF_IS_FIRST_TIME_RUN = "isFirstTimeRun";
27 | public final static String PREF_RINGTONE_URI = "ringtoneUri";
28 | public final static String PREF_NORTH_LAT = "northLat";
29 | public final static String PREF_WEST_LON = "westLon";
30 | public final static String PREF_SOUTH_LAT = "southLat";
31 | public final static String PREF_EAST_LON = "eastLon";
32 | public final static String PREF_USE_VIBRATE = "useVibrate";
33 | public final static String PREF_IS_ALARM_ON = "isAlarmOn";
34 | public final static String PREF_SAVED_LOCATIONS = "savedLocations";
35 | public final static String PREF_LOCATION_TECHNIQUE = "locationTechnique";
36 | public final static String PREF_IS_IN_ZONE = "isInZone";
37 | public final static String PREF_ALARM_SET_TIME = "alarmSetTime";
38 |
39 | public final static int LOCATION_TECHNIQUE_LOW_POWER = R.string.lowpower;
40 | public final static int LOCATION_TECHNIQUE_BALANCED_POWER = R.string.balancedpowacc;
41 | public final static int LOCATION_TECHNIQUE_HIGH_ACCURACY = R.string.highaccuracy;
42 |
43 |
44 | // Analytics
45 |
46 | public static void trackEvent(Context context, String category, String action, String label,
47 | Long value) {
48 | EasyTracker easyTracker = EasyTracker.getInstance(context);
49 | easyTracker.send(MapBuilder.createEvent(category, action, label, value)
50 | .build());
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/com/vellut/geoalarm/SettingsActivity.java:
--------------------------------------------------------------------------------
1 | package com.vellut.geoalarm;
2 |
3 | import com.google.analytics.tracking.android.EasyTracker;
4 |
5 | import android.app.Activity;
6 | import android.content.SharedPreferences;
7 | import android.os.Bundle;
8 | import android.preference.EditTextPreference;
9 | import android.preference.ListPreference;
10 | import android.preference.Preference;
11 | import android.preference.PreferenceCategory;
12 | import android.preference.PreferenceFragment;
13 | import android.preference.PreferenceManager;
14 |
15 | public class SettingsActivity extends Activity implements
16 | SharedPreferences.OnSharedPreferenceChangeListener {
17 |
18 | private SettingsFragment settingsFragment;
19 |
20 | @Override
21 | public void onCreate(Bundle savedInstanceState) {
22 | super.onCreate(savedInstanceState);
23 |
24 | settingsFragment = new SettingsFragment();
25 | getFragmentManager().beginTransaction()
26 | .replace(android.R.id.content, settingsFragment).commit();
27 | }
28 |
29 | @Override
30 | protected void onStart() {
31 | super.onStart();
32 | EasyTracker.getInstance(this).activityStart(this);
33 | }
34 |
35 | @Override
36 | protected void onStop() {
37 | super.onStop();
38 | EasyTracker.getInstance(this).activityStop(this);
39 | }
40 |
41 | @Override
42 | protected void onResume() {
43 | super.onResume();
44 | PreferenceManager.getDefaultSharedPreferences(this)
45 | .registerOnSharedPreferenceChangeListener(this);
46 | }
47 |
48 | @Override
49 | protected void onPause() {
50 | super.onPause();
51 | PreferenceManager.getDefaultSharedPreferences(this)
52 | .unregisterOnSharedPreferenceChangeListener(this);
53 | }
54 |
55 | public static class SettingsFragment extends PreferenceFragment {
56 | @Override
57 | public void onCreate(Bundle savedInstanceState) {
58 | super.onCreate(savedInstanceState);
59 |
60 | addPreferencesFromResource(R.xml.settings);
61 |
62 | for (int i = 0; i < getPreferenceScreen().getPreferenceCount(); i++) {
63 | initSummary(getPreferenceScreen().getPreference(i));
64 | }
65 | }
66 | }
67 |
68 | @Override
69 | public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
70 | String key) {
71 | Preference pref = settingsFragment.findPreference(key);
72 | updateSummary(pref);
73 |
74 | GeoAlarmUtils.trackEvent(this, "ui_action", "button_press", "changed_location_technique",
75 | null);
76 | }
77 |
78 | private static void initSummary(Preference p) {
79 | if (p instanceof PreferenceCategory) {
80 | PreferenceCategory pCat = (PreferenceCategory) p;
81 | for (int i = 0; i < pCat.getPreferenceCount(); i++) {
82 | initSummary(pCat.getPreference(i));
83 | }
84 | } else {
85 | updateSummary(p);
86 | }
87 | }
88 |
89 | private static void updateSummary(Preference p) {
90 | if (p instanceof ListPreference) {
91 | ListPreference listPref = (ListPreference) p;
92 | p.setSummary(listPref.getEntry());
93 | }
94 | if (p instanceof EditTextPreference) {
95 | EditTextPreference editTextPref = (EditTextPreference) p;
96 | p.setSummary(editTextPref.getText());
97 | }
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
23 |
24 |
30 |
33 |
36 |
37 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
59 |
60 |
61 |
62 |
65 |
66 |
70 |
71 |
74 |
75 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/src/com/vellut/geoalarm/ReceiveTransitionsIntentService.java:
--------------------------------------------------------------------------------
1 | package com.vellut.geoalarm;
2 |
3 | import android.app.IntentService;
4 | import android.app.Notification;
5 | import android.app.NotificationManager;
6 | import android.app.PendingIntent;
7 | import android.content.Context;
8 | import android.content.Intent;
9 | import android.net.Uri;
10 | import android.os.SystemClock;
11 | import android.support.v4.app.NotificationCompat;
12 | import android.support.v4.app.TaskStackBuilder;
13 | import android.util.Log;
14 |
15 | import com.google.android.gms.location.Geofence;
16 | import com.google.android.gms.location.LocationClient;
17 |
18 | public class ReceiveTransitionsIntentService extends IntentService {
19 |
20 | public ReceiveTransitionsIntentService() {
21 | super("ReceiveTransitionsIntentService");
22 | }
23 |
24 | @Override
25 | protected void onHandleIntent(Intent intent) {
26 | Log.d(GeoAlarmUtils.APPTAG,
27 | "ReceiveTransitionsItentService HandleIntent");
28 |
29 | // First check for errors
30 | if (LocationClient.hasError(intent)) {
31 | int errorCode = LocationClient.getErrorCode(intent);
32 | // Log the error
33 | Log.e(GeoAlarmUtils.APPTAG,
34 | getString(R.string.geofence_transition_error_detail,
35 | errorCode));
36 | } else {
37 | int transition = LocationClient.getGeofenceTransition(intent);
38 | // Test that a valid transition was reported
39 | if (transition == Geofence.GEOFENCE_TRANSITION_ENTER) {
40 | Log.d(GeoAlarmUtils.APPTAG, "GeoAlarm triggered");
41 | long alarmSetTime = intent.getExtras().getLong(
42 | GeoAlarmUtils.EXTRA_ALARM_SET_TIME);
43 | long currentTime = SystemClock.elapsedRealtime();
44 | if (currentTime - alarmSetTime > GeoAlarmUtils.MIN_DTIME) {
45 | boolean isUseVbirate = intent.getExtras().getBoolean(
46 | GeoAlarmUtils.EXTRA_USE_VIBRATE);
47 | String ringtoneUri = intent.getExtras().getString(
48 | GeoAlarmUtils.EXTRA_RINGTONE_URI);
49 | sendNotification(ringtoneUri, isUseVbirate);
50 | }
51 | }
52 | }
53 |
54 | ReceiveTransitionsBroadcastReceiver.completeWakefulIntent(intent);
55 | }
56 |
57 | private void sendNotification(String ringtoneUri, boolean isUseVibrate) {
58 |
59 | // Create an explicit content Intent that starts the main Activity
60 | Intent notificationIntent = new Intent(getApplicationContext(),
61 | MainActivity.class);
62 | notificationIntent.setAction(GeoAlarmUtils.ACTION_STOP_ALARM);
63 |
64 | // Construct a task stack
65 | TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
66 |
67 | // Adds the main Activity to the task stack as the parent
68 | stackBuilder.addParentStack(MainActivity.class);
69 |
70 | // Push the content Intent onto the stack
71 | stackBuilder.addNextIntent(notificationIntent);
72 |
73 | // Get a PendingIntent containing the entire back stack
74 | PendingIntent notificationPendingIntent = stackBuilder
75 | .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
76 |
77 | // Get a notification builder that's compatible with platform versions
78 | // >= 4
79 | NotificationCompat.Builder builder = new NotificationCompat.Builder(
80 | this);
81 |
82 | // Set the notification contents
83 | builder.setSmallIcon(R.drawable.ic_notification)
84 | .setContentTitle(
85 | getString(R.string.geofence_transition_notification_title))
86 | .setContentText(
87 | getString(R.string.geofence_transition_notification_text))
88 | .setContentIntent(notificationPendingIntent)
89 | .setAutoCancel(true);
90 |
91 | if (ringtoneUri != null) {
92 | builder.setSound(Uri.parse(ringtoneUri),
93 | Notification.STREAM_DEFAULT);
94 | }
95 |
96 | if (isUseVibrate) {
97 | builder.setVibrate(new long[] { 0, 200, 1000, 200, 1000, 200, 1000,
98 | 200, 1000 });
99 | }
100 |
101 | // Get an instance of the Notification manager
102 | NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
103 |
104 | // Issue the notification
105 | mNotificationManager.notify(GeoAlarmUtils.GEOFENCE_NOTIFICATION_ID,
106 | builder.build());
107 | }
108 |
109 | }
110 |
--------------------------------------------------------------------------------
/src/com/vellut/geoalarm/AlarmWakeUpService.java:
--------------------------------------------------------------------------------
1 | package com.vellut.geoalarm;
2 |
3 | import android.app.Notification;
4 | import android.app.NotificationManager;
5 | import android.app.PendingIntent;
6 | import android.app.Service;
7 | import android.content.Context;
8 | import android.content.Intent;
9 | import android.location.Location;
10 | import android.net.Uri;
11 | import android.os.Bundle;
12 | import android.os.CountDownTimer;
13 | import android.os.HandlerThread;
14 | import android.os.IBinder;
15 | import android.os.Looper;
16 | import android.os.SystemClock;
17 | import android.support.v4.app.NotificationCompat;
18 | import android.support.v4.app.TaskStackBuilder;
19 | import android.util.Log;
20 |
21 | import com.google.android.gms.common.ConnectionResult;
22 | import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks;
23 | import com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener;
24 | import com.google.android.gms.location.LocationClient;
25 | import com.google.android.gms.location.LocationListener;
26 | import com.google.android.gms.location.LocationRequest;
27 |
28 | public class AlarmWakeUpService extends Service implements ConnectionCallbacks,
29 | OnConnectionFailedListener, LocationListener {
30 |
31 | private LocationClient locationClient;
32 | private Intent wakefulIntent;
33 | private Looper serviceLooper;
34 | GeoAlarm geoAlarm;
35 |
36 | @Override
37 | public int onStartCommand(Intent intent, int flags, int startId) {
38 | locationClient = new LocationClient(this, this, this);
39 | locationClient.connect();
40 | wakefulIntent = intent;
41 |
42 | Log.i(GeoAlarmUtils.APPTAG, "In AlarmWakeUp Service");
43 |
44 | return START_NOT_STICKY;
45 | }
46 |
47 | @Override
48 | public void onDestroy() {
49 | serviceLooper.quit();
50 | }
51 |
52 | @Override
53 | public IBinder onBind(Intent intent) {
54 | return null;
55 | }
56 |
57 | @Override
58 | public void onConnectionFailed(ConnectionResult arg0) {
59 | quitWakefulService();
60 | }
61 |
62 | @Override
63 | public void onConnected(Bundle bundle) {
64 | LocationRequest request = new LocationRequest();
65 |
66 | Log.i(GeoAlarmUtils.APPTAG, "In AlarmWakeUp Service: CONNECTED");
67 |
68 | geoAlarm = new GeoAlarm();
69 | geoAlarm.restorePreferences(this);
70 | int priority = geoAlarm.getLocationPriority(this);
71 |
72 | long timeout = 60 * 1000;
73 | HandlerThread thread = new HandlerThread("AlarmWakeUpServiceBackground");
74 | thread.start();
75 | serviceLooper = thread.getLooper();
76 | request.setNumUpdates(1).setFastestInterval(1).setInterval(1)
77 | .setPriority(priority).setExpirationDuration(timeout);
78 | locationClient.requestLocationUpdates(request, this, serviceLooper);
79 |
80 | // timeout for kill and free wake lock
81 | /*
82 | * new CountDownTimer(timeout, timeout) { public void onTick(long
83 | * millisUntilFinished) { }
84 | *
85 | * public void onFinish() { Log.i(GeoAlarmUtils.APPTAG,
86 | * "Timeout: No Location Fix"); quitWakefulService(); } }.start();
87 | */
88 | }
89 |
90 | @Override
91 | public void onDisconnected() {
92 | quitWakefulService();
93 | }
94 |
95 | @Override
96 | public void onLocationChanged(Location location) {
97 | try {
98 | double latitude = location.getLatitude();
99 | double longitude = location.getLongitude();
100 |
101 | // fIXME add a precision fuzz
102 |
103 | Log.i(GeoAlarmUtils.APPTAG, "OnLocationChanged " + latitude + " "
104 | + longitude);
105 |
106 | if (latitude >= geoAlarm.zone.southwest.latitude
107 | && latitude <= geoAlarm.zone.northeast.latitude
108 | && longitude >= geoAlarm.zone.southwest.longitude
109 | && longitude <= geoAlarm.zone.northeast.longitude) {
110 |
111 | if (!geoAlarm.isInZone) {
112 | // Only fire if not already in Zone
113 | Log.i(GeoAlarmUtils.APPTAG, "Alarm has fired: In Zone");
114 |
115 | geoAlarm.isInZone = true;
116 | geoAlarm.saveLocationZone(this);
117 |
118 | long alarmSetTime = geoAlarm.alarmSetTime;
119 | long currentTime = SystemClock.elapsedRealtime();
120 | if (currentTime - alarmSetTime > GeoAlarmUtils.MIN_DTIME) {
121 | String ringtoneUri = null;
122 | if (geoAlarm.ringtoneUri != null) {
123 | ringtoneUri = geoAlarm.ringtoneUri.toString();
124 | }
125 | boolean isUseVibrate = geoAlarm.isUseVibrate;
126 | sendNotification(ringtoneUri, isUseVibrate);
127 | }
128 |
129 | }
130 | } else {
131 | Log.i(GeoAlarmUtils.APPTAG, "Has Left Zone");
132 | geoAlarm.isInZone = false;
133 | geoAlarm.saveLocationZone(this);
134 | }
135 |
136 | } catch (Exception e) {
137 | Log.e(GeoAlarmUtils.APPTAG, "Error onlocationch", e);
138 | } finally {
139 | quitWakefulService();
140 | }
141 | }
142 |
143 | private void sendNotification(String ringtoneUri, boolean isUseVibrate) {
144 |
145 | // Create an explicit content Intent that starts the main Activity
146 | Intent notificationIntent = new Intent(getApplicationContext(),
147 | MainActivity.class);
148 | notificationIntent.setAction(GeoAlarmUtils.ACTION_STOP_ALARM);
149 |
150 | // Construct a task stack
151 | TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
152 |
153 | // Adds the main Activity to the task stack as the parent
154 | stackBuilder.addParentStack(MainActivity.class);
155 |
156 | // Push the content Intent onto the stack
157 | stackBuilder.addNextIntent(notificationIntent);
158 |
159 | // Get a PendingIntent containing the entire back stack
160 | PendingIntent notificationPendingIntent = stackBuilder
161 | .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
162 |
163 | // Get a notification builder that's compatible with platform versions
164 | // >= 4
165 | NotificationCompat.Builder builder = new NotificationCompat.Builder(
166 | this);
167 |
168 | // Set the notification contents
169 | builder.setSmallIcon(R.drawable.ic_notification)
170 | .setContentTitle(
171 | getString(R.string.geofence_transition_notification_title))
172 | .setContentText(
173 | getString(R.string.geofence_transition_notification_text))
174 | .setContentIntent(notificationPendingIntent)
175 | .setAutoCancel(true);
176 |
177 | if (ringtoneUri != null) {
178 | builder.setSound(Uri.parse(ringtoneUri),
179 | Notification.STREAM_DEFAULT);
180 | }
181 |
182 | if (isUseVibrate) {
183 | builder.setVibrate(new long[] { 0, 200, 1000, 200, 1000, 200, 1000,
184 | 200, 1000 });
185 | }
186 |
187 | // Get an instance of the Notification manager
188 | NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
189 |
190 | // Issue the notification
191 | mNotificationManager.notify(GeoAlarmUtils.GEOFENCE_NOTIFICATION_ID,
192 | builder.build());
193 | }
194 |
195 | private void quitWakefulService() {
196 | AlarmWakeUpBroadcastReceiver.completeWakefulIntent(wakefulIntent);
197 | stopSelf();
198 | }
199 |
200 | }
201 |
--------------------------------------------------------------------------------
/src/com/vellut/geoalarm/GeoAlarm.java:
--------------------------------------------------------------------------------
1 | package com.vellut.geoalarm;
2 |
3 | import java.lang.reflect.Type;
4 | import java.util.ArrayList;
5 | import java.util.List;
6 |
7 | import android.app.AlarmManager;
8 | import android.app.PendingIntent;
9 | import android.content.Context;
10 | import android.content.Intent;
11 | import android.content.SharedPreferences;
12 | import android.location.LocationProvider;
13 | import android.media.RingtoneManager;
14 | import android.net.Uri;
15 | import android.os.SystemClock;
16 | import android.preference.PreferenceManager;
17 | import android.util.Log;
18 |
19 | import com.google.android.gms.location.Geofence;
20 | import com.google.android.gms.location.LocationClient;
21 | import com.google.android.gms.location.LocationClient.OnAddGeofencesResultListener;
22 | import com.google.android.gms.location.LocationClient.OnRemoveGeofencesResultListener;
23 | import com.google.android.gms.location.LocationRequest;
24 | import com.google.android.gms.maps.model.LatLng;
25 | import com.google.android.gms.maps.model.LatLngBounds;
26 | import com.google.gson.Gson;
27 | import com.google.gson.GsonBuilder;
28 | import com.google.gson.reflect.TypeToken;
29 | import com.vellut.geoalarm.io.LatLngBoundsDeserializer;
30 | import com.vellut.geoalarm.io.LatLngBoundsSerializer;
31 |
32 | public class GeoAlarm {
33 |
34 | public boolean isFirstTimeRun;
35 | public boolean isAlarmOn;
36 | public Uri ringtoneUri;
37 | public LatLngBounds zone;
38 | public boolean isUseVibrate;
39 | public ArrayList savedLocations;
40 | public String locationTechnique;
41 | public boolean isInZone;
42 | public long alarmSetTime;
43 |
44 | private GsonBuilder builder;
45 |
46 | public GeoAlarm() {
47 | this.savedLocations = new ArrayList();
48 |
49 | builder = new GsonBuilder();
50 | builder.registerTypeAdapter(LatLngBounds.class,
51 | new LatLngBoundsSerializer());
52 | builder.registerTypeAdapter(LatLngBounds.class,
53 | new LatLngBoundsDeserializer());
54 | }
55 |
56 | public void restorePreferences(Context context) {
57 | SharedPreferences settings = PreferenceManager
58 | .getDefaultSharedPreferences(context);
59 |
60 | isFirstTimeRun = settings.getBoolean(
61 | GeoAlarmUtils.PREF_IS_FIRST_TIME_RUN, true);
62 |
63 | if (!isFirstTimeRun) {
64 | Log.d(GeoAlarmUtils.APPTAG, "Not First Time Run");
65 | double north = getDouble(settings.getString(
66 | GeoAlarmUtils.PREF_NORTH_LAT, null));
67 | double south = getDouble(settings.getString(
68 | GeoAlarmUtils.PREF_SOUTH_LAT, null));
69 | double west = getDouble(settings.getString(
70 | GeoAlarmUtils.PREF_WEST_LON, null));
71 | double east = getDouble(settings.getString(
72 | GeoAlarmUtils.PREF_EAST_LON, null));
73 | zone = new LatLngBounds(new LatLng(south, west), new LatLng(north,
74 | east));
75 | }
76 |
77 | if (isFirstTimeRun) {
78 | // Default alarm sound
79 | Uri defaultUri = RingtoneManager
80 | .getDefaultUri(RingtoneManager.TYPE_ALARM);
81 | if (defaultUri == null) {
82 | defaultUri = RingtoneManager
83 | .getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
84 | if (defaultUri == null) {
85 | defaultUri = RingtoneManager
86 | .getDefaultUri(RingtoneManager.TYPE_RINGTONE);
87 | }
88 | }
89 | ringtoneUri = defaultUri;
90 | locationTechnique = context
91 | .getString(R.string.pref_location_technique_defaultvalue);
92 | isInZone = false;
93 | alarmSetTime = 0;
94 | } else {
95 | String sRingtoneUri = settings.getString(
96 | GeoAlarmUtils.PREF_RINGTONE_URI, null);
97 | if (sRingtoneUri == null) {
98 | ringtoneUri = null;
99 | } else {
100 | ringtoneUri = Uri.parse(sRingtoneUri);
101 | }
102 |
103 | String sSavedLocations = settings.getString(
104 | GeoAlarmUtils.PREF_SAVED_LOCATIONS, null);
105 | if (sSavedLocations != null) {
106 | try {
107 | savedLocations = deserializeFromString(sSavedLocations);
108 | } catch (Exception ex) {
109 | Log.e(GeoAlarmUtils.APPTAG,
110 | "Error getting saved locations", ex);
111 | }
112 | }
113 |
114 | }
115 |
116 | isUseVibrate = settings.getBoolean(GeoAlarmUtils.PREF_USE_VIBRATE,
117 | false);
118 |
119 | isAlarmOn = settings.getBoolean(GeoAlarmUtils.PREF_IS_ALARM_ON, false);
120 |
121 | locationTechnique = settings
122 | .getString(
123 | GeoAlarmUtils.PREF_LOCATION_TECHNIQUE,
124 | context.getString(R.string.pref_location_technique_defaultvalue));
125 |
126 | isInZone = settings.getBoolean(GeoAlarmUtils.PREF_IS_IN_ZONE, false);
127 | alarmSetTime = settings.getLong(GeoAlarmUtils.PREF_ALARM_SET_TIME, 0);
128 | }
129 |
130 | public void reloadPreferenceForKey(Context context, String key) {
131 | if (key.equals(GeoAlarmUtils.PREF_LOCATION_TECHNIQUE)) {
132 | locationTechnique = PreferenceManager.getDefaultSharedPreferences(
133 | context).getString(key, "");
134 | }
135 | }
136 |
137 | private double getDouble(String sDouble) {
138 | if (sDouble == null) {
139 | return Double.NaN;
140 | } else {
141 | return Double.parseDouble(sDouble);
142 | }
143 | }
144 |
145 | public void savePreferences(Context context) {
146 | SharedPreferences settings = PreferenceManager
147 | .getDefaultSharedPreferences(context);
148 | SharedPreferences.Editor editor = settings.edit();
149 |
150 | editor.putBoolean(GeoAlarmUtils.PREF_IS_FIRST_TIME_RUN, isFirstTimeRun);
151 | editor.putString(GeoAlarmUtils.PREF_NORTH_LAT,
152 | Double.toString(zone.northeast.latitude));
153 | editor.putString(GeoAlarmUtils.PREF_SOUTH_LAT,
154 | Double.toString(zone.southwest.latitude));
155 | editor.putString(GeoAlarmUtils.PREF_WEST_LON,
156 | Double.toString(zone.southwest.longitude));
157 | editor.putString(GeoAlarmUtils.PREF_EAST_LON,
158 | Double.toString(zone.northeast.longitude));
159 | if (ringtoneUri != null) {
160 | editor.putString(GeoAlarmUtils.PREF_RINGTONE_URI,
161 | ringtoneUri.toString());
162 | } else {
163 | editor.putString(GeoAlarmUtils.PREF_RINGTONE_URI, null);
164 | }
165 | editor.putBoolean(GeoAlarmUtils.PREF_USE_VIBRATE, isUseVibrate);
166 | editor.putBoolean(GeoAlarmUtils.PREF_IS_ALARM_ON, isAlarmOn);
167 | editor.putString(GeoAlarmUtils.PREF_SAVED_LOCATIONS,
168 | serializeToString(savedLocations));
169 | editor.putLong(GeoAlarmUtils.PREF_ALARM_SET_TIME, alarmSetTime);
170 |
171 | // no save for locationTechnique : managed by PreferenceManager
172 |
173 | editor.commit();
174 | }
175 |
176 | public void saveLocationZone(Context context) {
177 | SharedPreferences settings = PreferenceManager
178 | .getDefaultSharedPreferences(context);
179 | SharedPreferences.Editor editor = settings.edit();
180 |
181 | editor.putBoolean(GeoAlarmUtils.PREF_IS_IN_ZONE, isInZone);
182 |
183 | editor.commit();
184 | }
185 |
186 | private String serializeToString(ArrayList locations) {
187 | Gson gson = builder.create();
188 | Type type = new TypeToken>() {
189 | }.getType();
190 | String ser = gson.toJson(locations, type);
191 | return ser;
192 | }
193 |
194 | private ArrayList deserializeFromString(String json) {
195 | Gson gson = builder.create();
196 | Type type = new TypeToken>() {
197 | }.getType();
198 | Object obj = gson.fromJson(json, type);
199 | return (ArrayList) obj;
200 | }
201 |
202 | public int getLocationPriority(Context context) {
203 | if(locationTechnique.equals(context.getString(GeoAlarmUtils.LOCATION_TECHNIQUE_LOW_POWER))) {
204 | return LocationRequest.PRIORITY_LOW_POWER;
205 | } else if(locationTechnique.equals(context.getString(GeoAlarmUtils.LOCATION_TECHNIQUE_BALANCED_POWER))) {
206 | return LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY;
207 | } else if(locationTechnique.equals(context.getString(GeoAlarmUtils.LOCATION_TECHNIQUE_HIGH_ACCURACY))) {
208 | return LocationRequest.PRIORITY_HIGH_ACCURACY;
209 | } else {
210 | Log.d(GeoAlarmUtils.APPTAG, "Should not happen: No Power");
211 | return LocationRequest.PRIORITY_NO_POWER;
212 | }
213 | }
214 |
215 | public void setAlarm(Context context, LocationClient locationClient,
216 | OnAddGeofencesResultListener listener) {
217 | /*
218 | * PendingIntent transitionPendingIntent =
219 | * getTransitionPendingIntent(context); List geofences = new
220 | * ArrayList(); geofences.add(buildGeofence());
221 | * locationClient.addGeofences(geofences, transitionPendingIntent,
222 | * listener);
223 | */
224 |
225 | AlarmManager am = (AlarmManager) context
226 | .getSystemService(Context.ALARM_SERVICE);
227 | PendingIntent alarmWakeUpPendingIntent = getAlarmWakeUpPendingIntent(context);
228 | am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, 2 * 60 * 1000,
229 | 2 * 60 * 1000, alarmWakeUpPendingIntent);
230 | alarmSetTime = SystemClock.elapsedRealtime();
231 | }
232 |
233 | public void disableAlarm(Context context, LocationClient locationClient,
234 | OnRemoveGeofencesResultListener listener) {
235 | /*
236 | * List geofenceIds = new ArrayList();
237 | * geofenceIds.add(GeoAlarmUtils.GEOFENCE_REQUEST_ID);
238 | * locationClient.removeGeofences(geofenceIds, listener);
239 | */
240 |
241 | AlarmManager am = (AlarmManager) context
242 | .getSystemService(Context.ALARM_SERVICE);
243 | PendingIntent alarmWakeUpPendingIntent = getAlarmWakeUpPendingIntent(context);
244 | am.cancel(alarmWakeUpPendingIntent);
245 | }
246 |
247 | private PendingIntent getTransitionPendingIntent(Context context) {
248 | Intent intent = new Intent(context,
249 | ReceiveTransitionsBroadcastReceiver.class);
250 | if (ringtoneUri != null) {
251 | intent.putExtra(GeoAlarmUtils.EXTRA_RINGTONE_URI,
252 | ringtoneUri.toString());
253 | }
254 | intent.putExtra(GeoAlarmUtils.EXTRA_USE_VIBRATE, isUseVibrate);
255 | intent.putExtra(GeoAlarmUtils.EXTRA_ALARM_SET_TIME,
256 | SystemClock.elapsedRealtime());
257 |
258 | return PendingIntent.getBroadcast(context, 0, intent,
259 | PendingIntent.FLAG_UPDATE_CURRENT);
260 | }
261 |
262 | private PendingIntent getAlarmWakeUpPendingIntent(Context context) {
263 | Intent intent = new Intent(context, AlarmWakeUpBroadcastReceiver.class);
264 | return PendingIntent.getBroadcast(context, 0, intent, 0);
265 | }
266 |
267 | private Geofence buildGeofence() {
268 | LatLng center = zone.getCenter();
269 | double dLng = Math.abs(zone.northeast.longitude
270 | - zone.southwest.longitude);
271 | double dLat = Math.abs(zone.northeast.latitude
272 | - zone.southwest.latitude);
273 | float radius = (float) Math.min(dLng, dLat);
274 | radius *= 111320; // Length of a degree in meter at equator
275 |
276 | Log.d(GeoAlarmUtils.APPTAG, "Center " + center.latitude + " "
277 | + center.longitude + " " + radius);
278 |
279 | return new Geofence.Builder()
280 | .setRequestId(GeoAlarmUtils.GEOFENCE_REQUEST_ID)
281 | .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER)
282 | .setExpirationDuration(Geofence.NEVER_EXPIRE)
283 | .setCircularRegion(center.latitude, center.longitude, radius)
284 | .build();
285 | }
286 |
287 | }
288 |
--------------------------------------------------------------------------------
/icon_notification.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
429 |
--------------------------------------------------------------------------------
/src/com/vellut/geoalarm/SwipeDismissListViewTouchListener.java:
--------------------------------------------------------------------------------
1 | package com.vellut.geoalarm;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.animation.ValueAnimator;
6 | import android.graphics.Rect;
7 | import android.os.SystemClock;
8 | import android.view.MotionEvent;
9 | import android.view.VelocityTracker;
10 | import android.view.View;
11 | import android.view.ViewConfiguration;
12 | import android.view.ViewGroup;
13 | import android.view.ViewPropertyAnimator;
14 | import android.widget.AbsListView;
15 | import android.widget.ListView;
16 |
17 | import java.util.ArrayList;
18 | import java.util.Collections;
19 | import java.util.List;
20 |
21 | /**
22 | * A {@link View.OnTouchListener} that makes the list items in a {@link ListView}
23 | * dismissable. {@link ListView} is given special treatment because by default it handles touches
24 | * for its list items... i.e. it's in charge of drawing the pressed state (the list selector),
25 | * handling list item clicks, etc.
26 | *
27 | *
After creating the listener, the caller should also call
28 | * {@link ListView#setOnScrollListener(AbsListView.OnScrollListener)}, passing
29 | * in the scroll listener returned by {@link #makeScrollListener()}. If a scroll listener is
30 | * already assigned, the caller should still pass scroll changes through to this listener. This will
31 | * ensure that this {@link SwipeDismissListViewTouchListener} is paused during list view
32 | * scrolling.
This class Requires API level 12 or later due to use of {@link
53 | * ViewPropertyAnimator}.
54 | *
55 | * @see SwipeDismissTouchListener
56 | */
57 | public class SwipeDismissListViewTouchListener implements View.OnTouchListener {
58 | // Cached ViewConfiguration and system-wide constant values
59 | private int mSlop;
60 | private int mMinFlingVelocity;
61 | private int mMaxFlingVelocity;
62 | private long mAnimationTime;
63 |
64 | // Fixed properties
65 | private ListView mListView;
66 | private DismissCallbacks mCallbacks;
67 | private int mViewWidth = 1; // 1 and not 0 to prevent dividing by zero
68 |
69 | // Transient properties
70 | private List mPendingDismisses = new ArrayList();
71 | private int mDismissAnimationRefCount = 0;
72 | private float mDownX;
73 | private float mDownY;
74 | private boolean mSwiping;
75 | private int mSwipingSlop;
76 | private VelocityTracker mVelocityTracker;
77 | private int mDownPosition;
78 | private View mDownView;
79 | private boolean mPaused;
80 |
81 | /**
82 | * The callback interface used by {@link SwipeDismissListViewTouchListener} to inform its client
83 | * about a successful dismissal of one or more list item positions.
84 | */
85 | public interface DismissCallbacks {
86 | /**
87 | * Called to determine whether the given position can be dismissed.
88 | */
89 | boolean canDismiss(int position);
90 |
91 | /**
92 | * Called when the user has indicated they she would like to dismiss one or more list item
93 | * positions.
94 | *
95 | * @param listView The originating {@link ListView}.
96 | * @param reverseSortedPositions An array of positions to dismiss, sorted in descending
97 | * order for convenience.
98 | */
99 | void onDismiss(ListView listView, int[] reverseSortedPositions);
100 | }
101 |
102 | /**
103 | * Constructs a new swipe-to-dismiss touch listener for the given list view.
104 | *
105 | * @param listView The list view whose items should be dismissable.
106 | * @param callbacks The callback to trigger when the user has indicated that she would like to
107 | * dismiss one or more list items.
108 | */
109 | public SwipeDismissListViewTouchListener(ListView listView, DismissCallbacks callbacks) {
110 | ViewConfiguration vc = ViewConfiguration.get(listView.getContext());
111 | mSlop = vc.getScaledTouchSlop();
112 | mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * 16;
113 | mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
114 | mAnimationTime = listView.getContext().getResources().getInteger(
115 | android.R.integer.config_shortAnimTime);
116 | mListView = listView;
117 | mCallbacks = callbacks;
118 | }
119 |
120 | /**
121 | * Enables or disables (pauses or resumes) watching for swipe-to-dismiss gestures.
122 | *
123 | * @param enabled Whether or not to watch for gestures.
124 | */
125 | public void setEnabled(boolean enabled) {
126 | mPaused = !enabled;
127 | }
128 |
129 | /**
130 | * Returns an {@link AbsListView.OnScrollListener} to be added to the {@link
131 | * ListView} using {@link ListView#setOnScrollListener(AbsListView.OnScrollListener)}.
132 | * If a scroll listener is already assigned, the caller should still pass scroll changes through
133 | * to this listener. This will ensure that this {@link SwipeDismissListViewTouchListener} is
134 | * paused during list view scrolling.