Methods Summary |
---|
private boolean | addHisoricalRecord(android.widget.ActivityChooserModel$HistoricalRecord historicalRecord)Adds a historical record.
final boolean added = mHistoricalRecords.add(historicalRecord);
if (added) {
mHistoricalRecordsChanged = true;
pruneExcessiveHistoricalRecordsIfNeeded();
persistHistoricalDataIfNeeded();
sortActivitiesIfNeeded();
notifyChanged();
}
return added;
|
public android.content.Intent | chooseActivity(int index)Chooses a activity to handle the current intent. This will result in
adding a historical record for that action and construct intent with
its component name set such that it can be immediately started by the
client.
Note: By calling this method the client guarantees
that the returned intent will be started. This intent is returned to
the client solely to let additional customization before the start.
synchronized (mInstanceLock) {
if (mIntent == null) {
return null;
}
ensureConsistentState();
ActivityResolveInfo chosenActivity = mActivities.get(index);
ComponentName chosenName = new ComponentName(
chosenActivity.resolveInfo.activityInfo.packageName,
chosenActivity.resolveInfo.activityInfo.name);
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;
}
|
private void | ensureConsistentState()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.
boolean stateChanged = loadActivitiesIfNeeded();
stateChanged |= readHistoricalDataIfNeeded();
pruneExcessiveHistoricalRecordsIfNeeded();
if (stateChanged) {
sortActivitiesIfNeeded();
notifyChanged();
}
|
protected void | finalize()
super.finalize();
mPackageMonitor.unregister();
|
public static android.widget.ActivityChooserModel | get(android.content.Context context, java.lang.String historyFileName)Gets the data model backed by the contents of the provided file with historical data.
Note that only one data model is backed by a given file, thus multiple calls with
the same file name will return the same model instance. If no such instance is present
it is created.
Note: To use the default historical data file clients should explicitly
pass as file name {@link #DEFAULT_HISTORY_FILE_NAME}. If no persistence of the choice
history is desired clients should pass null for the file name. In such
case a new model is returned for each invocation.
Always use difference historical data files for semantically different actions.
For example, sharing is different from importing.
synchronized (sRegistryLock) {
ActivityChooserModel dataModel = sDataModelRegistry.get(historyFileName);
if (dataModel == null) {
dataModel = new ActivityChooserModel(context, historyFileName);
sDataModelRegistry.put(historyFileName, dataModel);
}
return dataModel;
}
|
public android.content.pm.ResolveInfo | getActivity(int index)Gets an activity at a given index.
synchronized (mInstanceLock) {
ensureConsistentState();
return mActivities.get(index).resolveInfo;
}
|
public int | getActivityCount()Gets the number of activities that can handle the intent.
synchronized (mInstanceLock) {
ensureConsistentState();
return mActivities.size();
}
|
public int | getActivityIndex(android.content.pm.ResolveInfo activity)Gets the index of a the given activity.
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;
}
|
public android.content.pm.ResolveInfo | getDefaultActivity()Gets the default activity, The default activity is defined as the one
with highest rank i.e. the first one in the list of activities that can
handle the intent.
synchronized (mInstanceLock) {
ensureConsistentState();
if (!mActivities.isEmpty()) {
return mActivities.get(0).resolveInfo;
}
}
return null;
|
public int | getHistoryMaxSize()Gets the history max size.
synchronized (mInstanceLock) {
return mHistoryMaxSize;
}
|
public int | getHistorySize()Gets the history size.
synchronized (mInstanceLock) {
ensureConsistentState();
return mHistoricalRecords.size();
}
|
public android.content.Intent | getIntent()Gets the intent for which a activity is being chosen.
synchronized (mInstanceLock) {
return mIntent;
}
|
private boolean | loadActivitiesIfNeeded()Loads the activities for the current intent if needed which is
if they are not already loaded for the current intent.
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);
ActivityInfo activityInfo = resolveInfo.activityInfo;
if (ActivityManager.checkComponentPermission(activityInfo.permission,
android.os.Process.myUid(), activityInfo.applicationInfo.uid,
activityInfo.exported) == PackageManager.PERMISSION_GRANTED) {
mActivities.add(new ActivityResolveInfo(resolveInfo));
}
}
return true;
}
return false;
|
private void | persistHistoricalDataIfNeeded()Persists the history data to the backing file if the latter
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.
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);
}
|
private void | pruneExcessiveHistoricalRecordsIfNeeded()Prunes older excessive records to guarantee maxHistorySize.
final int pruneCount = mHistoricalRecords.size() - mHistoryMaxSize;
if (pruneCount <= 0) {
return;
}
mHistoricalRecordsChanged = true;
for (int i = 0; i < pruneCount; i++) {
HistoricalRecord prunedRecord = mHistoricalRecords.remove(0);
if (DEBUG) {
Log.i(LOG_TAG, "Pruned: " + prunedRecord);
}
}
|
private boolean | readHistoricalDataIfNeeded()Reads the historical data if necessary which is it has
changed, there is a history file, and there is not persist
in progress.
if (mCanReadHistoricalData && mHistoricalRecordsChanged &&
!TextUtils.isEmpty(mHistoryFileName)) {
mCanReadHistoricalData = false;
mReadShareHistoryCalled = true;
readHistoricalDataImpl();
return true;
}
return false;
|
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);
}
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();
}
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.");
}
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 " + readRecord.toString());
}
}
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 */
}
}
}
|
public void | setActivitySorter(android.widget.ActivityChooserModel$ActivitySorter activitySorter)Sets the sorter for ordering activities based on historical data and an intent.
synchronized (mInstanceLock) {
if (mActivitySorter == activitySorter) {
return;
}
mActivitySorter = activitySorter;
if (sortActivitiesIfNeeded()) {
notifyChanged();
}
}
|
public void | setDefaultActivity(int index)Sets the default activity. The default activity is set by adding a
historical record with weight high enough that this activity will
become the highest ranked. Such a strategy guarantees that the default
will eventually change if not used. Also the weight of the record for
setting a default is inflated with a constant amount to guarantee that
it will stay as default for awhile.
synchronized (mInstanceLock) {
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);
}
|
public void | setHistoryMaxSize(int historyMaxSize)Sets the maximal size of the historical data. Defaults to
{@link #DEFAULT_HISTORY_MAX_LENGTH}
Note: Setting this property will immediately
enforce the specified max history size by dropping enough old
historical records to enforce the desired size. Thus, any
records that exceed the history size will be discarded and
irreversibly lost.
synchronized (mInstanceLock) {
if (mHistoryMaxSize == historyMaxSize) {
return;
}
mHistoryMaxSize = historyMaxSize;
pruneExcessiveHistoricalRecordsIfNeeded();
if (sortActivitiesIfNeeded()) {
notifyChanged();
}
}
|
public void | setIntent(android.content.Intent intent)Sets an intent for which to choose a activity.
Note: Clients must set only semantically similar
intents for each data model.
synchronized (mInstanceLock) {
if (mIntent == intent) {
return;
}
mIntent = intent;
mReloadActivities = true;
ensureConsistentState();
}
|
public void | setOnChooseActivityListener(android.widget.ActivityChooserModel$OnChooseActivityListener listener)Sets the listener for choosing an activity.
synchronized (mInstanceLock) {
mActivityChoserModelPolicy = listener;
}
|
private boolean | sortActivitiesIfNeeded()Sorts the activities if necessary which is if there is a
sorter, there are some activities to sort, and there is some
historical data.
if (mActivitySorter != null && mIntent != null
&& !mActivities.isEmpty() && !mHistoricalRecords.isEmpty()) {
mActivitySorter.sort(mIntent, mActivities,
Collections.unmodifiableList(mHistoricalRecords));
return true;
}
return false;
|