/*******************************************************************************
* Copyright 2011, 2012, 2013 fanfou.com, Xiaoke, Zhang
*
* 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.fanfou.app.opensource.service;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import android.annotation.SuppressLint;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.MimeTypeMap;
import android.widget.RemoteViews;
import com.fanfou.app.opensource.AppContext;
import com.fanfou.app.opensource.NewVersionPage;
import com.fanfou.app.opensource.R;
import com.fanfou.app.opensource.http.SimpleClient;
import com.fanfou.app.opensource.update.AppVersionInfo;
import com.fanfou.app.opensource.util.CommonHelper;
import com.fanfou.app.opensource.util.IOHelper;
/**
* @author mcxiaoke
* @version 1.0 2011.09.04
* @version 2.0 2011.10.31
* @version 2.1 2011.11.24
* @version 2.2 2011.11.25
* @version 2.3 2011.11.28
* @version 2.4 2011.12.02
* @version 2.5 2011.12.19
* @version 2.6 2011.12.30
* @version 2.7 2012.01.05
* @version 2.8 2012.01.16
*
*/
public class DownloadService extends BaseIntentService {
@SuppressLint("HandlerLeak")
private class DownloadHandler extends Handler {
private static final long UPDATE_TIME = 1500;
private long lastTime = 0;
@Override
public void handleMessage(final Message msg) {
if (AppContext.DEBUG) {
log("DownloadHandler what=" + msg.what + " progress="
+ msg.arg1);
}
if (DownloadService.MSG_PROGRESS == msg.what) {
final long now = System.currentTimeMillis();
if ((now - this.lastTime) < DownloadHandler.UPDATE_TIME) {
return;
}
final int progress = msg.arg1;
updateProgress(progress);
this.lastTime = System.currentTimeMillis();
} else if (DownloadService.MSG_SUCCESS == msg.what) {
DownloadService.this.nm
.cancel(DownloadService.NOTIFICATION_PROGRESS_ID);
final String filePath = msg.getData().getString(
Constants.EXTRA_FILENAME);
CommonHelper.open(DownloadService.this, filePath);
}
}
}
private static final String TAG = DownloadService.class.getSimpleName();
public static final String UPDATE_VERSION_FILE = "http://apps.fanfou.com/android/update.json";
private static final int NOTIFICATION_PROGRESS_ID = -12345;
public static final long AUTO_UPDATE_INTERVAL = 2 * 3600 * 1000L;
private NotificationManager nm;
private Notification notification;
private Handler mHandler;
public static final int TYPE_CHECK = 0;
public static final int TYPE_DOWNLOAD = 1;
private static final int MSG_PROGRESS = 0;
private static final int MSG_SUCCESS = 1;
public static AppVersionInfo fetchVersionInfo() {
final SimpleClient client = new SimpleClient(AppContext.getAppContext());
try {
final HttpResponse response = client
.get(DownloadService.UPDATE_VERSION_FILE);
final int statusCode = response.getStatusLine().getStatusCode();
if (AppContext.DEBUG) {
Log.d(DownloadService.TAG, "statusCode=" + statusCode);
}
if (statusCode == 200) {
final String content = EntityUtils.toString(
response.getEntity(), HTTP.UTF_8);
if (AppContext.DEBUG) {
Log.d(DownloadService.TAG, "response=" + content);
}
return AppVersionInfo.parse(content);
}
} catch (final IOException e) {
if (AppContext.DEBUG) {
e.printStackTrace();
}
} catch (final Exception e) {
if (AppContext.DEBUG) {
e.printStackTrace();
}
} finally {
}
return null;
}
public static Intent getNewVersionIntent(final Context context,
final AppVersionInfo info) {
final Intent intent = new Intent(context, NewVersionPage.class);
intent.putExtra(Constants.EXTRA_DATA, info);
return intent;
}
private final static PendingIntent getPendingIntent(final Context context) {
final Intent intent = new Intent(context, DownloadService.class);
intent.putExtra(Constants.EXTRA_TYPE, DownloadService.TYPE_CHECK);
final PendingIntent pi = PendingIntent.getService(context, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
return pi;
}
public static void notifyUpdate(final AppVersionInfo info,
final Context context) {
final String versionInfo = info.versionName;
final NotificationManager nm = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
final Notification notification = new Notification(
R.drawable.ic_notify_icon, "饭否客户端有新版本:" + versionInfo,
System.currentTimeMillis());
final PendingIntent contentIntent = PendingIntent.getActivity(context,
0, DownloadService.getNewVersionIntent(context, info), 0);
notification.setLatestEventInfo(context, "饭否客户端有新版本:" + versionInfo,
"点击查看更新内容", contentIntent);
notification.flags |= Notification.FLAG_AUTO_CANCEL;
nm.notify(2, notification);
}
public static void showUpdateConfirmDialog(final Context context,
final AppVersionInfo info) {
final DialogInterface.OnClickListener downListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int which) {
DownloadService.startDownload(context, info);
}
};
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("发现新版本,是否升级?").setCancelable(true)
.setNegativeButton("以后再说", null);
builder.setPositiveButton("立即升级", downListener);
final StringBuffer sb = new StringBuffer();
sb.append("安装版本:").append(AppContext.appVersionName).append("(Build")
.append(AppContext.appVersionCode).append(")");
sb.append("\n最新版本:").append(info.versionName).append("(Build")
.append(info.versionCode).append(")");
sb.append("\n更新日期:").append(info.releaseDate);
sb.append("\n更新级别:").append(info.forceUpdate ? "重要升级" : "一般升级");
sb.append("\n更新内容:\n").append(info.changelog);
builder.setMessage(sb.toString());
final AlertDialog dialog = builder.create();
dialog.show();
}
public static void startDownload(final Context context,
final AppVersionInfo info) {
final Intent intent = new Intent(context, DownloadService.class);
intent.putExtra(Constants.EXTRA_TYPE, DownloadService.TYPE_DOWNLOAD);
intent.putExtra(Constants.EXTRA_URL, info);
context.startService(intent);
}
public static void unset(final Context context) {
final AlarmManager am = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
am.cancel(DownloadService.getPendingIntent(context));
if (AppContext.DEBUG) {
Log.d(DownloadService.TAG, "unset");
}
}
public DownloadService() {
super("DownloadService");
}
private void check() {
final AppVersionInfo info = DownloadService.fetchVersionInfo();
if (AppContext.DEBUG) {
Log.d(DownloadService.TAG, "check() VersionInfo=" + info);
if (info != null) {
DownloadService.notifyUpdate(info, this);
return;
}
}
if ((info != null) && (info.versionCode > AppContext.appVersionCode)) {
DownloadService.notifyUpdate(info, this);
}
}
private void download(final AppVersionInfo info) {
if ((info == null) || TextUtils.isEmpty(info.downloadUrl)) {
return;
}
showProgress();
final String url = info.downloadUrl;
final int versionCode = info.versionCode;
if (AppContext.DEBUG) {
Log.v(DownloadService.TAG, "download apk file url: " + url
+ " versionCode:" + versionCode);
}
InputStream is = null;
BufferedOutputStream bos = null;
final SimpleClient client = new SimpleClient(AppContext.getAppContext());
boolean needDownload = true;
final File file = new File(IOHelper.getDownloadDir(this), "fanfouapp_"
+ versionCode + ".apk");
try {
final HttpResponse response = client.get(url);
final int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) {
final HttpEntity entity = response.getEntity();
final long totalSize = entity.getContentLength();
long download = 0;
if (file.exists() && file.isFile()
&& (file.length() == totalSize)) {
needDownload = false;
download = totalSize;
if (AppContext.DEBUG) {
Log.v(DownloadService.TAG,
"download apk file is already exists: "
+ file.getAbsolutePath());
}
}
if (needDownload) {
is = entity.getContent();
bos = new BufferedOutputStream(new FileOutputStream(file));
final byte[] buffer = new byte[8196];
int read = -1;
while ((read = is.read(buffer)) != -1) {
bos.write(buffer, 0, read);
download += read;
final int progress = (int) ((100.0 * download) / totalSize);
sendProgressMessage(progress);
if (AppContext.DEBUG) {
log("progress=" + progress);
}
}
bos.flush();
}
if (download >= totalSize) {
sendSuccessMessage(file);
}
}
} catch (final IOException e) {
if (AppContext.DEBUG) {
Log.e(DownloadService.TAG, "download error: " + e.getMessage());
e.printStackTrace();
}
} finally {
this.nm.cancel(DownloadService.NOTIFICATION_PROGRESS_ID);
IOHelper.forceClose(is);
IOHelper.forceClose(bos);
}
}
@SuppressWarnings("unused")
private PendingIntent getInstallPendingIntent(final String fileName) {
final String mimeType = MimeTypeMap.getSingleton()
.getMimeTypeFromExtension(CommonHelper.getExtension(fileName));
if (mimeType != null) {
final Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(fileName)), mimeType);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
final PendingIntent pi = PendingIntent.getActivity(this, 0, intent,
0);
return pi;
}
return null;
}
private void log(final String message) {
Log.d("DownloadService", message);
}
@Override
public void onCreate() {
super.onCreate();
this.mHandler = new DownloadHandler();
}
@Override
protected void onHandleIntent(final Intent intent) {
this.nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
final int type = intent.getIntExtra(Constants.EXTRA_TYPE,
DownloadService.TYPE_CHECK);
if (AppContext.DEBUG) {
Log.d(DownloadService.TAG, "onHandleIntent type=" + type);
}
if (type == DownloadService.TYPE_CHECK) {
check();
} else if (type == DownloadService.TYPE_DOWNLOAD) {
final AppVersionInfo info = intent
.getParcelableExtra(Constants.EXTRA_URL);
log("onHandleIntent TYPE_DOWNLOAD info=" + info);
if ((info != null) && !TextUtils.isEmpty(info.downloadUrl)) {
download(info);
}
}
}
private void sendProgressMessage(final int progress) {
final Message message = new Message();
message.what = DownloadService.MSG_PROGRESS;
message.arg1 = progress;
this.mHandler.sendMessage(message);
}
private void sendSuccessMessage(final File file) {
final Message message = new Message();
message.what = DownloadService.MSG_SUCCESS;
message.getData().putString(Constants.EXTRA_FILENAME,
file.getAbsolutePath());
this.mHandler.sendMessage(message);
}
private void showProgress() {
this.notification = new Notification(R.drawable.ic_notify_download,
"正在下载饭否客户端", System.currentTimeMillis());
this.notification.flags |= Notification.FLAG_ONGOING_EVENT;
this.notification.flags |= Notification.FLAG_AUTO_CANCEL;
this.notification.contentIntent = PendingIntent.getActivity(this, 0,
new Intent(), 0);
final RemoteViews view = new RemoteViews(getPackageName(),
R.layout.download_notification);
view.setTextViewText(R.id.download_notification_text, "正在下载饭否客户端 0%");
view.setProgressBar(R.id.download_notification_progress, 100, 0, false);
this.notification.contentView = view;
this.nm.notify(DownloadService.NOTIFICATION_PROGRESS_ID,
this.notification);
}
private void updateProgress(final int progress) {
final RemoteViews view = new RemoteViews(getPackageName(),
R.layout.download_notification);
view.setTextViewText(R.id.download_notification_text, "正在下载饭否客户端 "
+ progress + "%");
view.setInt(R.id.download_notification_progress, "setProgress",
progress);
this.notification.contentView = view;
this.nm.notify(DownloadService.NOTIFICATION_PROGRESS_ID,
this.notification);
}
}