package com.nolanlawson.apptracker.helper;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import com.nolanlawson.apptracker.db.AppHistoryDbHelper;
import com.nolanlawson.apptracker.db.AppHistoryEntry;
import com.nolanlawson.apptracker.db.SortType;
import com.nolanlawson.apptracker.util.Pair;
import com.nolanlawson.apptracker.util.StopWatch;
import com.nolanlawson.apptracker.util.UtilLogger;
public class ActivityInfoHelper {
private static UtilLogger log = new UtilLogger(ActivityInfoHelper.class);
private static int sIconSize = -1;
public static String loadLabelFromAppHistoryEntry(Context context, AppHistoryEntry appHistoryEntry,
ActivityInfo activityInfo, PackageManager packageManager) {
//StopWatch stopWatch = new StopWatch("loadLabelFromAppHistoryEntry()");
String label;
if (appHistoryEntry.getLabel() == null) {
// not loaded yet; load now
CharSequence labelAsCharSequence = activityInfo.loadLabel(packageManager);
label = labelAsCharSequence.toString();
AppHistoryDbHelper dbHelper = new AppHistoryDbHelper(context);
try {
synchronized (AppHistoryDbHelper.class) {
dbHelper.setLabel(appHistoryEntry.getId(), label);
}
} finally {
dbHelper.close();
}
} else {
// already loaded
label = appHistoryEntry.getLabel();
}
//stopWatch.log(log);
return label;
}
public static Bitmap loadIconFromAppHistoryEntry(Context context, AppHistoryEntry appHistoryEntry,
ActivityInfo activityInfo, PackageManager packageManager) {
//StopWatch stopWatch = new StopWatch("loadIconFromAppHistoryEntry()");
Bitmap iconBitmap;
if (appHistoryEntry.getIconBlob() == null) {
// icon has not been loaded into db yet; load now
Drawable iconDrawable = activityInfo.loadIcon(packageManager);
iconBitmap = convertIconToBitmap(context, iconDrawable);
// only cache if the user wants us to cache the icon
if (PreferenceHelper.getEnableIconCachingPreference(context)) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
iconBitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
byte[] bytes = out.toByteArray();
AppHistoryDbHelper dbHelper = new AppHistoryDbHelper(context);
try {
synchronized (AppHistoryDbHelper.class) {
dbHelper.setIconBlob(appHistoryEntry.getId(), bytes);
}
} finally {
dbHelper.close();
}
}
} else {
// icon already loaded; just get it from the database
byte[] blob = appHistoryEntry.getIconBlob();
iconBitmap = BitmapFactory.decodeByteArray(blob, 0, blob.length);
}
//stopWatch.log(log);
return iconBitmap;
}
/**
* Get the package infos for each app history entry fetched from the db, and check
* to see if they were uninstalled or not.
* @param context
* @param appWidgetId
* @param dbHelper
* @param packageManager
* @param pageNumber
* @param sortType
* @return
*/
public static List<Pair<AppHistoryEntry,ActivityInfo>> getActivityInfos(
Context context, AppHistoryDbHelper dbHelper,
PackageManager packageManager, int pageNumber, int limit, SortType sortType,
boolean showExcludedApps) {
log.d("getActivityInfos(), pageNumber: %d, limit: %d, sortType: %s, showExcludedApps: %s",
pageNumber, limit, sortType, showExcludedApps);
if (sortType == SortType.Alphabetic) {
// have to be sure to load all the labels
loadLabelsInAdvance(context, dbHelper, packageManager);
}
List<Pair<AppHistoryEntry,ActivityInfo>> activityInfos = new ArrayList<Pair<AppHistoryEntry,ActivityInfo>>();
List<AppHistoryEntry> appHistories;
mainloop: while (true) {
StopWatch stopWatch = new StopWatch("findInstalledAppHistoryEntries()");
synchronized (AppHistoryDbHelper.class) {
appHistories = dbHelper.findInstalledAppHistoryEntries(sortType, limit,
pageNumber * limit, showExcludedApps);
}
stopWatch.log(log);
for (AppHistoryEntry appHistory : appHistories) {
ActivityInfo activityInfo = getActivityInfo(packageManager, appHistory);
if (activityInfo == null) { // uninstalled
synchronized (AppHistoryDbHelper.class) {
// update the database to reflect that the app is uninstalled
dbHelper.setInstalled(appHistory.getId(), false);
}
activityInfos.clear();
// try to select from the database again, while skipping the uninstalled one
continue mainloop;
}
activityInfos.add(new Pair<AppHistoryEntry,ActivityInfo>(appHistory, activityInfo));
}
break;
}
log.d("Received %d appHistories", appHistories.size());
if (appHistories.isEmpty()) {
log.d("No app history entries yet; canceling update");
return Collections.emptyList();
}
return activityInfos;
}
private static void loadLabelsInAdvance(Context context,
AppHistoryDbHelper dbHelper, PackageManager packageManager) {
log.d("loading labels in advance");
// if the sort type is alphabetic, then we have to make sure that all the
// labels are loaded in the database first
List<AppHistoryEntry> labellessEntries = null;
synchronized (AppHistoryDbHelper.class) {
labellessEntries = dbHelper.findInstalledAppHistoryEntriesWithNullLabels();
}
log.d("found %d labelless app history entries", labellessEntries.size());
for (AppHistoryEntry appHistoryEntry : labellessEntries) {
ActivityInfo activityInfo = getActivityInfo(packageManager, appHistoryEntry);
loadLabelFromAppHistoryEntry(context, appHistoryEntry, activityInfo, packageManager);
}
}
private static ActivityInfo getActivityInfo(
PackageManager packageManager,
AppHistoryEntry appHistoryEntry) {
ActivityInfo activityInfo = null;
ComponentName componentName = appHistoryEntry.toComponentName();
try {
activityInfo = packageManager.getActivityInfo(componentName, 0);
} catch (NameNotFoundException e) {
log.e(e, "package no longer installed: %s and %s", appHistoryEntry, componentName);
}
return activityInfo;
}
private static Bitmap convertIconToBitmap(Context context, Drawable drawable) {
if (sIconSize == -1) {
sIconSize = context.getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
}
return toBitmap(drawable, sIconSize, sIconSize);
}
private static Bitmap toBitmap(Drawable drawable, int width, int height) {
Bitmap bmp = Bitmap.createBitmap(width, height, Config.ARGB_8888);
Canvas c = new Canvas(bmp);
drawable.setBounds(new Rect(0,0,width,height));
drawable.draw(c);
return bmp;
}
}