package org.aisen.weibo.sina.ui.fragment.secondgroups;
import android.app.WallpaperManager;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.graphics.Matrix;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.view.WindowManager;
import com.squareup.okhttp.Call;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import org.aisen.android.common.context.GlobalContext;
import org.aisen.android.common.utils.FileUtils;
import org.aisen.android.common.utils.KeyGenerator;
import org.aisen.android.common.utils.Logger;
import org.aisen.android.common.utils.SystemUtils;
import org.aisen.android.component.bitmaploader.BitmapLoader;
import org.aisen.android.network.task.TaskException;
import org.aisen.android.network.task.WorkTask;
import org.aisen.weibo.sina.R;
import org.aisen.weibo.sina.base.AppSettings;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.net.HttpURLConnection;
import java.text.DecimalFormat;
import java.util.Hashtable;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.GZIPInputStream;
/**
* 用来下载壁纸<br/>
* 或者设置壁纸<br/>
* 最多同时下载3个壁纸,其他队列一次下载,可以点击下载进度条停止下载任务
*
* @author wangdan
*/
public class WallpaperDownloadTask extends WorkTask<Void, Long, Boolean> {
static final String TAG = "WallpaperTask";
private final static int PUBLISH_INTERVAL_TIME = 100;
private static int DEFAULT_CONNECT_TIMEOUT = 15000;
private static int DEFAULT_SO_TIMEOUT = 20000;
// 下载墙纸
public static synchronized void download(Context context, String url, String id, OnProgressCallback callback) {
Logger.d(TAG, "触发一次壁纸下载");
handleWallpaper(context, url, id, callback, Type.download);
}
// 下载并设置强制
public static synchronized void settingWallpaper(Context context, String url, String id, OnProgressCallback callback) {
Logger.d(TAG, "触发一次壁纸设置");
handleWallpaper(context, url, id, callback, Type.setting);
}
// 停止下载
public static synchronized void cancelTask(Context context, String url, String id, OnProgressCallback callback) {
String key = KeyGenerator.generateMD5(url);
// 如果已经有了线程在运行
if (runningTask.containsKey(key)) {
WallpaperDownloadTask task = runningTask.get(key).get();
Logger.v(TAG, task + "");
if (task == null) {
runningTask.remove(key);
}
// 已经有线程在运行
else {
runningTask.remove(key);
if (callback != null) {
callback.onCanceled(url);
}
task.cancel(true);
}
}
}
// 每次初始化View的时候,重置一下状态
public static synchronized void bindWallpaper(Context context, String url, String id, OnProgressCallback callback) {
handleWallpaper(context, url, id, callback, Type.bindview);
}
private static synchronized void handleWallpaper(Context context, final String url, final String id, final OnProgressCallback callback, Type type) {
String key = KeyGenerator.generateMD5(url);
boolean isRunning = false;
// 如果已经有了线程在运行
if (runningTask.containsKey(key)) {
WallpaperDownloadTask task = runningTask.get(key).get();
Logger.v(TAG, task + "");
if (task == null) {
runningTask.remove(key);
}
// 已经有线程在运行
else {
isRunning = true;
Logger.d(TAG, "已经有线程在运行了,重置一下线程, oldType = %s, newType = %s", task.mType.toString(), type.toString());
task.reset(type, callback);
}
}
// 如果没有运行
if (!isRunning) {
if (type == Type.bindview) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
callback.setInit(url);
}
});
} else {
WallpaperDownloadTask task = new WallpaperDownloadTask(context, url, id);
// 如果是设置壁纸,且已经下载,就不回调UI
File file = getWallpaperSaveFile(url);
// 如果是设置壁纸,而且壁纸已经存在本地了,
if (type == Type.setting && file.exists()) {
task.callbackRef = new WeakReference<OnProgressCallback>(callback);
task.mType = type;
if (type == Type.download)
task.mTypeFlag = task.mTypeFlag | 0x01;
if (type == Type.setting)
task.mTypeFlag = task.mTypeFlag | 0x02;
} else {
task.reset(type, callback);
}
task.executeOnExecutor(OFFLINE_EXECUTOR);
Logger.d(TAG, "开启壁纸下载线程");
runningTask.put(key, new WeakReference<WallpaperDownloadTask>(task));
}
}
}
private static Hashtable<String, WeakReference<WallpaperDownloadTask>> runningTask = new Hashtable<String, WeakReference<WallpaperDownloadTask>>();
private enum Type {
download, // 下载
setting, // 设置壁纸
bindview // 绑定一下view
}
private Context mContext;
private Type mType;
private int mTypeFlag = 0x00;// 0x01:下载,0x02:设置壁纸,0x03:下载+设置壁纸
private WeakReference<OnProgressCallback> callbackRef;
private String mImageUrl;
private String mImageId;
private long total = -1;
private long progress = -1;
private static OkHttpClient httpClient = new OkHttpClient();
private final static int CONN_TIMEOUT = 30000;
private final static int READ_TIMEOUT = 30000;
static {
httpClient.setConnectTimeout(CONN_TIMEOUT, TimeUnit.MILLISECONDS);
httpClient.setReadTimeout(READ_TIMEOUT, TimeUnit.MILLISECONDS);
}
private Call mCall;
public WallpaperDownloadTask(Context context, String url, String imageId) {
this.mImageUrl = url;
this.mContext = context;
this.mImageId = imageId;
}
public void reset(Type type, final OnProgressCallback callback) {
if (type != null && type != Type.bindview)
this.mType = type;
if (type == Type.download) {
mTypeFlag = mTypeFlag | 0x01;
}
if (type == Type.setting) {
mTypeFlag = mTypeFlag | 0x02;
}
this.callbackRef = new WeakReference<OnProgressCallback>(callback);
callback.onProgressUpdate(mImageUrl, progress, total, mTypeFlag);
}
@Override
public Boolean workInBackground(Void... params) throws TaskException {
// 先看本地相册是否已经存了这个图片,如果存了就不下载了
File file = getWallpaperSaveFile(mImageUrl);
// ImageLoader也未下载该图片
if (file != null && !file.exists())
file = BitmapLoader.getInstance().getCacheFile(mImageUrl);
// 文件不存在,先下载
if (file != null && !file.exists())
file = doDownload(file);
// 不管是下载还是设置,都首先保存壁纸
saveWallpaper(file);
// 是否是设置壁纸
if ((mTypeFlag & 0x02) > 0)
setWallpaper(mContext, file, callbackRef.get());
return true;
}
@Override
protected void onProgressUpdate(Long... values) {
super.onProgressUpdate(values);
OnProgressCallback callback = callbackRef.get();
if (callback != null && values != null && values.length > 1) {
long total = values[1];
long progress = values[0];
callback.onProgressUpdate(mImageUrl, progress, total, mTypeFlag);
}
}
@Override
protected void onSuccess(Boolean result) {
super.onSuccess(result);
OnProgressCallback callback = callbackRef.get();
if (mType == Type.setting) {
Logger.d(TAG, "设置壁纸成功 ---> " + callback + "");
if (callback != null)
callback.showMessage(mImageUrl, mContext.getResources().getString(R.string.txt_set_wallpaper_suc));
} else if (mType == Type.download) {
Logger.d(TAG, "下载壁纸成功 ---> " + callback + "");
if (callback != null)
callback.showMessage(mImageUrl, mContext.getResources().getString(R.string.txt_save_wallpaper_suc));
}
}
@Override
protected void onFailure(TaskException exception) {
super.onFailure(exception);
OnProgressCallback callback = callbackRef.get();
// 如果是取消下载,
if (callback != null) {
if ("-100".equals(exception.getCode())) {
if (isRunning()) {
Logger.d(TAG, "取消下载");
callback.onCanceled(mImageUrl);
}
} else {
Logger.d(TAG, "下载异常 ---> %s", exception.getMessage() + "");
callback.showMessage(mImageUrl, exception.getMessage());
}
}
}
private boolean isRunning() {
if (isCancelled()) {
return false;
}
String key = KeyGenerator.generateMD5(mImageUrl);
if (runningTask != null && runningTask.containsKey(key)) {
return runningTask.get(key).get() == this;
}
return false;
}
@Override
protected void onFinished() {
super.onFinished();
runningTask.remove(KeyGenerator.generateMD5(mImageUrl));
OnProgressCallback callback = callbackRef.get();
if (callback != null) {
callback.setInit(mImageUrl);
}
}
// 设置壁纸
public static void setWallpaper(Context context, File file, final OnProgressCallback callback) throws TaskException {
long time = System.currentTimeMillis();
try {
WallpaperManager wallpaperManager = WallpaperManager.getInstance(GlobalContext.getInstance());
try {
DisplayMetrics dm = new DisplayMetrics();
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
windowManager.getDefaultDisplay().getMetrics(dm);
int width = dm.widthPixels;
int height = dm.heightPixels;
int navigationBarHeight = SystemUtils.getNavigationBarHeight(context);
int wallpaperWidth = width;
int wallpaperHeight = height;
Options opts = new Options();
opts.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), opts);
boolean decode = false;
if (opts.outHeight > height + navigationBarHeight) {
decode = true;
}
Logger.d(TAG, "image height = %d, screen height = %d", opts.outHeight, height + navigationBarHeight);
if (decode) {
// docode的时间会稍微长一点,idol3测试在1S内,所以先显示一个99%的进度
// if (progress <= 0) {
// progress = 999l;
// total = 1000l;
// publishProgress(progress, total);
// }
opts.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeStream(new FileInputStream(file));
Matrix matrix = new Matrix();
float scale = wallpaperHeight * 1.0f / bitmap.getHeight();
matrix.setScale(scale, scale);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
ByteArrayOutputStream out = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
wallpaperManager.setStream(in);
if (callback != null) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
callback.onSetWallpaper(true);
}
});
}
Logger.d(TAG, "设置处理后的壁纸耗时 : " + (System.currentTimeMillis() - time));
return;
}
} catch (Throwable e) {
Logger.printExc(WallpaperDownloadTask.class, e);
}
wallpaperManager.setStream(new FileInputStream(file));
Logger.d(TAG, "设置原壁纸耗时 : " + (System.currentTimeMillis() - time));
if (callback != null) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
callback.onSetWallpaper(true);
}
});
}
} catch (Exception e) {
Logger.printExc(WallpaperDownloadTask.class, e);
if (callback != null) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
callback.onSetWallpaper(false);
}
});
}
throw new TaskException("", context.getResources().getString(R.string.txt_set_wallpaper_fail));
}
}
// 保存壁纸到相册
private File saveWallpaper(File file) throws TaskException {
File savedFile = getWallpaperSaveFile(mImageUrl);
if (savedFile.exists())
return savedFile;
if (file.exists()) {
try {
FileUtils.copyFile(file, savedFile);
Logger.v("保存成功!:" + savedFile.getAbsolutePath());
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT) {
// 解决在部分机器缓存更新不及时问题
mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"
+ Environment.getExternalStorageDirectory())));
}
try {
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Uri uri = Uri.fromFile(savedFile);
intent.setData(uri);
mContext.sendBroadcast(intent);
} catch (Exception e) {
}
} catch (Exception e) {
e.printStackTrace();
}
return savedFile;
}
return null;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
// 停止网络请求,如果不为空
try {
if (mCall != null) {
mCall.cancel();
}
} catch (Throwable e) {
}
return super.cancel(mayInterruptIfRunning);
}
// 下载壁纸
private File doDownload(File file) throws TaskException {
if (!isRunning())
throw new TaskException("-100", "");
//判断网络是否连接
if(SystemUtils.getNetworkType(GlobalContext.getInstance()) == SystemUtils.NetWorkType.none){
throw new TaskException("", mContext.getResources().getString(org.aisen.android.R.string.comm_error_noneNetwork));
}
// 先下载一个临时文件
File tempFile = new File(file.getAbsolutePath() + ".tmp");
// 如果图片没有content-length
final int defaultLength = 8 * 1024 * 1024;
// 开始下载图片
try {
total = 0;
progress = 0;
// 发布进度
publishProgress(progress, total);
Logger.d(TAG, "开始下载壁纸 ---> %s", mImageUrl);
Request request = new Request.Builder().get().url(mImageUrl).build();
mCall = httpClient.newCall(request);
Response response = mCall.execute();
if (response == null) {
throw new TaskException(TaskException.TaskError.failIOError.toString());
}
int statusCode = response.code();
if (!(statusCode == HttpURLConnection.HTTP_OK || statusCode == HttpURLConnection.HTTP_PARTIAL)) {
throw new TaskException(TaskException.TaskError.failIOError.toString());
}
InputStream imageStream = null;
// 写临时文件
FileOutputStream out = new FileOutputStream(tempFile);
try {
String encoding = response.header("Content-Encoding");
if (encoding != null && !TextUtils.isEmpty(encoding) &&
"gzip".equals(encoding)) {
imageStream = new GZIPInputStream(response.body().byteStream());
Logger.w(TAG, "解压gzip文件, 解压前大小:");
} else {
imageStream = response.body().byteStream();
}
try {
total = response.body().contentLength();
} catch (Exception e) {
// 容错处理,如果未读到大小,默认为8M
total = defaultLength;
}
Logger.d(TAG, "Content-Length = " + total);
if (total < 0)
total = defaultLength;
// 获取图片数据
byte[] buffer = new byte[1024 * 8];
int readLen = -1;
long lastPublishTime = 0l;
while ((readLen = imageStream.read(buffer)) != -1) {
if (!isRunning())
throw new TaskException("-100", "");
progress += readLen;
out.write(buffer, 0, readLen);
Logger.v(TAG, "下载进度, %s / %s", getUnit(progress), getUnit(total));
// 发布进度
long now = System.currentTimeMillis();
if (now - lastPublishTime > PUBLISH_INTERVAL_TIME) {
lastPublishTime = now;
publishProgress(progress, total);
}
}
publishProgress(progress, progress);
Logger.d(TAG, "total : " + total + " readLen : " + readLen);
out.flush();
} catch (IOException e) {
Logger.printExc(WallpaperDownloadTask.class, e);
throw e;
} finally {
out.close();
if (imageStream != null)
imageStream.close();
}
// // 验证一下是否GZip压缩
// try {
// String encoding = response.header("Content-Encoding");
// if (encoding != null && !TextUtils.isEmpty(encoding) &&
// "gzip".equals(encoding)) {
// File ttf = tempFile;
// tempFile = decodeGZipFile(tempFile);
// ttf.delete();
// Logger.w(TAG, "解压gzip文件, 解压前大小:" + total + ", 解压后:" + tempFile.length());
// }
// } catch (Throwable e) {
// Logger.printExc(WallpaperDownloadTask.class, e);
// }
} catch (Throwable e) {
Logger.printExc(WallpaperDownloadTask.class, e);
throw new TaskException("", mContext.getResources().getString(R.string.down_faild));
}
Logger.d(TAG, "File-Length = " + tempFile.length());
Logger.d(TAG, "下载文件成功,path = " + tempFile.getAbsolutePath());
// 重命名之前,对临时文件,做一次图片校验,用BitmapFactory解析一下
Options opts = new Options();
opts.inJustDecodeBounds = true;
BitmapFactory.decodeFile(tempFile.getAbsolutePath(), opts);
// 如果解析的宽高度小于1,默认为非图片
Logger.d(TAG, "图片解析尺寸 %s x %s", opts.outWidth, opts.outHeight);
if (opts.outWidth < 1 && opts.outHeight < 1) {
throw new TaskException("", mContext.getResources().getString(R.string.down_faild));
}
Logger.d(TAG, "下载壁纸完成");
if (!tempFile.renameTo(file))
throw new TaskException("", mContext.getResources().getString(R.string.down_faild));
return file;
}
// 壁纸的本地保存文件
public static File getWallpaperSaveFile(String imageUrl) {
String name = KeyGenerator.generateMD5(imageUrl);
String suffix = ".jpg";
File savedFile = new File(SystemUtils.getSdcardPath() + File.separator + AppSettings.getImageSavePath() +
File.separator + "Wallpaper" + File.separator + name + suffix);
try {
if (!savedFile.getParentFile().exists())
savedFile.getParentFile().mkdirs();
} catch (Throwable e) {
Logger.printExc(WallpaperDownloadTask.class, e);
}
return savedFile;
}
public interface OnProgressCallback {
// 回调下载进度
void onProgressUpdate(String image, long progress, long total, int flag);
// 设置壁纸成功
void onSetWallpaper(boolean success);
// 回调弹框消息
void showMessage(String image, String text);
// 取消下载
void onCanceled(String image);
// 未下载,初始化视图
void setInit(String image);
}
// 限制同时下载3个的线程池
private static Executor OFFLINE_EXECUTOR = Executors.newFixedThreadPool(3, new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "WallpaperTask #" + mCount.getAndIncrement());
}
});
// 仅用于日志使用
private String getUnit(long length) {
String sizeStr;
if (length * 1.0f / 1024 / 1024 > 1)
sizeStr = String.format("%s M", new DecimalFormat("#.00").format(length * 1.0d / 1024 / 1024));
else
sizeStr = String.format("%s Kb", new DecimalFormat("#.00").format(length * 1.0d / 1024));
return sizeStr;
}
public static File decodeGZipFile(File file) throws Throwable {
GZIPInputStream gzipIn = null;
FileOutputStream gzipOut = null;
try {
gzipIn = new GZIPInputStream(new FileInputStream(file));
file = new File(file.getAbsolutePath() + ".gziptemp");
gzipOut = new FileOutputStream(file);
byte[] buf = new byte[1024 * 8];
int num = -1;
while ((num = gzipIn.read(buf, 0, buf.length)) != -1) {
gzipOut.write(buf, 0, num);
}
} finally {
if (gzipIn != null)
gzipIn.close();
if (gzipOut != null)
gzipOut.close();
}
return file;
}
}