blob: 0f430b0a3ce61dcd058a9ccb12c82b3a1c9d15ac [file] [log] [blame]
Justin Klaassen4b3af052014-05-27 17:53:10 -07001/*
Justin Klaassen06c49442015-06-04 14:39:27 -07002 * Copyright (C) 2015 The Android Open Source Project
Justin Klaassen4b3af052014-05-27 17:53:10 -07003 *
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
17package com.android.calculator2;
18
19import android.content.Context;
Justin Klaassen06c49442015-06-04 14:39:27 -070020import android.graphics.Color;
Aurimas Liutikas8c43f062018-03-28 08:10:28 -070021import androidx.viewpager.widget.PagerAdapter;
22import androidx.viewpager.widget.ViewPager;
Justin Klaassen4b3af052014-05-27 17:53:10 -070023import android.util.AttributeSet;
Annie Chin13cd1002017-02-01 12:48:00 -080024import android.util.Log;
Justin Klaassenda3b3692016-02-05 13:45:29 -080025import android.view.GestureDetector;
Justin Klaassen06c49442015-06-04 14:39:27 -070026import android.view.MotionEvent;
Justin Klaassen4b3af052014-05-27 17:53:10 -070027import android.view.View;
28import android.view.ViewGroup;
29
30public class CalculatorPadViewPager extends ViewPager {
31
32 private final PagerAdapter mStaticPagerAdapter = new PagerAdapter() {
33 @Override
34 public int getCount() {
35 return getChildCount();
36 }
37
38 @Override
Justin Klaassenda3b3692016-02-05 13:45:29 -080039 public View instantiateItem(ViewGroup container, final int position) {
40 final View child = getChildAt(position);
Justin Klaassen788431a2016-07-19 21:42:50 -070041
42 // Set a OnClickListener to scroll to item's position when it isn't the current item.
43 child.setOnClickListener(new OnClickListener() {
Justin Klaassenda3b3692016-02-05 13:45:29 -080044 @Override
45 public void onClick(View v) {
46 setCurrentItem(position, true /* smoothScroll */);
47 }
48 });
Justin Klaassen2cf12892016-08-08 11:58:26 -070049 // Set an OnTouchListener to always return true for onTouch events so that a touch
50 // sequence cannot pass through the item to the item below.
51 child.setOnTouchListener(new OnTouchListener() {
52 @Override
53 public boolean onTouch(View v, MotionEvent event) {
54 v.onTouchEvent(event);
55 return true;
56 }
57 });
Justin Klaassenda3b3692016-02-05 13:45:29 -080058
Justin Klaassen2cf12892016-08-08 11:58:26 -070059 // Set an OnHoverListener to always return true for onHover events so that focus cannot
Justin Klaassen788431a2016-07-19 21:42:50 -070060 // pass through the item to the item below.
61 child.setOnHoverListener(new OnHoverListener() {
62 @Override
63 public boolean onHover(View v, MotionEvent event) {
64 v.onHoverEvent(event);
65 return true;
66 }
67 });
68 // Make the item focusable so it can be selected via a11y.
69 child.setFocusable(true);
70 // Set the content description of the item which will be used by a11y to identify it.
71 child.setContentDescription(getPageTitle(position));
72
Justin Klaassenda3b3692016-02-05 13:45:29 -080073 return child;
Justin Klaassen4b3af052014-05-27 17:53:10 -070074 }
75
76 @Override
77 public void destroyItem(ViewGroup container, int position, Object object) {
78 removeViewAt(position);
79 }
80
81 @Override
82 public boolean isViewFromObject(View view, Object object) {
83 return view == object;
84 }
85
86 @Override
87 public float getPageWidth(int position) {
Justin Klaassen0587d222014-06-19 10:44:21 -070088 return position == 1 ? 7.0f / 9.0f : 1.0f;
Justin Klaassen4b3af052014-05-27 17:53:10 -070089 }
Justin Klaassen788431a2016-07-19 21:42:50 -070090
91 @Override
92 public CharSequence getPageTitle(int position) {
93 final String[] pageDescriptions = getContext().getResources()
94 .getStringArray(R.array.desc_pad_pages);
95 return pageDescriptions[position];
96 }
Justin Klaassen4b3af052014-05-27 17:53:10 -070097 };
Justin Klaassen5c324202014-06-10 20:27:20 -070098
99 private final OnPageChangeListener mOnPageChangeListener = new SimpleOnPageChangeListener() {
Justin Klaassen5c324202014-06-10 20:27:20 -0700100 @Override
101 public void onPageSelected(int position) {
Justin Klaassen06c49442015-06-04 14:39:27 -0700102 for (int i = getChildCount() - 1; i >= 0; --i) {
Justin Klaassen3d7653e2015-06-08 15:27:55 -0700103 final View child = getChildAt(i);
Justin Klaassenda3b3692016-02-05 13:45:29 -0800104 // Only the "peeking" or covered page should be clickable.
105 child.setClickable(i != position);
106
Justin Klaassen3d7653e2015-06-08 15:27:55 -0700107 // Prevent clicks and accessibility focus from going through to descendants of
108 // other pages which are covered by the current page.
Justin Klaassen2cf12892016-08-08 11:58:26 -0700109 if (child instanceof ViewGroup) {
110 final ViewGroup childViewGroup = (ViewGroup) child;
111 for (int j = childViewGroup.getChildCount() - 1; j >= 0; --j) {
112 childViewGroup.getChildAt(j)
113 .setImportantForAccessibility(i == position
114 ? IMPORTANT_FOR_ACCESSIBILITY_AUTO
115 : IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
116 }
117 }
Justin Klaassen5c324202014-06-10 20:27:20 -0700118 }
119 }
120 };
121
Justin Klaassen4b3af052014-05-27 17:53:10 -0700122 private final PageTransformer mPageTransformer = new PageTransformer() {
123 @Override
124 public void transformPage(View view, float position) {
Justin Klaassen5c324202014-06-10 20:27:20 -0700125 if (position < 0.0f) {
Justin Klaassen4b3af052014-05-27 17:53:10 -0700126 // Pin the left page to the left side.
127 view.setTranslationX(getWidth() * -position);
Justin Klaassen5c324202014-06-10 20:27:20 -0700128 view.setAlpha(Math.max(1.0f + position, 0.0f));
Justin Klaassen4b3af052014-05-27 17:53:10 -0700129 } else {
Justin Klaassen5c324202014-06-10 20:27:20 -0700130 // Use the default slide transition when moving to the next page.
131 view.setTranslationX(0.0f);
132 view.setAlpha(1.0f);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700133 }
134 }
135 };
136
Justin Klaassenda3b3692016-02-05 13:45:29 -0800137 private final GestureDetector.SimpleOnGestureListener mGestureWatcher =
138 new GestureDetector.SimpleOnGestureListener() {
Justin Klaassen788431a2016-07-19 21:42:50 -0700139 @Override
140 public boolean onDown(MotionEvent e) {
141 // Return true so calls to onSingleTapUp are not blocked.
142 return true;
143 }
Justin Klaassenda3b3692016-02-05 13:45:29 -0800144
Justin Klaassen788431a2016-07-19 21:42:50 -0700145 @Override
146 public boolean onSingleTapUp(MotionEvent ev) {
147 if (mClickedItemIndex != -1) {
148 getChildAt(mClickedItemIndex).performClick();
149 mClickedItemIndex = -1;
150 return true;
151 }
152 return super.onSingleTapUp(ev);
153 }
154 };
Justin Klaassenda3b3692016-02-05 13:45:29 -0800155
156 private final GestureDetector mGestureDetector;
157
158 private int mClickedItemIndex = -1;
159
Justin Klaassen4b3af052014-05-27 17:53:10 -0700160 public CalculatorPadViewPager(Context context) {
Justin Klaassen06c49442015-06-04 14:39:27 -0700161 this(context, null /* attrs */);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700162 }
163
164 public CalculatorPadViewPager(Context context, AttributeSet attrs) {
165 super(context, attrs);
166
Justin Klaassenda3b3692016-02-05 13:45:29 -0800167 mGestureDetector = new GestureDetector(context, mGestureWatcher);
168 mGestureDetector.setIsLongpressEnabled(false);
169
Justin Klaassen4b3af052014-05-27 17:53:10 -0700170 setAdapter(mStaticPagerAdapter);
Justin Klaassen06c49442015-06-04 14:39:27 -0700171 setBackgroundColor(Color.BLACK);
Annie Chin26e159e2016-05-18 15:17:14 -0700172 setPageMargin(-getResources().getDimensionPixelSize(R.dimen.pad_page_margin));
Justin Klaassen4b3af052014-05-27 17:53:10 -0700173 setPageTransformer(false, mPageTransformer);
Justin Klaassen06c49442015-06-04 14:39:27 -0700174 addOnPageChangeListener(mOnPageChangeListener);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700175 }
176
177 @Override
178 protected void onFinishInflate() {
179 super.onFinishInflate();
180
181 // Invalidate the adapter's data set since children may have been added during inflation.
Justin Klaassen06c49442015-06-04 14:39:27 -0700182 getAdapter().notifyDataSetChanged();
183
184 // Let page change listener know about our initial position.
185 mOnPageChangeListener.onPageSelected(getCurrentItem());
186 }
187
188 @Override
189 public boolean onInterceptTouchEvent(MotionEvent ev) {
Annie Chin13cd1002017-02-01 12:48:00 -0800190 try {
191 // Always intercept touch events when a11y focused since otherwise they will be
192 // incorrectly offset by a11y before being dispatched to children.
193 if (isAccessibilityFocused() || super.onInterceptTouchEvent(ev)) {
194 return true;
Justin Klaassen8e367262016-12-09 08:11:33 -0800195 }
196
Annie Chin13cd1002017-02-01 12:48:00 -0800197 // Only allow the current item to receive touch events.
198 final int action = ev.getActionMasked();
199 if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN) {
200 // If a child is a11y focused then we must always intercept the touch event
201 // since it will be incorrectly offset by a11y.
202 final int childCount = getChildCount();
203 for (int childIndex = childCount - 1; childIndex >= 0; --childIndex) {
204 if (getChildAt(childIndex).isAccessibilityFocused()) {
Justin Klaassen8e367262016-12-09 08:11:33 -0800205 mClickedItemIndex = childIndex;
Annie Chin13cd1002017-02-01 12:48:00 -0800206 return true;
Justin Klaassen8e367262016-12-09 08:11:33 -0800207 }
Annie Chin13cd1002017-02-01 12:48:00 -0800208 }
209
210 if (action == MotionEvent.ACTION_DOWN) {
211 mClickedItemIndex = -1;
212 }
213
214 // Otherwise if touch is on a non-current item then intercept.
215 final int actionIndex = ev.getActionIndex();
216 final float x = ev.getX(actionIndex) + getScrollX();
217 final float y = ev.getY(actionIndex) + getScrollY();
218 for (int i = childCount - 1; i >= 0; --i) {
219 final int childIndex = getChildDrawingOrder(childCount, i);
220 final View child = getChildAt(childIndex);
221 if (child.getVisibility() == VISIBLE
222 && x >= child.getLeft() && x < child.getRight()
223 && y >= child.getTop() && y < child.getBottom()) {
224 if (action == MotionEvent.ACTION_DOWN) {
225 mClickedItemIndex = childIndex;
226 }
227 return childIndex != getCurrentItem();
228 }
Justin Klaassen06c49442015-06-04 14:39:27 -0700229 }
230 }
Justin Klaassen06c49442015-06-04 14:39:27 -0700231
Annie Chin13cd1002017-02-01 12:48:00 -0800232 return false;
233 } catch (IllegalArgumentException e) {
234 Log.e("Calculator", "Error intercepting touch event", e);
235 return false;
236 }
Justin Klaassen4b3af052014-05-27 17:53:10 -0700237 }
Justin Klaassenda3b3692016-02-05 13:45:29 -0800238
239 @Override
240 public boolean onTouchEvent(MotionEvent ev) {
Annie Chin13cd1002017-02-01 12:48:00 -0800241 try {
242 // Allow both the gesture detector and super to handle the touch event so they both see
243 // the full sequence of events. This should be safe since the gesture detector only
244 // handle clicks and super only handles swipes.
245 mGestureDetector.onTouchEvent(ev);
246 return super.onTouchEvent(ev);
247 } catch (IllegalArgumentException e) {
248 Log.e("Calculator", "Error processing touch event", e);
249 return false;
250 }
Justin Klaassenda3b3692016-02-05 13:45:29 -0800251 }
Justin Klaassen4b3af052014-05-27 17:53:10 -0700252}