package com.nolanlawson.apptracker;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;
import com.nolanlawson.apptracker.data.LoadedAppHistoryAdapter;
import com.nolanlawson.apptracker.data.LoadedAppHistoryEntry;
import com.nolanlawson.apptracker.db.AppHistoryDbHelper;
import com.nolanlawson.apptracker.db.AppHistoryEntry;
import com.nolanlawson.apptracker.db.SortType;
import com.nolanlawson.apptracker.helper.ActivityInfoHelper;
import com.nolanlawson.apptracker.helper.PreferenceHelper;
import com.nolanlawson.apptracker.helper.ServiceHelper;
import com.nolanlawson.apptracker.util.ArrayUtil;
import com.nolanlawson.apptracker.util.Pair;
import com.nolanlawson.apptracker.util.UtilLogger;
public class AppTrackerActivity extends Activity implements OnClickListener, OnItemClickListener {
public static String ACTION_EXCLUDE_APPS = "com.nolanlawson.apptracker.action.EXCLUDE_APPS";
private static final int LOAD_BATCH_SIZE = 4; // how many apps to put in the list at once
private static UtilLogger log = new UtilLogger(AppTrackerActivity.class);
private boolean listLoading = false;
private LinearLayout buttonsLinearLayout, headerLinearLayout;
private Button mainButton;
private TextView headerDescriptionTextView;
private ListView listView;
private LoadedAppHistoryAdapter adapter;
private SortType sortType = SortType.Recent;
private boolean excludeAppsMode = false;
private Handler handler = new Handler(Looper.getMainLooper());
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
log.d("onCreate()");
Intent intent = getIntent();
if (intent != null && intent.getAction() != null && intent.getAction().equals(ACTION_EXCLUDE_APPS)) {
excludeAppsMode = true;
setTitle(R.string.apps_to_exclude_title);
}
setContentView(R.layout.main);
setUpWidgets(false);
adapter = new LoadedAppHistoryAdapter(
this, R.layout.app_history_item, new ArrayList<LoadedAppHistoryEntry>(), sortType, excludeAppsMode);
}
@Override
protected void onResume() {
super.onResume();
log.d("onResume()");
ServiceHelper.startBackgroundServiceIfNotAlreadyRunning(getApplicationContext());
setUpList();
if (excludeAppsMode) {
// we're in "exclude apps" mode, so set this up appropriately
setUpAsExcludeAppsMode();
}
showFirstRunDialog();
}
@Override
protected void onPause() {
super.onPause();
log.d("onPause()");
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
log.d("onWindowFocusChanged()");
}
@Override
protected void onDestroy() {
super.onDestroy();
log.d("onDestroy()");
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
log.d("onConfigurationChanged()");
int indexToView = 0;
if (listView.getCount() > 0) {
indexToView = listView.getFirstVisiblePosition();
}
// just redraw the widgets while doing as little work as possible
setContentView(R.layout.main);
setUpWidgets(true);
listView.setAdapter(adapter);
// reset the list to the specified index
if (listView.getCount() > indexToView) {
listView.setSelection(indexToView);
}
if (excludeAppsMode) {
// we're in "exclude apps" mode, so set this up appropriately
setUpAsExcludeAppsMode();
}
changeButtonAndHeaderData();
}
@Override
public void onItemClick(AdapterView<?> l, View v, int position, long id) {
final LoadedAppHistoryEntry appHistoryEntry = adapter.getItem(position);
if (excludeAppsMode) {
// just check the box if we're in exclude apps mode
CheckBox checkBox = (CheckBox) v.findViewById(R.id.app_history_list_check_box);
checkBox.performClick();
} else {
// otherwise give a choice of options
AlertDialog.Builder builder = new AlertDialog.Builder(this);
final String[] choices = getResources().getStringArray(R.array.app_touch_choices);
builder.setCancelable(true)
.setSingleChoiceItems(R.array.app_touch_choices, -1, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (choices[which].equals(getApplicationContext().getText(R.string.choice_exclude))) {
// exclude
AppHistoryDbHelper dbHelper = new AppHistoryDbHelper(getApplicationContext());
try {
synchronized (AppHistoryDbHelper.class) {
dbHelper.setExcluded(appHistoryEntry.getAppHistoryEntry().getId(), true);
}
} finally {
dbHelper.close();
}
adapter.remove(appHistoryEntry);
} else if (choices[which].equals(getApplicationContext().getText(R.string.choice_uninstall))) {
// uninstall
Uri packageURI = Uri.fromParts("package",appHistoryEntry.getAppHistoryEntry().getPackageName(), null);
Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, packageURI);
startActivity(uninstallIntent);
} else {
// launch
Intent intent = appHistoryEntry.getAppHistoryEntry().toIntent();
startActivity(intent);
}
dialog.dismiss();
}
})
.setTitle(appHistoryEntry.getTitle())
.setIcon(new BitmapDrawable(appHistoryEntry.getIconBitmap()))
.show();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_refresh:
setUpList();
break;
case R.id.menu_settings:
Intent settingsIntent = new Intent(this, SettingsActivity.class);
startActivity(settingsIntent);
break;
case R.id.menu_user_guide:
Intent userGuideIntent = new Intent(this, HtmlFileActivity.class);
userGuideIntent.setAction(HtmlFileActivity.ACTION_USER_GUIDE);
startActivity(userGuideIntent);
break;
case R.id.menu_about:
Intent aboutIntent = new Intent(this, HtmlFileActivity.class);
aboutIntent.setAction(HtmlFileActivity.ACTION_ABOUT);
startActivity(aboutIntent);
break;
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
// no options if exclude apps mode
if (excludeAppsMode) {
for (int i = 0; i < menu.size(); i++) {
menu.getItem(i).setVisible(false);
}
}
return super.onPrepareOptionsMenu(menu);
}
private void setUpAsExcludeAppsMode() {
buttonsLinearLayout.setVisibility(View.GONE);
}
private void setUpWidgets(boolean listAlreadyLoaded) {
headerLinearLayout = (LinearLayout) findViewById(R.id.apps_header_layout);
buttonsLinearLayout = (LinearLayout) findViewById(R.id.main_buttons_linear_layout);
mainButton = (Button) findViewById(R.id.main_button);
mainButton.setOnClickListener(this);
headerDescriptionTextView = (TextView) headerLinearLayout.findViewById(R.id.app_history_list_description);
listView = (ListView) findViewById(android.R.id.list);
listView.setOnItemClickListener(this);
changeButtonAndHeaderData();
}
private void showFirstRunDialog() {
boolean isFirstRun = PreferenceHelper.getFirstRunPreference(getApplicationContext());
if (isFirstRun) {
AlertDialog.Builder builder = new Builder(this);
builder.setMessage(R.string.first_run_message);
builder.setCancelable(false);
builder.setIcon(R.drawable.holmes_icon);
builder.setTitle(R.string.first_run_title);
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
PreferenceHelper.setFirstRunPreference(getApplicationContext(), false);
}
});
builder.create().show();
}
}
private void setUpList() {
synchronized (AppTrackerActivity.class) {
if (listLoading) {
return; // somebody else already doing it
} else {
listLoading = true; // we're doing it
}
}
adapter.clear();
// set up the list in the background with a "loading" progress because it's slooooow
final Context context = getApplicationContext();
LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final View progressView = layoutInflater.inflate(R.layout.progress_footer, null);
listView.addFooterView(progressView, null, false);
listView.setAdapter(adapter);
AsyncTask<Void, LoadedAppHistoryEntry, Void> task =
new AsyncTask<Void, LoadedAppHistoryEntry, Void>(){
@Override
protected Void doInBackground(Void... params) {
AppHistoryDbHelper dbHelper = new AppHistoryDbHelper(getApplicationContext());
try {
PackageManager packageManager = getPackageManager();
List<Pair<AppHistoryEntry, ActivityInfo>> pairs =
ActivityInfoHelper.getActivityInfos(context, dbHelper, packageManager,0, Integer.MAX_VALUE, SortType.Recent, excludeAppsMode);
List<LoadedAppHistoryEntry> entryList = new ArrayList<LoadedAppHistoryEntry>();
for (int i = 0; i < pairs.size(); i++) {
Pair<AppHistoryEntry, ActivityInfo> pair = pairs.get(i);
LoadedAppHistoryEntry loadedEntry = LoadedAppHistoryEntry.fromAppHistoryEntry(
pair.getFirst(), pair.getSecond(), packageManager, getApplicationContext());
entryList.add(loadedEntry);
// batch the updates for a smoother-looking UI
if (entryList.size() == LOAD_BATCH_SIZE || i == pairs.size() - 1) {
publishProgress(entryList.toArray(new LoadedAppHistoryEntry[entryList.size()]));
entryList.clear();
}
}
return null;
} finally {
dbHelper.close();
}
}
@Override
protected void onProgressUpdate(LoadedAppHistoryEntry... values) {
super.onProgressUpdate(values);
adapter.setNotifyOnChange(false);
for (LoadedAppHistoryEntry entry : values) {
adapter.add(entry);
}
adapter.sort(excludeAppsMode ? LoadedAppHistoryEntry.orderByLabel() : LoadedAppHistoryEntry.orderBy(sortType));
adapter.notifyDataSetChanged();
}
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
listView.removeFooterView(progressView);
synchronized (AppTrackerActivity.class) {
listLoading = false;
}
}
};
task.execute((Void)null);
}
private void changeSortType(SortType newSortType) {
sortType = newSortType;
adapter.setSortType(sortType);
adapter.sort(excludeAppsMode ? LoadedAppHistoryEntry.orderByLabel() : LoadedAppHistoryEntry.orderBy(sortType));
adapter.notifyDataSetChanged();
// reset the list to the beginning
handler.post(new Runnable() {
@Override
public void run() {
if (listView.getCount() > 0) {
listView.setSelection(0);
}
}
});
changeButtonAndHeaderData();
}
private void changeButtonAndHeaderData() {
String[] printableSortTypes = getResources().getStringArray(R.array.sort_type_display_list);
Drawable drawable = getResources().getDrawable(SortType.getDrawableIcon(sortType));
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
WindowManager windowManager = getWindowManager();
boolean portrait = windowManager.getDefaultDisplay().getWidth() < windowManager.getDefaultDisplay().getHeight();
if (portrait) {
mainButton.setCompoundDrawables(drawable, null, null, null);
} else { // landscape
mainButton.setCompoundDrawables(null, drawable, null, null);
}
mainButton.setText(printableSortTypes[sortType.ordinal()]);
if (excludeAppsMode) {
headerDescriptionTextView.setText(R.string.app_exclude_text);
} else {
String[] descriptions = getResources().getStringArray(R.array.sort_type_descriptions);
headerDescriptionTextView.setText(descriptions[sortType.ordinal()]);
}
}
@Override
public void onClick(View v) {
String[] entries = getResources().getStringArray(R.array.sort_type_display_list);
// disable Alphabetic for now
entries = ArrayUtil.copyOf(entries, entries.length - 1);
new AlertDialog.Builder(this)
.setTitle(R.string.choose_sort_type)
.setCancelable(true)
.setSingleChoiceItems(entries, sortType.ordinal(), new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
changeSortType(SortType.values()[which]);
dialog.dismiss();
}
})
.show();
}
}