/**
* 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;
}
}
}