package com.erakk.lnreader; import android.app.ActivityManager; import android.app.ActivityManager.RunningServiceInfo; import android.app.Application; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.AsyncTask; import android.os.AsyncTask.Status; import android.os.CountDownTimer; import android.os.IBinder; import android.preference.PreferenceManager; import android.util.Log; import android.widget.Toast; import com.erakk.lnreader.callback.IExtendedCallbackNotifier; import com.erakk.lnreader.model.DownloadModel; import com.erakk.lnreader.service.AutoBackupService; import com.erakk.lnreader.service.UpdateService; import com.erakk.lnreader.task.AsyncTaskResult; import java.util.ArrayList; import java.util.Date; import java.util.Hashtable; /* * http://www.devahead.com/blog/2011/06/extending-the-android-application-class-and-dealing-with-singleton/ */ public class LNReaderApplication extends Application { private static final String TAG = LNReaderApplication.class.toString(); private static UpdateService updateService = null; private static AutoBackupService autoBackupService = null; private static LNReaderApplication instance; private static Hashtable<String, AsyncTask<?, ?, ?>> runningTasks; private static ArrayList<DownloadModel> downloadList; private static final Object lock = new Object(); private static IExtendedCallbackNotifier<DownloadModel> downloadNotifier = null; @Override public void onCreate() { super.onCreate(); // Initialise the singletons so their instances // are bound to the application process. initSingletons(); instance = this; doBindService(); doBindAutoBackupService(); Log.d(TAG, "Application created."); } public static LNReaderApplication getInstance() { return instance; } protected void initSingletons() { if (runningTasks == null) runningTasks = new Hashtable<String, AsyncTask<?, ?, ?>>(); if (downloadList == null) downloadList = new ArrayList<DownloadModel>(); } // region AsyncTask listing method public AsyncTask<?, ?, ?> getTask(String key) { return runningTasks.get(key); } public boolean addTask(String key, AsyncTask<?, ?, ?> task) { synchronized (lock) { if (runningTasks.containsKey(key)) { AsyncTask<?, ?, ?> tempTask = runningTasks.get(key); if (tempTask != null && tempTask.getStatus() != Status.FINISHED) return false; } runningTasks.put(key, task); return true; } } public boolean removeTask(String key) { synchronized (lock) { if (!runningTasks.containsKey(key)) return false; runningTasks.remove(key); return true; } } // endregion public boolean isOnline() { ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo netInfo = cm.getActiveNetworkInfo(); if (netInfo != null && netInfo.isConnectedOrConnecting()) { return true; } return false; } // region DownloadActivity method public void setDownloadNotifier(IExtendedCallbackNotifier<DownloadModel> notifier) { LNReaderApplication.downloadNotifier = notifier; } public int addDownload(String id, String friendlyName) { synchronized (lock) { if (isDownloadExists(id)) return -1; downloadList.add(new DownloadModel(id, friendlyName, 0)); if (downloadNotifier != null) downloadNotifier.onCompleteCallback(null, null); return downloadList.size(); } } public void removeDownload(String id) { synchronized (lock) { for (int i = 0; i < downloadList.size(); i++) { if (downloadList.get(i).getDownloadId().equalsIgnoreCase(id)) { downloadList.remove(i); break; } } } if (downloadNotifier != null) downloadNotifier.onCompleteCallback(null, null); } public String getDownloadName(String id) { for (final DownloadModel tempModel : downloadList) { if (tempModel.getDownloadId().equalsIgnoreCase(id)) { return tempModel.getDownloadName(); } } return null; } public boolean isDownloadExists(String id) { synchronized (lock) { for (final DownloadModel tempModel : downloadList) { if (tempModel.getDownloadId().equalsIgnoreCase(id)) { return true; } } return false; } } public ArrayList<DownloadModel> getDownloadList() { return downloadList; } public void updateDownload(String id, Integer progress, String message) { /* * Although this may seem an attempt at a fake incremental download bar * its actually a progressbar smoother. */ for (final DownloadModel tempModel : downloadList) { if (tempModel.getDownloadId().equalsIgnoreCase(id)) { int smoothTime = 1000; int tickTime = 25; // Download status message tempModel.setDownloadMessage(message); if (downloadNotifier != null) downloadNotifier.onCompleteCallback(null, null); if (progress > 0) { Integer oldProgress = tempModel.getDownloadProgress(); int tempIncrease = (progress - oldProgress); if (tempIncrease < smoothTime / tickTime) { smoothTime = tickTime * tempIncrease; tempIncrease = 1; } else tempIncrease /= (smoothTime / tickTime); final Integer Increment = tempIncrease; new CountDownTimer(smoothTime, tickTime) { @Override public void onTick(long millisUntilFinished) { if (tempModel != null) { tempModel.setDownloadProgress(tempModel.getDownloadProgress() + Increment); } if (downloadNotifier != null) downloadNotifier.onCompleteCallback(null, null); } @Override public void onFinish() { } }.start(); } return; } } } // endregion // region UpdateService method private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className, IBinder binder) { updateService = ((UpdateService.MyBinder) binder).getService(); Log.d(UpdateService.TAG, "onServiceConnected"); Toast.makeText(getApplicationContext(), "Connected", Toast.LENGTH_SHORT).show(); } @Override public void onServiceDisconnected(ComponentName className) { updateService = null; Log.d(UpdateService.TAG, "onServiceDisconnected"); } }; private void doBindService() { bindService(new Intent(this, UpdateService.class), mConnection, Context.BIND_AUTO_CREATE); Log.d(UpdateService.TAG, "doBindService"); } public void setUpdateServiceListener(IExtendedCallbackNotifier<AsyncTaskResult<?>> notifier) { if (updateService != null) { updateService.setOnCallbackNotifier(notifier); } } public void runUpdateService(boolean force, IExtendedCallbackNotifier<AsyncTaskResult<?>> notifier) { if (updateService == null) doBindService(); updateService.force = force; updateService.setOnCallbackNotifier(notifier); updateService.onStartCommand(null, BIND_AUTO_CREATE, (int) (new Date().getTime() / 1000)); } public void cancelUpdateService() { if (updateService != null) updateService.cancelUpdate(); } // endregion // region CSS caching method. // Also used for caching javascript. private Hashtable<Integer, String> cssCache = null; public String ReadCss(int styleId) { if (cssCache == null) cssCache = new Hashtable<Integer, String>(); if (!cssCache.containsKey(styleId)) { cssCache.put(styleId, UIHelper.readRawStringResources(getApplicationContext(), styleId)); } return cssCache.get(styleId); } // endregion public void resetFirstRun() { SharedPreferences.Editor edit = PreferenceManager.getDefaultSharedPreferences(this).edit(); edit.remove(Constants.PREF_FIRST_RUN); edit.commit(); } public void restartApplication() { Intent i = getBaseContext().getPackageManager().getLaunchIntentForPackage(getBaseContext().getPackageName()); i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(i); } // region AutoBackup Service method private ServiceConnection mConnection2 = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className, IBinder binder) { autoBackupService = ((AutoBackupService.AutoBackupServiceBinder) binder).getService(); Log.d(AutoBackupService.TAG, "onServiceConnected"); } @Override public void onServiceDisconnected(ComponentName className) { autoBackupService = null; Log.d(AutoBackupService.TAG, "onServiceDisconnected"); } }; private void doBindAutoBackupService() { bindService(new Intent(this, AutoBackupService.class), mConnection2, Context.BIND_AUTO_CREATE); Log.d(AutoBackupService.TAG, "doBindService"); } public void setAutoBackupServiceListener(IExtendedCallbackNotifier<AsyncTaskResult<?>> notifier) { if (autoBackupService != null) { autoBackupService.setOnCallbackNotifier(notifier); } } public void runAutoBackupService(IExtendedCallbackNotifier<AsyncTaskResult<?>> notifier) { if (autoBackupService == null) doBindAutoBackupService(); autoBackupService.setOnCallbackNotifier(notifier); autoBackupService.onStartCommand(null, BIND_AUTO_CREATE, (int) (new Date().getTime() / 1000)); } // endregion // region service helper /** * http://stackoverflow.com/a/5921190 * * @param serviceClass * @return */ private boolean isMyServiceRunning(Class<?> serviceClass) { ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { if (serviceClass.getName().equals(service.service.getClassName())) { return true; } } return false; } @Override public void onLowMemory() { /* * java.lang.IllegalArgumentException * in android.app.LoadedApk.forgetServiceDispatcher * * probable crash: updateService is not checked if it exists after onLowMemory. * Technically fixed. needs checking. */ if (mConnection != null && isMyServiceRunning(UpdateService.class)) { Log.w(TAG, "Low Memory, Trying to unbind updateService..."); try { unbindService(mConnection); mConnection = null; Log.i(TAG, "Unbind updateService done."); } catch (Exception ex) { Log.e(TAG, "Failed to unbind.", ex); } } if (mConnection2 != null && isMyServiceRunning(AutoBackupService.class)) { Log.w(TAG, "Low Memory, Trying to unbind autoBackupService..."); try { unbindService(mConnection2); mConnection2 = null; Log.i(TAG, "Unbind autoBackupService done."); } catch (Exception ex) { Log.e(TAG, "Failed to unbind.", ex); } } super.onLowMemory(); } // endregion }