Bug 1158295 - Dynamically determine space between search engines in search engine bar. r=mcomella
authorSebastian Kaspari <s.kaspari@gmail.com>
Thu, 04 Jun 2015 18:13:07 -0700
changeset 248152 70924ca5242302f1e0d1a2e5f3dfcd33770154d2
parent 248151 646bc6779d35f9f9db3088fb6fdfe53d7355a6d4
child 248153 c3e6440a410d4b9485c6e58f76fdbb78d9c1ef2e
push id60888
push userkwierso@gmail.com
push dateThu, 11 Jun 2015 01:38:38 +0000
treeherdermozilla-inbound@39e638ed06bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcomella
bugs1158295
milestone41.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 1158295 - Dynamically determine space between search engines in search engine bar. r=mcomella
mobile/android/base/home/SearchEngineBar.java
mobile/android/base/resources/layout/browser_search.xml
mobile/android/base/resources/layout/search_engine_bar_item.xml
--- a/mobile/android/base/home/SearchEngineBar.java
+++ b/mobile/android/base/home/SearchEngineBar.java
@@ -4,45 +4,63 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
  package org.mozilla.gecko.home;
 
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
-import android.widget.FrameLayout;
 import android.widget.ImageView;
 
 import org.mozilla.gecko.R;
-import org.mozilla.gecko.widget.FaviconView;
 import org.mozilla.gecko.widget.TwoWayView;
 
 import java.util.ArrayList;
 import java.util.List;
 
 public class SearchEngineBar extends TwoWayView
                              implements AdapterView.OnItemClickListener {
     private static final String LOGTAG = "Gecko" + SearchEngineBar.class.getSimpleName();
 
+    private static final float ICON_CONTAINER_MIN_WIDTH_DP = 72;
+    private static final float DIVIDER_HEIGHT_DP = 1;
+
     public interface OnSearchBarClickListener {
         public void onSearchBarClickListener(SearchEngine searchEngine);
     }
 
     private final SearchEngineAdapter adapter;
+    private final Paint dividerPaint;
+    private final float minIconContainerWidth;
+    private final float dividerHeight;
+
+    private int iconContainerWidth;
     private OnSearchBarClickListener onSearchBarClickListener;
 
     public SearchEngineBar(final Context context, final AttributeSet attrs) {
         super(context, attrs);
 
+        dividerPaint = new Paint();
+        dividerPaint.setColor(getResources().getColor(R.color.divider_light));
+        dividerPaint.setStyle(Paint.Style.FILL_AND_STROKE);
+
+        final DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
+        minIconContainerWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, ICON_CONTAINER_MIN_WIDTH_DP, displayMetrics);
+        dividerHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DIVIDER_HEIGHT_DP, displayMetrics);
+
+        iconContainerWidth =  (int) minIconContainerWidth;
+
         adapter = new SearchEngineAdapter();
         setAdapter(adapter);
         setOnItemClickListener(this);
     }
 
     @Override
     public void onItemClick(final AdapterView<?> parent, final View view, final int position,
             final long id) {
@@ -58,16 +76,44 @@ public class SearchEngineBar extends Two
     protected void setOnSearchBarClickListener(final OnSearchBarClickListener listener) {
         onSearchBarClickListener = listener;
     }
 
     protected void setSearchEngines(final List<SearchEngine> searchEngines) {
         adapter.setSearchEngines(searchEngines);
     }
 
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        final int searchEngineCount = getCount();
+
+        if (searchEngineCount > 0) {
+            final float availableWidthPerContainer = getMeasuredWidth() / searchEngineCount;
+
+            final int desiredIconContainerSize = (int) Math.max(
+                    availableWidthPerContainer,
+                    minIconContainerWidth
+            );
+
+            if (desiredIconContainerSize != iconContainerWidth) {
+                iconContainerWidth = desiredIconContainerSize;
+                adapter.notifyDataSetChanged();
+            }
+        }
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+
+        canvas.drawRect(0, 0, getWidth(), dividerHeight, dividerPaint);
+    }
+
     public class SearchEngineAdapter extends BaseAdapter {
         List<SearchEngine> searchEngines = new ArrayList<>();
 
         public void setSearchEngines(final List<SearchEngine> searchEngines) {
             this.searchEngines = searchEngines;
             notifyDataSetChanged();
         }
 
@@ -90,49 +136,21 @@ public class SearchEngineBar extends Two
         public View getView(final int position, final View convertView, final ViewGroup parent) {
             final View view;
             if (convertView == null) {
                 view = LayoutInflater.from(getContext()).inflate(R.layout.search_engine_bar_item, parent, false);
             } else {
                 view = convertView;
             }
 
+            view.setLayoutParams(new LayoutParams(iconContainerWidth, ViewGroup.LayoutParams.MATCH_PARENT));
+
             final ImageView faviconView = (ImageView) view.findViewById(R.id.search_engine_icon);
             final SearchEngine searchEngine = searchEngines.get(position);
             faviconView.setImageBitmap(searchEngine.getIcon());
 
-            final View container = view.findViewById(R.id.search_engine_icon_container);
             final String desc = getResources().getString(R.string.search_bar_item_desc, searchEngine.getEngineIdentifier());
-            container.setContentDescription(desc);
+            view.setContentDescription(desc);
 
             return view;
         }
     }
