1 /*
2  * Copyright (C) 2014 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 
17 package com.android.deskclock;
18 
19 import android.content.Context;
20 import android.support.v4.view.ViewConfigurationCompat;
21 import android.support.v4.view.ViewPager;
22 import android.util.AttributeSet;
23 import android.view.MotionEvent;
24 import android.view.View;
25 import android.view.ViewConfiguration;
26 import android.view.ViewParent;
27 
28 public class VerticalViewPager extends ViewPager {
29     // TODO Remove the hack of using a parent view pager
30     private ViewPager mParentViewPager;
31     private float mLastMotionX;
32     private float mLastMotionY;
33     private float mTouchSlop;
34     private boolean mVerticalDrag;
35     private boolean mHorizontalDrag;
36 
37     // Vertical transit page transformer
38     private final ViewPager.PageTransformer mPageTransformer = new ViewPager.PageTransformer() {
39         @Override
40         public void transformPage(View view, float position) {
41             final int pageWidth = view.getWidth();
42             final int pageHeight = view.getHeight();
43             if (position < -1) {
44                 // This page is way off-screen to the left.
45                 view.setAlpha(0);
46             } else if (position <= 1) {
47                 view.setAlpha(1);
48                 // Counteract the default slide transition
49                 view.setTranslationX(pageWidth * -position);
50                 // set Y position to swipe in from top
51                 float yPosition = position * pageHeight;
52                 view.setTranslationY(yPosition);
53             } else {
54                 // This page is way off-screen to the right.
55                 view.setAlpha(0);
56             }
57         }
58     };
59 
VerticalViewPager(Context context)60     public VerticalViewPager(Context context) {
61         super(context, null);
62     }
63 
VerticalViewPager(Context context, AttributeSet attrs)64     public VerticalViewPager(Context context, AttributeSet attrs) {
65         super(context, attrs);
66         final ViewConfiguration configuration = ViewConfiguration.get(context);
67         mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
68         init();
69     }
70 
init()71     private void init() {
72         // Make page transit vertical
73         setPageTransformer(true, mPageTransformer);
74         // Get rid of the overscroll drawing that happens on the left and right (the ripple)
75         setOverScrollMode(View.OVER_SCROLL_NEVER);
76     }
77 
78     @Override
onTouchEvent(MotionEvent ev)79     public boolean onTouchEvent(MotionEvent ev) {
80         try {
81             initializeParent();
82             final float x = ev.getX();
83             final float y = ev.getY();
84             switch (ev.getAction()) {
85                 case MotionEvent.ACTION_DOWN: {
86                     mLastMotionX = x;
87                     mLastMotionY = y;
88                     if (!mParentViewPager.onTouchEvent(ev))
89                         return false;
90                     return verticalDrag(ev);
91                 }
92                 case MotionEvent.ACTION_MOVE: {
93                     final float xDiff = Math.abs(x - mLastMotionX);
94                     final float yDiff = Math.abs(y - mLastMotionY);
95                     if (!mHorizontalDrag && !mVerticalDrag) {
96                         if (xDiff > mTouchSlop && xDiff > yDiff) { // Swiping left and right
97                             mHorizontalDrag = true;
98                         } else if (yDiff > mTouchSlop && yDiff > xDiff) { //Swiping up and down
99                             mVerticalDrag = true;
100                         }
101                     }
102                     if (mHorizontalDrag) {
103                         return mParentViewPager.onTouchEvent(ev);
104                     } else if (mVerticalDrag) {
105                         return verticalDrag(ev);
106                     }
107                 }
108                 case MotionEvent.ACTION_UP: {
109                     if (mHorizontalDrag) {
110                         mHorizontalDrag = false;
111                         return mParentViewPager.onTouchEvent(ev);
112                     }
113                     if (mVerticalDrag) {
114                         mVerticalDrag = false;
115                         return verticalDrag(ev);
116                     }
117                 }
118             }
119             // Set both flags to false in case user lifted finger in the parent view pager
120             mHorizontalDrag = false;
121             mVerticalDrag = false;
122         } catch (Exception e) {
123             // The mParentViewPager shouldn't be null, but just in case. If this happens,
124             // app should not crash, instead just ignore the user swipe input
125             // TODO: handle the exception gracefully
126         }
127         return false;
128     }
129 
initializeParent()130     private void initializeParent() {
131         if (mParentViewPager == null) {
132             // This vertical view pager is nested in the frame layout inside the timer tab
133             // (fragment), which is nested inside the horizontal view pager. Therefore,
134             // it needs 3 layers to get all the way to the horizontal view pager.
135             final ViewParent parent = getParent().getParent().getParent();
136             if (parent instanceof ViewPager) {
137                 mParentViewPager = (ViewPager) parent;
138             }
139         }
140     }
141 
verticalDrag(MotionEvent ev)142     private boolean verticalDrag(MotionEvent ev) {
143         final float x = ev.getX();
144         final float y = ev.getY();
145         ev.setLocation(y, x);
146         return super.onTouchEvent(ev);
147     }
148 }
149