/** * Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr) * This file is part of CSipSimple. * * CSipSimple 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. * If you own a pjsip commercial license you can also redistribute it * and/or modify it under the terms of the GNU Lesser General Public License * as an android library. * * CSipSimple 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 CSipSimple. If not, see <http://www.gnu.org/licenses/>. */ package com.csipsimple.service; import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.ResponseHandler; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import android.app.Activity; import android.app.IntentService; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Intent; import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat.Builder; import android.text.TextUtils; import android.view.View; import android.widget.RemoteViews; import com.csipsimple.R; import com.csipsimple.ui.SipHome; import com.csipsimple.utils.Log; import com.csipsimple.utils.MD5; public class Downloader extends IntentService { private final static String THIS_FILE = "Downloader"; public final static String EXTRA_ICON = "icon"; public final static String EXTRA_TITLE = "title"; public final static String EXTRA_OUTPATH = "outpath"; public final static String EXTRA_CHECK_MD5 = "checkMd5"; public final static String EXTRA_PENDING_FINISH_INTENT = "pendingIntent"; private static final int NOTIF_DOWNLOAD = 0; private NotificationManager notificationManager; private DefaultHttpClient client; public Downloader() { super("Downloader"); } @Override public void onCreate() { super.onCreate(); notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); client = new DefaultHttpClient(); } @Override public void onDestroy() { super.onDestroy(); client.getConnectionManager().shutdown(); } @Override protected void onHandleIntent(Intent intent) { HttpGet getMethod = new HttpGet(intent.getData().toString()); int result = Activity.RESULT_CANCELED; String outPath = intent.getStringExtra(EXTRA_OUTPATH); boolean checkMd5 = intent.getBooleanExtra(EXTRA_CHECK_MD5, false); int icon = intent.getIntExtra(EXTRA_ICON, 0); String title = intent.getStringExtra(EXTRA_TITLE); boolean showNotif = (icon > 0 && !TextUtils.isEmpty(title)); // Build notification Builder nb = new NotificationCompat.Builder(this); nb.setWhen(System.currentTimeMillis()); nb.setContentTitle(title); nb.setSmallIcon(android.R.drawable.stat_sys_download); nb.setOngoing(true); Intent i = new Intent(this, SipHome.class); nb.setContentIntent(PendingIntent.getActivity(this, 0, i, PendingIntent.FLAG_UPDATE_CURRENT)); RemoteViews contentView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.download_notif); contentView.setImageViewResource(R.id.status_icon, icon); contentView.setTextViewText(R.id.status_text, getResources().getString(R.string.downloading_text)); contentView.setProgressBar(R.id.status_progress, 50, 0, false); contentView.setViewVisibility(R.id.status_progress_wrapper, View.VISIBLE); nb.setContent(contentView); final Notification notification = showNotif ? nb.build() : null; notification.contentView = contentView; if(!TextUtils.isEmpty(outPath)) { try { File output = new File(outPath); if (output.exists()) { output.delete(); } if(notification != null) { notificationManager.notify(NOTIF_DOWNLOAD, notification); } ResponseHandler<Boolean> responseHandler = new FileStreamResponseHandler(output, new Progress() { private int oldState = 0; @Override public void run(long progress, long total) { //Log.d(THIS_FILE, "Progress is "+progress+" on "+total); int newState = (int) Math.round(progress * 50.0f/total); if(oldState != newState) { notification.contentView.setProgressBar(R.id.status_progress, 50, newState, false); notificationManager.notify(NOTIF_DOWNLOAD, notification); oldState = newState; } } }); boolean hasReply = client.execute(getMethod, responseHandler); if(hasReply) { if(checkMd5) { URL url = new URL(intent.getData().toString().concat(".md5sum")); InputStream content = (InputStream) url.getContent(); if(content != null) { BufferedReader br = new BufferedReader(new InputStreamReader(content)); String downloadedMD5 = ""; try { downloadedMD5 = br.readLine().split(" ")[0]; } catch (NullPointerException e) { throw new IOException("md5_verification : no sum on server"); } if (!MD5.checkMD5(downloadedMD5, output)) { throw new IOException("md5_verification : incorrect"); } } } PendingIntent pendingIntent = (PendingIntent) intent.getParcelableExtra(EXTRA_PENDING_FINISH_INTENT); try { Runtime.getRuntime().exec("chmod 644 " + outPath); } catch (IOException e) { Log.e(THIS_FILE, "Unable to make the apk file readable", e); } Log.d(THIS_FILE, "Download finished of : " + outPath); if(pendingIntent != null) { notification.contentIntent = pendingIntent; notification.flags = Notification.FLAG_AUTO_CANCEL; notification.icon = android.R.drawable.stat_sys_download_done; notification.contentView.setViewVisibility(R.id.status_progress_wrapper, View.GONE); notification.contentView.setTextViewText(R.id.status_text, getResources().getString(R.string.done) // TODO should be a parameter of this class +" - Click to install"); notificationManager.notify(NOTIF_DOWNLOAD, notification); /* try { pendingIntent.send(); notificationManager.cancel(NOTIF_DOWNLOAD); } catch (CanceledException e) { Log.e(THIS_FILE, "Impossible to start pending intent for download finish"); } */ }else { Log.w(THIS_FILE, "Invalid pending intent for finish !!!"); } result = Activity.RESULT_OK; } } catch (IOException e) { Log.e(THIS_FILE, "Exception in download", e); } } if(result == Activity.RESULT_CANCELED) { notificationManager.cancel(NOTIF_DOWNLOAD); } } public interface Progress { void run(long progress, long total); } private class FileStreamResponseHandler implements ResponseHandler<Boolean> { private Progress mProgress; private File mFile; FileStreamResponseHandler(File outputFile, Progress progress) { mFile = outputFile; mProgress = progress; } public Boolean handleResponse(HttpResponse response) throws ClientProtocolException, IOException { FileOutputStream fos = new FileOutputStream(mFile.getPath()); HttpEntity entity = response.getEntity(); boolean done = false; try { if (entity != null) { Long length = entity.getContentLength(); InputStream input = entity.getContent(); byte[] buffer = new byte[4096]; int size = 0; int total = 0; while (true) { size = input.read(buffer); if (size == -1) break; fos.write(buffer, 0, size); total += size; mProgress.run(total, length); } done = true; } }catch(IOException e) { Log.e(THIS_FILE, "Problem on downloading"); }finally { fos.close(); } return done; } } }