1596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang/* 2596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang * Copyright (C) 2014 The Android Open Source Project 3596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang * 4596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang * Licensed under the Apache License, Version 2.0 (the "License"); 5596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang * you may not use this file except in compliance with the License. 6596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang * You may obtain a copy of the License at 7596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang * 8596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang * http://www.apache.org/licenses/LICENSE-2.0 9596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang * 10596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang * Unless required by applicable law or agreed to in writing, software 11596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang * distributed under the License is distributed on an "AS IS" BASIS, 12596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang * See the License for the specific language governing permissions and 14596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang * limitations under the License. 15596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang */ 16596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang 17596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhangpackage com.android.deskclock; 18596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang 19596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhangimport android.content.Context; 20596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhangimport android.support.v4.view.ViewConfigurationCompat; 21596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhangimport android.support.v4.view.ViewPager; 22596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhangimport android.util.AttributeSet; 23596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhangimport android.view.MotionEvent; 24596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhangimport android.view.View; 25596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhangimport android.view.ViewConfiguration; 26596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhangimport android.view.ViewParent; 27596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang 28596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhangpublic class VerticalViewPager extends ViewPager { 29596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang // TODO Remove the hack of using a parent view pager 30596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang private ViewPager mParentViewPager; 31596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang private float mLastMotionX; 32596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang private float mLastMotionY; 33596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang private float mTouchSlop; 34596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang private boolean mVerticalDrag; 35596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang private boolean mHorizontalDrag; 36596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang 37596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang // Vertical transit page transformer 38596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang private final ViewPager.PageTransformer mPageTransformer = new ViewPager.PageTransformer() { 39596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang @Override 40596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang public void transformPage(View view, float position) { 41596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang final int pageWidth = view.getWidth(); 42596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang final int pageHeight = view.getHeight(); 43596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang if (position < -1) { 44596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang // This page is way off-screen to the left. 45596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang view.setAlpha(0); 46596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang } else if (position <= 1) { 47596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang view.setAlpha(1); 48596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang // Counteract the default slide transition 49596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang view.setTranslationX(pageWidth * -position); 50596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang // set Y position to swipe in from top 51596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang float yPosition = position * pageHeight; 52596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang view.setTranslationY(yPosition); 53596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang } else { 54596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang // This page is way off-screen to the right. 55596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang view.setAlpha(0); 56596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang } 57596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang } 58596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang }; 59596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang 60596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang public VerticalViewPager(Context context) { 61596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang super(context, null); 62596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang } 63596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang 64596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang public VerticalViewPager(Context context, AttributeSet attrs) { 65596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang super(context, attrs); 66596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang final ViewConfiguration configuration = ViewConfiguration.get(context); 67596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration); 68596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang init(); 69596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang } 70596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang 71596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang private void init() { 72596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang // Make page transit vertical 73596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang setPageTransformer(true, mPageTransformer); 74596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang // Get rid of the overscroll drawing that happens on the left and right (the ripple) 75596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang setOverScrollMode(View.OVER_SCROLL_NEVER); 76596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang } 77596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang 78596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang @Override 79596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang public boolean onTouchEvent(MotionEvent ev) { 80596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang try { 81596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang initializeParent(); 82596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang final float x = ev.getX(); 83596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang final float y = ev.getY(); 84596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang switch (ev.getAction()) { 85596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang case MotionEvent.ACTION_DOWN: { 86596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang mLastMotionX = x; 87596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang mLastMotionY = y; 88596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang if (!mParentViewPager.onTouchEvent(ev)) 89596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang return false; 90596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang return verticalDrag(ev); 91596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang } 92596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang case MotionEvent.ACTION_MOVE: { 93596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang final float xDiff = Math.abs(x - mLastMotionX); 94596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang final float yDiff = Math.abs(y - mLastMotionY); 95596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang if (!mHorizontalDrag && !mVerticalDrag) { 96596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang if (xDiff > mTouchSlop && xDiff > yDiff) { // Swiping left and right 97596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang mHorizontalDrag = true; 98596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang } else if (yDiff > mTouchSlop && yDiff > xDiff) { //Swiping up and down 99596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang mVerticalDrag = true; 100596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang } 101596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang } 102596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang if (mHorizontalDrag) { 103596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang return mParentViewPager.onTouchEvent(ev); 104596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang } else if (mVerticalDrag) { 105596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang return verticalDrag(ev); 106596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang } 107596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang } 108596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang case MotionEvent.ACTION_UP: { 109596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang if (mHorizontalDrag) { 110596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang mHorizontalDrag = false; 111596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang return mParentViewPager.onTouchEvent(ev); 112596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang } 113596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang if (mVerticalDrag) { 114596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang mVerticalDrag = false; 115596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang return verticalDrag(ev); 116596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang } 117596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang } 118596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang } 119596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang // Set both flags to false in case user lifted finger in the parent view pager 120596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang mHorizontalDrag = false; 121596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang mVerticalDrag = false; 122596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang } catch (Exception e) { 123596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang // The mParentViewPager shouldn't be null, but just in case. If this happens, 124596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang // app should not crash, instead just ignore the user swipe input 125596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang // TODO: handle the exception gracefully 126596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang } 127596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang return false; 128596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang } 129596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang 130596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang private void initializeParent() { 131596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang if (mParentViewPager == null) { 132596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang // This vertical view pager is nested in the frame layout inside the timer tab 133596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang // (fragment), which is nested inside the horizontal view pager. Therefore, 134596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang // it needs 3 layers to get all the way to the horizontal view pager. 135596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang final ViewParent parent = getParent().getParent().getParent(); 136596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang if (parent instanceof ViewPager) { 137596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang mParentViewPager = (ViewPager) parent; 138596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang } 139596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang } 140596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang } 141596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang 142596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang private boolean verticalDrag(MotionEvent ev) { 143596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang final float x = ev.getX(); 144596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang final float y = ev.getY(); 145596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang ev.setLocation(y, x); 146596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang return super.onTouchEvent(ev); 147596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang } 148596d8dc712f18192354f44b1ee9c91a09296bd82rachelzhang} 149