Java tutorial
/* * Copyright (C) 2013 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.cyanogenmod.settings.device; import android.app.Notification; import android.app.NotificationManager; import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.AsyncTask; import android.os.IBinder; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.preference.PreferenceManager; import android.util.Log; import com.cyanogenmod.settings.device.R; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.Date; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; public class LtoDownloadService extends Service { private static final String TAG = "LtoDownloadService"; private static final boolean ALOGV = true; private static final String LTO_SOURCE_URI_PATTERN = "http://gllto.glpals.com/%s/v2/latest/lto2.dat"; private static final File LTO_DESTINATION_FILE = new File("/data/gps/lto.dat"); public static final String KEY_ENABLED = "lto_download_enabled"; public static final String KEY_FILE_TYPE = "lto_download_file"; public static final String KEY_INTERVAL = "lto_download_interval"; public static final String KEY_WIFI_ONLY = "lto_download_wifi_only"; public static final String KEY_LAST_DOWNLOAD = "last_lto_download"; public static final String EXTRA_FORCE_DOWNLOAD = "force_download"; public static final String ACTION_STATE_CHANGE = "com.cyanogenmod.settings.device.LtoDownloadService.STATE_CHANGE"; public static final String EXTRA_STATE = "state"; public static final String EXTRA_SUCCESS = "success"; public static final String EXTRA_PROGRESS = "progress"; public static final String EXTRA_TIMESTAMP = "timestamp"; public static final int STATE_IDLE = 0; public static final int STATE_DOWNLOADING = 1; private static final String FILE_TYPE_DEFAULT = "7day"; private static final boolean WIFI_ONLY_DEFAULT = true; private static final int DOWNLOAD_TIMEOUT = 20000; /* 20 seconds */ private LtoDownloadTask mTask; private NotificationManager mNotificationManager; private static final int NOTIFICATION_ID = R.string.lto_downloading_data_notification; @Override public int onStartCommand(Intent intent, int flags, int startId) { if (mTask != null && mTask.getStatus() != AsyncTask.Status.FINISHED) { if (ALOGV) Log.v(TAG, "LTO download is still active, not starting new download"); return START_REDELIVER_INTENT; } boolean forceDownload = intent.getBooleanExtra(EXTRA_FORCE_DOWNLOAD, false); if (!shouldDownload(forceDownload)) { Log.d(TAG, "Service started, but shouldn't download ... stopping"); stopSelf(); return START_NOT_STICKY; } SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); String type = prefs.getString(KEY_FILE_TYPE, FILE_TYPE_DEFAULT); String uri = String.format(LTO_SOURCE_URI_PATTERN, type); mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); mTask = new LtoDownloadTask(uri, LTO_DESTINATION_FILE); mTask.execute(); return START_REDELIVER_INTENT; } @Override public IBinder onBind(Intent intent) { return null; } @Override public void onDestroy() { if (mTask != null && mTask.getStatus() != AsyncTask.Status.FINISHED) { mTask.cancel(true); mTask = null; } } private boolean shouldDownload(boolean forceDownload) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); NetworkInfo info = cm.getActiveNetworkInfo(); boolean hasConnection = false; if (info == null || !info.isConnected()) { if (ALOGV) Log.v(TAG, "No network connection is available for LTO download"); } else if (forceDownload) { if (ALOGV) Log.v(TAG, "Download was forced, overriding network type check"); hasConnection = true; } else { boolean wifiOnly = prefs.getBoolean(KEY_WIFI_ONLY, WIFI_ONLY_DEFAULT); if (wifiOnly && info.getType() != ConnectivityManager.TYPE_WIFI) { if (ALOGV) { Log.v(TAG, "Active network is of type " + info.getTypeName() + ", but Wifi only was selected"); } } else { hasConnection = true; } } if (!hasConnection) { return false; } if (forceDownload) { return true; } if (!prefs.getBoolean(KEY_ENABLED, false)) { return false; } long now = System.currentTimeMillis(); long lastDownload = LtoDownloadUtils.getLastDownload(this); long due = lastDownload + LtoDownloadUtils.getDownloadInterval(this); if (ALOGV) { Log.v(TAG, "Now " + now + " due " + due + "(" + new Date(due) + ")"); } if (lastDownload != 0 && now < due) { if (ALOGV) Log.v(TAG, "LTO download is not due yet"); return false; } return true; } private class LtoDownloadTask extends AsyncTask<Void, Integer, Integer> { private String mSource; private File mDestination; private File mTempFile; private WakeLock mWakeLock; private static final int RESULT_SUCCESS = 0; private static final int RESULT_FAILURE = 1; private static final int RESULT_CANCELLED = 2; public LtoDownloadTask(String source, File destination) { mSource = source; mDestination = destination; try { mTempFile = File.createTempFile("lto-download", null, getCacheDir()); } catch (IOException e) { Log.w(TAG, "Could not create temporary file", e); } PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); } @Override protected void onPreExecute() { mWakeLock.acquire(); reportStateChange(STATE_DOWNLOADING, null, null); showDownloadNotification(0, true); } @Override protected Integer doInBackground(Void... params) { BufferedInputStream in = null; BufferedOutputStream out = null; int result = RESULT_SUCCESS; try { HttpParams httpParams = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(httpParams, DOWNLOAD_TIMEOUT); HttpConnectionParams.setSoTimeout(httpParams, DOWNLOAD_TIMEOUT); HttpClient client = new DefaultHttpClient(httpParams); HttpGet request = new HttpGet(); request.setURI(new URI(mSource)); HttpResponse response = client.execute(request); HttpEntity entity = response.getEntity(); File outputFile = mTempFile != null ? mTempFile : mDestination; in = new BufferedInputStream(entity.getContent()); out = new BufferedOutputStream(new FileOutputStream(outputFile)); byte[] buffer = new byte[2048]; int count, total = 0; long length = entity.getContentLength(); while ((count = in.read(buffer, 0, buffer.length)) != -1) { if (isCancelled()) { result = RESULT_CANCELLED; break; } out.write(buffer, 0, count); total += count; if (length > 0) { float progress = (float) total * 100 / length; publishProgress((int) progress); } } Log.d(TAG, "Downloaded " + total + "/" + length + " bytes of LTO data"); if (total == 0 || (length > 0 && total != length)) { result = RESULT_FAILURE; } in.close(); out.close(); } catch (IOException e) { Log.w(TAG, "Failed downloading LTO data", e); result = RESULT_FAILURE; } catch (URISyntaxException e) { Log.e(TAG, "URI syntax wrong", e); result = RESULT_FAILURE; } finally { try { if (in != null) { in.close(); } if (out != null) { out.close(); } } catch (IOException e) { e.printStackTrace(); } } Log.d(TAG, "return " + result); return result; } @Override protected void onProgressUpdate(Integer... progress) { reportStateChange(STATE_DOWNLOADING, null, progress[0]); showDownloadNotification(progress[0].intValue(), false); } @Override protected void onPostExecute(Integer result) { cancelDownloadNotificacion(); if (result != null) { finish(result); } } @Override protected void onCancelled() { cancelDownloadNotificacion(); finish(RESULT_CANCELLED); } private void finish(int result) { if (mTempFile != null) { if (result == RESULT_SUCCESS) { mDestination.delete(); if (!mTempFile.renameTo(mDestination)) { Log.w(TAG, "Could not move temporary file to destination"); } else { mDestination.setReadable(true, false); } } mTempFile.delete(); } else if (result != RESULT_SUCCESS) { mDestination.delete(); } else { mDestination.setReadable(true, false); } Context context = LtoDownloadService.this; if (result == RESULT_SUCCESS) { long now = System.currentTimeMillis(); SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit(); editor.putLong(KEY_LAST_DOWNLOAD, now); editor.apply(); LtoDownloadUtils.scheduleNextDownload(context, now); } else if (result == RESULT_FAILURE) { /* failure, schedule next download in 1 hour */ long lastDownload = LtoDownloadUtils.getLastDownload(context); lastDownload += 60 * 60 * 1000; LtoDownloadUtils.scheduleNextDownload(context, lastDownload); } else { /* cancelled, likely due to lost network - we'll get restarted * when network comes back */ } reportStateChange(STATE_IDLE, result == RESULT_SUCCESS, null); mWakeLock.release(); stopSelf(); } } private void reportStateChange(int state, Boolean success, Integer progress) { Intent intent = new Intent(ACTION_STATE_CHANGE); intent.putExtra(EXTRA_STATE, state); if (success != null) { intent.putExtra(EXTRA_SUCCESS, success); } if (progress != null) { intent.putExtra(EXTRA_PROGRESS, progress); } intent.putExtra(EXTRA_TIMESTAMP, new Date().getTime()); sendStickyBroadcast(intent); } private void showDownloadNotification(int progress, boolean indeterminate) { // Create a notification Bitmap largeIcon = (((BitmapDrawable) getResources() .getDrawable(com.cyanogenmod.settings.device.R.drawable.stat_sys_download)).getBitmap()); String title = getString(R.string.lto_downloading_data_notification); Notification.Builder builder = new Notification.Builder(this).setContentTitle(title) .setSmallIcon(android.R.drawable.stat_sys_download).setLargeIcon(largeIcon) .setProgress(100, progress, indeterminate).setWhen(0); Notification notification = builder.build(); notification.tickerText = title; notification.flags = Notification.FLAG_NO_CLEAR; mNotificationManager.notify(NOTIFICATION_ID, notification); } private void cancelDownloadNotificacion() { mNotificationManager.cancel(NOTIFICATION_ID); } }