Bug 942270 - Allow ActionViews in prompts. r=bnicholson
authorWes Johnston <wjohnston@mozilla.com>
Fri, 14 Mar 2014 14:41:20 -0700
changeset 190786 4c8b77b42f5534d3fbc91a6803b1436ea254f5e0
parent 190785 6838701222096aa2d37a6b3afe71dd23820fe6cd
child 190787 3122101fa40f66b1c35e2eb0dc69cf1f94ad1a29
push id3503
push userraliiev@mozilla.com
push dateMon, 28 Apr 2014 18:51:11 +0000
treeherdermozilla-beta@c95ac01e332e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbnicholson
bugs942270
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 942270 - Allow ActionViews in prompts. r=bnicholson
mobile/android/base/prompts/PromptListAdapter.java
mobile/android/base/resources/values/dimens.xml
mobile/android/base/resources/values/styles.xml
mobile/android/base/widget/GeckoActionProvider.java
--- a/mobile/android/base/prompts/PromptListAdapter.java
+++ b/mobile/android/base/prompts/PromptListAdapter.java
@@ -1,48 +1,56 @@
 package org.mozilla.gecko.prompts;
 
+import org.mozilla.gecko.menu.MenuItemActionView;
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.GeckoAppShell;
+import org.mozilla.gecko.gfx.BitmapUtils;
+import org.mozilla.gecko.widget.GeckoActionProvider;
 
 import org.json.JSONArray;
 import org.json.JSONObject;
 import org.json.JSONException;
 
 import android.content.Context;
+import android.content.Intent;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.AdapterView;
 import android.widget.CheckedTextView;
 import android.widget.TextView;
 import android.widget.ListView;
+import android.widget.ArrayAdapter;
+import android.widget.AdapterView;
+import android.util.TypedValue;
 
 import java.util.ArrayList;
 
 public class PromptListAdapter extends ArrayAdapter<PromptListItem> {
     private static final int VIEW_TYPE_ITEM = 0;
     private static final int VIEW_TYPE_GROUP = 1;
-    private static final int VIEW_TYPE_COUNT = 2;
+    private static final int VIEW_TYPE_ACTIONS = 2;
+    private static final int VIEW_TYPE_COUNT = 3;
 
     private static final String LOGTAG = "GeckoPromptListAdapter";
 
     private final int mResourceId;
     private Drawable mBlankDrawable;
     private Drawable mMoreDrawable;
     private static int mGroupPaddingSize;
     private static int mLeftRightTextWithIconPadding;
     private static int mTopBottomTextWithIconPadding;
     private static int mIconSize;
     private static int mMinRowSize;
     private static int mIconTextPadding;
+    private static float mTextSize;
     private static boolean mInitialized = false;
 
     PromptListAdapter(Context context, int textViewResourceId, PromptListItem[] objects) {
         super(context, textViewResourceId, objects);
         mResourceId = textViewResourceId;
         init();
     }
 
@@ -50,25 +58,29 @@ public class PromptListAdapter extends A
         if (!mInitialized) {
             Resources res = getContext().getResources();
             mGroupPaddingSize = (int) (res.getDimension(R.dimen.prompt_service_group_padding_size));
             mLeftRightTextWithIconPadding = (int) (res.getDimension(R.dimen.prompt_service_left_right_text_with_icon_padding));
             mTopBottomTextWithIconPadding = (int) (res.getDimension(R.dimen.prompt_service_top_bottom_text_with_icon_padding));
             mIconTextPadding = (int) (res.getDimension(R.dimen.prompt_service_icon_text_padding));
             mIconSize = (int) (res.getDimension(R.dimen.prompt_service_icon_size));
             mMinRowSize = (int) (res.getDimension(R.dimen.prompt_service_min_list_item_height));
+            mTextSize = res.getDimension(R.dimen.menu_item_textsize);
+
             mInitialized = true;
         }
     }
 
     @Override
     public int getItemViewType(int position) {
         PromptListItem item = getItem(position);
         if (item.isGroup) {
             return VIEW_TYPE_GROUP;
+        } else if (item.showAsActions) {
+            return VIEW_TYPE_ACTIONS;
         } else {
             return VIEW_TYPE_ITEM;
         }
     }
 
     @Override
     public int getViewTypeCount() {
         return VIEW_TYPE_COUNT;
@@ -156,43 +168,88 @@ public class PromptListAdapter extends A
         for (int i = 0; i< length; i++) {
             if (isSelected(i)) {
                 return i;
             }
         }
         return -1;
     }
 
