| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package org.chromium.chrome.browser.ntp; |
| |
| import android.annotation.SuppressLint; |
| import android.content.Context; |
| import android.graphics.Canvas; |
| import android.os.Build; |
| import android.util.AttributeSet; |
| import android.view.GestureDetector; |
| import android.view.KeyEvent; |
| import android.view.MotionEvent; |
| import android.view.View; |
| import android.view.inputmethod.EditorInfo; |
| import android.view.inputmethod.InputConnection; |
| import android.widget.ScrollView; |
| |
| import org.chromium.base.Log; |
| import org.chromium.chrome.R; |
| import org.chromium.chrome.browser.widget.FadingShadow; |
| |
| /** |
| * Simple wrapper on top of a ScrollView that will acquire focus when tapped. Ensures the |
| * New Tab page receives focus when clicked. This is only used in the Incognito NTP. |
| */ |
| public class NewTabPageScrollView extends ScrollView { |
| private static final String TAG = "NewTabPageScrollView"; |
| |
| private GestureDetector mGestureDetector; |
| private FadingShadow mFadingShadow; |
| |
| /** |
| * Constructor needed to inflate from XML. |
| */ |
| public NewTabPageScrollView(Context context, AttributeSet attrs) { |
| super(context, attrs); |
| |
| mGestureDetector = new GestureDetector( |
| getContext(), new GestureDetector.SimpleOnGestureListener() { |
| @Override |
| public boolean onSingleTapUp(MotionEvent e) { |
| boolean retVal = super.onSingleTapUp(e); |
| requestFocus(); |
| return retVal; |
| } |
| }); |
| } |
| |
| /** |
| * Enables drawing a shadow at the bottom of the view when the view's content extends beyond |
| * the bottom of the view. This is exactly the same as a fading edge, except that the shadow |
| * color can have an alpha component, whereas a fading edge color must be opaque. |
| * |
| * @param shadowColor The color of the shadow, e.g. 0x11000000. |
| */ |
| public void enableBottomShadow(int shadowColor) { |
| mFadingShadow = new FadingShadow(shadowColor); |
| setFadingEdgeLength(getResources().getDimensionPixelSize(R.dimen.ntp_shadow_height)); |
| } |
| |
| @Override |
| public boolean onInterceptTouchEvent(MotionEvent ev) { |
| mGestureDetector.onTouchEvent(ev); |
| return super.onInterceptTouchEvent(ev); |
| } |
| |
| @Override |
| @SuppressLint("ClickableViewAccessibility") |
| public boolean onTouchEvent(MotionEvent ev) { |
| // Action down would already have been handled in onInterceptTouchEvent |
| if (ev.getActionMasked() != MotionEvent.ACTION_DOWN) { |
| mGestureDetector.onTouchEvent(ev); |
| } |
| try { |
| return super.onTouchEvent(ev); |
| } catch (IllegalArgumentException ex) { |
| // In JB MR0 and earlier, an ACTION_MOVE that is not preceded by an ACTION_DOWN event |
| // causes a crash. This can happen under normal circumstances (e.g. going back to the |
| // NTP while a finger is down on the screen) and should not crash. The most reliable way |
| // to prevent this crash is to catch the exception. http://crbug.com/293822 |
| if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1 |
| && ev.getActionMasked() == MotionEvent.ACTION_MOVE |
| && "pointerIndex out of range".equals(ex.getMessage())) { |
| Log.d(TAG, "Ignoring pointerIndex out of range exception."); |
| return true; |
| } |
| throw ex; |
| } |
| } |
| |
| @Override |
| public void focusableViewAvailable(View v) { |
| // To avoid odd jumps during NTP animation transitions, we do not attempt to give focus |
| // to child views if this scroll view already has focus. |
| if (hasFocus()) return; |
| super.focusableViewAvailable(v); |
| } |
| |
| @Override |
| public boolean executeKeyEvent(KeyEvent event) { |
| // Ignore all key events except arrow keys and spacebar. Otherwise, the ScrollView consumes |
| // unwanted events (including the hardware menu button and app-level keyboard shortcuts). |
| // http://crbug.com/308322 |
| switch (event.getKeyCode()) { |
| case KeyEvent.KEYCODE_DPAD_UP: |
| case KeyEvent.KEYCODE_DPAD_DOWN: |
| case KeyEvent.KEYCODE_SPACE: |
| return super.executeKeyEvent(event); |
| default: |
| return false; |
| } |
| } |
| |
| @Override |
| public InputConnection onCreateInputConnection(EditorInfo outAttrs) { |
| // Fixes lanscape transitions when unfocusing the URL bar: crbug.com/288546 |
| outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN; |
| return super.onCreateInputConnection(outAttrs); |
| } |
| |
| @Override |
| protected void dispatchDraw(Canvas canvas) { |
| super.dispatchDraw(canvas); |
| if (mFadingShadow != null) { |
| setVerticalFadingEdgeEnabled(true); |
| float shadowStrength = getBottomFadingEdgeStrength(); |
| float shadowHeight = getVerticalFadingEdgeLength(); |
| setVerticalFadingEdgeEnabled(false); |
| mFadingShadow.drawShadow(this, canvas, FadingShadow.POSITION_BOTTOM, |
| shadowHeight, shadowStrength); |
| } |
| } |
| } |