/*
* Copyright (C) 2010 mAPPn.Inc
*
* 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.mappn.gfan.ui;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.os.Process;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.mappn.gfan.Constants;
import com.mappn.gfan.R;
import com.mappn.gfan.Session;
import com.mappn.gfan.common.ApiAsyncTask;
import com.mappn.gfan.common.ApiAsyncTask.ApiRequestListener;
import com.mappn.gfan.common.MarketAPI;
import com.mappn.gfan.common.download.DownloadManager;
import com.mappn.gfan.common.download.DownloadManager.Request;
import com.mappn.gfan.common.util.DBUtils;
import com.mappn.gfan.common.util.ImageUtils;
import com.mappn.gfan.common.util.Utils;
import com.mappn.gfan.common.vo.DownloadInfo;
import com.mappn.gfan.common.vo.DownloadItem;
import com.mappn.gfan.common.vo.UpgradeInfo;
/**
* GfanClient ListView associating adapter<br>
* It has lazyload feature, which load data on-demand.
*
* @author andrew.wang
*
*/
public class AppsManagerAdapter extends BaseAdapter implements Observer, ApiRequestListener {
static final int REFRESH = 0;
static final int UPDATE_ITEM = 1;
static final int REMOVE_ITEM = 2;
static final int CHECK_DOWNLOAD_TITLE = 3;
static final int CHECK_UPDATE_TITLE = 4;
static final String ITEM_DOWNLOAD_TITLE = "download_title";
static final String ITEM_INSTALLED_TITLE = "installed_title";
static final String ITEM_UPDATE_ALL = "update_all";
/** 分隔项 */
static final int VIEW_TYPE_TITLE = 0;
/** 下载任务区 */
static final int VIEW_TYPE_DOWNLOADING = 1;
/** 下载完成项目 */
static final int VIEW_TYPE_DOWNLOADED = 2;
/** 更新全部项目 */
static final int VIEW_TYPE_UPDATE_ALL = 3;
/** 已安装项目 */
static final int VIEW_TYPE_INSTALLED = 4;
// weight group download area
static final int WEIGHT_DOWNLOAD_TITLE = 0;
static final int WEIGHT_DOWNLOAD_DOWNLOADING = 1;
static final int WEIGHT_DOWNLOAD_PENDING = 2;
static final int WEIGHT_DOWNLOAD_COMPLETE = 3;
// weight group installed area
static final int WEIGHT_NORMAL_TITLE = 5;
static final int WEIGHT_NORMAL_UPDATE_ALL = 6;
static final int WEIGHT_NORMAL_UPDATE = 7;
static final int WEIGHT_NORMAL_INSTALLED = 8;
static final String DOWNLOAD_GROUP = "download_group";
static final String INSTALLED_GROUP = "installed_group";
static final String UPDATE_ALL_ITEM = "update_all_item";
// 列表后台数据
private ListOrderedMap mDataSource;
private LayoutInflater mInflater;
private Context mContext;
private Session mSession;
/* 可更新列表 */
private HashMap<String, UpgradeInfo> mUpdateList;
/* 下载列表 */
private HashMap<String, DownloadInfo> mDownloadingList;
/* 下载管理器 */
private DownloadManager mDownloadManager;
/**
* Application list adapter
*
* @param context
* application context
* @param data
* the datasource behind the listview
* @param resource
* list item view layout resource
* @param from
* the keys array of data source which you want to bind to the view
* @param to
* array of according view id
* @param hasGroup
* whether has place holder
*/
public AppsManagerAdapter(Context context, ListOrderedMap data) {
if (data == null) {
mDataSource = new ListOrderedMap();
} else {
mDataSource = data;
}
mContext = context;
mSession = Session.get(context);
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mSession.addObserver(this);
mDownloadManager = mSession.getDownloadManager();
mUpdateList = mSession.getUpdateList();
mDownloadingList = mSession.getDownloadingList();
init();
}
/*
* How many items are in the data set represented by this Adapter.
*/
@Override
public int getCount() {
if (mDataSource == null) {
return 0;
}
return mDataSource.size();
}
@Override
public Object getItem(int position) {
if (mDataSource != null && position < getCount()) {
return mDataSource.getValue(position);
}
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public boolean isEmpty() {
if (mDataSource == null || mDataSource.size() == 0) {
return true;
}
return super.isEmpty();
}
@Override
public boolean areAllItemsEnabled() {
return false;
}
/*
* Clear all the data
*/
public void clearData() {
if (mDataSource != null) {
mDataSource.clear();
notifyDataSetChanged();
}
}
@Override
public int getItemViewType(int position) {
AppItem item = mDataSource.getValue(position);
int viewType = item == null ? VIEW_TYPE_INSTALLED : item.mViewType;
return viewType;
}
/*
* Return the view types of the list adapter
*/
@Override
public int getViewTypeCount() {
return 5;
}
@Override
public boolean isEnabled(int position) {
boolean result = !isPlaceHolder(position);
return result;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// reach here when list is not at the end
View v;
int viewType = mDataSource.getValue(position).mViewType;
if (convertView == null) {
v = newView(position, parent, viewType);
} else {
v = convertView;
}
bindView(position, v, viewType);
return v;
}
/*
* Main UI handler
*/
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case REFRESH :
refresh();
break;
case UPDATE_ITEM:
AppItem item = (AppItem) msg.obj;
mDataSource.put(item.mKey, item);
checkToRemoveDownloadHeader();
break;
case REMOVE_ITEM:
AppItem delItem = (AppItem) msg.obj;
// 其它项目,直接删除
mDataSource.remove(delItem.mKey);
checkToRemoveDownloadHeader();
break;
case CHECK_DOWNLOAD_TITLE:
// 检查是否需要添加下载区Title
int downloadCounter = (Integer) msg.obj;
AppItem group = mDataSource.getValue(DOWNLOAD_GROUP);
if(downloadCounter > 0 && group == null) {
group = new AppItem();
group.mTitle = mContext.getString(R.string.all_downloads);
group.mInfo = null;
group.mViewType = VIEW_TYPE_TITLE;
group.mWeight = WEIGHT_DOWNLOAD_TITLE;
group.mKey = DOWNLOAD_GROUP;
mDataSource.put(DOWNLOAD_GROUP, group);
} else if(downloadCounter == 0 && group != null){
mDataSource.remove(DOWNLOAD_GROUP);
}
break;
case CHECK_UPDATE_TITLE:
// 检查全部更新项目
int updateCounter = (Integer) msg.obj;
AppItem updateAllItem = mDataSource.getValue(UPDATE_ALL_ITEM);
if (updateCounter > 1 && updateAllItem == null) {
updateAllItem = new AppItem();
updateAllItem.mInfo = mContext.getString(R.string.download_all_update);
updateAllItem.mViewType = VIEW_TYPE_UPDATE_ALL;
updateAllItem.mWeight = WEIGHT_NORMAL_UPDATE_ALL;
updateAllItem.mKey = UPDATE_ALL_ITEM;
mDataSource.put(UPDATE_ALL_ITEM, updateAllItem);
} else if (updateCounter <= 1 && updateAllItem != null) {
mDataSource.remove(UPDATE_ALL_ITEM);
}
break;
default:
break;
}
}
};
/*
* Create new view object and cache some views associated with it
*/
private View newView(int position, ViewGroup parent, int viewType) {
View v = null;
View[] holder = null;
if (viewType == VIEW_TYPE_TITLE) {
v = mInflater.inflate(R.layout.activity_apps_manager_list_separator, parent, false);
holder = new View[2];
holder[0] = v.findViewById(R.id.title);
holder[1] = v.findViewById(R.id.info);
} else if (viewType == VIEW_TYPE_DOWNLOADING) {
v = mInflater.inflate(R.layout.activity_apps_manager_downloading_item, parent, false);
holder = new View[5];
holder[0] = v.findViewById(R.id.iv_logo);
holder[1] = v.findViewById(R.id.tv_name);
holder[2] = v.findViewById(R.id.progressbar);
holder[3] = v.findViewById(R.id.info);
holder[4] = v.findViewById(R.id.tv_operation);
} else if (viewType == VIEW_TYPE_DOWNLOADED) {
v = mInflater.inflate(R.layout.activity_apps_manager_uninstalled_item, parent, false);
holder = new View[3];
holder[0] = v.findViewById(R.id.app_icon);
holder[1] = v.findViewById(R.id.app_name);
holder[2] = v.findViewById(R.id.info);
} else if(viewType == VIEW_TYPE_UPDATE_ALL) {
v = mInflater.inflate(R.layout.activity_apps_manager_update_all_item, parent, false);
holder = new View[1];
holder[0] = v.findViewById(R.id.info);
} else {
v = mInflater.inflate(R.layout.activity_apps_manager_installed_item, parent, false);
holder = new View[5];
holder[0] = v.findViewById(R.id.iv_logo);
holder[1] = v.findViewById(R.id.tv_name);
holder[2] = v.findViewById(R.id.tv_current_version);
holder[3] = v.findViewById(R.id.tv_update_version);
holder[4] = v.findViewById(R.id.tv_operation);
}
v.setTag(holder);
return v;
}
/*
* bind the background data to the view
*/
private void bindView(int position, View view, int viewType) {
final AppItem item = (AppItem) mDataSource.getValue(position);
if (item == null) {
return;
}
final View[] holder = (View[]) view.getTag();
if (viewType == VIEW_TYPE_TITLE) {
bindTitleView(holder, item);
} else if (viewType == VIEW_TYPE_DOWNLOADING) {
bindDownloadingView(position, holder, item);
} else if (viewType == VIEW_TYPE_DOWNLOADED) {
bindUninstalledView(holder, item);
} else if (viewType == VIEW_TYPE_UPDATE_ALL) {
bindUpdateAllView(holder, item);
} else {
bindInstalledView(position, holder, item);
}
}
/*
* Bind views for downloading item
*/
private void bindDownloadingView(int position, View[] holder, AppItem item) {
if (item.mIcon instanceof Drawable) {
setImageView((ImageView) holder[0], (Drawable) item.mIcon);
} else if (item.mIcon instanceof String) {
setImageView((ImageView) holder[0], (String) item.mIcon);
}
setTextView((TextView) holder[1], item.mAppName);
setProgressBar((ProgressBar) holder[2], item.mProgress);
setTextView((TextView) holder[3], item.mInfo);
setTextView(position, (TextView) holder[4], item.mWeight);
}
/*
* Bind views for uninstalled item
*/
private void bindUninstalledView(View[] holder, AppItem item) {
if (item.mIcon instanceof Drawable) {
setImageView((ImageView) holder[0], (Drawable) item.mIcon);
} else if (item.mIcon instanceof String) {
setImageView((ImageView) holder[0], (String) item.mIcon);
}
setTextView((TextView) holder[1], item.mAppName);
setTextView((TextView) holder[2], item.mInfo);
}
/*
* Bind views for update all item
*/
private void bindUpdateAllView(View[] holder, AppItem item) {
setTextView((TextView) holder[0], item.mInfo);
}
/*
* Bind views for splitter item
*/
private void bindTitleView(View[] holder, AppItem item) {
// left info
setTextView((TextView) holder[0], item.mTitle);
// right info
setTextView((TextView) holder[1], item.mInfo);
}
/*
* Bind views for installed item
*/
private void bindInstalledView(int position, View[] holder, AppItem item) {
// set app icon
setImageView((ImageView) holder[0], (Drawable) item.mIcon);
// set app name
setTextView((TextView) holder[1], item.mAppName);
// set current version
setTextView((TextView) holder[2], item.mCurrentVersionString, item.mNewVersionString);
// set new version
// setTextView((TextView) holder[3], item.mNewVersionString);
// set uninstall button
setTextView(position, (TextView) holder[4], item.mWeight);
}
/*
* Utility for TextView
*/
private void setTextView(TextView v, CharSequence text) {
if (TextUtils.isEmpty(text)) {
v.setVisibility(View.GONE);
} else {
v.setText(text);
v.setVisibility(View.VISIBLE);
}
}
private void setTextView(TextView v, CharSequence currentVersion, CharSequence newVersion) {
if (!TextUtils.isEmpty(newVersion)) {
v.setText(newVersion);
v.setTextColor(mContext.getResources().getColor(R.color.info_font_light_orange));
v.setVisibility(View.VISIBLE);
return;
}
if (!TextUtils.isEmpty(currentVersion)) {
v.setText(currentVersion);
v.setTextAppearance(mContext, R.style.app_text_style3);
v.setVisibility(View.VISIBLE);
return;
}
v.setVisibility(View.GONE);
}
/*
* Bind CompundDrawable TextView
*/
private void setTextView(int position, TextView v, int weight) {
if(WEIGHT_NORMAL_INSTALLED == weight) {
v.setText(R.string.operation_uninstall);
v.setCompoundDrawablesWithIntrinsicBounds(null,
mContext.getResources().getDrawable(R.drawable.btn_uninstall), null, null);
v.setVisibility(View.VISIBLE);
} else if(WEIGHT_NORMAL_UPDATE == weight) {
AppItem item = mDataSource.getValue(position);
if (TextUtils.isEmpty(item.mFilePath)) {
v.setText(R.string.operation_update);
v.setCompoundDrawablesWithIntrinsicBounds(null,
mContext.getResources().getDrawable(R.drawable.down_btn_10), null, null);
v.setVisibility(View.VISIBLE);
} else {
// 更新下载完成
v.setText(R.string.download_status_downloaded);
v.setCompoundDrawablesWithIntrinsicBounds(null,
mContext.getResources().getDrawable(R.drawable.down_btn_9), null, null);
v.setVisibility(View.VISIBLE);
}
} else if(WEIGHT_DOWNLOAD_DOWNLOADING == weight){
v.setText(R.string.cancel_downloads);
v.setCompoundDrawablesWithIntrinsicBounds(null,
mContext.getResources().getDrawable(R.drawable.btn_cancel), null, null);
v.setVisibility(View.VISIBLE);
} else {
v.setVisibility(View.INVISIBLE);
}
v.setTag(position);
v.setOnClickListener(mOperationListener);
}
/*
* Utility for ImageView(get rid of old drawable)
*/
private static void setImageView(ImageView v, Drawable drawable) {
Drawable old = v.getDrawable();
if (old != null) {
old.setCallback(null);
}
v.setImageDrawable(drawable);
v.setVisibility(View.VISIBLE);
}
/*
* Utility for ImageView(get rid of old drawable)
*/
private void setImageView(ImageView v, String url) {
Drawable old = v.getDrawable();
if (old != null) {
old.setCallback(null);
}
ImageUtils.download(mContext, url, v);
v.setVisibility(View.VISIBLE);
}
/*
* Utility for ProgressBar
*/
private static void setProgressBar(ProgressBar v, int progress) {
v.setProgress(progress);
v.setVisibility(View.VISIBLE);
}
/*
* Operation Listener
*/
private OnClickListener mOperationListener = new OnClickListener() {
@Override
public void onClick(View v) {
int position = (Integer) v.getTag();
AppItem item = mDataSource.getValue(position);
if (item.mWeight == WEIGHT_NORMAL_INSTALLED) {
// 卸载应用
Utils.uninstallApk(mContext, item.mPackageName);
Utils.trackEvent(mContext, Constants.GROUP_7,
Constants.CLICK_UNINSTALL);
} else if (item.mWeight == WEIGHT_NORMAL_UPDATE) {
if (!TextUtils.isEmpty(item.mFilePath)) {
Utils.installApk(mContext, new File(item.mFilePath));
} else {
// 更新应用
MarketAPI.getDownloadUrl(mContext, AppsManagerAdapter.this, item.mProductId,
Constants.SOURCE_TYPE_GFAN);
Utils.trackEvent(mContext, Constants.GROUP_7,
Constants.CLICK_UPDATE);
// 开始更新单个项目
item.mWeight = WEIGHT_NORMAL_INSTALLED;
item.mViewType = VIEW_TYPE_INSTALLED;
item.mKey = item.mPackageName;
sendMessage(UPDATE_ITEM, item);
}
} else if (item.mWeight == WEIGHT_DOWNLOAD_DOWNLOADING) {
// 取消下载
mDownloadManager.cancelDownload(item.mId);
}
mHandler.sendEmptyMessage(REFRESH);
}
};
/*
* Identify whether current item is only a place holder
*/
private boolean isPlaceHolder(int position) {
if (mDataSource == null) {
return false;
}
if (position >= mDataSource.size()) {
return false;
}
AppItem item = mDataSource.getValue(position);
return item.mViewType == VIEW_TYPE_TITLE;
}
/**
* 初始化我的应用列表<br>
* 1 -- 获取用户安装的所有应用<br>
* 2 -- 获取可更新的应用<br>
* 3 -- 获取用户下载的应用(下载队列中、正在下载、下载完成但没有安装)<br>
*/
private void init() {
Thread refreshThread = new Thread() {
@Override
public void run() {
loadInstalledApps();
refreshUpdateApps();
initDownloadingApps();
}
};
refreshThread.setPriority(Process.THREAD_PRIORITY_BACKGROUND);
refreshThread.start();
refreshThread = null;
}
private void onChanged(final int signal) {
Thread initThread = new Thread() {
@Override
public void run() {
if(signal == Constants.INFO_UPDATE) {
refreshUpdateApps();
} else if(signal == Constants.INFO_REFRESH) {
refreshDownloadingApps();
}
mHandler.sendEmptyMessage(REFRESH);
}
};
initThread.setPriority(Process.THREAD_PRIORITY_BACKGROUND);
initThread.start();
initThread = null;
}
/*
* 刷新可更新的应用
*/
private void refreshUpdateApps() {
mHandler.post(new Runnable() {
@Override
public void run() {
if (mUpdateList != null) {
Collection<UpgradeInfo> values = mUpdateList.values();
for (UpgradeInfo info : values) {
AppItem item = mDataSource.getValue(info.pkgName);
if (item != null) {
item.mNewVersion = info.versionName;
item.mNewVersionString = mContext.getString(R.string.new_version,
info.versionName);
item.mWeight = WEIGHT_NORMAL_UPDATE;
item.mProductId = info.pid;
item.mIsUpdate = true;
sendMessage(UPDATE_ITEM, item);
}
}
}
}
});
}
/*
* 更新下载任务
*/
private void initDownloadingApps() {
mHandler.post(new Runnable() {
@Override
public void run() {
refreshDownloadingApps();
mHandler.sendEmptyMessage(REFRESH);
}
});
}
private void checkToRemoveDownloadHeader() {
// 当第二个项目不为下载项目时,说明需要移除下载区Title
if (mDataSource != null && mDataSource.size() > 1) {
AppItem item = mDataSource.getValue(1);
if (item.mWeight > WEIGHT_DOWNLOAD_COMPLETE) {
AppItem group = mDataSource.getValue(DOWNLOAD_GROUP);
if(group != null) {
sendMessage(REMOVE_ITEM, group);
}
}
}
}
/*
* 加载已经安装的应用
*/
private void loadInstalledApps() {
List<PackageInfo> apps = Utils.getInstalledApps(mContext);
final PackageManager pm = mContext.getPackageManager();
// 加入已安装应用区域的Title
AppItem groupTitle = new AppItem();
groupTitle.mTitle = mContext.getString(R.string.all_apps);
groupTitle.mInfo = mContext.getString(R.string.app_counter, apps.size());
groupTitle.mViewType = VIEW_TYPE_TITLE;
groupTitle.mWeight = WEIGHT_NORMAL_TITLE;
// 保存已经安装的应用数量以便更新
groupTitle.mData = apps.size();
groupTitle.mKey = INSTALLED_GROUP;
sendMessage(UPDATE_ITEM, groupTitle);
// 加载已安装的应用列表项
for (PackageInfo info : apps) {
AppItem application = new AppItem();
Drawable icon = null;
try {
icon = info.applicationInfo.loadIcon(pm);
} catch (OutOfMemoryError err) {
Utils.W("OutOfMemoryError when loading icon drawables from installed apps.");
}
application.mIcon = icon;
application.mAppName = (String)info.applicationInfo.loadLabel(pm);
String currentVersion = TextUtils.isEmpty(info.versionName) ? mContext
.getString(R.string.warning_unknown_version) : info.versionName;
application.mCurrentVersion = currentVersion;
application.mCurrentVersionString = mContext.getString(R.string.current_version, currentVersion);
application.mPackageName = info.packageName;
application.mViewType = VIEW_TYPE_INSTALLED;
application.mKey = info.packageName;
application.mWeight = WEIGHT_NORMAL_INSTALLED;
sendMessage(UPDATE_ITEM, application);
}
}
/*
* 获取单个应用的信息
*/
private static AppItem getApkInfo(Context context, String packageName) {
final PackageManager pm = context.getPackageManager();
try {
PackageInfo info = pm.getPackageInfo(packageName, 0);
AppItem application = new AppItem();
Drawable icon = null;
try {
icon = info.applicationInfo.loadIcon(pm);
} catch (OutOfMemoryError err) {
Utils.W("OutOfMemoryError when loading icon drawables from installed apps.");
}
application.mIcon = icon;
application.mAppName = (String) info.applicationInfo.loadLabel(pm);
application.mCurrentVersion = info.versionName;
application.mCurrentVersionString = context.getString(R.string.current_version,
info.versionName);
application.mPackageName = info.packageName;
application.mViewType = VIEW_TYPE_INSTALLED;
application.mKey = info.packageName;
application.mWeight = WEIGHT_NORMAL_INSTALLED;
return application;
} catch (NameNotFoundException e) {
Utils.E("getApkInfo NameNotFoundException for " + packageName, e);
}
return null;
}
private void sendMessage(int what, Object obj) {
Message msg = mHandler.obtainMessage(what);
msg.obj = obj;
mHandler.sendMessage(msg);
}
/*
* 更新下载任务
*/
private void refreshDownloadingApps() {
if (mDownloadingList == null) {
return;
}
Collection<DownloadInfo> taskList = mDownloadingList.values();
int downloadCounter = 0;
int updateCounter = mUpdateList.size();
for (DownloadInfo info : taskList) {
// 过期的项目(用户取消下载),从列表中删除
if (info.mStatus == DownloadManager.Impl.STATUS_CANCELED) {
AppItem item = mDataSource.getValue(info.mPackageName);
if (item != null) {
if (item.mIsUpdate) {
item.mViewType = VIEW_TYPE_INSTALLED;
item.mWeight = WEIGHT_NORMAL_UPDATE;
sendMessage(UPDATE_ITEM, item);
} else {
sendMessage(REMOVE_ITEM, item);
}
}
continue;
}
String packageName = info.mPackageName;
AppItem item = mDataSource.getValue(packageName);
if (item == null) {
item = new AppItem();
item.mAppName = info.mAppName;
item.mPackageName = packageName;
item.mKey = packageName;
item.mIcon = info.mIconUrl;
}
item.mId = info.id;
downloadCounter++;
if (info.mStatus == DownloadManager.Impl.STATUS_SUCCESS) {
// 下载成功的项目
if (item.mIsUpdate) {
item.mViewType = VIEW_TYPE_INSTALLED;
item.mWeight = WEIGHT_NORMAL_UPDATE;
// 下载成功的更新项目不需要显示在下载区域
downloadCounter--;
} else {
item.mViewType = VIEW_TYPE_DOWNLOADED;
item.mWeight = WEIGHT_DOWNLOAD_COMPLETE;
}
item.mFilePath = info.mFilePath;
item.mInfo = mContext.getString(R.string.download_over);
} else {
// 下载过程中的项目
item.mViewType = VIEW_TYPE_DOWNLOADING;
item.mWeight = WEIGHT_DOWNLOAD_DOWNLOADING;
item.mProgress = info.mProgressNumber;
item.mInfo = Utils.calculateRemainBytes(mContext, info.mCurrentSize,
info.mTotalSize);
}
sendMessage(UPDATE_ITEM, item);
if (mUpdateList.containsKey(item.mKey)) {
updateCounter--;
}
}
// 检查是否显示Download Title
sendMessage(CHECK_DOWNLOAD_TITLE, downloadCounter);
// 检查是否显示UpdateAll
sendMessage(CHECK_UPDATE_TITLE, updateCounter);
}
/*package*/ void refresh() {
notifyDataSetChanged();
}
@SuppressWarnings("unchecked")
@Override
public void update(Observable arg0, Object arg1) {
if (arg1 instanceof Integer) {
onChanged((Integer) arg1);
} else if (arg1 instanceof HashMap) {
mDownloadingList = (HashMap<String, DownloadInfo>) arg1;
onChanged(Constants.INFO_REFRESH);
}
}
/* package */ void updateAll() {
if (mUpdateList == null) {
return;
}
for (String key : mUpdateList.keySet()) {
if (mDownloadingList != null && mDownloadingList.containsKey(key)) {
// 已经更新过的应用
continue;
}
// 更新应用
AppItem item = mDataSource.getValue(key);
MarketAPI.getDownloadUrl(mContext, AppsManagerAdapter.this, item.mProductId,
Constants.SOURCE_TYPE_GFAN);
// 开始更新单个项目
item.mWeight = WEIGHT_NORMAL_INSTALLED;
item.mViewType = VIEW_TYPE_INSTALLED;
item.mKey = item.mPackageName;
sendMessage(UPDATE_ITEM, item);
}
mHandler.sendEmptyMessage(REFRESH);
}
@Override
public void onSuccess(int method, Object obj) {
if (method == MarketAPI.ACTION_GET_DOWNLOAD_URL) {
DownloadItem info = (DownloadItem) obj;
AppItem item = mDataSource.getValue(info.packageName);
Request request = new Request(Uri.parse(info.url));
request.setTitle(item.mAppName);
request.setPackageName(info.packageName);
request.setSourceType(com.mappn.gfan.common.download.Constants.DOWNLOAD_FROM_MARKET);
request.setMD5(info.fileMD5);
item.mId = mDownloadManager.enqueue(request);
mUpdateList.remove(info.packageName);
}
}
@Override
public void onError(int method, int statusCode) {
if (statusCode == ApiAsyncTask.BUSSINESS_ERROR) {
Utils.makeEventToast(mContext, mContext.getString(R.string.alert_no_download_url),
false);
} else if (statusCode == ApiAsyncTask.TIMEOUT_ERROR) {
Utils.makeEventToast(mContext, mContext.getString(R.string.no_data), false);
}
}
/***
* 更新已经安装的应用数
*/
private void updateAppNumber() {
List<PackageInfo> apps = Utils.getInstalledApps(mContext);
AppItem item = mDataSource.getValue(INSTALLED_GROUP);
item.mData = apps.size();
item.mInfo = mContext.getString(R.string.app_counter, apps.size());
sendMessage(UPDATE_ITEM, item);
}
/**
* 卸载一个应用
*/
/*package*/ void removedAppWithPackageName(String packageName) {
AppItem item = mDataSource.getValue(packageName);
if(item != null) {
sendMessage(REMOVE_ITEM, item);
}
updateAppNumber();
mHandler.sendEmptyMessage(REFRESH);
}
/**
* 安装一个应用
*/
/*package*/ void installAppWithPackageName(String packageName) {
AppItem item = mDataSource.getValue(packageName);
if (item != null) {
if (item.mIsUpdate) {
// 用户点击升级安装
item.mIsUpdate = false;
item.mCurrentVersionString = item.mNewVersionString;
item.mNewVersionString = null;
item.mViewType = VIEW_TYPE_INSTALLED;
item.mWeight = WEIGHT_NORMAL_INSTALLED;
sendMessage(UPDATE_ITEM, item);
} else {
// 用户全新安装
AppItem app = getApkInfo(mContext, packageName);
if (app != null) {
sendMessage(UPDATE_ITEM, app);
} else {
sendMessage(REMOVE_ITEM, item);
}
}
} else {
// 用户全新安装
AppItem app = getApkInfo(mContext, packageName);
if (app != null) {
sendMessage(UPDATE_ITEM, app);
}
}
updateAppNumber();
mHandler.sendEmptyMessage(REFRESH);
}
/* package */void delApp(int pos) {
AppItem item = mDataSource.getValue(pos);
if (item != null && !TextUtils.isEmpty(item.mFilePath)) {
boolean result = new File(item.mFilePath).delete();
if (result) {
sendMessage(REMOVE_ITEM, item);
}
}
mHandler.sendEmptyMessage(REFRESH);
}
/* package */void ignoreUpdate(int pos) {
AppItem item = mDataSource.getValue(pos);
item.mWeight = WEIGHT_NORMAL_INSTALLED;
item.mNewVersionString = "";
DBUtils.ignoreUpdate(mContext, item.mPackageName);
mUpdateList.remove(item.mPackageName);
mSession.setUpdateList(mUpdateList);
mSession.setUpgradeNumber(mSession.getUpgradeNumber() - 1);
sendMessage(UPDATE_ITEM, item);
// 检查是否显示UpdateAll Title
sendMessage(CHECK_UPDATE_TITLE, mSession.getUpgradeNumber());
mHandler.sendEmptyMessage(REFRESH);
}
/* package */void close() {
mSession.deleteObserver(this);
mDataSource = null;
mInflater = null;
mContext = null;
mSession = null;
mUpdateList = null;
mDownloadingList = null;
mDownloadManager = null;
}
/**
* 应用列表项<br>
*
* 列表采用LinkedHashMap结构,Key是每个应用包名,Value是每个列表项,提高更新查找效率。<br>
* 对于下载部分,由于存在包名重复的可能性,所以采用数据库ID作为包名。<br>
*
* @author Andrew
* @date 2011-5-18
*/
public static class AppItem {
/** 数据库ID */
public long mId;
/** Map Key */
public String mKey;
/** 产品ID(用于检查更新时,判断此应用是否存在于机锋市场) */
public String mProductId;
/** Title(Left信息) */
public String mTitle;
/** Info(Right信息) */
public String mInfo;
/** Info(Extra信息) */
public String mInfo2;
/** 应用名 */
public String mAppName;
/** 包名 */
public String mPackageName;
/** ICON(Drawable或者url) */
public Object mIcon;
/** 存储自定义数据 */
public Object mData;
/** 当然版本 */
public String mCurrentVersion;
/** 当然版本(装饰后字符串) */
public String mCurrentVersionString;
/** 升级版本 */
public String mNewVersion;
/**升级版本(装饰后字符串) */
public String mNewVersionString;
/** 文件路径 */
public String mFilePath;
/** 列表项权重(用来排序) */
public int mWeight;
/** 下载项目的进度 */
public int mProgress;
/** 列表项的视图种类(本地已安装、下载中、下载完成...) */
public int mViewType;
/** 标识是否更新项目*/
public boolean mIsUpdate;
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "[" + mAppName + "] weight " + mWeight;
}
}
public static class ListOrderedMap {
/** The content map */
private HashMap<String, AppItem> map;
private ArrayList<AppItem> list;
private Comparator<AppItem> mSortComparator;
private boolean isRefreshed;
public ListOrderedMap() {
map = new HashMap<String, AppItem>();
mSortComparator = new Comparator<AppItem>() {
@Override
public int compare(AppItem object1, AppItem object2) {
int weight1 = object1.mWeight;
int weight2 = object2.mWeight;
return weight1 - weight2;
}
};
}
public AppItem put(String key, AppItem value) {
synchronized (map) {
isRefreshed = false;
return map.put(key, value);
}
}
public AppItem remove(String key) {
synchronized (map) {
isRefreshed = false;
return map.remove(key);
}
}
public void clear() {
synchronized (map) {
map.clear();
list.clear();
}
}
public AppItem getValue(int index) {
synchronized (map) {
while (!isRefreshed) {
refresh();
}
return list.get(index);
}
}
public AppItem getValue(String key) {
synchronized (map) {
return map.get(key);
}
}
private void refresh() {
synchronized (map) {
Collection<AppItem> values = map.values();
if (values == null) {
return;
}
ArrayList<AppItem> tempList = new ArrayList<AppItem>(values);
Collections.sort(tempList, mSortComparator);
list = tempList;
isRefreshed = true;
}
}
public int size() {
synchronized (map) {
if (map == null) {
return 0;
}
return map.size();
}
}
}
}