Merge "ActivityChooserModel does not handle package changes on the thread that created it." into jb-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
b1ae25cb37
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user