Bug 891183: Added custom tab strip in about:home for tablets [r=sriram]

This commit is contained in:
Shilpan Bhagat 2013-08-13 17:08:16 -07:00
parent d0d587a6b0
commit 7b14cbcad6
13 changed files with 347 additions and 18 deletions

View File

@ -240,6 +240,7 @@ FENNEC_JAVA_FILES = \
home/SearchLoader.java \ home/SearchLoader.java \
home/SimpleCursorLoader.java \ home/SimpleCursorLoader.java \
home/SuggestClient.java \ home/SuggestClient.java \
home/TabMenuStrip.java \
home/TopBookmarkItemView.java \ home/TopBookmarkItemView.java \
home/TopBookmarksAdapter.java \ home/TopBookmarksAdapter.java \
home/TopBookmarksView.java \ home/TopBookmarksView.java \
@ -476,6 +477,7 @@ RES_LAYOUT = \
res/layout/home_history_list.xml \ res/layout/home_history_list.xml \
res/layout/home_most_recent_page.xml \ res/layout/home_most_recent_page.xml \
res/layout/home_most_visited_page.xml \ res/layout/home_most_visited_page.xml \
res/layout/home_pager.xml \
res/layout/home_reading_list_page.xml \ res/layout/home_reading_list_page.xml \
res/layout/home_search_item_row.xml \ res/layout/home_search_item_row.xml \
res/layout/home_suggestion_prompt.xml \ res/layout/home_suggestion_prompt.xml \
@ -500,6 +502,7 @@ RES_LAYOUT = \
res/layout/remote_tabs_child.xml \ res/layout/remote_tabs_child.xml \
res/layout/remote_tabs_group.xml \ res/layout/remote_tabs_group.xml \
res/layout/search_engine_row.xml \ res/layout/search_engine_row.xml \
res/layout/tab_menu_strip.xml \
res/layout/tabs_panel.xml \ res/layout/tabs_panel.xml \
res/layout/tabs_counter.xml \ res/layout/tabs_counter.xml \
res/layout/tabs_panel_header.xml \ res/layout/tabs_panel_header.xml \
@ -521,6 +524,7 @@ RES_LAYOUT = \
RES_LAYOUT_LARGE_V11 = \ RES_LAYOUT_LARGE_V11 = \
res/layout-large-v11/browser_toolbar.xml \ res/layout-large-v11/browser_toolbar.xml \
res/layout-large-v11/home_pager.xml \
$(NULL) $(NULL)
RES_LAYOUT_LARGE_LAND_V11 = \ RES_LAYOUT_LARGE_LAND_V11 = \
@ -641,6 +645,7 @@ RES_DRAWABLE_MDPI = \
res/drawable-mdpi/bookmark_folder_closed.png \ res/drawable-mdpi/bookmark_folder_closed.png \
res/drawable-mdpi/bookmark_folder_opened.png \ res/drawable-mdpi/bookmark_folder_opened.png \
res/drawable-mdpi/desktop_notification.png \ res/drawable-mdpi/desktop_notification.png \
res/drawable-mdpi/home_tab_menu_strip.9.png \
res/drawable-mdpi/ic_menu_addons_filler.png \ res/drawable-mdpi/ic_menu_addons_filler.png \
res/drawable-mdpi/ic_menu_bookmark_add.png \ res/drawable-mdpi/ic_menu_bookmark_add.png \
res/drawable-mdpi/ic_menu_bookmark_remove.png \ res/drawable-mdpi/ic_menu_bookmark_remove.png \
@ -749,6 +754,7 @@ RES_DRAWABLE_HDPI = \
res/drawable-hdpi/alert_mic.png \ res/drawable-hdpi/alert_mic.png \
res/drawable-hdpi/alert_mic_camera.png \ res/drawable-hdpi/alert_mic_camera.png \
res/drawable-hdpi/arrow_popup_bg.9.png \ res/drawable-hdpi/arrow_popup_bg.9.png \
res/drawable-hdpi/home_tab_menu_strip.9.png \
res/drawable-hdpi/ic_menu_addons_filler.png \ res/drawable-hdpi/ic_menu_addons_filler.png \
res/drawable-hdpi/ic_menu_bookmark_add.png \ res/drawable-hdpi/ic_menu_bookmark_add.png \
res/drawable-hdpi/ic_menu_bookmark_remove.png \ res/drawable-hdpi/ic_menu_bookmark_remove.png \
@ -844,6 +850,7 @@ RES_DRAWABLE_XHDPI = \
res/drawable-xhdpi/alert_mic.png \ res/drawable-xhdpi/alert_mic.png \
res/drawable-xhdpi/alert_mic_camera.png \ res/drawable-xhdpi/alert_mic_camera.png \
res/drawable-xhdpi/arrow_popup_bg.9.png \ res/drawable-xhdpi/arrow_popup_bg.9.png \
res/drawable-xhdpi/home_tab_menu_strip.9.png \
res/drawable-xhdpi/ic_menu_addons_filler.png \ res/drawable-xhdpi/ic_menu_addons_filler.png \
res/drawable-xhdpi/ic_menu_bookmark_add.png \ res/drawable-xhdpi/ic_menu_bookmark_add.png \
res/drawable-xhdpi/ic_menu_bookmark_remove.png \ res/drawable-xhdpi/ic_menu_bookmark_remove.png \

