Java tutorial
/* * Copyright (C) 2014 Joakim Andersson * Copyright (C) 2014 blunden * * This file is part of PactrackDroid, an Android application to keep * track of parcels sent with the Swedish mail service (Posten). * * PactrackDroid 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 2 of the License, or * (at your option) any later version. * * PactrackDroid 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 nu.firetech.android.pactrack.backend; import java.util.ArrayList; import nu.firetech.android.pactrack.R; import nu.firetech.android.pactrack.common.RefreshContext; import nu.firetech.android.pactrack.frontend.MainActivity; import nu.firetech.android.pactrack.frontend.UICommon; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.Cursor; import android.graphics.BitmapFactory; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v4.app.Fragment; import android.support.v4.app.NotificationCompat; import android.util.Log; public class ParcelUpdater extends BroadcastReceiver implements Runnable { private static final String TAG = "<PactrackDroid> ParcelUpdater"; public static void update(final RefreshContext ctx, Cursor parcel) { ArrayList<Bundle> workParcels = new ArrayList<Bundle>(); workParcels.add(cursorToBundle(parcel)); new Thread(new ParcelUpdater(workParcels, ctx)).start(); } public static void updateAll(boolean autoOnly, final RefreshContext ctx, ParcelDbAdapter dbAdapter) { final Cursor parcel = dbAdapter.fetchAllParcels(autoOnly); if (parcel == null) { UICommon.dbErrorDialog((Context) ctx); return; } ArrayList<Bundle> workParcels = new ArrayList<Bundle>(); parcel.moveToFirst(); while (!parcel.isAfterLast()) { workParcels.add(cursorToBundle(parcel)); parcel.moveToNext(); } parcel.close(); if (workParcels.isEmpty()) { ctx.refreshDone(); return; } new Thread(new ParcelUpdater(workParcels, ctx)).start(); } private static Bundle cursorToBundle(Cursor parcel) { Bundle parcelBundle = new Bundle(); parcelBundle.putLong(ParcelDbAdapter.KEY_ROWID, parcel.getLong(parcel.getColumnIndexOrThrow(ParcelDbAdapter.KEY_ROWID))); parcelBundle.putString(ParcelDbAdapter.KEY_PARCEL, parcel.getString(parcel.getColumnIndexOrThrow(ParcelDbAdapter.KEY_PARCEL))); parcelBundle.putString(ParcelDbAdapter.KEY_NAME, parcel.getString(parcel.getColumnIndexOrThrow(ParcelDbAdapter.KEY_NAME))); return parcelBundle; } //////////////////////////////////////////////////////////////////////////////// private ConnectivityManager mConnectivityManager; private static boolean sWaitingForDataConnection = false; private static final Object sLock = new Object(); @Override public void onReceive(final Context ctx, Intent intent) { if (!isDataConnected()) { return; } Log.d(TAG, "We are back online!"); synchronized (sLock) { sLock.notifyAll(); ctx.unregisterReceiver(ParcelUpdater.this); sWaitingForDataConnection = false; } } private void registerConnectionListener(Context ctx) { synchronized (sLock) { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); ctx.registerReceiver(this, intentFilter); sWaitingForDataConnection = true; } } //////////////////////////////////////////////////////////////////////////////// private ArrayList<Bundle> mWorkParcels = null; private ParcelDbAdapter mDbAdapter = null; private RefreshContext mCtx = null; private Context mAndroidCtx = null; private Handler mHandler = null; private ParcelUpdater(ArrayList<Bundle> workParcels, RefreshContext ctx) { mWorkParcels = workParcels; mCtx = ctx; if (ctx instanceof Context) { mAndroidCtx = (Context) ctx; } else if (ctx instanceof Fragment) { mAndroidCtx = ((Fragment) ctx).getActivity(); } else { throw new IllegalArgumentException("Unknown context type!"); } ParcelJsonParser.loadConsumerId(mAndroidCtx); mConnectivityManager = (ConnectivityManager) mAndroidCtx.getSystemService(Context.CONNECTIVITY_SERVICE); mHandler = ctx.startRefreshProgress(workParcels.size()); if (mHandler != null) { mHandler.sendMessage(Message.obtain(mHandler, 0, workParcels.get(0))); } } @Override public void run() { synchronized (sLock) { if (sWaitingForDataConnection) { Log.i(TAG, "Another update is waiting for data connection. Skipping"); return; } } //wait for a data connection while (!isDataConnected()) { registerConnectionListener(mAndroidCtx); synchronized (sLock) { Log.d(TAG, "No data connection, waiting for a data connection"); try { sLock.wait(); } catch (InterruptedException e) { Log.e(TAG, "Error while waiting for connection", e); } } } mDbAdapter = new ParcelDbAdapter(mAndroidCtx); mDbAdapter.open(); NotificationManager notMgr = (NotificationManager) mAndroidCtx .getSystemService(Context.NOTIFICATION_SERVICE); for (int i = 0; i < mWorkParcels.size(); i++) { Bundle parcel = mWorkParcels.get(i); if (mHandler != null) { mHandler.sendMessage(Message.obtain(mHandler, i, parcel)); } updateParcel(parcel, mDbAdapter, notMgr); } if (mHandler != null) { mHandler.sendEmptyMessage(mWorkParcels.size()); } new Handler(mAndroidCtx.getMainLooper()) { @Override public void handleMessage(Message m) { mCtx.refreshDone(); } }.sendEmptyMessage(0); mDbAdapter.close(); } public void setContext(RefreshContext newContext) { mCtx = newContext; } private boolean isDataConnected() { NetworkInfo info = mConnectivityManager.getActiveNetworkInfo(); return (info != null && info.isConnected()); } private void updateParcel(Bundle parcel, ParcelDbAdapter dbAdapter, NotificationManager notMgr) { Long rowId = parcel.getLong(ParcelDbAdapter.KEY_ROWID); String parcelId = parcel.getString(ParcelDbAdapter.KEY_PARCEL); Parcel parcelData = ParcelJsonParser.fetch(parcelId); mDbAdapter.updateParcelData(rowId, parcelData); if (parcelData.getParcel() != null) { parcelId = parcelData.getParcel(); } int newEvents = 0; StringBuilder eventList = new StringBuilder(); for (ParcelEvent eventData : parcelData.getEvents()) { //loop through events if (mDbAdapter.addEvent(rowId, eventData)) { //if event was new if (newEvents > 0) { eventList.append("\n"); } eventList.append(eventData.toString()); newEvents++; } } if (newEvents > 0 && !mCtx.showsNews()) { Preferences prefs = Preferences.getPreferences(mAndroidCtx); //TODO Join existing notifications in some clever way... if (prefs.getNotificationEnabled()) { String parcelName = parcel.getString(ParcelDbAdapter.KEY_NAME); if (parcelName == null) { parcelName = mAndroidCtx.getString(R.string.generic_parcel_name, parcelId); } Intent intent = new Intent(mAndroidCtx, MainActivity.class).putExtra(ParcelDbAdapter.KEY_ROWID, rowId); PendingIntent contentIntent = PendingIntent.getActivity(mAndroidCtx, rowId.hashCode(), intent, 0); int stringId = (newEvents > 1 ? R.string.notification_ticker : R.string.notification_ticker_one); NotificationCompat.Builder n = new NotificationCompat.Builder(mAndroidCtx) .setSmallIcon(R.drawable.notification) .setTicker(mAndroidCtx.getString(stringId, parcelName)).setWhen(System.currentTimeMillis()) .setContentTitle(parcelName) .setContentText(newEvents == 1 ? eventList.toString() : mAndroidCtx.getString(R.string.notification_message, newEvents)) .setContentIntent(contentIntent).setSound(prefs.getNotificationSound()) .extend(new NotificationCompat.WearableExtender().setBackground(BitmapFactory .decodeResource(mAndroidCtx.getResources(), R.drawable.wearable_background))); if (newEvents > 1) { n.setStyle(new NotificationCompat.BigTextStyle().bigText(eventList.toString())); } if (prefs.getNotificationLight()) { n.setLights(prefs.getNotificationColor(), prefs.getNotificationOntime(), prefs.getNotificationOfftime()); } if (prefs.getNotificationVibrate()) { n.setDefaults(Notification.DEFAULT_VIBRATE); } notMgr.notify(rowId.hashCode(), n.build()); } } } }