summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYorke Lee <yorkelee@google.com>2014-04-12 12:42:06 -0700
committerYorke Lee <yorkelee@google.com>2014-04-16 10:04:02 -0700
commite00c9fe163d19ee380b922e3fcbe736216d78ccc (patch)
tree18a9d714c73ff5620ca6e2b99b98312b06aecbac
parent32e7495570c1e8ba9cfd4b32998e8b13fd37f46a (diff)
Use ViewPager in main Dialer view
This change replaces PhoneFavoriteFragment with ListsFragment, a fragment that contains a Viewpager that will eventually contain 3 fragments - Speed Dial, Recents (an abridged call log) and All Contacts. For now, only speed dial and all contacts are in the viewpager due to the call log fragment not playing nice with being embedded in a parent fragment. ViewPagerTabs is a newly added custom view that serves as a indicator for ViewPager tabs. It behaves similarly to the newly deprecated ActionBar tabs, but can be placed anywhere on screen. Bug: 13935070 Change-Id: I916c516dc295246b2a95de2f0dc726784c2ee0cc
-rw-r--r--res/layout/lists_fragment.xml36
-rw-r--r--res/layout/phone_favorites_fragment.xml1
-rw-r--r--res/layout/phone_favorites_menu.xml6
-rw-r--r--res/values/strings.xml22
-rw-r--r--src/com/android/dialer/DialtactsActivity.java113
-rw-r--r--src/com/android/dialer/list/ListsFragment.java100
-rw-r--r--src/com/android/dialer/list/PhoneFavoriteFragment.java27
-rw-r--r--src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java3
-rw-r--r--src/com/android/dialer/list/ViewPagerTabs.java169
9 files changed, 399 insertions, 78 deletions
diff --git a/res/layout/lists_fragment.xml b/res/layout/lists_fragment.xml
new file mode 100644
index 000000000..d4995e0f2
--- /dev/null
+++ b/res/layout/lists_fragment.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingTop="?android:attr/actionBarSize"
+ android:orientation="vertical"
+ android:id="@+id/lists_frame">
+ <com.android.dialer.list.ViewPagerTabs
+ android:id="@+id/lists_pager_header"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/actionBarSize"
+ android:textAllCaps="true"
+ android:orientation="horizontal"
+ android:layout_gravity="top"/>
+ <android.support.v4.view.ViewPager
+ android:id="@+id/lists_pager"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1">
+ </android.support.v4.view.ViewPager>
+</LinearLayout>
diff --git a/res/layout/phone_favorites_fragment.xml b/res/layout/phone_favorites_fragment.xml
index 91acb9058..7a1f05ae6 100644
--- a/res/layout/phone_favorites_fragment.xml
+++ b/res/layout/phone_favorites_fragment.xml
@@ -35,7 +35,6 @@
android:id="@+id/contact_tile_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:paddingTop="?android:attr/actionBarSize"
android:clipToPadding="false"
android:fadingEdge="none"
android:divider="@null" />
diff --git a/res/layout/phone_favorites_menu.xml b/res/layout/phone_favorites_menu.xml
index 387ea5b3f..0f7aa219e 100644
--- a/res/layout/phone_favorites_menu.xml
+++ b/res/layout/phone_favorites_menu.xml
@@ -16,11 +16,13 @@
-->
<!-- The phone favorites menu appears on the main dialer screen above the favorite callers area,
- and provides access to the All Contacts list. -->
+ and provides access to the All Contacts list. This is 1dp tall as a temporary hack to hide
+ it because it is no longer being used. It should be removed from its parent adapter entirely
+ eventually. -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/phone_favorites_menu"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="1dp"
android:paddingLeft="@dimen/favorites_menu_padding_horizontal"
android:paddingRight="@dimen/favorites_menu_padding_horizontal"
android:paddingTop="@dimen/favorites_menu_padding_top"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c608273ff..d67c788da 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -624,14 +624,6 @@
[CHAR LIMIT=30] -->
<string name="dialer_hint_find_contact">Type a name or phone number</string>
- <!-- Title for the call log tab containing the list of all voicemails and calls
- [CHAR LIMIT=15] -->
- <string name="call_log_all_title">All</string>
-
- <!-- Title for the call log tab containing the list of all missed calls only
- [CHAR LIMIT=15] -->
- <string name="call_log_missed_title">Missed</string>
-
<!-- String resource for the font-family to use for the call log activity's title
Do not translate. -->
<string name="call_log_activity_title_font_family">sans-serif-light</string>
@@ -705,6 +697,20 @@
<!-- Do not translate. -->
<string name="dialpad_pound_letters"></string>
+ <!-- Title for the call log tab containing the list of all voicemails and calls
+ [CHAR LIMIT=15] -->
+ <string name="call_log_all_title">All</string>
+
+ <!-- Title for the call log tab containing the list of all missed calls only
+ [CHAR LIMIT=15] -->
+ <string name="call_log_missed_title">Missed</string>
+
+ <string name="tab_speed_dial">Speed Dial</string>
+
+ <string name="tab_recents">Recents</string>
+
+ <string name="tab_all_contacts">Contacts</string>
+
<!-- Title of fragment that displays all contacts -->
<string name="show_all_contacts_title">All contacts</string>
<!-- Title of show all contacts button -->
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index c59db95dc..8b9b71ada 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -71,6 +71,7 @@ import com.android.dialer.dialpad.SmartDialPrefix;
import com.android.dialer.interactions.PhoneNumberInteraction;
import com.android.dialer.list.AllContactsActivity;
import com.android.dialer.list.DragDropController;
+import com.android.dialer.list.ListsFragment;
import com.android.dialer.list.OnDragDropListener;
import com.android.dialer.list.OnListFragmentScrolledListener;
import com.android.dialer.list.PhoneFavoriteFragment;
@@ -94,7 +95,8 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
DialpadFragment.HostInterface,
PhoneFavoriteFragment.OnShowAllContactsListener,
PhoneFavoriteFragment.HostInterface,
- OnDragDropListener, View.OnLongClickListener {
+ OnDragDropListener, View.OnLongClickListener,
+ OnPhoneNumberPickerActionListener {
private static final String TAG = "DialtactsActivity";
public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -129,11 +131,6 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
private static final int ANIMATION_DURATION = 250;
/**
- * The main fragment displaying the user's favorites and frequent contacts
- */
- private PhoneFavoriteFragment mPhoneFavoriteFragment;
-
- /**
* Fragment containing the dialpad that slides into view
*/
private DialpadFragment mDialpadFragment;
@@ -148,6 +145,11 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
*/
private SmartDialSearchFragment mSmartDialSearchFragment;
+ /**
+ * Fragment containing the speed dial list, recents list, and all contacts list.
+ */
+ private ListsFragment mListsFragment;
+
private View mFakeActionBar;
private View mMenuButton;
private View mCallHistoryButton;
@@ -188,6 +190,21 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
private DialerDatabaseHelper mDialerDatabaseHelper;
+ private class OverflowPopupMenu extends PopupMenu {
+ public OverflowPopupMenu(Context context, View anchor) {
+ super(context, anchor);
+ }
+
+ @Override
+ public void show() {
+ final Menu menu = getMenu();
+ final MenuItem clearFrequents = menu.findItem(R.id.menu_clear_frequents);
+ // TODO: Check mPhoneFavoriteFragment.hasFrequents()
+ clearFrequents.setVisible(true);
+ super.show();
+ }
+ }
+
/**
* Listener used when one of phone numbers in search UI is selected. This will initiate a
* phone call using the phone number.
@@ -287,7 +304,7 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
// is null. Otherwise the fragment manager takes care of recreating these fragments.
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
- .add(R.id.dialtacts_frame, new PhoneFavoriteFragment(), TAG_FAVORITES_FRAGMENT)
+ .add(R.id.dialtacts_frame, new ListsFragment(), TAG_FAVORITES_FRAGMENT)
.add(R.id.dialtacts_container, new DialpadFragment(), TAG_DIALPAD_FRAGMENT)
.commit();
} else {
@@ -369,12 +386,8 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
if (mFragmentsFrame != null) {
mFragmentsFrame.setAlpha(1.0f);
}
- } else if (fragment instanceof PhoneFavoriteFragment) {
- mPhoneFavoriteFragment = (PhoneFavoriteFragment) fragment;
- mPhoneFavoriteFragment.setListener(mPhoneFavoriteListener);
- if (mFragmentsFrame != null) {
- mFragmentsFrame.setAlpha(1.0f);
- }
+ } else if (fragment instanceof ListsFragment) {
+ mListsFragment = (ListsFragment) fragment;
}
}
@@ -582,7 +595,7 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
.setInterpolator(hideActionBarInterpolator).setDuration(ANIMATION_DURATION);
}
- if (mPhoneFavoriteFragment != null && mPhoneFavoriteFragment.isVisible()) {
+ if (mListsFragment != null && mListsFragment.isVisible()) {
// If the favorites fragment is showing, fade to blank.
mFragmentsFrame.animate().alpha(0.0f);
}
@@ -608,12 +621,20 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
.setInterpolator(showActionBarInterpolator).setDuration(ANIMATION_DURATION);
}
- if (mPhoneFavoriteFragment != null && mPhoneFavoriteFragment.isVisible()) {
+ if (mListsFragment != null && mListsFragment.isVisible()) {
mFragmentsFrame.animate().alpha(1.0f);
}
getActionBar().show();
}
+ private void hideInputMethod(View view) {
+ final InputMethodManager imm = (InputMethodManager) getSystemService(
+ Context.INPUT_METHOD_SERVICE);
+ if (imm != null && view != null) {
+ imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
+ }
+ }
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
if (DEBUG) {
@@ -711,29 +732,6 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
return !isDialIntent(getIntent()) ? CALL_ORIGIN_DIALTACTS : null;
}
- private final PhoneFavoriteFragment.Listener mPhoneFavoriteListener =
- new PhoneFavoriteFragment.Listener() {
- @Override
- public void onContactSelected(Uri contactUri) {
- PhoneNumberInteraction.startInteractionForPhoneCall(
- DialtactsActivity.this, contactUri, getCallOrigin());
- }
-
- @Override
- public void onCallNumberDirectly(String phoneNumber) {
- Intent intent = CallUtil.getCallIntent(phoneNumber, getCallOrigin());
- startActivity(intent);
- }
- };
-
- private void hideInputMethod(View view) {
- final InputMethodManager imm = (InputMethodManager) getSystemService(
- Context.INPUT_METHOD_SERVICE);
- if (imm != null && view != null) {
- imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
- }
- }
-
/**
* Shows the search fragment
*/
@@ -752,12 +750,10 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
final FragmentTransaction transaction = getFragmentManager().beginTransaction();
SearchFragment fragment;
- if (mInDialpadSearch) {
+ if (mInDialpadSearch && mSmartDialSearchFragment != null) {
transaction.remove(mSmartDialSearchFragment);
- } else if (mInRegularSearch) {
+ } else if (mInRegularSearch && mRegularSearchFragment != null) {
transaction.remove(mRegularSearchFragment);
- } else {
- transaction.remove(mPhoneFavoriteFragment);
}
final String tag;
@@ -860,19 +856,10 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
@Override
public void onListFragmentScroll(int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
-
- // Hide the action bar when scrolling down in the speed dial list, and show it again when
- // scrolling back up.
- if (firstVisibleItem > mPreviousFirstVisibleItem) {
- getActionBar().hide();
- } else if (firstVisibleItem < mPreviousFirstVisibleItem) {
- getActionBar().show();
- }
- mPreviousFirstVisibleItem = firstVisibleItem;
+ // TODO: No-op for now. This should eventually show/hide the actionBar based on
+ // interactions with the ListsFragments.
}
- private int mPreviousFirstVisibleItem = 0;
-
@Override
public void setDialButtonEnabled(boolean enabled) {
if (mDialButton != null) {
@@ -943,4 +930,24 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
public void setDragDropController(DragDropController dragController) {
mRemoveViewContainer.setDragDropController(dragController);
}
+
+ @Override
+ public void onPickPhoneNumberAction(Uri dataUri) {
+ mPhoneNumberPickerActionListener.onPickPhoneNumberAction(dataUri);
+ }
+
+ @Override
+ public void onCallNumberDirectly(String phoneNumber) {
+ mPhoneNumberPickerActionListener.onCallNumberDirectly(phoneNumber);
+ }
+
+ @Override
+ public void onShortcutIntentCreated(Intent intent) {
+ mPhoneNumberPickerActionListener.onShortcutIntentCreated(intent);
+ }
+
+ @Override
+ public void onHomeInActionBarSelected() {
+ mPhoneNumberPickerActionListener.onHomeInActionBarSelected();
+ }
}
diff --git a/src/com/android/dialer/list/ListsFragment.java b/src/com/android/dialer/list/ListsFragment.java
new file mode 100644
index 000000000..768d3601e
--- /dev/null
+++ b/src/com/android/dialer/list/ListsFragment.java
@@ -0,0 +1,100 @@
+package com.android.dialer.list;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.os.Bundle;
+import android.support.v13.app.FragmentPagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
+import com.android.dialer.R;
+
+/**
+ * Fragment that is used as the main screen of the Dialer.
+ *
+ * Contains a ViewPager that contains various contact lists like the Speed Dial list and the
+ * All Contacts list. This will also eventually contain the logic that allows sliding the
+ * ViewPager containing the lists up above the shortcut cards and pin it against the top of the
+ * screen.
+ */
+public class ListsFragment extends Fragment {
+
+ private ViewPager mViewPager;
+ private ViewPagerAdapter mViewPagerAdapter;
+ private PhoneFavoriteFragment mSpeedDialFragment;
+ private AllContactsFragment mAllContactsFragment;
+
+ private OnPhoneNumberPickerActionListener mNumberPickerListener;
+
+ private static final int TAB_INDEX_SPEED_DIAL = 0;
+ private static final int TAB_INDEX_ALL_CONTACTS = 1;
+
+ private String[] mTabTitles;
+
+ private static final int TAB_INDEX_COUNT = 2;
+
+ public class ViewPagerAdapter extends FragmentPagerAdapter {
+ public ViewPagerAdapter(FragmentManager fm) {
+ super(fm);
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ switch (position) {
+ case TAB_INDEX_SPEED_DIAL:
+ mSpeedDialFragment = new PhoneFavoriteFragment();
+ return mSpeedDialFragment;
+ case TAB_INDEX_ALL_CONTACTS:
+ mAllContactsFragment = new AllContactsFragment();
+ mAllContactsFragment.setOnPhoneNumberPickerActionListener(
+ mNumberPickerListener);
+ return mAllContactsFragment;
+ }
+ throw new IllegalStateException("No fragment at position " + position);
+ }
+
+ @Override
+ public int getCount() {
+ return TAB_INDEX_COUNT;
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ return mTabTitles[position];
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ final View parentView = inflater.inflate(R.layout.lists_fragment, container, false);
+ mViewPager = (ViewPager) parentView.findViewById(R.id.lists_pager);
+ mViewPagerAdapter = new ViewPagerAdapter(getChildFragmentManager());
+ mViewPager.setAdapter(mViewPagerAdapter);
+ mViewPager.setOffscreenPageLimit(1);
+
+ mTabTitles = new String[TAB_INDEX_COUNT];
+ mTabTitles[TAB_INDEX_SPEED_DIAL] = getResources().getString(R.string.tab_speed_dial);
+ mTabTitles[TAB_INDEX_ALL_CONTACTS] = getResources().getString(R.string.tab_all_contacts);
+
+ ViewPagerTabs tabs = (ViewPagerTabs) parentView.findViewById(R.id.lists_pager_header);
+ tabs.setViewPager(mViewPager);
+ return parentView;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+
+ try {
+ mNumberPickerListener = (OnPhoneNumberPickerActionListener) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString()
+ + " must implement OnPhoneNumberPickerActionListener");
+ }
+ }
+}
diff --git a/src/com/android/dialer/list/PhoneFavoriteFragment.java b/src/com/android/dialer/list/PhoneFavoriteFragment.java
index a56c3c546..c0dbd8ca5 100644
--- a/src/com/android/dialer/list/PhoneFavoriteFragment.java
+++ b/src/com/android/dialer/list/PhoneFavoriteFragment.java
@@ -51,6 +51,7 @@ import com.android.contacts.common.GeoUtil;
import com.android.contacts.common.list.ContactEntry;
import com.android.contacts.common.list.ContactListItemView;
import com.android.contacts.common.list.ContactTileView;
+import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
import com.android.dialer.DialtactsActivity;
import com.android.dialer.R;
import com.android.dialer.calllog.CallLogAdapter;
@@ -102,11 +103,6 @@ public class PhoneFavoriteFragment extends Fragment implements OnItemClickListen
public void onShowAllContacts();
}
- public interface Listener {
- public void onContactSelected(Uri contactUri);
- public void onCallNumberDirectly(String phoneNumber);
- }
-
public interface HostInterface {
public void setDragDropController(DragDropController controller);
}
@@ -155,15 +151,15 @@ public class PhoneFavoriteFragment extends Fragment implements OnItemClickListen
private class ContactTileAdapterListener implements ContactTileView.Listener {
@Override
public void onContactSelected(Uri contactUri, Rect targetRect) {
- if (mListener != null) {
- mListener.onContactSelected(contactUri);
+ if (mPhoneNumberPickerActionListener != null) {
+ mPhoneNumberPickerActionListener.onPickPhoneNumberAction(contactUri);
}
}
@Override
public void onCallNumberDirectly(String phoneNumber) {
- if (mListener != null) {
- mListener.onCallNumberDirectly(phoneNumber);
+ if (mPhoneNumberPickerActionListener != null) {
+ mPhoneNumberPickerActionListener.onCallNumberDirectly(phoneNumber);
}
}
@@ -189,7 +185,7 @@ public class PhoneFavoriteFragment extends Fragment implements OnItemClickListen
}
}
- private Listener mListener;
+ private OnPhoneNumberPickerActionListener mPhoneNumberPickerActionListener;
private OnListFragmentScrolledListener mActivityScrollListener;
private OnShowAllContactsListener mShowAllContactsListener;
@@ -362,6 +358,13 @@ public class PhoneFavoriteFragment extends Fragment implements OnItemClickListen
+ " must implement OnDragDropListener and HostInterface");
}
+ try {
+ mPhoneNumberPickerActionListener = (OnPhoneNumberPickerActionListener) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString()
+ + " must implement PhoneFavoritesFragment.listener");
+ }
+
// Use initLoader() instead of restartLoader() to refraining unnecessary reload.
// This method call implicitly assures ContactTileLoaderListener's onLoadFinished() will
// be called, on which we'll check if "all" contacts should be reloaded again or not.
@@ -391,10 +394,6 @@ public class PhoneFavoriteFragment extends Fragment implements OnItemClickListen
mShowAllContactsListener.onShowAllContacts();
}
- public void setListener(Listener listener) {
- mListener = listener;
- }
-
@Override
public void onVoicemailStatusFetched(Cursor statusCursor) {
// no-op
diff --git a/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java b/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java
index 81d682b8b..849d6514a 100644
--- a/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java
+++ b/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java
@@ -115,6 +115,9 @@ public class PhoneFavoriteMergedAdapter extends BaseAdapter {
mCallLogAdapter.registerDataSetObserver(mObserver);
mContactTileAdapter.registerDataSetObserver(mObserver);
mPhoneFavoritesMenu = phoneFavoritesMenu;
+ // Temporary hack to hide the favorites menu because it is not being used.
+ // It should be removed from this adapter entirely eventually.
+ mPhoneFavoritesMenu.setVisibility(View.GONE);
mTileInteractionTeaserView = tileInteractionTeaserView;
mCallLogQueryHandler = new CallLogQueryHandler(mContext.getContentResolver(),
mCallLogQueryHandlerListener);
diff --git a/src/com/android/dialer/list/ViewPagerTabs.java b/src/com/android/dialer/list/ViewPagerTabs.java
new file mode 100644
index 000000000..45f468ddd
--- /dev/null
+++ b/src/com/android/dialer/list/ViewPagerTabs.java
@@ -0,0 +1,169 @@
+package com.android.dialer.list;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.HorizontalScrollView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.dialer.R;
+
+/**
+ * Lightweight implementation of ViewPager tabs. This looks similar to traditional actionBar tabs,
+ * but allows for the view containing the tabs to be placed anywhere on screen. Text-related
+ * attributes can also be assigned in XML - these will get propogated to the child TextViews
+ * automatically.
+ */
+public class ViewPagerTabs extends HorizontalScrollView implements ViewPager.OnPageChangeListener {
+
+ ViewPager mPager;
+ /**
+ * Linearlayout that will contain the TextViews serving as tabs. This is the only child
+ * of the parent HorizontalScrollView.
+ */
+ LinearLayout mChild;
+ final ColorStateList mTextColor;
+ final int mTextSize;
+ final boolean mTextAllCaps;
+ int mPrevSelected = -1;
+ int mSidePadding;
+
+ private static final int TAB_SIDE_PADDING_IN_DPS = 10;
+
+ private static final int[] ATTRS = new int[] {
+ android.R.attr.textAppearance,
+ android.R.attr.textSize,
+ android.R.attr.textColor,
+ android.R.attr.textAllCaps
+ };
+
+ /**
+ * Simulates actionbar tab behavior by showing a toast with the tab title when long clicked.
+ */
+ private class OnTabLongClickListener implements OnLongClickListener {
+ final int mPosition;
+
+ public OnTabLongClickListener(int position) {
+ mPosition = position;
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ final int[] screenPos = new int[2];
+ getLocationOnScreen(screenPos);
+
+ final Context context = getContext();
+ final int width = getWidth();
+ final int height = getHeight();
+ final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
+
+ Toast toast = Toast.makeText(context, mPager.getAdapter().getPageTitle(mPosition),
+ Toast.LENGTH_SHORT);
+
+ // Show the toast under the tab
+ toast.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL,
+ (screenPos[0] + width / 2) - screenWidth / 2, screenPos[1] + height);
+
+ toast.show();
+ return true;
+ }
+ }
+
+ public ViewPagerTabs(Context context) {
+ this(context, null);
+ }
+
+ public ViewPagerTabs(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ViewPagerTabs(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ setFillViewport(true);
+
+ mSidePadding = (int) (getResources().getDisplayMetrics().density * TAB_SIDE_PADDING_IN_DPS);
+
+ final TypedArray a = context.obtainStyledAttributes(attrs, ATTRS);
+ mTextSize = a.getDimensionPixelSize(1, 0);
+ mTextColor = a.getColorStateList(2);
+ mTextAllCaps = a.getBoolean(3, false);
+
+ mChild = new LinearLayout(context);
+ addView(mChild,
+ new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
+ }
+
+ public void setViewPager(ViewPager viewPager) {
+ mPager = viewPager;
+ mPager.setOnPageChangeListener(this);
+ addTabs(mPager.getAdapter());
+ }
+
+ private void addTabs(PagerAdapter adapter) {
+ final int count = adapter.getCount();
+ for (int i = 0; i < count; i++) {
+ addTab(adapter.getPageTitle(i), i);
+ }
+ }
+
+ private void addTab(CharSequence tabTitle, final int position) {
+ final TextView textView = new TextView(getContext());
+ textView.setText(tabTitle);
+ textView.setBackgroundResource(R.drawable.action_bar_tab);
+ textView.setGravity(Gravity.CENTER);
+ textView.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mPager.setCurrentItem(position);
+ }
+ });
+
+ textView.setOnLongClickListener(new OnTabLongClickListener(position));
+
+ // Assign various text appearance related attributes to child views.
+ if (mTextSize > 0) {
+ textView.setTextSize(mTextSize);
+ }
+ if (mTextColor != null) {
+ textView.setTextColor(mTextColor);
+ }
+ textView.setAllCaps(mTextAllCaps);
+ textView.setPadding(mSidePadding, 0, mSidePadding, 0);
+ mChild.addView(textView, new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.MATCH_PARENT, 1));
+ // Default to the first child being selected
+ if (position == 0) {
+ mPrevSelected = 0;
+ textView.setSelected(true);
+ }
+ }
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ if (mPrevSelected >= 0) {
+ mChild.getChildAt(mPrevSelected).setSelected(false);
+ }
+ final View selectedChild = mChild.getChildAt(position);
+ selectedChild.setSelected(true);
+ // Update scroll position
+ final int scrollPos = selectedChild.getLeft() - (getWidth() - selectedChild.getWidth()) / 2;
+ smoothScrollTo(scrollPos, 0);
+ mPrevSelected = position;
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ }
+} \ No newline at end of file