+    private View getActionView(PromptListItem item) {
+        GeckoActionProvider provider = GeckoActionProvider.getForType(item.getIntent().getType(), getContext());
+        provider.setIntent(item.getIntent());
+        return provider.onCreateActionView();
+    }
+
+    private void updateActionView(final PromptListItem item, final MenuItemActionView view, final ListView list, final int position) {
+        view.setTitle(item.label);
+        view.setIcon(item.getIcon());
+        view.setSubMenuIndicator(item.isParent);
+        view.setMenuItemClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                ListView.OnItemClickListener listener = list.getOnItemClickListener();
+                if (listener != null) {
+                    listener.onItemClick(list, view, position, position);
+                }
+
+                final GeckoActionProvider provider = GeckoActionProvider.getForType(item.getIntent().getType(), getContext());
+                IntentChooserPrompt prompt = new IntentChooserPrompt(getContext(), provider);
+                prompt.show(item.label, getContext(), new IntentHandler() {
+                    @Override
+                    public void onIntentSelected(final Intent intent, final int p) {
+                        provider.chooseActivity(p);
+                    }
+
+                    @Override
+                    public void onCancelled() {
+                        // do nothing
+                    }
+                });
+            }
+        });
+    }
+
     @Override
     public View getView(int position, View convertView, ViewGroup parent) {
         PromptListItem item = getItem(position);
         int type = getItemViewType(position);
         ViewHolder viewHolder = null;
 
         if (convertView == null) {
-            int resourceId = mResourceId;
-            if (item.isGroup) {
-                resourceId = R.layout.list_item_header;
+            if (type == VIEW_TYPE_ACTIONS) {
+                convertView = getActionView(item);
+            } else {
+                int resourceId = mResourceId;
+                if (item.isGroup) {
+                    resourceId = R.layout.list_item_header;
+                }
+
+                LayoutInflater mInflater = LayoutInflater.from(getContext());
+                convertView = mInflater.inflate(resourceId, null);
+                convertView.setMinimumHeight(mMinRowSize);
+
+                TextView tv = (TextView) convertView.findViewById(android.R.id.text1);
+                tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
+                viewHolder = new ViewHolder(tv, tv.getPaddingLeft(), tv.getPaddingRight(),
+                                            tv.getPaddingTop(), tv.getPaddingBottom());
+
+                convertView.setTag(viewHolder);
             }
-            LayoutInflater mInflater = LayoutInflater.from(getContext());
-            convertView = mInflater.inflate(resourceId, null);
-            convertView.setMinimumHeight(mMinRowSize);
-
-            TextView tv = (TextView) convertView.findViewById(android.R.id.text1);
-            viewHolder = new ViewHolder(tv, tv.getPaddingLeft(), tv.getPaddingRight(),
-                                        tv.getPaddingTop(), tv.getPaddingBottom());
-
-            convertView.setTag(viewHolder);
         } else {
             viewHolder = (ViewHolder) convertView.getTag();
         }
 
-        viewHolder.textView.setText(item.label);
-        maybeUpdateCheckedState((ListView) parent, position, item, viewHolder);
-        maybeUpdateIcon(item, viewHolder.textView);
+        if (type == VIEW_TYPE_ACTIONS) {
+            updateActionView(item, (MenuItemActionView) convertView, (ListView) parent, position);
+        } else {
+            viewHolder.textView.setText(item.label);
+            maybeUpdateCheckedState((ListView) parent, position, item, viewHolder);
+            maybeUpdateIcon(item, viewHolder.textView);
+        }
 
         return convertView;
     }
 
     private static class ViewHolder {
         public final TextView textView;
         public final int paddingLeft;
         public final int paddingRight;
--- a/mobile/android/base/resources/values/dimens.xml
+++ b/mobile/android/base/resources/values/dimens.xml
@@ -41,28 +41,30 @@
 
     <dimen name="doorhanger_input_width">250dp</dimen>
     <dimen name="doorhanger_spinner_textsize">9sp</dimen>
     <dimen name="doorhanger_padding">15dp</dimen>
     <dimen name="doorhanger_textsize_small">8sp</dimen>
 
     <dimen name="flow_layout_spacing">6dp</dimen>
     <dimen name="menu_item_icon">21dp</dimen>
+    <dimen name="menu_item_textsize">16sp</dimen>
     <dimen name="menu_item_state_icon">18dp</dimen>
     <dimen name="menu_item_row_height">44dp</dimen>
     <dimen name="menu_item_row_width">240dp</dimen>
     <dimen name="menu_item_more_offset">5dp</dimen>
+    <dimen name="menu_item_textsize">16sp</dimen>
     <dimen name="menu_popup_arrow_offset">8dp</dimen>
     <dimen name="menu_popup_arrow_margin">5dip</dimen>
     <dimen name="menu_popup_arrow_width">40dip</dimen>
     <dimen name="menu_popup_offset">12dp</dimen>
     <dimen name="menu_popup_width">256dp</dimen>
     <dimen name="nav_button_border_width">0.75dp</dimen>
     <dimen name="prompt_service_group_padding_size">32dp</dimen>
-    <dimen name="prompt_service_icon_size">72dp</dimen>
+    <dimen name="prompt_service_icon_size">36dp</dimen>
     <dimen name="prompt_service_icon_text_padding">10dp</dimen>
     <dimen name="prompt_service_inputs_padding">16dp</dimen>
     <dimen name="prompt_service_left_right_text_with_icon_padding">10dp</dimen>
     <dimen name="prompt_service_top_bottom_text_with_icon_padding">8dp</dimen>
     <dimen name="prompt_service_min_list_item_height">48dp</dimen>
     <dimen name="remote_tab_child_row_height">64dp</dimen>
     <dimen name="remote_tab_group_row_height">26dp</dimen>
     <dimen name="searchpreferences_icon_size">32dp</dimen>
--- a/mobile/android/base/resources/values/styles.xml
+++ b/mobile/android/base/resources/values/styles.xml
@@ -102,16 +102,17 @@
     <style name="Widget.MenuItemDefault">
         <item name="android:paddingLeft">10dip</item>
         <item name="android:paddingRight">10dip</item>
         <item name="android:drawablePadding">6dip</item>
         <item name="android:gravity">center_vertical</item>
         <item name="android:textAppearance">@style/TextAppearance</item>
         <item name="android:singleLine">true</item>
         <item name="android:ellipsize">middle</item>
+        <item name="android:textSize">@dimen/menu_item_textsize</item>
     </style>
 
     <style name="Widget.TwoLinePageRow" />
 
     <style name="Widget.TwoLinePageRow.Title">
         <item name="android:textAppearance">@style/TextAppearance.Widget.Home.ItemTitle</item>
         <item name="android:singleLine">true</item>
         <item name="android:ellipsize">none</item>
@@ -253,17 +254,17 @@
         Hence, Gecko's TextAppearance is based on text over light theme and
         TextAppearance.Inverse is based on text over dark theme.
     -->
     <style name="TextAppearance">
         <item name="android:textColor">?android:attr/textColorPrimary</item>
         <item name="android:textColorHighlight">@color/text_color_highlight</item>
         <item name="android:textColorHint">?android:attr/textColorHint</item>
         <item name="android:textColorLink">?android:attr/textColorLink</item>
-        <item name="android:textSize">16sp</item>
+        <item name="android:textSize">@dimen/menu_item_textsize</item>
         <item name="android:textStyle">normal</item>
     </style>
 
     <style name="TextAppearance.Inverse">
         <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
         <item name="android:textColorHint">?android:attr/textColorHintInverse</item>
         <item name="android:textColorHighlight">@color/text_color_highlight_inverse</item>
         <item name="android:textColorLink">?android:attr/textColorLink</item>
--- a/mobile/android/base/widget/GeckoActionProvider.java
+++ b/mobile/android/base/widget/GeckoActionProvider.java
@@ -15,16 +15,17 @@ import android.graphics.drawable.Drawabl
 import android.view.ActionProvider;
 import android.view.MenuItem;
 import android.view.MenuItem.OnMenuItemClickListener;
 import android.view.SubMenu;
 import android.view.View;
 import android.view.View.OnClickListener;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 
 public class GeckoActionProvider extends ActionProvider {
     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.
@@ -39,16 +40,30 @@ public class GeckoActionProvider extends
 
     //  History file.
     private String mHistoryFileName = DEFAULT_HISTORY_FILE_NAME;
 
     private OnTargetSelectedListener mOnTargetListener;
 
     private final Callbacks mCallbacks = new Callbacks();
 
+    private static HashMap<String, GeckoActionProvider> mProviders = new HashMap<String, GeckoActionProvider>();
+
+    // Gets the action provider for a particular mimetype
+    public static GeckoActionProvider getForType(String type, Context context) {
+        if (!mProviders.keySet().contains(type)) {
+            GeckoActionProvider provider = new GeckoActionProvider(context);
+
+            String subType = type.substring(0, type.indexOf("/"));
+            provider.setHistoryFileName("history-" + subType + ".xml");
+            mProviders.put(type, provider);
+        }
+        return mProviders.get(type);
+    }
+
     public GeckoActionProvider(Context context) {
         super(context);
         mContext = context;
     }
 
     @Override
     public View onCreateActionView() {
         // Create the view and set its data model.
@@ -136,16 +151,20 @@ public class GeckoActionProvider extends
         // 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) { 
             ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mHistoryFileName);
             Intent launchIntent = dataModel.chooseActivity(index);
@@ -163,13 +182,12 @@ public class GeckoActionProvider extends
         public boolean onMenuItemClick(MenuItem item) {
             chooseActivity(item.getItemId());
             return true;
         }
 
         @Override
         public void onClick(View view) {
             Integer index = (Integer) view.getTag();
-            ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mHistoryFileName);
             chooseActivity(index);
         }
     }
 }