1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.messaging.ui;
17 
18 import android.content.Context;
19 import android.content.res.ColorStateList;
20 import android.content.res.TypedArray;
21 import android.graphics.Outline;
22 import android.graphics.drawable.ColorDrawable;
23 import androidx.viewpager.widget.PagerAdapter;
24 import androidx.viewpager.widget.ViewPager;
25 import android.util.AttributeSet;
26 import android.util.TypedValue;
27 import android.view.Gravity;
28 import android.view.View;
29 import android.view.ViewOutlineProvider;
30 import android.widget.FrameLayout;
31 import android.widget.HorizontalScrollView;
32 import android.widget.LinearLayout;
33 import android.widget.TextView;
34 import android.widget.Toast;
35 
36 import com.android.messaging.Factory;
37 import com.android.messaging.R;
38 import com.android.messaging.util.OsUtil;
39 
40 /**
41  * Lightweight implementation of ViewPager tabs. This looks similar to traditional actionBar tabs,
42  * but allows for the view containing the tabs to be placed anywhere on screen. Text-related
43  * attributes can also be assigned in XML - these will get propogated to the child TextViews
44  * automatically.
45  *
46  * Note: this file is taken from the AOSP /packages/apps/ContactsCommon/src/com/android/contacts/
47  * common/list/ViewPagerTabs.java. Some platform specific API calls (e.g. ViewOutlineProvider which
48  * assumes L and above) have been modified to support down to Api Level 16.
49  */
50 public class ViewPagerTabs extends HorizontalScrollView implements ViewPager.OnPageChangeListener {
51 
52     ViewPager mPager;
53     private ViewPagerTabStrip mTabStrip;
54 
55     /**
56      * Linearlayout that will contain the TextViews serving as tabs. This is the only child
57      * of the parent HorizontalScrollView.
58      */
59     final int mTextStyle;
60     final ColorStateList mTextColor;
61     final int mTextSize;
62     final boolean mTextAllCaps;
63     int mPrevSelected = -1;
64     int mSidePadding;
65 
66     private static final int TAB_SIDE_PADDING_IN_DPS = 10;
67 
68     // TODO: This should use <declare-styleable> in the future
69     private static final int[] ATTRS = new int[] {
70         android.R.attr.textSize,
71         android.R.attr.textStyle,
72         android.R.attr.textColor,
73         android.R.attr.textAllCaps
74     };
75 
76     /**
77      * Shows a toast with the tab description when long-clicked.
78      */
79     private class OnTabLongClickListener implements OnLongClickListener {
80         final String mTabDescription;
81 
OnTabLongClickListener(String tabDescription)82         public OnTabLongClickListener(String tabDescription) {
83             mTabDescription = tabDescription;
84         }
85 
86         @Override
onLongClick(View v)87         public boolean onLongClick(View v) {
88             final int[] screenPos = new int[2];
89             getLocationOnScreen(screenPos);
90 
91             final Context context = getContext();
92             final int width = getWidth();
93             final int height = getHeight();
94             final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
95 
96             Toast toast = Toast.makeText(context, mTabDescription, Toast.LENGTH_SHORT);
97 
98             // Show the toast under the tab
99             toast.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL,
100                     (screenPos[0] + width / 2) - screenWidth / 2, screenPos[1] + height);
101 
102             toast.show();
103             return true;
104         }
105     }
106 
ViewPagerTabs(Context context)107     public ViewPagerTabs(Context context) {
108         this(context, null);
109     }
110 
ViewPagerTabs(Context context, AttributeSet attrs)111     public ViewPagerTabs(Context context, AttributeSet attrs) {
112         this(context, attrs, 0);
113     }
114 
ViewPagerTabs(Context context, AttributeSet attrs, int defStyle)115     public ViewPagerTabs(Context context, AttributeSet attrs, int defStyle) {
116         super(context, attrs, defStyle);
117         setFillViewport(true);
118 
119         mSidePadding = (int) (getResources().getDisplayMetrics().density * TAB_SIDE_PADDING_IN_DPS);
120 
121         final TypedArray a = context.obtainStyledAttributes(attrs, ATTRS);
122         mTextSize = a.getDimensionPixelSize(0, 0);
123         mTextStyle = a.getInt(1, 0);
124         mTextColor = a.getColorStateList(2);
125         mTextAllCaps = a.getBoolean(3, false);
126 
127         mTabStrip = new ViewPagerTabStrip(context);
128         addView(mTabStrip,
129                 new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
130         a.recycle();
131 
132         // enable shadow casting from view bounds
133         if (OsUtil.isAtLeastL()) {
134             setOutlineProvider(new ViewOutlineProvider() {
135                 @Override
136                 public void getOutline(View view, Outline outline) {
137                     outline.setRect(0, 0, view.getWidth(), view.getHeight());
138                 }
139             });
140         }
141     }
142 
setViewPager(ViewPager viewPager)143     public void setViewPager(ViewPager viewPager) {
144         mPager = viewPager;
145         addTabs(mPager.getAdapter());
146     }
147 
addTabs(PagerAdapter adapter)148     private void addTabs(PagerAdapter adapter) {
149         mTabStrip.removeAllViews();
150 
151         final int count = adapter.getCount();
152         for (int i = 0; i < count; i++) {
153             addTab(adapter.getPageTitle(i), i);
154         }
155     }
156 
addTab(CharSequence tabTitle, final int position)157     private void addTab(CharSequence tabTitle, final int position) {
158         final TextView textView = new TextView(getContext());
159         textView.setText(tabTitle);
160         textView.setBackgroundResource(R.drawable.contact_picker_tab_background_selector);
161         textView.setGravity(Gravity.CENTER);
162         textView.setOnClickListener(new OnClickListener() {
163             @Override
164             public void onClick(View v) {
165                 mPager.setCurrentItem(getRtlPosition(position));
166             }
167         });
168 
169         // Assign various text appearance related attributes to child views.
170         if (mTextStyle > 0) {
171             textView.setTypeface(textView.getTypeface(), mTextStyle);
172         }
173         if (mTextSize > 0) {
174             textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
175         }
176         if (mTextColor != null) {
177             textView.setTextColor(mTextColor);
178         }
179         textView.setAllCaps(mTextAllCaps);
180         textView.setPadding(mSidePadding, 0, mSidePadding, 0);
181         mTabStrip.addView(textView, new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
182                 LayoutParams.MATCH_PARENT, 1));
183         // Default to the first child being selected
184         if (position == 0) {
185             mPrevSelected = 0;
186             textView.setSelected(true);
187         }
188     }
189 
190     @Override
onPageScrolled(int position, float positionOffset, int positionOffsetPixels)191     public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
192         position = getRtlPosition(position);
193         int tabStripChildCount = mTabStrip.getChildCount();
194         if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
195             return;
196         }
197 
198         mTabStrip.onPageScrolled(position, positionOffset, positionOffsetPixels);
199     }
200 
201     @Override
onPageSelected(int position)202     public void onPageSelected(int position) {
203         position = getRtlPosition(position);
204         int tabStripChildCount = mTabStrip.getChildCount();
205         if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
206             return;
207         }
208 
209         if (mPrevSelected >= 0 && mPrevSelected < tabStripChildCount) {
210             mTabStrip.getChildAt(mPrevSelected).setSelected(false);
211         }
212         final View selectedChild = mTabStrip.getChildAt(position);
213         selectedChild.setSelected(true);
214 
215         // Update scroll position
216         final int scrollPos = selectedChild.getLeft() - (getWidth() - selectedChild.getWidth()) / 2;
217         smoothScrollTo(scrollPos, 0);
218         mPrevSelected = position;
219     }
220 
221     @Override
onPageScrollStateChanged(int state)222     public void onPageScrollStateChanged(int state) {
223     }
224 
getRtlPosition(int position)225     private int getRtlPosition(int position) {
226         if (OsUtil.isAtLeastJB_MR2() && Factory.get().getApplicationContext().getResources()
227                 .getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
228             return mTabStrip.getChildCount() - 1 - position;
229         }
230         return position;
231     }
232 
getSelectedItemPosition()233     public int getSelectedItemPosition() {
234         return mPrevSelected;
235     }
236 }
237