376 lines
15 KiB
Java
376 lines
15 KiB
Java
|
/*
|
||
|
* Copyright (C) 2013 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
package android.support.v7.widget;
|
||
|
|
||
|
import android.content.Context;
|
||
|
import android.content.Intent;
|
||
|
import android.content.pm.PackageManager;
|
||
|
import android.content.pm.ResolveInfo;
|
||
|
import android.graphics.drawable.Drawable;
|
||
|
import android.os.Build;
|
||
|
import android.support.v4.view.ActionProvider;
|
||
|
import android.support.v7.appcompat.R;
|
||
|
import android.support.v7.internal.widget.ActivityChooserModel;
|
||
|
import android.support.v7.internal.widget.ActivityChooserView;
|
||
|
import android.support.v7.internal.widget.TintManager;
|
||
|
import android.util.TypedValue;
|
||
|
import android.view.Menu;
|
||
|
import android.view.MenuItem;
|
||
|
import android.view.MenuItem.OnMenuItemClickListener;
|
||
|
import android.view.SubMenu;
|
||
|
import android.view.View;
|
||
|
import android.support.v7.internal.widget.ActivityChooserModel.OnChooseActivityListener;
|
||
|
|
||
|
/**
|
||
|
* This is a provider for a share action. It is responsible for creating views
|
||
|
* that enable data sharing and also to show a sub menu with sharing activities
|
||
|
* if the hosting item is placed on the overflow menu.
|
||
|
*
|
||
|
* <p class="note"><strong>Note:</strong> This class is included in the <a
|
||
|
* href="{@docRoot}tools/extras/support-library.html">support library</a> for compatibility
|
||
|
* with API level 7 and higher. If you're developing your app for API level 14 and higher
|
||
|
* <em>only</em>, you should instead use the framework {@link android.widget.ShareActionProvider}
|
||
|
* class.</p>
|
||
|
*
|
||
|
* <p>
|
||
|
* Here is how to use the action provider with custom backing file in a {@link MenuItem}:
|
||
|
* </p>
|
||
|
* <pre><code>
|
||
|
* // In {@link android.app.Activity#onCreateOptionsMenu Activity.onCreateOptionsMenu()}
|
||
|
* public boolean onCreateOptionsMenu(Menu menu) {
|
||
|
* // Get the menu item.
|
||
|
* MenuItem menuItem = menu.findItem(R.id.my_menu_item);
|
||
|
* // Get the provider and hold onto it to set/change the share intent.
|
||
|
* mShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(menuItem);
|
||
|
* // Set history different from the default before getting the action
|
||
|
* // view since a call to {@link android.support.v4.view.MenuItemCompat#getActionView(android.view.MenuItem) MenuItemCompat.getActionView()} calls
|
||
|
* // {@link ActionProvider#onCreateActionView()} which uses the backing file name. Omit this
|
||
|
* // line if using the default share history file is desired.
|
||
|
* mShareActionProvider.setShareHistoryFileName("custom_share_history.xml");
|
||
|
* . . .
|
||
|
* }
|
||
|
*
|
||
|
* // Somewhere in the application.
|
||
|
* public void doShare(Intent shareIntent) {
|
||
|
* // When you want to share set the share intent.
|
||
|
* mShareActionProvider.setShareIntent(shareIntent);
|
||
|
* }
|
||
|
* </code></pre>
|
||
|
* <p>
|
||
|
* <strong>Note:</strong> While the sample snippet demonstrates how to use this provider
|
||
|
* in the context of a menu item, the use of the provider is not limited to menu items.
|
||
|
* </p>
|
||
|
*
|
||
|
* <div class="special reference">
|
||
|
* <h3>Developer Guides</h3>
|
||
|
*
|
||
|
* <p>For information about how to use {@link ShareActionProvider}, see the
|
||
|
* <a href="{@docRoot}guide/topics/ui/actionbar.html#ActionProvider">Action Bar</a> API guide.</p>
|
||
|
* </div>
|
||
|
*
|
||
|
* @see ActionProvider
|
||
|
*/
|
||
|
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 perform some action before the sharing is
|
||
|
* actually performed.
|
||
|
* <p>
|
||
|
* <strong>Note:</strong> Modifying the intent is not permitted and
|
||
|
* any changes to the latter will be ignored.
|
||
|
* </p>
|
||
|
* <p>
|
||
|
* <strong>Note:</strong> You should <strong>not</strong> handle the
|
||
|
* intent here. This callback aims to notify the client that a
|
||
|
* sharing is being performed, so the client can update the UI
|
||
|
* if necessary.
|
||
|
* </p>
|
||
|
*
|
||
|
* @param source The source of the notification.
|
||
|
* @param intent The intent for launching the chosen share target.
|
||
|
* @return The return result is ignored. Always return false for consistency.
|
||
|
*/
|
||
|
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;
|
||
|
|
||
|
/**
|
||
|
* The the maximum number activities shown in the sub-menu.
|
||
|
*/
|
||
|
private int mMaxShownActivityCount = DEFAULT_INITIAL_ACTIVITY_COUNT;
|
||
|
|
||
|
/**
|
||
|
* Listener for handling menu item clicks.
|
||
|
*/
|
||
|
private final ShareMenuItemOnMenuItemClickListener mOnMenuItemClickListener =
|
||
|
new ShareMenuItemOnMenuItemClickListener();
|
||
|
|
||
|
/**
|
||
|
* The default name for storing share history.
|
||
|
*/
|
||
|
public static final String DEFAULT_SHARE_HISTORY_FILE_NAME = "share_history.xml";
|
||
|
|
||
|
/**
|
||
|
* Context for accessing resources.
|
||
|
*/
|
||
|
private final Context mContext;
|
||
|
|
||
|
/**
|
||
|
* The name of the file with share history data.
|
||
|
*/
|
||
|
private String mShareHistoryFileName = DEFAULT_SHARE_HISTORY_FILE_NAME;
|
||
|
|
||
|
private OnShareTargetSelectedListener mOnShareTargetSelectedListener;
|
||
|
|
||
|
private OnChooseActivityListener mOnChooseActivityListener;
|
||
|
|
||
|
/**
|
||
|
* Creates a new instance.
|
||
|
*
|
||
|
* @param context Context for accessing resources.
|
||
|
*/
|
||
|
public ShareActionProvider(Context context) {
|
||
|
super(context);
|
||
|
mContext = context;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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
|
||
|
public View onCreateActionView() {
|
||
|
// Create the view and set its data model.
|
||
|
ActivityChooserView activityChooserView = new ActivityChooserView(mContext);
|
||
|
if (!activityChooserView.isInEditMode()) {
|
||
|
ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName);
|
||
|
activityChooserView.setActivityChooserModel(dataModel);
|
||
|
}
|
||
|
|
||
|
// Lookup and set the expand action icon.
|
||
|
TypedValue outTypedValue = new TypedValue();
|
||
|
mContext.getTheme().resolveAttribute(R.attr.actionModeShareDrawable, outTypedValue, true);
|
||
|
Drawable drawable = TintManager.getDrawable(mContext, outTypedValue.resourceId);
|
||
|
activityChooserView.setExpandActivityOverflowButtonDrawable(drawable);
|
||
|
activityChooserView.setProvider(this);
|
||
|
|
||
|
// Set content description.
|
||
|
activityChooserView.setDefaultActionButtonContentDescription(
|
||
|
R.string.abc_shareactionprovider_share_with_application);
|
||
|
activityChooserView.setExpandActivityOverflowButtonContentDescription(
|
||
|
R.string.abc_shareactionprovider_share_with);
|
||
|
|
||
|
return activityChooserView;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritDoc}
|
||
|
*/
|
||
|
@Override
|
||
|
public boolean hasSubMenu() {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* {@inheritDoc}
|
||
|
*/
|
||
|
@Override
|
||
|
public void onPrepareSubMenu(SubMenu subMenu) {
|
||
|
// Clear since the order of items may change.
|
||
|
subMenu.clear();
|
||
|
|
||
|
ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName);
|
||
|
PackageManager packageManager = mContext.getPackageManager();
|
||
|
|
||
|
final int expandedActivityCount = dataModel.getActivityCount();
|
||
|
final int collapsedActivityCount = Math.min(expandedActivityCount, mMaxShownActivityCount);
|
||
|
|
||
|
// Populate the sub-menu with a sub set of the activities.
|
||
|
for (int i = 0; i < collapsedActivityCount; i++) {
|
||
|
ResolveInfo activity = dataModel.getActivity(i);
|
||
|
subMenu.add(0, i, i, activity.loadLabel(packageManager))
|
||
|
.setIcon(activity.loadIcon(packageManager))
|
||
|
.setOnMenuItemClickListener(mOnMenuItemClickListener);
|
||
|
}
|
||
|
|
||
|
if (collapsedActivityCount < expandedActivityCount) {
|
||
|
// Add a sub-menu for showing all activities as a list item.
|
||
|
SubMenu expandedSubMenu = subMenu.addSubMenu(Menu.NONE, collapsedActivityCount,
|
||
|
collapsedActivityCount,
|
||
|
mContext.getString(R.string.abc_activity_chooser_view_see_all));
|
||
|
for (int i = 0; i < expandedActivityCount; i++) {
|
||
|
ResolveInfo activity = dataModel.getActivity(i);
|
||
|
expandedSubMenu.add(0, i, i, activity.loadLabel(packageManager))
|
||
|
.setIcon(activity.loadIcon(packageManager))
|
||
|
.setOnMenuItemClickListener(mOnMenuItemClickListener);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the file name of a file for persisting the share history which
|
||
|
* history will be used for ordering share targets. This file will be used
|
||
|
* for all view created by {@link #onCreateActionView()}. Defaults to
|
||
|
* {@link #DEFAULT_SHARE_HISTORY_FILE_NAME}. Set to <code>null</code>
|
||
|
* if share history should not be persisted between sessions.
|
||
|
* <p>
|
||
|
* <strong>Note:</strong> The history file name can be set any time, however
|
||
|
* only the action views created by {@link #onCreateActionView()} after setting
|
||
|
* the file name will be backed by the provided file. Therefore, if you want to
|
||
|
* use different history files for sharing specific types of content, every time
|
||
|
* you change the history file {@link #setShareHistoryFileName(String)} you must
|
||
|
* call {@link android.app.Activity#invalidateOptionsMenu()} to recreate the
|
||
|
* action view. You should <strong>not</strong> call
|
||
|
* {@link android.app.Activity#invalidateOptionsMenu()} from
|
||
|
* {@link android.app.Activity#onCreateOptionsMenu(Menu)}."
|
||
|
* <p>
|
||
|
* <code>
|
||
|
* private void doShare(Intent intent) {
|
||
|
* if (IMAGE.equals(intent.getMimeType())) {
|
||
|
* mShareActionProvider.setHistoryFileName(SHARE_IMAGE_HISTORY_FILE_NAME);
|
||
|
* } else if (TEXT.equals(intent.getMimeType())) {
|
||
|
* mShareActionProvider.setHistoryFileName(SHARE_TEXT_HISTORY_FILE_NAME);
|
||
|
* }
|
||
|
* mShareActionProvider.setIntent(intent);
|
||
|
* invalidateOptionsMenu();
|
||
|
* }
|
||
|
* <code>
|
||
|
*
|
||
|
* @param shareHistoryFile The share history file name.
|
||
|
*/
|
||
|
public void setShareHistoryFileName(String shareHistoryFile) {
|
||
|
mShareHistoryFileName = shareHistoryFile;
|
||
|
setActivityChooserPolicyIfNeeded();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets an intent with information about the share action. Here is a
|
||
|
* sample for constructing a share intent:
|
||
|
* <p>
|
||
|
* <pre>
|
||
|
* <code>
|
||
|
* Intent shareIntent = new Intent(Intent.ACTION_SEND);
|
||
|
* shareIntent.setType("image/*");
|
||
|
* Uri uri = Uri.fromFile(new File(getFilesDir(), "foo.jpg"));
|
||
|
* shareIntent.putExtra(Intent.EXTRA_STREAM, uri.toString());
|
||
|
* </pre>
|
||
|
* </code>
|
||
|
* </p>
|
||
|
*
|
||
|
* @param shareIntent The share intent.
|
||
|
*
|
||
|
* @see Intent#ACTION_SEND
|
||
|
* @see Intent#ACTION_SEND_MULTIPLE
|
||
|
*/
|
||
|
public void setShareIntent(Intent shareIntent) {
|
||
|
if (shareIntent != null) {
|
||
|
final String action = shareIntent.getAction();
|
||
|
if (Intent.ACTION_SEND.equals(action) || Intent.ACTION_SEND_MULTIPLE.equals(action)) {
|
||
|
updateIntent(shareIntent);
|
||
|
}
|
||
|
}
|
||
|
ActivityChooserModel dataModel = ActivityChooserModel.get(mContext,
|
||
|
mShareHistoryFileName);
|
||
|
dataModel.setIntent(shareIntent);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Reusable listener for handling share item clicks.
|
||
|
*/
|
||
|
private class ShareMenuItemOnMenuItemClickListener implements OnMenuItemClickListener {
|
||
|
@Override
|
||
|
public boolean onMenuItemClick(MenuItem item) {
|
||
|
ActivityChooserModel dataModel = ActivityChooserModel.get(mContext,
|
||
|
mShareHistoryFileName);
|
||
|
final int itemId = item.getItemId();
|
||
|
Intent launchIntent = dataModel.chooseActivity(itemId);
|
||
|
if (launchIntent != null) {
|
||
|
final String action = launchIntent.getAction();
|
||
|
if (Intent.ACTION_SEND.equals(action) ||
|
||
|
Intent.ACTION_SEND_MULTIPLE.equals(action)) {
|
||
|
updateIntent(launchIntent);
|
||
|
}
|
||
|
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 ShareActivityChooserModelPolicy();
|
||
|
}
|
||
|
ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName);
|
||
|
dataModel.setOnChooseActivityListener(mOnChooseActivityListener);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Policy that delegates to the {@link OnShareTargetSelectedListener}, if such.
|
||
|
*/
|
||
|
private class ShareActivityChooserModelPolicy implements OnChooseActivityListener {
|
||
|
@Override
|
||
|
public boolean onChooseActivity(ActivityChooserModel host, Intent intent) {
|
||
|
if (mOnShareTargetSelectedListener != null) {
|
||
|
mOnShareTargetSelectedListener.onShareTargetSelected(
|
||
|
ShareActionProvider.this, intent);
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void updateIntent(Intent intent) {
|
||
|
if (Build.VERSION.SDK_INT >= 21) {
|
||
|
// If we're on Lollipop, we can open the intent as a document
|
||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT |
|
||
|
Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
|
||
|
} else {
|
||
|
// Else, we will use the old CLEAR_WHEN_TASK_RESET flag
|
||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||
|
}
|
||
|
}
|
||
|
}
|