Java tutorial
/* * This source is part of the * _____ ___ ____ * __ / / _ \/ _ | / __/___ _______ _ * / // / , _/ __ |/ _/_/ _ \/ __/ _ `/ * \___/_/|_/_/ |_/_/ (_)___/_/ \_, / * /___/ * repository. * * Copyright (C) 2015 Benoit 'BoD' Lubek (BoD@JRAF.org) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.jraf.android.dcn.handheld.app.geofencing; import android.app.IntentService; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.Bitmap; import android.net.Uri; import android.preference.PreferenceManager; import android.support.v4.app.NotificationCompat; import android.text.SpannableString; import android.text.Spanned; import android.text.style.TextAppearanceSpan; import com.google.android.gms.location.Geofence; import com.google.android.gms.location.GeofencingEvent; import org.jraf.android.dcn.R; import org.jraf.android.dcn.common.wear.WearHelper; import org.jraf.android.dcn.handheld.Constants; import org.jraf.android.dcn.handheld.app.addressinfo.list.AddressInfoLoader; import org.jraf.android.dcn.handheld.model.addressinfo.AddressInfo; import org.jraf.android.util.log.wrapper.Log; import org.jraf.android.util.string.StringUtil; import java.util.ArrayList; import java.util.List; public class GeofencingService extends IntentService { public static final String ACTION_REFRESH_GEOFENCES = "ACTION_REFRESH_GEOFENCES"; public static final String ACTION_DISMISS_NOTIFICATION = "ACTION_DISMISS_NOTIFICATION"; private static final int NOTIFICATION_RESPONSIVENESS_MS = 5 * 1000; // 4 seconds private static final float RADIUS_M = 200; private static final int DISMISS_TIMEOUT_MS = 6 * 60 * 1000; // 6 minutes private static final int NOTIFICATION_ID = 0; private GeofencingHelper mGeofencingHelper = GeofencingHelper.get(); private WearHelper mWearHelper = WearHelper.get(); public GeofencingService() { super(GeofencingService.class.getName()); } @Override protected void onHandleIntent(Intent intent) { Log.d("intent=" + StringUtil.toString(intent)); String action = intent.getAction(); if (action == null) action = ""; // Avoid null to be able to use switch switch (action) { case ACTION_REFRESH_GEOFENCES: // Triggered by app logic mGeofencingHelper.connect(this); // Blocking SharedPreferences preferenceManager = PreferenceManager.getDefaultSharedPreferences(this); boolean enabled = preferenceManager.getBoolean(Constants.PREF_GEOFENCING_ENABLED, Constants.PREF_GEOFENCING_ENABLED_DEFAULT); if (enabled) { refreshGeofences(); } else { removeAllGeofences(); // Also dismiss any prior notifications dismissNotification(); } break; case ACTION_DISMISS_NOTIFICATION: // Triggered when dismissing the handheld notification dismissNotification(); break; default: // Triggered by the geofence system GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent); if (geofencingEvent == null) { Log.w("Received a null GeofencingEvent: ignore"); return; } if (geofencingEvent.hasError()) { Log.w("Received an error GeofencingEvent (errorCode=" + geofencingEvent.getErrorCode() + "): ignore"); return; } // Get the corresponding AddressInfo (if several, only consider the first one) String geofenceId = geofencingEvent.getTriggeringGeofences().get(0).getRequestId(); List<AddressInfo> addressInfoList = AddressInfoLoader.retrieveAddressInfoList(this); AddressInfo addressInfo = null; for (AddressInfo add : addressInfoList) { if (add.uri.toString().equals(geofenceId)) { // Found it addressInfo = add; break; } } if (addressInfo == null) { // Can happen if the AddressInfo has been deleted, and the geofences were not refreshed (maybe after a // manual contact edit, or a contact sync?) Log.w("The geofence id does not match any AddressInfo: ignore"); return; } switch (geofencingEvent.getGeofenceTransition()) { case Geofence.GEOFENCE_TRANSITION_ENTER: showEnteredNotification(addressInfo); break; case Geofence.GEOFENCE_TRANSITION_EXIT: case Geofence.GEOFENCE_TRANSITION_DWELL: dismissNotification(); break; } break; } } private void refreshGeofences() { Log.d(); // First remove all geofences removeAllGeofences(); // Add the fresh list of geofences List<AddressInfo> addressInfoList = AddressInfoLoader.retrieveAddressInfoList(this); List<Geofence> geofenceList = new ArrayList<>(addressInfoList.size()); for (AddressInfo addressInfo : addressInfoList) { geofenceList.add(toGeofence(addressInfo)); } mGeofencingHelper.addGeofences(geofenceList); } private Geofence toGeofence(AddressInfo addressInfo) { Geofence.Builder geofenceBuilder = new Geofence.Builder(); geofenceBuilder.setRequestId(addressInfo.uri.toString()); geofenceBuilder.setExpirationDuration(Geofence.NEVER_EXPIRE); geofenceBuilder.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT | Geofence.GEOFENCE_TRANSITION_DWELL); geofenceBuilder.setNotificationResponsiveness(NOTIFICATION_RESPONSIVENESS_MS); geofenceBuilder.setCircularRegion(addressInfo.latitude, addressInfo.longitude, RADIUS_M); geofenceBuilder.setLoiteringDelay(DISMISS_TIMEOUT_MS); return geofenceBuilder.build(); } private void removeAllGeofences() { Log.d(); mGeofencingHelper.removeAllGeofences(); } /* * Notification. */ private void showEnteredNotification(AddressInfo addressInfo) { Log.d("addressInfo=" + addressInfo); String titleShort = getNotificationTitle(addressInfo, false); String titleLong = getNotificationTitle(addressInfo, true); String textShort = getNotificationText(addressInfo, false); String textLong = getNotificationText(addressInfo, true); NotificationCompat.Builder mainNotifBuilder = new NotificationCompat.Builder(this); // Small icon mainNotifBuilder.setSmallIcon(R.drawable.ic_stat_entered); // Make a bigger title SpannableString titleSpannable = new SpannableString(titleShort); Object span = new TextAppearanceSpan(this, R.style.NotificationContentTitleTextAppearance); titleSpannable.setSpan(span, 0, titleShort.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); // Title mainNotifBuilder.setContentTitle(titleSpannable); // Ticker mainNotifBuilder.setTicker(titleShort); // Text (short) mainNotifBuilder.setContentText(textShort); // Text (long) mainNotifBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(textLong)); // Misc mainNotifBuilder.setPriority(NotificationCompat.PRIORITY_HIGH); // Time sensitive, try to appear on top mainNotifBuilder.setCategory(NotificationCompat.CATEGORY_STATUS); // Not sure if this category is really the most appropriate mainNotifBuilder.setLights(0, 0, 0); // No light mainNotifBuilder.setShowWhen(false); // No date mainNotifBuilder.addPerson(addressInfo.contactInfo.contentLookupUri.toString()); // Contact photo Bitmap contactPhoto = addressInfo.getContactPhoto(this); if (contactPhoto != null) mainNotifBuilder.setLargeIcon(contactPhoto); // Dismiss intent Intent dismissIntent = new Intent(ACTION_DISMISS_NOTIFICATION, null, this, getClass()); PendingIntent dismissPendingIntent = PendingIntent.getService(this, 0, dismissIntent, PendingIntent.FLAG_UPDATE_CURRENT); mainNotifBuilder.setDeleteIntent(dismissPendingIntent); // Main action (click on the notification itself) Intent mainIntent = new Intent(Intent.ACTION_VIEW); mainIntent.setData(addressInfo.contactInfo.contentLookupUri); PendingIntent mainPendingIntent = PendingIntent.getActivity(this, 0, mainIntent, PendingIntent.FLAG_UPDATE_CURRENT); mainNotifBuilder.setContentIntent(mainPendingIntent); String phoneNumber = addressInfo.getContactPhoneNumber(this); if (phoneNumber != null) { // Call action Intent callIntent = new Intent(Intent.ACTION_DIAL); callIntent.setData(Uri.parse("tel:" + phoneNumber)); PendingIntent callPendingIntent = PendingIntent.getActivity(this, 0, callIntent, PendingIntent.FLAG_UPDATE_CURRENT); String callText = getString(R.string.notification_action_call); mainNotifBuilder.addAction(R.drawable.ic_action_call, callText, callPendingIntent); // Sms action Intent smsIntent = new Intent(Intent.ACTION_VIEW); smsIntent.setData(Uri.parse("sms:" + phoneNumber)); smsIntent.putExtra("sms_body", getString(R.string.notification_action_sms_body)); PendingIntent smsPendingIntent = PendingIntent.getActivity(this, 0, smsIntent, PendingIntent.FLAG_UPDATE_CURRENT); String smsText = getString(R.string.notification_action_sms); mainNotifBuilder.addAction(R.drawable.ic_action_sms, smsText, smsPendingIntent); } // Since we have a specific Wear notification, show this one only on handheld mainNotifBuilder.setLocalOnly(true); // Show it Notification notification = mainNotifBuilder.build(); NotificationManager notificationManager = (NotificationManager) getSystemService( Context.NOTIFICATION_SERVICE); notificationManager.notify(NOTIFICATION_ID, notification); // Show a Wear notification // Blocking mWearHelper.connect(this); mWearHelper.putNotification(titleLong, textShort, textLong, contactPhoto, addressInfo.contactInfo.contentLookupUri, phoneNumber); } private String getNotificationTitle(AddressInfo addressInfo, boolean longForm) { if (addressInfo.codeList.isEmpty()) { if (addressInfo.otherInfo == null) { return addressInfo.contactInfo.displayName; } return addressInfo.otherInfo; } String res = ""; int i = 0; String separator = longForm ? "\n" : " "; for (String code : addressInfo.codeList) { if (i > 0) { res += separator; } res += code; i++; } return res; } private String getNotificationText(AddressInfo addressInfo, boolean longForm) { if (addressInfo.codeList.isEmpty()) { if (addressInfo.otherInfo == null) { return null; } return addressInfo.contactInfo.displayName; } if (addressInfo.otherInfo == null) { return addressInfo.contactInfo.displayName; } String separator = longForm ? "\n" : " "; return addressInfo.otherInfo + separator + addressInfo.contactInfo.displayName; } private void dismissNotification() { Log.d(); NotificationManager notificationManager = (NotificationManager) getSystemService( Context.NOTIFICATION_SERVICE); notificationManager.cancel(NOTIFICATION_ID); // Dismiss Wear notification // Blocking mWearHelper.connect(this); mWearHelper.removeNotification(); } public static void refresh(Context context) { Log.d(); Intent intent = new Intent(context, GeofencingService.class); intent.setAction(ACTION_REFRESH_GEOFENCES); context.startService(intent); } @Override public void onDestroy() { mGeofencingHelper.disconnect(); mWearHelper.disconnect(); super.onDestroy(); } }