Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.widget;
     18 
     19 import android.content.Context;
     20 import android.content.Intent;
     21 import android.content.pm.PackageManager;
     22 import android.content.pm.ResolveInfo;
     23 import android.graphics.drawable.Drawable;
     24 import android.util.TypedValue;
     25 import android.view.ActionProvider;
     26 import android.view.Menu;
     27 import android.view.MenuItem;
     28 import android.view.MenuItem.OnMenuItemClickListener;
     29 import android.view.SubMenu;
     30 import android.view.View;
     31 import android.widget.ActivityChooserModel.OnChooseActivityListener;
     32 
     33 import com.android.internal.R;
     34 
     35 /**
     36  * This is a provider for a share action. It is responsible for creating views
     37  * that enable data sharing and also to show a sub menu with sharing activities
     38  * if the hosting item is placed on the overflow menu.
     39  * <p>
     40  * Here is how to use the action provider with custom backing file in a {@link MenuItem}:
     41  * </p>
     42  * <p>
     43  * <pre>
     44  * <code>
     45  *  // In Activity#onCreateOptionsMenu
     46  *  public boolean onCreateOptionsMenu(Menu menu) {
     47  *      // Get the menu item.
     48  *      MenuItem menuItem = menu.findItem(R.id.my_menu_item);
     49  *      // Get the provider and hold onto it to set/change the share intent.
     50  *      mShareActionProvider = (ShareActionProvider) menuItem.getActionProvider();
     51  *      // Set history different from the default before getting the action
     52  *      // view since a call to {@link MenuItem#getActionView() MenuItem.getActionView()} calls
     53  *      // {@link ActionProvider#onCreateActionView()} which uses the backing file name. Omit this
     54  *      // line if using the default share history file is desired.
     55  *      mShareActionProvider.setShareHistoryFileName("custom_share_history.xml");
     56  *      . . .
     57  *  }
     58  *
     59  *  // Somewhere in the application.
     60  *  public void doShare(Intent shareIntent) {
     61  *      // When you want to share set the share intent.
     62  *      mShareActionProvider.setShareIntent(shareIntent);
     63  *  }
     64  * </pre>
     65  * </code>
     66  * </p>
     67  * <p>
     68  * <strong>Note:</strong> While the sample snippet demonstrates how to use this provider
     69  * in the context of a menu item, the use of the provider is not limited to menu items.
     70  * </p>
     71  *
     72  * @see ActionProvider
     73  */
     74 public class ShareActionProvider extends ActionProvider {
     75 
     76     /**
     77      * Listener for the event of selecting a share target.
     78      */
     79     public interface OnShareTargetSelectedListener {
     80 
     81         /**
     82          * Called when a share target has been selected. The client can
     83          * decide whether to handle the intent or rely on the default
     84          * behavior which is launching it.
     85          * <p>
     86          * <strong>Note:</strong> Modifying the intent is not permitted and
     87          *     any changes to the latter will be ignored.
     88          * </p>
     89          *
     90          * @param source The source of the notification.
     91          * @param intent The intent for launching the chosen share target.
     92          * @return Whether the client has handled the intent.
     93          */
     94         public boolean onShareTargetSelected(ShareActionProvider source, Intent intent);
     95     }
     96 
     97     /**
     98      * The default for the maximal number of activities shown in the sub-menu.
     99      */
    100     private static final int DEFAULT_INITIAL_ACTIVITY_COUNT = 4;
    101 
    102     /**
    103      * The the maximum number activities shown in the sub-menu.
    104      */
    105     private int mMaxShownActivityCount = DEFAULT_INITIAL_ACTIVITY_COUNT;
    106 
    107     /**
    108      * Listener for handling menu item clicks.
    109      */
    110     private final ShareMenuItemOnMenuItemClickListener mOnMenuItemClickListener =
    111         new ShareMenuItemOnMenuItemClickListener();
    112 
    113     /**
    114      * The default name for storing share history.
    115      */
    116     public static final String DEFAULT_SHARE_HISTORY_FILE_NAME = "share_history.xml";
    117 
    118     /**
    119      * Context for accessing resources.
    120      */
    121     private final Context mContext;
    122 
    123     /**
    124      * The name of the file with share history data.
    125      */
    126     private String mShareHistoryFileName = DEFAULT_SHARE_HISTORY_FILE_NAME;
    127 
    128     private OnShareTargetSelectedListener mOnShareTargetSelectedListener;
    129 
    130     private OnChooseActivityListener mOnChooseActivityListener;
    131 
    132     /**
    133      * Creates a new instance.
    134      *
    135      * @param context Context for accessing resources.
    136      */
    137     public ShareActionProvider(Context context) {
    138         super(context);
    139         mContext = context;
    140     }
    141 
    142     /**
    143      * Sets a listener to be notified when a share target has been selected.
    144      * The listener can optionally decide to handle the selection and
    145      * not rely on the default behavior which is to launch the activity.
    146      * <p>
    147      * <strong>Note:</strong> If you choose the backing share history file
    148      *     you will still be notified in this callback.
    149      * </p>
    150      * @param listener The listener.
    151      */
    152     public void setOnShareTargetSelectedListener(OnShareTargetSelectedListener listener) {
    153         mOnShareTargetSelectedListener = listener;
    154         setActivityChooserPolicyIfNeeded();
    155     }
    156 
    157     /**
    158      * {@inheritDoc}
    159      */
    160     @Override
    161     public View onCreateActionView() {
    162         // Create the view and set its data model.
    163         ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName);
    164         ActivityChooserView activityChooserView = new ActivityChooserView(mContext);
    165         activityChooserView.setActivityChooserModel(dataModel);
    166 
    167         // Lookup and set the expand action icon.
    168         TypedValue outTypedValue = new TypedValue();
    169         mContext.getTheme().resolveAttribute(R.attr.actionModeShareDrawable, outTypedValue, true);
    170         Drawable drawable = mContext.getResources().getDrawable(outTypedValue.resourceId);
    171         activityChooserView.setExpandActivityOverflowButtonDrawable(drawable);
    172         activityChooserView.setProvider(this);
    173 
    174         // Set content description.
    175         activityChooserView.setDefaultActionButtonContentDescription(
    176                 R.string.shareactionprovider_share_with_application);
    177         activityChooserView.setExpandActivityOverflowButtonContentDescription(
    178                 R.string.shareactionprovider_share_with);
    179 
    180         return activityChooserView;
    181     }
    182 
    183     /**
    184      * {@inheritDoc}
    185      */
    186     @Override
    187     public boolean hasSubMenu() {
    188         return true;
    189     }
    190 
    191     /**
    192      * {@inheritDoc}
    193      */
    194     @Override
    195     public void onPrepareSubMenu(SubMenu subMenu) {
    196         // Clear since the order of items may change.
    197         subMenu.clear();
    198 
    199         ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName);
    200         PackageManager packageManager = mContext.getPackageManager();
    201 
    202         final int expandedActivityCount = dataModel.getActivityCount();
    203         final int collapsedActivityCount = Math.min(expandedActivityCount, mMaxShownActivityCount);
    204 
    205         // Populate the sub-menu with a sub set of the activities.
    206         for (int i = 0; i < collapsedActivityCount; i++) {
    207             ResolveInfo activity = dataModel.getActivity(i);
    208             subMenu.add(0, i, i, activity.loadLabel(packageManager))
    209                 .setIcon(activity.loadIcon(packageManager))
    210                 .setOnMenuItemClickListener(mOnMenuItemClickListener);
    211         }
    212 
    213         if (collapsedActivityCount < expandedActivityCount) {
    214             // Add a sub-menu for showing all activities as a list item.
    215             SubMenu expandedSubMenu = subMenu.addSubMenu(Menu.NONE, collapsedActivityCount,
    216                     collapsedActivityCount,
    217                     mContext.getString(R.string.activity_chooser_view_see_all));
    218             for (int i = 0; i < expandedActivityCount; i++) {
    219                 ResolveInfo activity = dataModel.getActivity(i);
    220                 expandedSubMenu.add(0, i, i, activity.loadLabel(packageManager))
    221                     .setIcon(activity.loadIcon(packageManager))
    222                     .setOnMenuItemClickListener(mOnMenuItemClickListener);
    223             }
    224         }
    225     }
    226 
    227     /**
    228      * Sets the file name of a file for persisting the share history which
    229      * history will be used for ordering share targets. This file will be used
    230      * for all view created by {@link #onCreateActionView()}. Defaults to
    231      * {@link #DEFAULT_SHARE_HISTORY_FILE_NAME}. Set to <code>null</code>
    232      * if share history should not be persisted between sessions.
    233      * <p>
    234      * <strong>Note:</strong> The history file name can be set any time, however
    235      * only the action views created by {@link #onCreateActionView()} after setting
    236      * the file name will be backed by the provided file.
    237      * <p>
    238      *
    239      * @param shareHistoryFile The share history file name.
    240      */
    241     public void setShareHistoryFileName(String shareHistoryFile) {
    242         mShareHistoryFileName = shareHistoryFile;
    243         setActivityChooserPolicyIfNeeded();
    244     }
    245 
    246     /**
    247      * Sets an intent with information about the share action. Here is a
    248      * sample for constructing a share intent:
    249      * <p>
    250      * <pre>
    251      * <code>
    252      *  Intent shareIntent = new Intent(Intent.ACTION_SEND);
    253      *  shareIntent.setType("image/*");
    254      *  Uri uri = Uri.fromFile(new File(getFilesDir(), "foo.jpg"));
    255      *  shareIntent.putExtra(Intent.EXTRA_STREAM, uri.toString());
    256      * </pre>
    257      * </code>
    258      * </p>
    259      *
    260      * @param shareIntent The share intent.
    261      *
    262      * @see Intent#ACTION_SEND
    263      * @see Intent#ACTION_SEND_MULTIPLE
    264      */
    265     public void setShareIntent(Intent shareIntent) {
    266         ActivityChooserModel dataModel = ActivityChooserModel.get(mContext,
    267             mShareHistoryFileName);
    268         dataModel.setIntent(shareIntent);
    269     }
    270 
    271     /**
    272      * Reusable listener for handling share item clicks.
    273      */
    274     private class ShareMenuItemOnMenuItemClickListener implements OnMenuItemClickListener {
    275         @Override
    276         public boolean onMenuItemClick(MenuItem item) {
    277             ActivityChooserModel dataModel = ActivityChooserModel.get(mContext,
    278                     mShareHistoryFileName);
    279             final int itemId = item.getItemId();
    280             Intent launchIntent = dataModel.chooseActivity(itemId);
    281             if (launchIntent != null) {
    282                 mContext.startActivity(launchIntent);
    283             }
    284             return true;
    285         }
    286     }
    287 
    288     /**
    289      * Set the activity chooser policy of the model backed by the current
    290      * share history file if needed which is if there is a registered callback.
    291      */
    292     private void setActivityChooserPolicyIfNeeded() {
    293         if (mOnShareTargetSelectedListener == null) {
    294             return;
    295         }
    296         if (mOnChooseActivityListener == null) {
    297             mOnChooseActivityListener = new ShareAcitivityChooserModelPolicy();
    298         }
    299         ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName);
    300         dataModel.setOnChooseActivityListener(mOnChooseActivityListener);
    301     }
    302 
    303     /**
    304      * Policy that delegates to the {@link OnShareTargetSelectedListener}, if such.
    305      */
    306     private class ShareAcitivityChooserModelPolicy implements OnChooseActivityListener {
    307         @Override
    308         public boolean onChooseActivity(ActivityChooserModel host, Intent intent) {
    309             if (mOnShareTargetSelectedListener != null) {
    310                 return mOnShareTargetSelectedListener.onShareTargetSelected(
    311                         ShareActionProvider.this, intent);
    312             }
    313             return false;
    314         }
    315     }
    316 }
    317