-
-    /**
-     * A Container to surround the SearchEngineBar. This is necessary so we can draw
-     * a divider across the entire width of the screen, but have the inner list layout
-     * not take up the full width of the screen so it can be centered within this container
-     * if there aren't enough items that it needs to scroll.
-     *
-     * Note: a better implementation would have this View inflating an inner layout so
-     * the containing layout doesn't need two "SearchEngineBar" Views but it wasn't
-     * worth the refactor time.
-     */
-    @SuppressWarnings("unused") // via XML
-    public static class SearchEngineBarContainer extends FrameLayout {
-        private final Paint dividerPaint;
-
-        public SearchEngineBarContainer(final Context context, final AttributeSet attrs) {
-            super(context, attrs);
-
-            dividerPaint = new Paint();
-            dividerPaint.setColor(getResources().getColor(R.color.divider_light));
-        }
-
-        @Override
-        public void onDraw(final Canvas canvas) {
-            super.onDraw(canvas);
-
-            canvas.drawLine(0, 0, getWidth(), 0, dividerPaint);
-        }
-    }
 }
--- a/mobile/android/base/resources/layout/browser_search.xml
+++ b/mobile/android/base/resources/layout/browser_search.xml
@@ -14,36 +14,22 @@
               android:layout="@layout/home_suggestion_prompt" />
 
     <view class="org.mozilla.gecko.home.BrowserSearch$HomeSearchListView"
             android:id="@+id/home_list_view"
             android:layout_width="match_parent"
             android:layout_height="0dp"
             android:layout_weight="1" />
 
-    <!-- The window background is set to our desired color, #fff, so
-         reduce overdraw by not drawing the background.
-
-         Note: this needs to be transparent and not null because we
-         draw a divider in onDraw. -->
-    <view class="org.mozilla.gecko.home.SearchEngineBar$SearchEngineBarContainer"
+    <!-- listSelector is too slow for showing pressed state
+         so we set the pressed colors on the child. -->
+    <org.mozilla.gecko.home.SearchEngineBar
+          android:id="@+id/search_engine_bar"
           android:layout_width="match_parent"
-          android:layout_height="wrap_content"
-          android:background="@android:color/transparent">
-
-        <!-- We add a marginTop so the outer container can draw a divider.
-
-             listSelector is too slow for showing pressed state
-             so we set the pressed colors on the child. -->
-        <org.mozilla.gecko.home.SearchEngineBar
-              android:id="@+id/search_engine_bar"
-              android:layout_width="wrap_content"
-              android:layout_height="48dp"
-              android:layout_marginTop="1dp"
-              android:orientation="horizontal"
-              android:layout_gravity="center_horizontal"
-              android:choiceMode="singleChoice"
-              android:listSelector="@android:color/transparent"
-              android:cacheColorHint="@android:color/transparent"/>
-
-   </view>
+          android:layout_height="48dp"
+          android:paddingTop="1dp"
+          android:orientation="horizontal"
+          android:layout_gravity="center_horizontal"
+          android:choiceMode="singleChoice"
+          android:listSelector="@android:color/transparent"
+          android:cacheColorHint="@android:color/transparent" />
 
 </LinearLayout>
--- a/mobile/android/base/resources/layout/search_engine_bar_item.xml
+++ b/mobile/android/base/resources/layout/search_engine_bar_item.xml
@@ -3,22 +3,25 @@
    - 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/. -->
 
 <!-- TwoWayView doesn't let us set the margin around items (except as
      gecko:itemMargin, but that doesn't increase the hit area) so we
      have to surround the main View by a ViewGroup to create a pressable margin.
 
      Note: the layout_height values are shared with the parent
-     View (browser_search at the time of this writing). -->
+     View (browser_search at the time of this writing).
+
+     The actual width of the FrameLayout is calculated at runtime by the
+     SearchEngineBar class to spread the icons across the device's width. -->
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/search_engine_icon_container"
+    android:layout_width="72dp"
     android:layout_height="match_parent"
-    android:layout_width="72dp"
     android:background="@color/pressed_about_page_header_grey">
 
     <!-- Width & height are set to make the Favicons as sharp as possible
          based on asset size. -->
     <ImageView
         android:id="@+id/search_engine_icon"
         android:layout_width="24dp"
         android:layout_height="24dp"