Merge "ActivityChooserModel does not handle package changes on the thread that created it." into jb-dev

This commit is contained in:
Svetoslav Ganov
2012-05-07 18:23:44 -07:00
committed by Android (Google) Code Review

View File

@@ -21,9 +21,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.database.DataSetObservable;
import android.database.DataSetObserver;
import android.os.AsyncTask;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import android.util.Xml;
@@ -42,10 +40,8 @@ import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* <p>
@@ -239,7 +235,7 @@ public class ActivityChooserModel extends DataSetObservable {
/**
* List of activities that can handle the current intent.
*/
private final List<ActivityResolveInfo> mActivites = new ArrayList<ActivityResolveInfo>();
private final List<ActivityResolveInfo> mActivities = new ArrayList<ActivityResolveInfo>();
/**
* List with historical choice records.
@@ -278,18 +274,18 @@ public class ActivityChooserModel extends DataSetObservable {
/**
* Flag whether choice history can be read. In general many clients can
* share the same data model and {@link #readHistoricalData()} may be called
* share the same data model and {@link #readHistoricalDataIfNeeded()} may be called
* by arbitrary of them any number of times. Therefore, this class guarantees
* that the very first read succeeds and subsequent reads can be performed
* only after a call to {@link #persistHistoricalData()} followed by change
* only after a call to {@link #persistHistoricalDataIfNeeded()} followed by change
* of the share records.
*/
private boolean mCanReadHistoricalData = true;
/**
* Flag whether the choice history was read. This is used to enforce that
* before calling {@link #persistHistoricalData()} a call to
* {@link #persistHistoricalData()} has been made. This aims to avoid a
* before calling {@link #persistHistoricalDataIfNeeded()} a call to
* {@link #persistHistoricalDataIfNeeded()} has been made. This aims to avoid a
* scenario in which a choice history file exits, it is not read yet and
* it is overwritten. Note that always all historical records are read in
* full and the file is rewritten. This is necessary since we need to
@@ -299,16 +295,16 @@ public class ActivityChooserModel extends DataSetObservable {
/**
* Flag whether the choice records have changed. In general many clients can
* share the same data model and {@link #persistHistoricalData()} may be called
* share the same data model and {@link #persistHistoricalDataIfNeeded()} may be called
* by arbitrary of them any number of times. Therefore, this class guarantees
* that choice history will be persisted only if it has changed.
*/
private boolean mHistoricalRecordsChanged = true;
/**
* Hander for scheduling work on client tread.
* Flag whether to reload the activities for the current intent.
*/
private final Handler mHandler = new Handler();
private boolean mReloadActivities = false;
/**
* Policy for controlling how the model handles chosen activities.
@@ -346,7 +342,6 @@ public class ActivityChooserModel extends DataSetObservable {
dataModel = new ActivityChooserModel(context, historyFileName);
sDataModelRegistry.put(historyFileName, dataModel);
}
dataModel.readHistoricalData();
return dataModel;
}
}
@@ -383,7 +378,8 @@ public class ActivityChooserModel extends DataSetObservable {
return;
}
mIntent = intent;
loadActivitiesLocked();
mReloadActivities = true;
ensureConsistentState();
}
}
@@ -407,7 +403,8 @@ public class ActivityChooserModel extends DataSetObservable {
*/
public int getActivityCount() {
synchronized (mInstanceLock) {
return mActivites.size();
ensureConsistentState();
return mActivities.size();
}
}
@@ -421,7 +418,8 @@ public class ActivityChooserModel extends DataSetObservable {
*/
public ResolveInfo getActivity(int index) {
synchronized (mInstanceLock) {
return mActivites.get(index).resolveInfo;
ensureConsistentState();
return mActivities.get(index).resolveInfo;
}
}
@@ -433,15 +431,18 @@ public class ActivityChooserModel extends DataSetObservable {
* @return The index if found, -1 otherwise.
*/
public int getActivityIndex(ResolveInfo activity) {
List<ActivityResolveInfo> activities = mActivites;
final int activityCount = activities.size();
for (int i = 0; i < activityCount; i++) {
ActivityResolveInfo currentActivity = activities.get(i);
if (currentActivity.resolveInfo == activity) {
return i;
synchronized (mInstanceLock) {
ensureConsistentState();
List<ActivityResolveInfo> activities = mActivities;
final int activityCount = activities.size();
for (int i = 0; i < activityCount; i++) {
ActivityResolveInfo currentActivity = activities.get(i);
if (currentActivity.resolveInfo == activity) {
return i;
}
}
return INVALID_INDEX;
}
return INVALID_INDEX;
}
/**
@@ -462,30 +463,34 @@ public class ActivityChooserModel extends DataSetObservable {
* @see OnChooseActivityListener
*/
public Intent chooseActivity(int index) {
ActivityResolveInfo chosenActivity = mActivites.get(index);
synchronized (mInstanceLock) {
ensureConsistentState();
ComponentName chosenName = new ComponentName(
chosenActivity.resolveInfo.activityInfo.packageName,
chosenActivity.resolveInfo.activityInfo.name);
ActivityResolveInfo chosenActivity = mActivities.get(index);
Intent choiceIntent = new Intent(mIntent);
choiceIntent.setComponent(chosenName);
ComponentName chosenName = new ComponentName(
chosenActivity.resolveInfo.activityInfo.packageName,
chosenActivity.resolveInfo.activityInfo.name);
if (mActivityChoserModelPolicy != null) {
// Do not allow the policy to change the intent.
Intent choiceIntentCopy = new Intent(choiceIntent);
final boolean handled = mActivityChoserModelPolicy.onChooseActivity(this,
choiceIntentCopy);
if (handled) {
return null;
Intent choiceIntent = new Intent(mIntent);
choiceIntent.setComponent(chosenName);
if (mActivityChoserModelPolicy != null) {
// Do not allow the policy to change the intent.
Intent choiceIntentCopy = new Intent(choiceIntent);
final boolean handled = mActivityChoserModelPolicy.onChooseActivity(this,
choiceIntentCopy);
if (handled) {
return null;
}
}
HistoricalRecord historicalRecord = new HistoricalRecord(chosenName,
System.currentTimeMillis(), DEFAULT_HISTORICAL_RECORD_WEIGHT);
addHisoricalRecord(historicalRecord);
return choiceIntent;
}
HistoricalRecord historicalRecord = new HistoricalRecord(chosenName,
System.currentTimeMillis(), DEFAULT_HISTORICAL_RECORD_WEIGHT);
addHisoricalRecord(historicalRecord);
return choiceIntent;
}
/**
@@ -494,7 +499,9 @@ public class ActivityChooserModel extends DataSetObservable {
* @param listener The listener.
*/
public void setOnChooseActivityListener(OnChooseActivityListener listener) {
mActivityChoserModelPolicy = listener;
synchronized (mInstanceLock) {
mActivityChoserModelPolicy = listener;
}
}
/**
@@ -508,8 +515,9 @@ public class ActivityChooserModel extends DataSetObservable {
*/
public ResolveInfo getDefaultActivity() {
synchronized (mInstanceLock) {
if (!mActivites.isEmpty()) {
return mActivites.get(0).resolveInfo;
ensureConsistentState();
if (!mActivities.isEmpty()) {
return mActivities.get(0).resolveInfo;
}
}
return null;
@@ -526,72 +534,50 @@ public class ActivityChooserModel extends DataSetObservable {
* @param index The index of the activity to set as default.
*/
public void setDefaultActivity(int index) {
ActivityResolveInfo newDefaultActivity = mActivites.get(index);
ActivityResolveInfo oldDefaultActivity = mActivites.get(0);
final float weight;
if (oldDefaultActivity != null) {
// Add a record with weight enough to boost the chosen at the top.
weight = oldDefaultActivity.weight - newDefaultActivity.weight
+ DEFAULT_ACTIVITY_INFLATION;
} else {
weight = DEFAULT_HISTORICAL_RECORD_WEIGHT;
}
ComponentName defaultName = new ComponentName(
newDefaultActivity.resolveInfo.activityInfo.packageName,
newDefaultActivity.resolveInfo.activityInfo.name);
HistoricalRecord historicalRecord = new HistoricalRecord(defaultName,
System.currentTimeMillis(), weight);
addHisoricalRecord(historicalRecord);
}
/**
* Reads the history data from the backing file if the latter
* was provided. Calling this method more than once before a call
* to {@link #persistHistoricalData()} has been made has no effect.
* <p>
* <strong>Note:</strong> Historical data is read asynchronously and
* as soon as the reading is completed any registered
* {@link DataSetObserver}s will be notified. Also no historical
* data is read until this method is invoked.
* <p>
*/
private void readHistoricalData() {
synchronized (mInstanceLock) {
if (!mCanReadHistoricalData || !mHistoricalRecordsChanged) {
return;
}
mCanReadHistoricalData = false;
mReadShareHistoryCalled = true;
if (!TextUtils.isEmpty(mHistoryFileName)) {
AsyncTask.SERIAL_EXECUTOR.execute(new HistoryLoader());
ensureConsistentState();
ActivityResolveInfo newDefaultActivity = mActivities.get(index);
ActivityResolveInfo oldDefaultActivity = mActivities.get(0);
final float weight;
if (oldDefaultActivity != null) {
// Add a record with weight enough to boost the chosen at the top.
weight = oldDefaultActivity.weight - newDefaultActivity.weight
+ DEFAULT_ACTIVITY_INFLATION;
} else {
weight = DEFAULT_HISTORICAL_RECORD_WEIGHT;
}
ComponentName defaultName = new ComponentName(
newDefaultActivity.resolveInfo.activityInfo.packageName,
newDefaultActivity.resolveInfo.activityInfo.name);
HistoricalRecord historicalRecord = new HistoricalRecord(defaultName,
System.currentTimeMillis(), weight);
addHisoricalRecord(historicalRecord);
}
}
/**
* Persists the history data to the backing file if the latter
* was provided. Calling this method before a call to {@link #readHistoricalData()}
* was provided. Calling this method before a call to {@link #readHistoricalDataIfNeeded()}
* throws an exception. Calling this method more than one without choosing an
* activity has not effect.
*
* @throws IllegalStateException If this method is called before a call to
* {@link #readHistoricalData()}.
* {@link #readHistoricalDataIfNeeded()}.
*/
private void persistHistoricalData() {
synchronized (mInstanceLock) {
if (!mReadShareHistoryCalled) {
throw new IllegalStateException("No preceding call to #readHistoricalData");
}
if (!mHistoricalRecordsChanged) {
return;
}
mHistoricalRecordsChanged = false;
mCanReadHistoricalData = true;
if (!TextUtils.isEmpty(mHistoryFileName)) {
AsyncTask.SERIAL_EXECUTOR.execute(new HistoryPersister());
}
private void persistHistoricalDataIfNeeded() {
if (!mReadShareHistoryCalled) {
throw new IllegalStateException("No preceding call to #readHistoricalData");
}
if (!mHistoricalRecordsChanged) {
return;
}
mHistoricalRecordsChanged = false;
if (!TextUtils.isEmpty(mHistoryFileName)) {
new PersistHistoryAsyncTask().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR,
new ArrayList<HistoricalRecord>(mHistoricalRecords), mHistoryFileName);
}
}
@@ -608,21 +594,7 @@ public class ActivityChooserModel extends DataSetObservable {
return;
}
mActivitySorter = activitySorter;
sortActivities();
}
}
/**
* Sorts the activities based on history and an intent. If
* a sorter is not specified this a default implementation is used.
*
* @see #setActivitySorter(ActivitySorter)
*/
private void sortActivities() {
synchronized (mInstanceLock) {
if (mActivitySorter != null && !mActivites.isEmpty()) {
mActivitySorter.sort(mIntent, mActivites,
Collections.unmodifiableList(mHistoricalRecords));
if (sortActivitiesIfNeeded()) {
notifyChanged();
}
}
@@ -647,8 +619,10 @@ public class ActivityChooserModel extends DataSetObservable {
return;
}
mHistoryMaxSize = historyMaxSize;
pruneExcessiveHistoricalRecordsLocked();
sortActivities();
pruneExcessiveHistoricalRecordsIfNeeded();
if (sortActivitiesIfNeeded()) {
notifyChanged();
}
}
}
@@ -670,6 +644,7 @@ public class ActivityChooserModel extends DataSetObservable {
*/
public int getHistorySize() {
synchronized (mInstanceLock) {
ensureConsistentState();
return mHistoricalRecords.size();
}
}
@@ -680,6 +655,79 @@ public class ActivityChooserModel extends DataSetObservable {
mPackageMonitor.unregister();
}
/**
* Ensures the model is in a consistent state which is the
* activities for the current intent have been loaded, the
* most recent history has been read, and the activities
* are sorted.
*/
private void ensureConsistentState() {
boolean stateChanged = loadActivitiesIfNeeded();
stateChanged |= readHistoricalDataIfNeeded();
pruneExcessiveHistoricalRecordsIfNeeded();
if (stateChanged) {
sortActivitiesIfNeeded();
notifyChanged();
}
}
/**
* Sorts the activities if necessary which is if there is a
* sorter, there are some activities to sort, and there is some
* historical data.
*
* @return Whether sorting was performed.
*/
private boolean sortActivitiesIfNeeded() {
if (mActivitySorter != null && mIntent != null
&& !mActivities.isEmpty() && !mHistoricalRecords.isEmpty()) {
mActivitySorter.sort(mIntent, mActivities,
Collections.unmodifiableList(mHistoricalRecords));
return true;
}
return false;
}
/**
* Loads the activities for the current intent if needed which is
* if they are not already loaded for the current intent.
*
* @return Whether loading was performed.
*/
private boolean loadActivitiesIfNeeded() {
if (mReloadActivities && mIntent != null) {
mReloadActivities = false;
mActivities.clear();
List<ResolveInfo> resolveInfos = mContext.getPackageManager()
.queryIntentActivities(mIntent, 0);
final int resolveInfoCount = resolveInfos.size();
for (int i = 0; i < resolveInfoCount; i++) {
ResolveInfo resolveInfo = resolveInfos.get(i);
mActivities.add(new ActivityResolveInfo(resolveInfo));
}
return true;
}
return false;
}
/**
* Reads the historical data if necessary which is it has
* changed, there is a history file, and there is not persist
* in progress.
*
* @return Whether reading was performed.
*/
private boolean readHistoricalDataIfNeeded() {
if (mCanReadHistoricalData && mHistoricalRecordsChanged &&
!TextUtils.isEmpty(mHistoryFileName)) {
mCanReadHistoricalData = false;
mReadShareHistoryCalled = true;
readHistoricalDataImpl();
return true;
}
return false;
}
/**
* Adds a historical record.
*
@@ -687,79 +735,34 @@ public class ActivityChooserModel extends DataSetObservable {
* @return True if the record was added.
*/
private boolean addHisoricalRecord(HistoricalRecord historicalRecord) {
synchronized (mInstanceLock) {
final boolean added = mHistoricalRecords.add(historicalRecord);
if (added) {
mHistoricalRecordsChanged = true;
pruneExcessiveHistoricalRecordsLocked();
persistHistoricalData();
sortActivities();
}
return added;
final boolean added = mHistoricalRecords.add(historicalRecord);
if (added) {
mHistoricalRecordsChanged = true;
pruneExcessiveHistoricalRecordsIfNeeded();
persistHistoricalDataIfNeeded();
sortActivitiesIfNeeded();
notifyChanged();
}
return added;
}
/**
* Prunes older excessive records to guarantee {@link #mHistoryMaxSize}.
* Prunes older excessive records to guarantee maxHistorySize.
*/
private void pruneExcessiveHistoricalRecordsLocked() {
List<HistoricalRecord> choiceRecords = mHistoricalRecords;
final int pruneCount = choiceRecords.size() - mHistoryMaxSize;
private void pruneExcessiveHistoricalRecordsIfNeeded() {
final int pruneCount = mHistoricalRecords.size() - mHistoryMaxSize;
if (pruneCount <= 0) {
return;
}
mHistoricalRecordsChanged = true;
for (int i = 0; i < pruneCount; i++) {
HistoricalRecord prunedRecord = choiceRecords.remove(0);
HistoricalRecord prunedRecord = mHistoricalRecords.remove(0);
if (DEBUG) {
Log.i(LOG_TAG, "Pruned: " + prunedRecord);
}
}
}
/**
* Loads the activities.
*/
private void loadActivitiesLocked() {
mActivites.clear();
if (mIntent != null) {
List<ResolveInfo> resolveInfos =
mContext.getPackageManager().queryIntentActivities(mIntent, 0);
final int resolveInfoCount = resolveInfos.size();
for (int i = 0; i < resolveInfoCount; i++) {
ResolveInfo resolveInfo = resolveInfos.get(i);
mActivites.add(new ActivityResolveInfo(resolveInfo));
}
sortActivities();
} else {
notifyChanged();
}
}
/**
* Prunes historical records for a package that goes away.
*
* @param packageName The name of the package that goes away.
*/
private void pruneHistoricalRecordsForPackageLocked(String packageName) {
boolean recordsRemoved = false;
List<HistoricalRecord> historicalRecords = mHistoricalRecords;
for (int i = 0; i < historicalRecords.size(); i++) {
HistoricalRecord historicalRecord = historicalRecords.get(i);
String recordPackageName = historicalRecord.activity.getPackageName();
if (recordPackageName.equals(packageName)) {
historicalRecords.remove(historicalRecord);
recordsRemoved = true;
}
}
if (recordsRemoved) {
mHistoricalRecordsChanged = true;
sortActivities();
}
}
/**
* Gets whether the given observer is already registered.
*
@@ -974,112 +977,72 @@ public class ActivityChooserModel extends DataSetObservable {
/**
* Command for reading the historical records from a file off the UI thread.
*/
private final class HistoryLoader implements Runnable {
public void run() {
FileInputStream fis = null;
try {
fis = mContext.openFileInput(mHistoryFileName);
} catch (FileNotFoundException fnfe) {
if (DEBUG) {
Log.i(LOG_TAG, "Could not open historical records file: " + mHistoryFileName);
}
return;
private void readHistoricalDataImpl() {
FileInputStream fis = null;
try {
fis = mContext.openFileInput(mHistoryFileName);
} catch (FileNotFoundException fnfe) {
if (DEBUG) {
Log.i(LOG_TAG, "Could not open historical records file: " + mHistoryFileName);
}
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(fis, null);
return;
}
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(fis, null);
int type = XmlPullParser.START_DOCUMENT;
while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
type = parser.next();
int type = XmlPullParser.START_DOCUMENT;
while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
type = parser.next();
}
if (!TAG_HISTORICAL_RECORDS.equals(parser.getName())) {
throw new XmlPullParserException("Share records file does not start with "
+ TAG_HISTORICAL_RECORDS + " tag.");
}
List<HistoricalRecord> historicalRecords = mHistoricalRecords;
historicalRecords.clear();
while (true) {
type = parser.next();
if (type == XmlPullParser.END_DOCUMENT) {
break;
}
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String nodeName = parser.getName();
if (!TAG_HISTORICAL_RECORD.equals(nodeName)) {
throw new XmlPullParserException("Share records file not well-formed.");
}
if (!TAG_HISTORICAL_RECORDS.equals(parser.getName())) {
throw new XmlPullParserException("Share records file does not start with "
+ TAG_HISTORICAL_RECORDS + " tag.");
}
List<HistoricalRecord> readRecords = new ArrayList<HistoricalRecord>();
while (true) {
type = parser.next();
if (type == XmlPullParser.END_DOCUMENT) {
break;
}
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String nodeName = parser.getName();
if (!TAG_HISTORICAL_RECORD.equals(nodeName)) {
throw new XmlPullParserException("Share records file not well-formed.");
}
String activity = parser.getAttributeValue(null, ATTRIBUTE_ACTIVITY);
final long time =
Long.parseLong(parser.getAttributeValue(null, ATTRIBUTE_TIME));
final float weight =
Float.parseFloat(parser.getAttributeValue(null, ATTRIBUTE_WEIGHT));
HistoricalRecord readRecord = new HistoricalRecord(activity, time,
weight);
readRecords.add(readRecord);
if (DEBUG) {
Log.i(LOG_TAG, "Read " + readRecord.toString());
}
}
String activity = parser.getAttributeValue(null, ATTRIBUTE_ACTIVITY);
final long time =
Long.parseLong(parser.getAttributeValue(null, ATTRIBUTE_TIME));
final float weight =
Float.parseFloat(parser.getAttributeValue(null, ATTRIBUTE_WEIGHT));
HistoricalRecord readRecord = new HistoricalRecord(activity, time, weight);
historicalRecords.add(readRecord);
if (DEBUG) {
Log.i(LOG_TAG, "Read " + readRecords.size() + " historical records.");
Log.i(LOG_TAG, "Read " + readRecord.toString());
}
}
synchronized (mInstanceLock) {
Set<HistoricalRecord> uniqueShareRecords =
new LinkedHashSet<HistoricalRecord>(readRecords);
// Make sure no duplicates. Example: Read a file with
// one record, add one record, persist the two records,
// add a record, read the persisted records - the
// read two records should not be added again.
List<HistoricalRecord> historicalRecords = mHistoricalRecords;
final int historicalRecordsCount = historicalRecords.size();
for (int i = historicalRecordsCount - 1; i >= 0; i--) {
HistoricalRecord historicalRecord = historicalRecords.get(i);
uniqueShareRecords.add(historicalRecord);
}
if (historicalRecords.size() == uniqueShareRecords.size()) {
return;
}
// Make sure the oldest records go to the end.
historicalRecords.clear();
historicalRecords.addAll(uniqueShareRecords);
mHistoricalRecordsChanged = true;
// Do this on the client thread since the client may be on the UI
// thread, wait for data changes which happen during sorting, and
// perform UI modification based on the data change.
mHandler.post(new Runnable() {
public void run() {
pruneExcessiveHistoricalRecordsLocked();
sortActivities();
}
});
}
} catch (XmlPullParserException xppe) {
Log.e(LOG_TAG, "Error reading historical recrod file: " + mHistoryFileName, xppe);
} catch (IOException ioe) {
Log.e(LOG_TAG, "Error reading historical recrod file: " + mHistoryFileName, ioe);
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException ioe) {
/* ignore */
}
if (DEBUG) {
Log.i(LOG_TAG, "Read " + historicalRecords.size() + " historical records.");
}
} catch (XmlPullParserException xppe) {
Log.e(LOG_TAG, "Error reading historical recrod file: " + mHistoryFileName, xppe);
} catch (IOException ioe) {
Log.e(LOG_TAG, "Error reading historical recrod file: " + mHistoryFileName, ioe);
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException ioe) {
/* ignore */
}
}
}
@@ -1088,21 +1051,21 @@ public class ActivityChooserModel extends DataSetObservable {
/**
* Command for persisting the historical records to a file off the UI thread.
*/
private final class HistoryPersister implements Runnable {
private final class PersistHistoryAsyncTask extends AsyncTask<Object, Void, Void> {
@Override
@SuppressWarnings("unchecked")
public Void doInBackground(Object... args) {
List<HistoricalRecord> historicalRecords = (List<HistoricalRecord>) args[0];
String hostoryFileName = (String) args[1];
public void run() {
FileOutputStream fos = null;
List<HistoricalRecord> records = null;
synchronized (mInstanceLock) {
records = new ArrayList<HistoricalRecord>(mHistoricalRecords);
}
try {
fos = mContext.openFileOutput(mHistoryFileName, Context.MODE_PRIVATE);
fos = mContext.openFileOutput(hostoryFileName, Context.MODE_PRIVATE);
} catch (FileNotFoundException fnfe) {
Log.e(LOG_TAG, "Error writing historical recrod file: " + mHistoryFileName, fnfe);
return;
Log.e(LOG_TAG, "Error writing historical recrod file: " + hostoryFileName, fnfe);
return null;
}
XmlSerializer serializer = Xml.newSerializer();
@@ -1112,11 +1075,12 @@ public class ActivityChooserModel extends DataSetObservable {
serializer.startDocument("UTF-8", true);
serializer.startTag(null, TAG_HISTORICAL_RECORDS);
final int recordCount = records.size();
final int recordCount = historicalRecords.size();
for (int i = 0; i < recordCount; i++) {
HistoricalRecord record = records.remove(0);
HistoricalRecord record = historicalRecords.remove(0);
serializer.startTag(null, TAG_HISTORICAL_RECORD);
serializer.attribute(null, ATTRIBUTE_ACTIVITY, record.activity.flattenToString());
serializer.attribute(null, ATTRIBUTE_ACTIVITY,
record.activity.flattenToString());
serializer.attribute(null, ATTRIBUTE_TIME, String.valueOf(record.time));
serializer.attribute(null, ATTRIBUTE_WEIGHT, String.valueOf(record.weight));
serializer.endTag(null, TAG_HISTORICAL_RECORD);
@@ -1138,6 +1102,7 @@ public class ActivityChooserModel extends DataSetObservable {
} catch (IOException ioe) {
Log.e(LOG_TAG, "Error writing historical recrod file: " + mHistoryFileName, ioe);
} finally {
mCanReadHistoricalData = true;
if (fos != null) {
try {
fos.close();
@@ -1146,6 +1111,7 @@ public class ActivityChooserModel extends DataSetObservable {
}
}
}
return null;
}
}
@@ -1155,33 +1121,8 @@ public class ActivityChooserModel extends DataSetObservable {
private final class DataModelPackageMonitor extends PackageMonitor {
@Override
public void onPackageAdded(String packageName, int uid) {
synchronized (mInstanceLock) {
loadActivitiesLocked();
}
}
@Override
public void onPackageAppeared(String packageName, int reason) {
synchronized (mInstanceLock) {
loadActivitiesLocked();
}
}
@Override
public void onPackageRemoved(String packageName, int uid) {
synchronized (mInstanceLock) {
pruneHistoricalRecordsForPackageLocked(packageName);
loadActivitiesLocked();
}
}
@Override
public void onPackageDisappeared(String packageName, int reason) {
synchronized (mInstanceLock) {
pruneHistoricalRecordsForPackageLocked(packageName);
loadActivitiesLocked();
}
public void onSomePackagesChanged() {
mReloadActivities = true;
}
}
}