package com.smartisanos.sidebar.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.provider.BaseColumns;
import android.text.TextUtils;
import android.util.Pair;
import android.widget.Toast;
import com.smartisanos.sidebar.R;
import com.smartisanos.sidebar.util.ResolveInfoGroup.SameGroupComparator;
public class ResolveInfoManager extends SQLiteOpenHelper {
private volatile static ResolveInfoManager sInstance;
public static ResolveInfoManager getInstance(Context context){
if(sInstance == null){
synchronized(ResolveInfoManager.class){
if(sInstance == null){
sInstance = new ResolveInfoManager(context);
}
}
}
return sInstance;
}
private static final String DB_NAME ="resolveinfo";
private static final int DB_VERSION = 1;
private static final List<String> sAutoAddPackageList;
private static final List<ComponentName> sAutoAddComponentList;
static {
// package
sAutoAddPackageList = new ArrayList<String>();
sAutoAddPackageList.add("com.sina.weibo");
sAutoAddPackageList.add("com.smartisanos.notes");
sAutoAddPackageList.add("com.android.email");
sAutoAddPackageList.add("com.android.calendar");
sAutoAddPackageList.add("com.taobao.taobao");
sAutoAddPackageList.add("com.evernote");
sAutoAddPackageList.add("com.wunderkinder.wunderlistandroid");
sAutoAddPackageList.add("com.android.mms");
sAutoAddPackageList.add("com.meitu.meiyancamera");
sAutoAddPackageList.add("com.google.android.youtube");
sAutoAddPackageList.add("com.facebook.katana");
sAutoAddPackageList.add("com.whatsapp");
sAutoAddPackageList.add("com.instagram.android");
// component
sAutoAddComponentList = new ArrayList<ComponentName>();
sAutoAddComponentList.add(new ComponentName("com.tencent.mm", "com.tencent.mm.ui.tools.ShareToTimeLineUI"));
sAutoAddComponentList.add(new ComponentName("com.tencent.mm", "com.tencent.mm.ui.tools.ShareImgUI"));
sAutoAddComponentList.add(new ComponentName("com.tencent.mobileqq", "com.tencent.mobileqq.activity.JumpActivity"));
sAutoAddComponentList.add(new ComponentName("com.tencent.mobileqq", "com.tencent.mobileqq.activity.qfileJumpActivity"));
sAutoAddComponentList.add(new ComponentName("com.tencent.mobileqqi", "com.tencent.mobileqq.activity.JumpActivity"));
sAutoAddComponentList.add(new ComponentName("com.tencent.mobileqqi", "com.tencent.mobileqq.activity.qfileJumpActivity"));
sAutoAddComponentList.add(new ComponentName("com.twitter.android", "com.twitter.android.composer.ComposerActivity"));
// check ourself
for (ComponentName cn : sAutoAddComponentList) {
if (sAutoAddPackageList.contains(cn.getPackageName())) {
throw new IllegalArgumentException("auto-add package contains auto-add component !");
}
}
}
private static final Set<String> sBlackList;
private static final Set<Pair<String, String>> sBlackCompList;
static {
sBlackList = new HashSet<String>();
sBlackList.add("com.smartisanos.sidebar");
sBlackList.add("com.android.phone");
sBlackList.add("com.android.contacts");
sBlackList.add("com.android.settings");
sBlackList.add("com.android.gallery3d");
sBlackList.add("com.android.nfc");
sBlackList.add("com.android.bluetooth");
sBlackList.add("com.intsig.BizCardReader");
sBlackList.add("com.intsig.camscanner");
sBlackList.add("com.tmall.wireless");
sBlackList.add("com.tencent.qqpimsecure");
sBlackList.add("com.eg.android.AlipayGphone");
sBlackList.add("com.mobisystems.office");
sBlackList.add("com.google.android.apps.maps");
sBlackList.add("com.UCMobile");
sBlackList.add("com.Qunar");
sBlackList.add("com.estrongs.android.pop");
sBlackList.add("com.alensw.PicFolder");
sBlackList.add("com.douban.frodo");
sBlackList.add("it.repix.android");
sBlackList.add("com.coolapk.market");
sBlackList.add("cn.poco.jane");
sBlackList.add("com.google.android.inputmethod.japanese");
sBlackList.add("tc.everphoto");
sBlackList.add("com.baidu.netdisk");
sBlackList.add("com.pinterest");
sBlackList.add("com.skype.raider");
sBlackList.add("com.wumii.android.mimi");
sBlackList.add("com.snapchat.android");
sBlackList.add("com.cleanmaster.mguard_cn");
sBlackList.add("com.taou.maimai");
sBlackList.add("com.instagram.layout");
sBlackList.add("com.soundcloud.android");
sBlackList.add("com.cyberlink.youcammakeup");
sBlackList.add("com.weheartit");
sBlackCompList = new HashSet<Pair<String, String>>();
sBlackCompList.add(new Pair<String, String>("com.tencent.mobileqq", "com.tencent.mobileqq.activity.ContactSyncJumpActivity"));
sBlackCompList.add(new Pair<String, String>("com.tencent.mm", "com.tencent.mm.plugin.accountsync.ui.ContactsSyncUI"));
}
public static final String[] ACTIONS = new String[] { Intent.ACTION_SEND,
Intent.ACTION_SEND_MULTIPLE };
private static final int sMaxNumber = 20;
private Context mContext;
private List<ResolveInfoGroup> mList = new ArrayList<ResolveInfoGroup>();
private List<ResolveInfoUpdateListener> mListeners = new ArrayList<ResolveInfoUpdateListener>();
private Handler mHandler;
private Toast mLimitToast;
private ResolveInfoManager(Context context) {
super(context, DB_NAME, null, DB_VERSION);
context = context.getApplicationContext();
mContext = context;
HandlerThread thread = new HandlerThread(ResolveInfoManager.class.getName());
thread.start();
mHandler = new ResolveInfoManagerHandler(thread.getLooper());
mHandler.obtainMessage(MSG_UPDATE_LIST).sendToTarget();
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + TABLE_RESOLVEINFO
+ " ( _id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ "packagename TEXT," + "names TEXT, " + "weight INTEGER"
+ ");");
List<ResolveInfoGroup> rigs = new ArrayList<ResolveInfoGroup>();
// pre install part 1, component name
for (ComponentName cn : sAutoAddComponentList) {
List<ResolveInfoGroup> list = getAllResolveInfoGroupByPackageName(cn.getPackageName());
if (list != null) {
for (ResolveInfoGroup rig : list) {
if (rig.containsComponent(cn)) {
if (!rigs.contains(rig)) {
rigs.add(rig);
}
}
}
}
}
// pre install part2, package name
for (String packageName : sAutoAddPackageList) {
List<ResolveInfoGroup> list = getAllResolveInfoGroupByPackageName(packageName);
if (list != null) {
rigs.addAll(list);
}
}
// update index
for (int i = 0; i < rigs.size(); ++i) {
rigs.get(i).setIndex(rigs.size() - 1 - i);
}
// add to database
for (ResolveInfoGroup rig : rigs) {
ContentValues cv = new ContentValues();
cv.put(ResolveInfoColumns.PACKAGE_NAME, rig.getPackageName());
cv.put(ResolveInfoColumns.COMPONENT_NAMES, rig.getComponentNames());
cv.put(ResolveInfoColumns.WEIGHT, rig.getIndex());
db.insert(TABLE_RESOLVEINFO, null, cv);
}
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// NA
}
public void addListener(ResolveInfoUpdateListener listener){
if (listener == null) {
return;
}
if (!mListeners.contains(listener)) {
mListeners.add(listener);
}
}
public void removeListener(ResolveInfoUpdateListener listener){
mListeners.remove(listener);
}
private void notifyUpdate(){
for(ResolveInfoUpdateListener li : mListeners){
li.onUpdate();
}
}
public void delete(ResolveInfoGroup rig) {
synchronized (mListeners) {
for (int i = 0; i < mList.size(); ++i) {
if (mList.get(i).equals(rig)) {
mList.remove(i).newRemoved = true;
notifyUpdate();
mHandler.obtainMessage(MSG_DELETE, rig).sendToTarget();
return;
}
}
}
}
public boolean addResolveInfoGroup(ResolveInfoGroup rig) {
return addResolveInfoGroupInternal(rig, true);
}
private boolean addResolveInfoGroupInternal(ResolveInfoGroup rig, boolean showToast) {
if (rig == null || rig.size() <= 0) {
return false;
}
synchronized (mList) {
if (mList.size() >= sMaxNumber) {
if (showToast) {
showToastDueToLimit();
}
return false;
}
for (int i = 0; i < mList.size(); ++i) {
if (mList.get(i).equals(rig)) {
return true;
}
}
if (mList.size() == 0) {
rig.setIndex(0);
} else {
int maxIndex = mList.get(0).getIndex();
for (int i = 1; i < mList.size(); ++i) {
if (mList.get(i).getIndex() > maxIndex) {
maxIndex = mList.get(i).getIndex();
}
}
rig.setIndex(maxIndex + 1);
}
rig.newAdded = true;
mList.add(0, rig);
}
mHandler.obtainMessage(MSG_SAVE, rig).sendToTarget();
notifyUpdate();
return true;
}
public void updateOrder() {
synchronized (mList) {
Collections.sort(mList, new ResolveInfoGroup.IndexComparator());
}
notifyUpdate();
mHandler.obtainMessage(MSG_SAVE_ORDER).sendToTarget();
}
private void showToastDueToLimit() {
if (mLimitToast != null) {
mLimitToast.cancel();
}
mLimitToast = Toast.makeText(mContext, mContext.getResources()
.getString(R.string.add_at_most_share_number, sMaxNumber),
Toast.LENGTH_SHORT);
mLimitToast.show();
}
private void saveOrderForList(){
List<ResolveInfoGroup> list = getAddedResolveInfoGroup();
SQLiteDatabase db = getWritableDatabase();
db.beginTransaction();
try {
for(ResolveInfoGroup rig : list){
int id = getId(rig);
if(id != 0){
ContentValues cv = new ContentValues();
cv.put(ResolveInfoColumns.PACKAGE_NAME, rig.getPackageName());
cv.put(ResolveInfoColumns.COMPONENT_NAMES, rig.getComponentNames());
cv.put(ResolveInfoColumns.WEIGHT, rig.getIndex());
db.update(TABLE_RESOLVEINFO, cv, ResolveInfoColumns._ID + "=?", new String[] { id + "" });
}
}
db.setTransactionSuccessful();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (db != null) {
db.endTransaction();
db.close();
}
}
}
private int getId(ResolveInfoGroup rig) {
Cursor cursor = null;
try {
cursor = getReadableDatabase().query(
TABLE_RESOLVEINFO,
null,
ResolveInfoColumns.PACKAGE_NAME + "=? and "
+ ResolveInfoColumns.COMPONENT_NAMES + "=?",
new String[] { rig.getPackageName(), rig.getComponentNames() },
null, null, null);
if (cursor.moveToFirst()) {
return cursor.getInt(cursor.getColumnIndex(ResolveInfoColumns._ID));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
}
}
return 0;
}
private void deleteFromDatabase(ResolveInfoGroup rig){
int id = getId(rig);
if(id != 0){
getWritableDatabase().delete(TABLE_RESOLVEINFO,
ResolveInfoColumns._ID + "=?", new String[] { id + "" });
}
}
private void saveToDatabase(ResolveInfoGroup rig){
ContentValues cv = new ContentValues();
cv.put(ResolveInfoColumns.PACKAGE_NAME, rig.getPackageName());
cv.put(ResolveInfoColumns.COMPONENT_NAMES, rig.getComponentNames());
cv.put(ResolveInfoColumns.WEIGHT, rig.getIndex());
getWritableDatabase().insert(TABLE_RESOLVEINFO, null, cv);
}
private void updateComponentList(){
List<ResolveInfoGroup> list = new ArrayList<ResolveInfoGroup>();
Cursor cursor = null;
try {
cursor = getReadableDatabase().query(TABLE_RESOLVEINFO, null,null, null, null, null, null);
if (cursor.moveToFirst()) {
do {
String pkgName = cursor.getString(cursor.getColumnIndex(ResolveInfoColumns.PACKAGE_NAME));
String componentNames = cursor.getString(cursor.getColumnIndex(ResolveInfoColumns.COMPONENT_NAMES));
int weight = cursor.getInt(cursor.getColumnIndex(ResolveInfoColumns.WEIGHT));
ResolveInfoGroup rig = ResolveInfoGroup.fromData(mContext, pkgName, componentNames);
if (rig != null) {
rig.setIndex(weight);
list.add(rig);
} else {
getWritableDatabase().delete(TABLE_RESOLVEINFO, "packagename=? and names=?", new String[] { pkgName, componentNames });
}
} while (cursor.moveToNext());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
}
}
Collections.sort(list, new ResolveInfoGroup.IndexComparator());
synchronized (mList) {
mList.clear();
mList.addAll(list);
}
notifyUpdate();
}
public boolean isResolveInfoGroupAdded(ResolveInfoGroup rig) {
synchronized (mList) {
for (ResolveInfoGroup addedItem : mList) {
if (addedItem == rig || addedItem.equals(rig)) {
return true;
}
}
}
return false;
}
public List<ResolveInfoGroup> getAddedResolveInfoGroup() {
List<ResolveInfoGroup> ret = new ArrayList<ResolveInfoGroup>();
synchronized (mList) {
ret.addAll(mList);
}
return ret;
}
public List<ResolveInfoGroup> getUnAddedResolveInfoGroup(){
List<ResolveInfoGroup> ret = getAllResolveInfoGroupByPackageName(null);
synchronized (mList) {
if (ret != null) {
for (int i = 0; i < ret.size(); ++i) {
for (int j = 0; j < mList.size(); ++j)
if (ret.get(i).equals(mList.get(j))) {
ret.remove(i);
i--;
break;
}
}
}
}
return ret;
}
public List<ResolveInfoGroup> getAllResolveInfoGroupByPackageName(String pkgName){
List<ResolveInfo> allri = getAllResolveInfoByPackageName(pkgName);
if(allri == null || allri.size() <= 0){
return null;
}
SameGroupComparator sgc = new SameGroupComparator();
Collections.sort(allri, sgc);
List<ResolveInfoGroup> ret = new ArrayList<ResolveInfoGroup>();
ret.add(new ResolveInfoGroup(mContext));
ret.get(0).add(allri.get(0));
for(int i = 1; i < allri.size(); ++ i){
if(sgc.compare(allri.get(i - 1), allri.get(i)) != 0){
ret.add(new ResolveInfoGroup(mContext));
}
ret.get(ret.size() - 1).add(allri.get(i));
}
return ret;
}
public List<ResolveInfo> getAllResolveInfoByPackageName(String packageName) {
List<ResolveInfo> ret = new ArrayList<ResolveInfo>();
for (String action : ACTIONS) {
Intent intent = new Intent(action);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setType("*/*");
intent.setPackage(packageName);
List<ResolveInfo> infos = mContext.getPackageManager().queryIntentActivities(intent, 0);
for (ResolveInfo ri : infos) {
if (sBlackList.contains(ri.activityInfo.packageName)) {
// NA
} else {
Pair<String, String> comp = new Pair<String, String>(ri.activityInfo.packageName, ri.activityInfo.name);
if (sBlackCompList.contains(comp)) {
// NA
} else {
ret.add(ri);
}
}
}
}
ListUtils.getDistinctList(ret, new MyComparator());
return ret;
}
public void onPackageRemoved(String packageName){
synchronized (mList) {
for (int i = 0; i < mList.size(); ++i) {
if (mList.get(i).getPackageName().equals(packageName)) {
mHandler.obtainMessage(MSG_DELETE, mList.get(i)).sendToTarget();
mList.remove(i);
i--;
}
}
}
notifyUpdate();
}
public void onPackageAdded(String packageName) {
List<ResolveInfoGroup> rigList = getAllResolveInfoGroupByPackageName(packageName);
if (rigList == null) {
// no needed resolveinfo
return;
}
// see component list first
boolean addComponent = false;
for (int i = 0; i < sAutoAddComponentList.size(); ++i) {
ComponentName cn = sAutoAddComponentList.get(i);
if (cn.getPackageName().equals(packageName)) {
for (ResolveInfoGroup rig : rigList) {
if (rig.containsComponent(cn)) {
addResolveInfoGroupInternal(rig, false);
addComponent = true;
break;
}
}
}
}
if (!addComponent && sAutoAddPackageList.contains(packageName)) {
for (ResolveInfoGroup rig : rigList) {
addResolveInfoGroupInternal(rig, false);
}
}
}
public void onPackageUpdate(String packageName) {
if (!TextUtils.isEmpty(packageName)) {
boolean changed = false;
synchronized (mList) {
for (int i = 0; i < mList.size(); ++i) {
ResolveInfoGroup rig = mList.get(i);
if (packageName.equals(rig.getPackageName())) {
if (!rig.isValid()) {
mList.remove(i);
i--;
mHandler.obtainMessage(MSG_DELETE, rig).sendToTarget();
changed = true;
}
}
}
}
if (changed) {
notifyUpdate();
}
}
}
public static class MyComparator implements Comparator<ResolveInfo> {
public final int compare(ResolveInfo a, ResolveInfo b) {
String pkgA = a.activityInfo.packageName;
String pkgB = b.activityInfo.packageName;
if (!pkgA.equals(pkgB)) {
return pkgA.compareTo(pkgB);
}
String nameA = a.activityInfo.name;
String nameB = b.activityInfo.name;
if (!nameA.equals(nameB)) {
return nameA.compareTo(nameB);
}
int iconA = a.getIconResource();
int iconB = b.getIconResource();
if (iconA != iconB){
if(iconA < iconB){
return -1;
}else{
return 1;
}
}else{
return 0;
}
}
}
private static final String TABLE_RESOLVEINFO = "resolveinfo";
static class ResolveInfoColumns implements BaseColumns{
static final String PACKAGE_NAME = "packagename";
static final String COMPONENT_NAMES = "names";
static final String WEIGHT = "weight";
}
public interface ResolveInfoUpdateListener{
void onUpdate();
}
private static final int MSG_SAVE = 0;
private static final int MSG_DELETE = 1;
private static final int MSG_UPDATE_LIST = 2;
private static final int MSG_SAVE_ORDER = 3;
private class ResolveInfoManagerHandler extends Handler {
public ResolveInfoManagerHandler(Looper looper) {
super(looper, null, false);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAVE:
saveToDatabase((ResolveInfoGroup) msg.obj);
break;
case MSG_DELETE:
deleteFromDatabase((ResolveInfoGroup) msg.obj);
break;
case MSG_UPDATE_LIST:
updateComponentList();
break;
case MSG_SAVE_ORDER:
saveOrderForList();
break;
}
}
}
public void onIconChanged(Set<String> packages){
synchronized(mList){
for(ResolveInfoGroup rig : mList){
if(packages.contains(rig.getPackageName())){
rig.onIconChanged();
}
}
}
notifyUpdate();
}
}