View File

@ -14,10 +14,13 @@ import android.os.Bundle;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter; import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager;
import android.view.ViewGroup.LayoutParams;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.View;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.EnumMap; import java.util.EnumMap;
@ -29,6 +32,7 @@ public class HomePager extends ViewPager {
private final Context mContext; private final Context mContext;
private volatile boolean mLoaded; private volatile boolean mLoaded;
private Decor mDecor;
// List of pages in order. // List of pages in order.
public enum Page { public enum Page {
@ -51,6 +55,21 @@ public class HomePager extends ViewPager {
public void onNewTabs(String[] urls); public void onNewTabs(String[] urls);
} }
interface OnTitleClickListener {
public void onTitleClicked(int index);
}
/**
* Special type of child views that could be added as pager decorations by default.
*/
interface Decor {
public void onAddPagerView(String title);
public void removeAllPagerViews();
public void onPageSelected(int position);
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);
public void setOnTitleClickListener(OnTitleClickListener onTitleClickListener);
}
public HomePager(Context context) { public HomePager(Context context) {
this(context, null); this(context, null);
} }
@ -64,6 +83,30 @@ public class HomePager extends ViewPager {
setOffscreenPageLimit(2); setOffscreenPageLimit(2);
} }
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
if (child instanceof Decor) {
((ViewPager.LayoutParams) params).isDecor = true;
mDecor = (Decor) child;
setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageSelected(int position) {
mDecor.onPageSelected(position);
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
mDecor.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
@Override
public void onPageScrollStateChanged(int state) { }
});
}
super.addView(child, index, params);
}
/** /**
* Loads and initializes the pager. * Loads and initializes the pager.
* *
@ -113,7 +156,8 @@ public class HomePager extends ViewPager {
return mLoaded; return mLoaded;
} }
class TabsAdapter extends FragmentStatePagerAdapter { class TabsAdapter extends FragmentStatePagerAdapter
implements OnTitleClickListener {
private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>(); private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
final class TabInfo { final class TabInfo {
@ -132,12 +176,26 @@ public class HomePager extends ViewPager {
public TabsAdapter(FragmentManager fm) { public TabsAdapter(FragmentManager fm) {
super(fm); super(fm);
if (mDecor != null) {
mDecor.removeAllPagerViews();
mDecor.setOnTitleClickListener(this);
}
} }
public void addTab(Page page, Class<?> clss, Bundle args, String title) { public void addTab(Page page, Class<?> clss, Bundle args, String title) {
TabInfo info = new TabInfo(page, clss, args, title); TabInfo info = new TabInfo(page, clss, args, title);
mTabs.add(info); mTabs.add(info);
notifyDataSetChanged(); notifyDataSetChanged();
if (mDecor != null) {
mDecor.onAddPagerView(title);
}
}
@Override
public void onTitleClicked(int index) {
setCurrentItem(index, true);
} }
public int getItemPosition(Page page) { public int getItemPosition(Page page) {

View File

@ -0,0 +1,202 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; 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.home;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.graphics.Rect;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.accessibility.AccessibilityEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewParent;
import android.view.ViewTreeObserver;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.util.Log;
import org.mozilla.gecko.home.HomePager;
import org.mozilla.gecko.R;
public class TabMenuStrip extends LinearLayout
implements HomePager.Decor,
View.OnFocusChangeListener {
private static final String LOGTAG = "GeckoTabMenuStrip";
private HomePager.OnTitleClickListener mOnTitleClickListener;
private Drawable mStrip;
private View mSelectedView;
// Data associated with the scrolling of the strip drawable.
private View toTab;
private View fromTab;
private float progress;
// This variable is used to predict the direction of scroll.
private float mPrevProgress;
public TabMenuStrip(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabMenuStrip);
final int stripResId = a.getResourceId(R.styleable.TabMenuStrip_strip, -1);
a.recycle();
if (stripResId != -1) {
mStrip = getResources().getDrawable(stripResId);
}
setWillNotDraw(false);
}
@Override
public void onAddPagerView(String title) {
final TextView button = (TextView) LayoutInflater.from(getContext()).inflate(R.layout.tab_menu_strip, this, false);
button.setText(title.toUpperCase());
addView(button);
button.setOnClickListener(new ViewClickListener(getChildCount() - 1));
button.setOnFocusChangeListener(this);
}
@Override
public void removeAllPagerViews() {
removeAllViews();
}
@Override
public void onPageSelected(final int position) {
mSelectedView = getChildAt(position);
// Callback to measure and draw the strip after the view is visible.
ViewTreeObserver vto = mSelectedView.getViewTreeObserver();
if (vto.isAlive()) {
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mSelectedView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
if (mStrip != null) {
mStrip.setBounds(mSelectedView.getLeft(),
mSelectedView.getTop(),
mSelectedView.getRight(),
mSelectedView.getBottom());
}
mPrevProgress = position;
}
});
}
}
// Page scroll animates the drawable and it's bounds from the previous to next child view.
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (mStrip == null) {
return;
}
setScrollingData(position, positionOffset);
final int fromTabLeft = fromTab.getLeft();
final int fromTabRight = fromTab.getRight();
final int toTabLeft = toTab.getLeft();
final int toTabRight = toTab.getRight();
mStrip.setBounds((int) (fromTabLeft + ((toTabLeft - fromTabLeft) * progress)),
0,
(int) (fromTabRight + ((toTabRight - fromTabRight) * progress)),
getHeight());
invalidate();
}
/*
* position + positionOffset goes from 0 to 2 as we scroll from page 1 to 3.
* Normalized progress is relative to the the direction the page is being scrolled towards.
* For this, we maintain direction of scroll with a state, and the child view we are moving towards and away from.
*/
private void setScrollingData(int position, float positionOffset) {
if (position >= getChildCount() - 1) {
return;
}
final float currProgress = position + positionOffset;
if (mPrevProgress > currProgress) {
toTab = getChildAt(position);
fromTab = getChildAt(position + 1);
progress = 1 - positionOffset;
} else {
toTab = getChildAt(position + 1);
fromTab = getChildAt(position);
progress = positionOffset;
}
mPrevProgress = currProgress;
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mStrip != null) {
mStrip.draw(canvas);
}
}
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (v == this && hasFocus && getChildCount() > 0) {
mSelectedView.requestFocus();
return;
}
if (!hasFocus) {
return;
}
int i = 0;
final int numTabs = getChildCount();
while (i < numTabs) {
View view = getChildAt(i);
if (view == v) {
view.requestFocus();
if (isShown()) {
// A view is focused so send an event to announce the menu strip state.
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
}
break;
}
i++;
}
}
@Override
public void setOnTitleClickListener(HomePager.OnTitleClickListener onTitleClickListener) {
mOnTitleClickListener = onTitleClickListener;
}
private class ViewClickListener implements OnClickListener {
private final int mIndex;
public ViewClickListener(int index) {
mIndex = index;
}
@Override
public void onClick(View view) {
if (mOnTitleClickListener != null) {
mOnTitleClickListener.onTitleClicked(mIndex);
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<!-- This file is used to include the home pager in gecko app
layout based on screen size -->
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:gecko="http://schemas.android.com/apk/res-auto">
<org.mozilla.gecko.home.HomePager android:id="@+id/home_pager"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@android:color/white"
android:visibility="gone">
<org.mozilla.gecko.home.TabMenuStrip android:layout_width="fill_parent"
android:layout_height="32dip"
android:background="@color/background_light"
android:layout_gravity="top"
gecko:strip="@drawable/home_tab_menu_strip"/>
</org.mozilla.gecko.home.HomePager>
</merge>

View File

@ -31,23 +31,7 @@
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent"> android:layout_height="fill_parent">
<org.mozilla.gecko.home.HomePager android:id="@+id/home_pager" <include layout="@layout/home_pager"/>
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@android:color/white"
android:visibility="gone"
android:focusableInTouchMode="true"
android:descendantFocusability="afterDescendants">
<org.mozilla.gecko.home.HomePagerTabStrip android:layout_width="fill_parent"
android:layout_height="32dip"
android:layout_gravity="top"
android:gravity="bottom"
android:background="@color/background_light"
gecko:tabIndicatorColor="@color/text_color_highlight"
android:textAppearance="@style/TextAppearance.Widget.HomePagerTabStrip"/>
</org.mozilla.gecko.home.HomePager>
</FrameLayout> </FrameLayout>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<!-- This file is used to include the home pager in gecko app
layout based on screen size -->
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:gecko="http://schemas.android.com/apk/res-auto">
<org.mozilla.gecko.home.HomePager android:id="@+id/home_pager"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@android:color/white"
android:visibility="gone">
<org.mozilla.gecko.home.HomePagerTabStrip android:layout_width="fill_parent"
android:layout_height="32dip"
android:layout_gravity="top"
android:gravity="bottom"
android:background="@color/background_light"
gecko:tabIndicatorColor="@color/text_color_highlight"
android:textAppearance="@style/TextAppearance.Widget.HomePagerTabStrip"/>
</org.mozilla.gecko.home.HomePager>
</merge>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:minWidth="@dimen/tabs_strip_button_width"
android:background="@drawable/tabs_panel_indicator"
android:paddingLeft="@dimen/tabs_strip_button_padding"
android:paddingRight="@dimen/tabs_strip_button_padding"
android:gravity="center"
android:focusable="true"
style="@style/TextAppearance.Widget.HomePagerTabMenuStrip"/>

View File

@ -216,5 +216,9 @@
<attr name="android:verticalSpacing"/> <attr name="android:verticalSpacing"/>
</declare-styleable> </declare-styleable>
<declare-styleable name="TabMenuStrip">
<attr name="strip" format="reference"/>
</declare-styleable>
</resources> </resources>

View File

@ -65,6 +65,8 @@
<dimen name="tabs_panel_list_padding">16dip</dimen> <dimen name="tabs_panel_list_padding">16dip</dimen>
<dimen name="tabs_list_divider_height">2dp</dimen> <dimen name="tabs_list_divider_height">2dp</dimen>
<dimen name="tabs_sidebar_width">200dp</dimen> <dimen name="tabs_sidebar_width">200dp</dimen>
<dimen name="tabs_strip_button_width">100dp</dimen>
<dimen name="tabs_strip_button_padding">18dp</dimen>
<dimen name="tabs_tray_horizontal_height">156dp</dimen> <dimen name="tabs_tray_horizontal_height">156dp</dimen>
<dimen name="text_selection_handle_width">47dp</dimen> <dimen name="text_selection_handle_width">47dp</dimen>
<dimen name="text_selection_handle_height">58dp</dimen> <dimen name="text_selection_handle_height">58dp</dimen>

View File

@ -293,6 +293,11 @@
<item name="android:textColor">?android:attr/textColorHint</item> <item name="android:textColor">?android:attr/textColorHint</item>
</style> </style>
<style name="TextAppearance.Widget.HomePagerTabMenuStrip" parent="TextAppearance.Small">
<item name="android:textColor">?android:attr/textColorHint</item>
<item name="android:textSize">14sp</item>
</style>
<style name="TextAppearance.Widget.TwoLinePageRow" /> <style name="TextAppearance.Widget.TwoLinePageRow" />
<style name="TextAppearance.Widget.TwoLinePageRow.Title" parent="TextAppearance.Medium"/> <style name="TextAppearance.Widget.TwoLinePageRow.Title" parent="TextAppearance.Medium"/>