diff options
-rw-r--r-- | api/current.txt | 5 | ||||
-rw-r--r-- | core/java/android/widget/ActivityChooserModel.java | 59 | ||||
-rw-r--r-- | core/java/android/widget/ActivityChooserView.java | 35 | ||||
-rw-r--r-- | core/java/android/widget/AdapterView.java | 3 | ||||
-rw-r--r-- | core/java/android/widget/ShareActionProvider.java | 75 |
5 files changed, 168 insertions, 9 deletions
diff --git a/api/current.txt b/api/current.txt index 1180440..861677c 100644 --- a/api/current.txt +++ b/api/current.txt @@ -26480,11 +26480,16 @@ package android.widget { public class ShareActionProvider extends android.view.ActionProvider { ctor public ShareActionProvider(android.content.Context); method public android.view.View onCreateActionView(); + method public void setOnShareTargetSelectedListener(android.widget.ShareActionProvider.OnShareTargetSelectedListener); method public void setShareHistoryFileName(java.lang.String); method public void setShareIntent(android.content.Intent); field public static final java.lang.String DEFAULT_SHARE_HISTORY_FILE_NAME = "share_history.xml"; } + public static abstract interface ShareActionProvider.OnShareTargetSelectedListener { + method public abstract boolean onShareTargetSelected(android.widget.ShareActionProvider, android.content.Intent); + } + public class SimpleAdapter extends android.widget.BaseAdapter implements android.widget.Filterable { ctor public SimpleAdapter(android.content.Context, java.util.List<? extends java.util.Map<java.lang.String, ?>>, int, java.lang.String[], int[]); method public int getCount(); diff --git a/core/java/android/widget/ActivityChooserModel.java b/core/java/android/widget/ActivityChooserModel.java index 4b0a6da..9fea506 100644 --- a/core/java/android/widget/ActivityChooserModel.java +++ b/core/java/android/widget/ActivityChooserModel.java @@ -131,6 +131,30 @@ public class ActivityChooserModel extends DataSetObservable { } /** + * Listener for choosing an activity. + */ + public interface OnChooseActivityListener { + + /** + * Called when an activity has been chosen. The client can decide whether + * an activity can be chosen and if so the caller of + * {@link ActivityChooserModel#chooseActivity(int)} will receive and {@link Intent} + * for launching it. + * <p> + * <strong>Note:</strong> Modifying the intent is not permitted and + * any changes to the latter will be ignored. + * </p> + * + * @param host The listener's host model. + * @param intent The intent for launching the chosen activity. + * @return Whether the intent is handled and should not be delivered to clients. + * + * @see ActivityChooserModel#chooseActivity(int) + */ + public boolean onChooseActivity(ActivityChooserModel host, Intent intent); + } + + /** * Flag for selecting debug mode. */ private static final boolean DEBUG = false; @@ -287,6 +311,11 @@ public class ActivityChooserModel extends DataSetObservable { private final Handler mHandler = new Handler(); /** + * Policy for controlling how the model handles chosen activities. + */ + private OnChooseActivityListener mActivityChoserModelPolicy; + + /** * 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 @@ -426,9 +455,11 @@ public class ActivityChooserModel extends DataSetObservable { * the client solely to let additional customization before the start. * </p> * - * @return Whether adding succeeded. + * @return An {@link Intent} for launching the activity or null if the + * policy has consumed the intent. * * @see HistoricalRecord + * @see OnChooseActivityListener */ public Intent chooseActivity(int index) { ActivityResolveInfo chosenActivity = mActivites.get(index); @@ -436,17 +467,37 @@ public class ActivityChooserModel extends DataSetObservable { ComponentName chosenName = new ComponentName( chosenActivity.resolveInfo.activityInfo.packageName, chosenActivity.resolveInfo.activityInfo.name); - HistoricalRecord historicalRecord = new HistoricalRecord(chosenName, - System.currentTimeMillis(), DEFAULT_HISTORICAL_RECORD_WEIGHT); - addHisoricalRecord(historicalRecord); 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; } /** + * Sets the listener for choosing an activity. + * + * @param listener The listener. + */ + public void setOnChooseActivityListener(OnChooseActivityListener listener) { + mActivityChoserModelPolicy = listener; + } + + /** * 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. diff --git a/core/java/android/widget/ActivityChooserView.java b/core/java/android/widget/ActivityChooserView.java index 45d73af..556414c 100644 --- a/core/java/android/widget/ActivityChooserView.java +++ b/core/java/android/widget/ActivityChooserView.java @@ -29,6 +29,8 @@ import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.widget.ActivityChooserModel.ActivityChooserModelClient; import com.android.internal.R; @@ -115,6 +117,19 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod } }; + private final OnGlobalLayoutListener mOnGlobalLayoutListener = new OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + if (isShowingPopup()) { + if (!isShown()) { + getListPopupWindow().dismiss(); + } else { + getListPopupWindow().show(); + } + } + } + }; + /** * Popup window for showing the activity overflow list. */ @@ -261,6 +276,8 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod throw new IllegalStateException("No data model. Did you call #setDataModel?"); } + getViewTreeObserver().addOnGlobalLayoutListener(mOnGlobalLayoutListener); + mAdapter.setMaxActivityCount(maxActivityCount); final int activityCount = mAdapter.getActivityCount(); @@ -292,6 +309,10 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod public boolean dismissPopup() { if (isShowingPopup()) { getListPopupWindow().dismiss(); + ViewTreeObserver viewTreeObserver = getViewTreeObserver(); + if (viewTreeObserver.isAlive()) { + viewTreeObserver.removeGlobalOnLayoutListener(mOnGlobalLayoutListener); + } } return true; } @@ -322,6 +343,10 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod if (dataModel != null) { dataModel.unregisterObserver(mModelDataSetOberver); } + ViewTreeObserver viewTreeObserver = getViewTreeObserver(); + if (viewTreeObserver.isAlive()) { + viewTreeObserver.removeGlobalOnLayoutListener(mOnGlobalLayoutListener); + } mIsAttachedToWindow = false; } @@ -433,8 +458,10 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod } } else { // The first item in the model is default action => adjust index - Intent launchIntent = mAdapter.getDataModel().chooseActivity(position + 1); - mContext.startActivity(launchIntent); + Intent launchIntent = mAdapter.getDataModel().chooseActivity(position + 1); + if (launchIntent != null) { + mContext.startActivity(launchIntent); + } } } break; default: @@ -449,7 +476,9 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod ResolveInfo defaultActivity = mAdapter.getDefaultActivity(); final int index = mAdapter.getDataModel().getActivityIndex(defaultActivity); Intent launchIntent = mAdapter.getDataModel().chooseActivity(index); - mContext.startActivity(launchIntent); + if (launchIntent != null) { + mContext.startActivity(launchIntent); + } } else if (view == mExpandActivityOverflowButton) { mIsSelectingDefaultActivity = false; showPopupUnchecked(mInitialActivityCount); diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index 9f5737e..f267458c 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -23,11 +23,11 @@ import android.os.SystemClock; import android.util.AttributeSet; import android.util.SparseArray; import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; import android.view.SoundEffectConstants; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; -import android.view.ContextMenu.ContextMenuInfo; import android.view.accessibility.AccessibilityEvent; @@ -278,6 +278,7 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { */ public boolean performItemClick(View view, int position, long id) { view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); + if (mOnItemClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); mOnItemClickListener.onItemClick(this, view, position, id); diff --git a/core/java/android/widget/ShareActionProvider.java b/core/java/android/widget/ShareActionProvider.java index 665109a..6e29024 100644 --- a/core/java/android/widget/ShareActionProvider.java +++ b/core/java/android/widget/ShareActionProvider.java @@ -28,6 +28,7 @@ import android.view.MenuItem; import android.view.MenuItem.OnMenuItemClickListener; import android.view.SubMenu; import android.view.View; +import android.widget.ActivityChooserModel.OnChooseActivityListener; import com.android.internal.R; @@ -73,6 +74,27 @@ import com.android.internal.R; public class ShareActionProvider extends ActionProvider { /** + * Listener for the event of selecting a share target. + */ + public interface OnShareTargetSelectedListener { + + /** + * Called when a share target has been selected. The client can + * decide whether to handle the intent or rely on the default + * behavior which is launching it. + * <p> + * <strong>Note:</strong> Modifying the intent is not permitted and + * any changes to the latter will be ignored. + * </p> + * + * @param source The source of the notification. + * @param intent The intent for launching the chosen share target. + * @return Whether the client has handled the intent. + */ + public boolean onShareTargetSelected(ShareActionProvider source, Intent intent); + } + + /** * The default for the maximal number of activities shown in the sub-menu. */ private static final int DEFAULT_INITIAL_ACTIVITY_COUNT = 4; @@ -103,6 +125,10 @@ public class ShareActionProvider extends ActionProvider { */ private String mShareHistoryFileName = DEFAULT_SHARE_HISTORY_FILE_NAME; + private OnShareTargetSelectedListener mOnShareTargetSelectedListener; + + private OnChooseActivityListener mOnChooseActivityListener; + /** * Creates a new instance. * @@ -114,6 +140,21 @@ public class ShareActionProvider extends ActionProvider { } /** + * Sets a listener to be notified when a share target has been selected. + * The listener can optionally decide to handle the selection and + * not rely on the default behavior which is to launch the activity. + * <p> + * <strong>Note:</strong> If you choose the backing share history file + * you will still be notified in this callback. + * </p> + * @param listener The listener. + */ + public void setOnShareTargetSelectedListener(OnShareTargetSelectedListener listener) { + mOnShareTargetSelectedListener = listener; + setActivityChooserPolicyIfNeeded(); + } + + /** * {@inheritDoc} */ @Override @@ -192,6 +233,7 @@ public class ShareActionProvider extends ActionProvider { */ public void setShareHistoryFileName(String shareHistoryFile) { mShareHistoryFileName = shareHistoryFile; + setActivityChooserPolicyIfNeeded(); } /** @@ -229,8 +271,39 @@ public class ShareActionProvider extends ActionProvider { mShareHistoryFileName); final int itemId = item.getItemId(); Intent launchIntent = dataModel.chooseActivity(itemId); - mContext.startActivity(launchIntent); + if (launchIntent != null) { + mContext.startActivity(launchIntent); + } return true; } } + + /** + * Set the activity chooser policy of the model backed by the current + * share history file if needed which is if there is a registered callback. + */ + private void setActivityChooserPolicyIfNeeded() { + if (mOnShareTargetSelectedListener == null) { + return; + } + if (mOnChooseActivityListener == null) { + mOnChooseActivityListener = new ShareAcitivityChooserModelPolicy(); + } + ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName); + dataModel.setOnChooseActivityListener(mOnChooseActivityListener); + } + + /** + * Policy that delegates to the {@link OnShareTargetSelectedListener}, if such. + */ + private class ShareAcitivityChooserModelPolicy implements OnChooseActivityListener { + @Override + public boolean onChooseActivity(ActivityChooserModel host, Intent intent) { + if (mOnShareTargetSelectedListener != null) { + return mOnShareTargetSelectedListener.onShareTargetSelected( + ShareActionProvider.this, intent); + } + return false; + } + } } |