diff -r 000000000000 -r 6474c204b198 mobile/android/base/widget/GeckoActionProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mobile/android/base/widget/GeckoActionProvider.java Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,228 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.widget; + +import org.mozilla.gecko.GeckoAppShell; +import org.mozilla.gecko.Telemetry; +import org.mozilla.gecko.TelemetryContract; +import org.mozilla.gecko.menu.MenuItemActionView; +import org.mozilla.gecko.util.ThreadUtils; + +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.view.MenuItem; +import android.view.MenuItem.OnMenuItemClickListener; +import android.view.SubMenu; +import android.view.View; +import android.view.View.OnClickListener; +import android.text.TextUtils; + +import java.util.ArrayList; +import java.util.HashMap; + +public class GeckoActionProvider { + private static int MAX_HISTORY_SIZE = 2; + + /** + * A listener to know when a target was selected. + * When setting a provider, the activity can listen to this, + * to close the menu. + */ + public interface OnTargetSelectedListener { + public void onTargetSelected(); + } + + private final Context mContext; + + public final static String DEFAULT_MIME_TYPE = "text/plain"; + + public static final String DEFAULT_HISTORY_FILE_NAME = "history.xml"; + + // History file. + private String mHistoryFileName = DEFAULT_HISTORY_FILE_NAME; + + private OnTargetSelectedListener mOnTargetListener; + + private final Callbacks mCallbacks = new Callbacks(); + + private static HashMap mProviders = new HashMap(); + + private static String getFilenameFromMimeType(String mimeType) { + String[] mime = mimeType.split("/"); + + // All text mimetypes use the default provider + if ("text".equals(mime[0])) { + return DEFAULT_HISTORY_FILE_NAME; + } + + return "history-" + mime[0] + ".xml"; + } + + // Gets the action provider for a particular mimetype + public static GeckoActionProvider getForType(String mimeType, Context context) { + if (!mProviders.keySet().contains(mimeType)) { + GeckoActionProvider provider = new GeckoActionProvider(context); + + // For empty types, we just return a default provider + if (TextUtils.isEmpty(mimeType)) { + return provider; + } + + provider.setHistoryFileName(getFilenameFromMimeType(mimeType)); + mProviders.put(mimeType, provider); + } + return mProviders.get(mimeType); + } + + public GeckoActionProvider(Context context) { + mContext = context; + } + + public View onCreateActionView() { + // Create the view and set its data model. + ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mHistoryFileName); + MenuItemActionView view = new MenuItemActionView(mContext, null); + view.addActionButtonClickListener(mCallbacks); + + final PackageManager packageManager = mContext.getPackageManager(); + int historySize = dataModel.getDistinctActivityCountInHistory(); + if (historySize > MAX_HISTORY_SIZE) { + historySize = MAX_HISTORY_SIZE; + } + + // Historical data is dependent on past selection of activities. + // Activity count is determined by the number of activities that can handle + // the particular intent. When no intent is set, the activity count is 0, + // while the history count can be a valid number. + if (historySize > dataModel.getActivityCount()) { + return view; + } + + for (int i = 0; i < historySize; i++) { + view.addActionButton(dataModel.getActivity(i).loadIcon(packageManager)); + } + + return view; + } + + public View getView() { + return onCreateActionView(); + } + + public boolean hasSubMenu() { + return true; + } + + public void onPrepareSubMenu(SubMenu subMenu) { + // Clear since the order of items may change. + subMenu.clear(); + + ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mHistoryFileName); + PackageManager packageManager = mContext.getPackageManager(); + + // Populate the sub-menu with a sub set of the activities. + final int count = dataModel.getActivityCount(); + for (int i = 0; i < count; i++) { + ResolveInfo activity = dataModel.getActivity(i); + subMenu.add(0, i, i, activity.loadLabel(packageManager)) + .setIcon(activity.loadIcon(packageManager)) + .setOnMenuItemClickListener(mCallbacks); + } + } + + public void setHistoryFileName(String historyFile) { + mHistoryFileName = historyFile; + } + + public Intent getIntent() { + ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mHistoryFileName); + return dataModel.getIntent(); + } + + public void setIntent(Intent intent) { + ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mHistoryFileName); + dataModel.setIntent(intent); + + // Inform the target listener to refresh it's UI, if needed. + if (mOnTargetListener != null) { + mOnTargetListener.onTargetSelected(); + } + } + + public void setOnTargetSelectedListener(OnTargetSelectedListener listener) { + mOnTargetListener = listener; + } + + public ArrayList getSortedActivites() { + ArrayList infos = new ArrayList(); + + ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mHistoryFileName); + PackageManager packageManager = mContext.getPackageManager(); + + // Populate the sub-menu with a sub set of the activities. + final int count = dataModel.getActivityCount(); + for (int i = 0; i < count; i++) { + infos.add(dataModel.getActivity(i)); + } + return infos; + } + + public void chooseActivity(int position) { + mCallbacks.chooseActivity(position); + } + + /** + * Listener for handling default activity / menu item clicks. + */ + private class Callbacks implements OnMenuItemClickListener, + OnClickListener { + private void chooseActivity(int index) { + final ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mHistoryFileName); + final Intent launchIntent = dataModel.chooseActivity(index); + if (launchIntent != null) { + // This may cause a download to happen. Make sure we're on the background thread. + ThreadUtils.postToBackgroundThread(new Runnable() { + @Override + public void run() { + // Share image downloads the image before sharing it. + String type = launchIntent.getType(); + if (Intent.ACTION_SEND.equals(launchIntent.getAction()) && type != null && type.startsWith("image/")) { + GeckoAppShell.downloadImageForIntent(launchIntent); + } + + launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); + mContext.startActivity(launchIntent); + } + }); + } + + if (mOnTargetListener != null) { + mOnTargetListener.onTargetSelected(); + } + } + + @Override + public boolean onMenuItemClick(MenuItem item) { + chooseActivity(item.getItemId()); + + // Context: Sharing via chrome mainmenu list (no explicit session is active) + Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST); + return true; + } + + @Override + public void onClick(View view) { + Integer index = (Integer) view.getTag(); + chooseActivity(index); + + // Context: Sharing via chrome mainmenu and content contextmenu quickshare (no explicit session is active) + Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.BUTTON); + } + } +}