ContentView.java revision a02191e04bc25c4935f804f2c080ae28663d096d
1// Copyright 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package org.chromium.content.browser;
6
7import android.content.Context;
8import android.content.res.Configuration;
9import android.graphics.Canvas;
10import android.graphics.Rect;
11import android.os.Build;
12import android.view.KeyEvent;
13import android.view.MotionEvent;
14import android.view.View;
15import android.view.accessibility.AccessibilityEvent;
16import android.view.accessibility.AccessibilityNodeInfo;
17import android.view.inputmethod.EditorInfo;
18import android.view.inputmethod.InputConnection;
19import android.widget.FrameLayout;
20
21import com.google.common.annotations.VisibleForTesting;
22
23import org.chromium.base.TraceEvent;
24import org.chromium.ui.base.WindowAndroid;
25
26/**
27 * The containing view for {@link ContentViewCore} that exists in the Android UI hierarchy and
28 * exposes the various {@link View} functionality to it.
29 *
30 * TODO(joth): Remove any methods overrides from this class that were added for WebView
31 *             compatibility.
32 */
33public class ContentView extends FrameLayout
34        implements ContentViewCore.InternalAccessDelegate, PageInfo {
35
36    private final ContentViewCore mContentViewCore;
37
38    private float mCurrentTouchOffsetX;
39    private float mCurrentTouchOffsetY;
40    private final int[] mLocationInWindow = new int[2];
41
42    /**
43     * Creates an instance of a ContentView.
44     * @param context The Context the view is running in, through which it can
45     *                access the current theme, resources, etc.
46     * @param nativeWebContents A pointer to the native web contents.
47     * @param windowAndroid An instance of the WindowAndroid.
48     * @return A ContentView instance.
49     */
50    public static ContentView newInstance(
51            Context context, long nativeWebContents, WindowAndroid windowAndroid) {
52        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
53            return new ContentView(context, nativeWebContents, windowAndroid);
54        } else {
55            return new JellyBeanContentView(context, nativeWebContents, windowAndroid);
56        }
57    }
58
59    protected ContentView(Context context, long nativeWebContents, WindowAndroid windowAndroid) {
60        super(context, null, android.R.attr.webViewStyle);
61
62        if (getScrollBarStyle() == View.SCROLLBARS_INSIDE_OVERLAY) {
63            setHorizontalScrollBarEnabled(false);
64            setVerticalScrollBarEnabled(false);
65        }
66
67        setFocusable(true);
68        setFocusableInTouchMode(true);
69
70        mContentViewCore = new ContentViewCore(context);
71        mContentViewCore.initialize(this, this, nativeWebContents, windowAndroid);
72    }
73
74    /**
75     * @return The URL of the page.
76     */
77    public String getUrl() {
78        return mContentViewCore.getUrl();
79    }
80
81    // PageInfo implementation.
82
83    @Override
84    public String getTitle() {
85        return mContentViewCore.getTitle();
86    }
87
88    @Override
89    public int getBackgroundColor() {
90        return mContentViewCore.getBackgroundColor();
91    }
92
93    @Override
94    public View getView() {
95        return this;
96    }
97
98    /**
99     * @return The core component of the ContentView that handles JNI communication.  Should only be
100     *         used for passing to native.
101     */
102    public ContentViewCore getContentViewCore() {
103        return mContentViewCore;
104    }
105
106    /**
107     * Destroy the internal state of the WebView. This method may only be called
108     * after the WebView has been removed from the view system. No other methods
109     * may be called on this WebView after this method has been called.
110     */
111    public void destroy() {
112        mContentViewCore.destroy();
113    }
114
115    /**
116     * Returns true initially, false after destroy() has been called.
117     * It is illegal to call any other public method after destroy().
118     */
119    public boolean isAlive() {
120        return mContentViewCore.isAlive();
121    }
122
123    @VisibleForTesting
124    public ContentViewClient getContentViewClient() {
125        return mContentViewCore.getContentViewClient();
126    }
127
128    /**
129     * Load url without fixing up the url string. Consumers of ContentView are responsible for
130     * ensuring the URL passed in is properly formatted (i.e. the scheme has been added if left
131     * off during user input).
132     *
133     * @param params Parameters for this load.
134     */
135    public void loadUrl(LoadUrlParams params) {
136        mContentViewCore.loadUrl(params);
137    }
138
139    /**
140     * @return Whether the current WebContents has a previous navigation entry.
141     */
142    public boolean canGoBack() {
143        return mContentViewCore.canGoBack();
144    }
145
146    /**
147     * @return Whether the current WebContents has a navigation entry after the current one.
148     */
149    public boolean canGoForward() {
150        return mContentViewCore.canGoForward();
151    }
152
153    /**
154     * Goes to the navigation entry before the current one.
155     */
156    public void goBack() {
157        mContentViewCore.goBack();
158    }
159
160    /**
161     * Goes to the navigation entry following the current one.
162     */
163    public void goForward() {
164        mContentViewCore.goForward();
165    }
166
167    /**
168     * Fling the ContentView from the current position.
169     * @param x Fling touch starting position
170     * @param y Fling touch starting position
171     * @param velocityX Initial velocity of the fling (X) measured in pixels per second.
172     * @param velocityY Initial velocity of the fling (Y) measured in pixels per second.
173     */
174    @VisibleForTesting
175    public void fling(long timeMs, int x, int y, int velocityX, int velocityY) {
176        mContentViewCore.flingForTest(timeMs, x, y, velocityX, velocityY);
177    }
178
179    /**
180     * To be called when the ContentView is shown.
181     **/
182    public void onShow() {
183        mContentViewCore.onShow();
184    }
185
186    /**
187     * To be called when the ContentView is hidden.
188     **/
189    public void onHide() {
190        mContentViewCore.onHide();
191    }
192
193    /**
194     * Hides the select action bar.
195     */
196    public void hideSelectActionBar() {
197        mContentViewCore.hideSelectActionBar();
198    }
199
200    // FrameLayout overrides.
201
202    // Needed by ContentViewCore.InternalAccessDelegate
203    @Override
204    public boolean drawChild(Canvas canvas, View child, long drawingTime) {
205        return super.drawChild(canvas, child, drawingTime);
206    }
207
208    // Needed by ContentViewCore.InternalAccessDelegate
209    @Override
210    public void onScrollChanged(int l, int t, int oldl, int oldt) {
211        super.onScrollChanged(l, t, oldl, oldt);
212    }
213
214    @Override
215    protected void onSizeChanged(int w, int h, int ow, int oh) {
216        TraceEvent.begin();
217        super.onSizeChanged(w, h, ow, oh);
218        mContentViewCore.onSizeChanged(w, h, ow, oh);
219        TraceEvent.end();
220    }
221
222    @Override
223    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
224        super.onLayout(changed, left, top, right, bottom);
225        if (changed) {
226            getLocationInWindow(mLocationInWindow);
227            mContentViewCore.onLocationInWindowChanged(mLocationInWindow[0], mLocationInWindow[1]);
228        }
229    }
230
231    @Override
232    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
233        return mContentViewCore.onCreateInputConnection(outAttrs);
234    }
235
236    @Override
237    public boolean onCheckIsTextEditor() {
238        return mContentViewCore.onCheckIsTextEditor();
239    }
240
241    @Override
242    protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
243        TraceEvent.begin();
244        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
245        mContentViewCore.onFocusChanged(gainFocus);
246        TraceEvent.end();
247    }
248
249    @Override
250    public void onWindowFocusChanged(boolean hasWindowFocus) {
251        super.onWindowFocusChanged(hasWindowFocus);
252        mContentViewCore.onWindowFocusChanged(hasWindowFocus);
253    }
254
255    @Override
256    public boolean onKeyUp(int keyCode, KeyEvent event) {
257        return mContentViewCore.onKeyUp(keyCode, event);
258    }
259
260    @Override
261    public boolean dispatchKeyEventPreIme(KeyEvent event) {
262        return mContentViewCore.dispatchKeyEventPreIme(event);
263    }
264
265    @Override
266    public boolean dispatchKeyEvent(KeyEvent event) {
267        if (isFocused()) {
268            return mContentViewCore.dispatchKeyEvent(event);
269        } else {
270            return super.dispatchKeyEvent(event);
271        }
272    }
273
274    @Override
275    public boolean onTouchEvent(MotionEvent event) {
276        MotionEvent offset = createOffsetMotionEvent(event);
277        boolean consumed = mContentViewCore.onTouchEvent(offset);
278        offset.recycle();
279        return consumed;
280    }
281
282    /**
283     * Mouse move events are sent on hover enter, hover move and hover exit.
284     * They are sent on hover exit because sometimes it acts as both a hover
285     * move and hover exit.
286     */
287    @Override
288    public boolean onHoverEvent(MotionEvent event) {
289        MotionEvent offset = createOffsetMotionEvent(event);
290        boolean consumed = mContentViewCore.onHoverEvent(offset);
291        offset.recycle();
292        super.onHoverEvent(event);
293        return consumed;
294    }
295
296    @Override
297    public boolean onGenericMotionEvent(MotionEvent event) {
298        return mContentViewCore.onGenericMotionEvent(event);
299    }
300
301    @Override
302    public boolean performLongClick() {
303        return false;
304    }
305
306    /**
307     * Sets the current amount to offset incoming touch events by.  This is used to handle content
308     * moving and not lining up properly with the android input system.
309     * @param dx The X offset in pixels to shift touch events.
310     * @param dy The Y offset in pixels to shift touch events.
311     */
312    public void setCurrentMotionEventOffsets(float dx, float dy) {
313        mCurrentTouchOffsetX = dx;
314        mCurrentTouchOffsetY = dy;
315    }
316
317    private MotionEvent createOffsetMotionEvent(MotionEvent src) {
318        MotionEvent dst = MotionEvent.obtain(src);
319        dst.offsetLocation(mCurrentTouchOffsetX, mCurrentTouchOffsetY);
320        return dst;
321    }
322
323    @Override
324    protected void onConfigurationChanged(Configuration newConfig) {
325        mContentViewCore.onConfigurationChanged(newConfig);
326    }
327
328    /**
329     * Currently the ContentView scrolling happens in the native side. In
330     * the Java view system, it is always pinned at (0, 0). scrollBy() and scrollTo()
331     * are overridden, so that View's mScrollX and mScrollY will be unchanged at
332     * (0, 0). This is critical for drawing ContentView correctly.
333     */
334    @Override
335    public void scrollBy(int x, int y) {
336        mContentViewCore.scrollBy(x, y);
337    }
338
339    @Override
340    public void scrollTo(int x, int y) {
341        mContentViewCore.scrollTo(x, y);
342    }
343
344    @Override
345    protected int computeHorizontalScrollExtent() {
346        // TODO(dtrainor): Need to expose scroll events properly to public. Either make getScroll*
347        // work or expose computeHorizontalScrollOffset()/computeVerticalScrollOffset as public.
348        return mContentViewCore.computeHorizontalScrollExtent();
349    }
350
351    @Override
352    protected int computeHorizontalScrollOffset() {
353        return mContentViewCore.computeHorizontalScrollOffset();
354    }
355
356    @Override
357    protected int computeHorizontalScrollRange() {
358        return mContentViewCore.computeHorizontalScrollRange();
359    }
360
361    @Override
362    protected int computeVerticalScrollExtent() {
363        return mContentViewCore.computeVerticalScrollExtent();
364    }
365
366    @Override
367    protected int computeVerticalScrollOffset() {
368        return mContentViewCore.computeVerticalScrollOffset();
369    }
370
371    @Override
372    protected int computeVerticalScrollRange() {
373        return mContentViewCore.computeVerticalScrollRange();
374    }
375
376    // End FrameLayout overrides.
377
378    @Override
379    public boolean awakenScrollBars(int startDelay, boolean invalidate) {
380        return mContentViewCore.awakenScrollBars(startDelay, invalidate);
381    }
382
383    @Override
384    public boolean awakenScrollBars() {
385        return super.awakenScrollBars();
386    }
387
388    public int getSingleTapX()  {
389        return mContentViewCore.getSingleTapX();
390    }
391
392    public int getSingleTapY()  {
393        return mContentViewCore.getSingleTapY();
394    }
395
396    @Override
397    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
398        super.onInitializeAccessibilityNodeInfo(info);
399        mContentViewCore.onInitializeAccessibilityNodeInfo(info);
400    }
401
402    /**
403     * Fills in scrolling values for AccessibilityEvents.
404     * @param event Event being fired.
405     */
406    @Override
407    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
408        super.onInitializeAccessibilityEvent(event);
409        mContentViewCore.onInitializeAccessibilityEvent(event);
410    }
411
412    @Override
413    protected void onAttachedToWindow() {
414        super.onAttachedToWindow();
415        mContentViewCore.onAttachedToWindow();
416    }
417
418    @Override
419    protected void onDetachedFromWindow() {
420        super.onDetachedFromWindow();
421        mContentViewCore.onDetachedFromWindow();
422    }
423
424    @Override
425    protected void onVisibilityChanged(View changedView, int visibility) {
426        super.onVisibilityChanged(changedView, visibility);
427        mContentViewCore.onVisibilityChanged(changedView, visibility);
428    }
429
430    /**
431     * Return the current scale of the WebView
432     * @return The current scale.
433     */
434    public float getScale() {
435        return mContentViewCore.getScale();
436    }
437
438    /**
439     * Enable or disable accessibility features.
440     */
441    public void setAccessibilityState(boolean state) {
442        mContentViewCore.setAccessibilityState(state);
443    }
444
445    /**
446     * Inform WebKit that Fullscreen mode has been exited by the user.
447     */
448    public void exitFullscreen() {
449        mContentViewCore.exitFullscreen();
450    }
451
452    /**
453     * Return content scroll y.
454     *
455     * @return The vertical scroll position in pixels.
456     */
457    public int getContentScrollY() {
458        return mContentViewCore.computeVerticalScrollOffset();
459    }
460
461    /**
462     * Return content height.
463     *
464     * @return The height of the content in pixels.
465     */
466    public int getContentHeight() {
467        return mContentViewCore.computeVerticalScrollRange();
468    }
469
470    ///////////////////////////////////////////////////////////////////////////////////////////////
471    //              Start Implementation of ContentViewCore.InternalAccessDelegate               //
472    ///////////////////////////////////////////////////////////////////////////////////////////////
473
474    @Override
475    public boolean super_onKeyUp(int keyCode, KeyEvent event) {
476        return super.onKeyUp(keyCode, event);
477    }
478
479    @Override
480    public boolean super_dispatchKeyEventPreIme(KeyEvent event) {
481        return super.dispatchKeyEventPreIme(event);
482    }
483
484    @Override
485    public boolean super_dispatchKeyEvent(KeyEvent event) {
486        return super.dispatchKeyEvent(event);
487    }
488
489    @Override
490    public boolean super_onGenericMotionEvent(MotionEvent event) {
491        return super.onGenericMotionEvent(event);
492    }
493
494    @Override
495    public void super_onConfigurationChanged(Configuration newConfig) {
496        super.onConfigurationChanged(newConfig);
497    }
498
499    @Override
500    public boolean super_awakenScrollBars(int startDelay, boolean invalidate) {
501        return super.awakenScrollBars(startDelay, invalidate);
502    }
503
504    ///////////////////////////////////////////////////////////////////////////////////////////////
505    //                End Implementation of ContentViewCore.InternalAccessDelegate               //
506    ///////////////////////////////////////////////////////////////////////////////////////////////
507}
508