Home | History | Annotate | Download | only in content_shell
      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 
      5 package org.chromium.content_shell;
      6 
      7 import android.content.Context;
      8 import android.graphics.drawable.ClipDrawable;
      9 import android.text.TextUtils;
     10 import android.util.AttributeSet;
     11 import android.view.KeyEvent;
     12 import android.view.View;
     13 import android.view.ViewGroup;
     14 import android.view.inputmethod.EditorInfo;
     15 import android.view.inputmethod.InputMethodManager;
     16 import android.widget.EditText;
     17 import android.widget.FrameLayout;
     18 import android.widget.ImageButton;
     19 import android.widget.LinearLayout;
     20 import android.widget.TextView;
     21 import android.widget.TextView.OnEditorActionListener;
     22 
     23 import org.chromium.base.CalledByNative;
     24 import org.chromium.base.JNINamespace;
     25 import org.chromium.content.browser.ContentView;
     26 import org.chromium.content.browser.ContentViewClient;
     27 import org.chromium.content.browser.ContentViewCore;
     28 import org.chromium.content.browser.ContentViewRenderView;
     29 import org.chromium.content_public.browser.LoadUrlParams;
     30 import org.chromium.content_public.browser.NavigationController;
     31 import org.chromium.content_public.browser.WebContents;
     32 import org.chromium.ui.base.WindowAndroid;
     33 
     34 /**
     35  * Container for the various UI components that make up a shell window.
     36  */
     37 @JNINamespace("content")
     38 public class Shell extends LinearLayout {
     39 
     40     private static final long COMPLETED_PROGRESS_TIMEOUT_MS = 200;
     41 
     42     private final Runnable mClearProgressRunnable = new Runnable() {
     43         @Override
     44         public void run() {
     45             mProgressDrawable.setLevel(0);
     46         }
     47     };
     48 
     49     private ContentViewCore mContentViewCore;
     50     private WebContents mWebContents;
     51     private NavigationController mNavigationController;
     52     private ContentViewClient mContentViewClient;
     53     private EditText mUrlTextView;
     54     private ImageButton mPrevButton;
     55     private ImageButton mNextButton;
     56     private ImageButton mStopButton;
     57     private ImageButton mReloadButton;
     58 
     59     private ClipDrawable mProgressDrawable;
     60 
     61     private long mNativeShell;
     62     private ContentViewRenderView mContentViewRenderView;
     63     private WindowAndroid mWindow;
     64 
     65     private boolean mLoading = false;
     66 
     67     /**
     68      * Constructor for inflating via XML.
     69      */
     70     public Shell(Context context, AttributeSet attrs) {
     71         super(context, attrs);
     72     }
     73 
     74     /**
     75      * Set the SurfaceView being renderered to as soon as it is available.
     76      */
     77     public void setContentViewRenderView(ContentViewRenderView contentViewRenderView) {
     78         FrameLayout contentViewHolder = (FrameLayout) findViewById(R.id.contentview_holder);
     79         if (contentViewRenderView == null) {
     80             if (mContentViewRenderView != null) {
     81                 contentViewHolder.removeView(mContentViewRenderView);
     82             }
     83         } else {
     84             contentViewHolder.addView(contentViewRenderView,
     85                     new FrameLayout.LayoutParams(
     86                             FrameLayout.LayoutParams.MATCH_PARENT,
     87                             FrameLayout.LayoutParams.MATCH_PARENT));
     88         }
     89         mContentViewRenderView = contentViewRenderView;
     90     }
     91 
     92     /**
     93      * Initializes the Shell for use.
     94      *
     95      * @param nativeShell The pointer to the native Shell object.
     96      * @param window The owning window for this shell.
     97      * @param client The {@link ContentViewClient} to be bound to any current or new
     98      *               {@link ContentViewCore}s associated with this shell.
     99      */
    100     public void initialize(long nativeShell, WindowAndroid window, ContentViewClient client) {
    101         mNativeShell = nativeShell;
    102         mWindow = window;
    103         mContentViewClient = client;
    104     }
    105 
    106     /**
    107      * Closes the shell and cleans up the native instance, which will handle destroying all
    108      * dependencies.
    109      */
    110     public void close() {
    111         if (mNativeShell == 0) return;
    112         nativeCloseShell(mNativeShell);
    113     }
    114 
    115     @CalledByNative
    116     private void onNativeDestroyed() {
    117         mWindow = null;
    118         mNativeShell = 0;
    119         mContentViewCore.destroy();
    120     }
    121 
    122     /**
    123      * @return Whether the Shell has been destroyed.
    124      * @see #onNativeDestroyed()
    125      */
    126     public boolean isDestroyed() {
    127         return mNativeShell == 0;
    128     }
    129 
    130     /**
    131      * @return Whether or not the Shell is loading content.
    132      */
    133     public boolean isLoading() {
    134         return mLoading;
    135     }
    136 
    137     @Override
    138     protected void onFinishInflate() {
    139         super.onFinishInflate();
    140 
    141         mProgressDrawable = (ClipDrawable) findViewById(R.id.toolbar).getBackground();
    142         initializeUrlField();
    143         initializeNavigationButtons();
    144     }
    145 
    146     private void initializeUrlField() {
    147         mUrlTextView = (EditText) findViewById(R.id.url);
    148         mUrlTextView.setOnEditorActionListener(new OnEditorActionListener() {
    149             @Override
    150             public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
    151                 if ((actionId != EditorInfo.IME_ACTION_GO) && (event == null ||
    152                         event.getKeyCode() != KeyEvent.KEYCODE_ENTER ||
    153                         event.getAction() != KeyEvent.ACTION_DOWN)) {
    154                     return false;
    155                 }
    156                 loadUrl(mUrlTextView.getText().toString());
    157                 setKeyboardVisibilityForUrl(false);
    158                 mContentViewCore.getContainerView().requestFocus();
    159                 return true;
    160             }
    161         });
    162         mUrlTextView.setOnFocusChangeListener(new OnFocusChangeListener() {
    163             @Override
    164             public void onFocusChange(View v, boolean hasFocus) {
    165                 setKeyboardVisibilityForUrl(hasFocus);
    166                 mNextButton.setVisibility(hasFocus ? GONE : VISIBLE);
    167                 mPrevButton.setVisibility(hasFocus ? GONE : VISIBLE);
    168                 if (!hasFocus) {
    169                     mUrlTextView.setText(mWebContents.getUrl());
    170                 }
    171             }
    172         });
    173         mUrlTextView.setOnKeyListener(new OnKeyListener() {
    174             @Override
    175             public boolean onKey(View v, int keyCode, KeyEvent event) {
    176                 if (keyCode == KeyEvent.KEYCODE_BACK) {
    177                     mContentViewCore.getContainerView().requestFocus();
    178                     return true;
    179                 }
    180                 return false;
    181             }
    182         });
    183     }
    184 
    185     /**
    186      * Loads an URL.  This will perform minimal amounts of sanitizing of the URL to attempt to
    187      * make it valid.
    188      *
    189      * @param url The URL to be loaded by the shell.
    190      */
    191     public void loadUrl(String url) {
    192         if (url == null) return;
    193 
    194         if (TextUtils.equals(url, mWebContents.getUrl())) {
    195             mNavigationController.reload(true);
    196         } else {
    197             mNavigationController.loadUrl(new LoadUrlParams(sanitizeUrl(url)));
    198         }
    199         mUrlTextView.clearFocus();
    200         // TODO(aurimas): Remove this when crbug.com/174541 is fixed.
    201         mContentViewCore.getContainerView().clearFocus();
    202         mContentViewCore.getContainerView().requestFocus();
    203     }
    204 
    205     /**
    206      * Given an URL, this performs minimal sanitizing to ensure it will be valid.
    207      * @param url The url to be sanitized.
    208      * @return The sanitized URL.
    209      */
    210     public static String sanitizeUrl(String url) {
    211         if (url == null) return null;
    212         if (url.startsWith("www.") || url.indexOf(":") == -1) url = "http://" + url;
    213         return url;
    214     }
    215 
    216     private void initializeNavigationButtons() {
    217         mPrevButton = (ImageButton) findViewById(R.id.prev);
    218         mPrevButton.setOnClickListener(new OnClickListener() {
    219             @Override
    220             public void onClick(View v) {
    221                 if (mNavigationController.canGoBack()) mNavigationController.goBack();
    222             }
    223         });
    224 
    225         mNextButton = (ImageButton) findViewById(R.id.next);
    226         mNextButton.setOnClickListener(new OnClickListener() {
    227             @Override
    228             public void onClick(View v) {
    229                 if (mNavigationController.canGoForward()) mNavigationController.goForward();
    230             }
    231         });
    232         mStopButton = (ImageButton) findViewById(R.id.stop);
    233         mStopButton.setOnClickListener(new OnClickListener() {
    234             @Override
    235             public void onClick(View v) {
    236                 if (mLoading) mWebContents.stop();
    237             }
    238         });
    239         mReloadButton = (ImageButton) findViewById(R.id.reload);
    240         mReloadButton.setOnClickListener(new OnClickListener() {
    241             @Override
    242             public void onClick(View v) {
    243                 mNavigationController.reload(true);
    244             }
    245         });
    246     }
    247 
    248     @SuppressWarnings("unused")
    249     @CalledByNative
    250     private void onUpdateUrl(String url) {
    251         mUrlTextView.setText(url);
    252     }
    253 
    254     @SuppressWarnings("unused")
    255     @CalledByNative
    256     private void onLoadProgressChanged(double progress) {
    257         removeCallbacks(mClearProgressRunnable);
    258         mProgressDrawable.setLevel((int) (10000.0 * progress));
    259         if (progress == 1.0) postDelayed(mClearProgressRunnable, COMPLETED_PROGRESS_TIMEOUT_MS);
    260     }
    261 
    262     @CalledByNative
    263     private void toggleFullscreenModeForTab(boolean enterFullscreen) {
    264     }
    265 
    266     @CalledByNative
    267     private boolean isFullscreenForTabOrPending() {
    268         return false;
    269     }
    270 
    271     @SuppressWarnings("unused")
    272     @CalledByNative
    273     private void setIsLoading(boolean loading) {
    274         mLoading = loading;
    275     }
    276 
    277     /**
    278      * Initializes the ContentView based on the native tab contents pointer passed in.
    279      * @param nativeWebContents The pointer to the native tab contents object.
    280      */
    281     @SuppressWarnings("unused")
    282     @CalledByNative
    283     private void initFromNativeTabContents(long nativeWebContents) {
    284         Context context = getContext();
    285         mContentViewCore = new ContentViewCore(context);
    286         ContentView cv = ContentView.newInstance(context, mContentViewCore);
    287         mContentViewCore.initialize(cv, cv, nativeWebContents, mWindow);
    288         mContentViewCore.setContentViewClient(mContentViewClient);
    289         mWebContents = mContentViewCore.getWebContents();
    290         mNavigationController = mWebContents.getNavigationController();
    291         if (getParent() != null) mContentViewCore.onShow();
    292         if (mWebContents.getUrl() != null) {
    293             mUrlTextView.setText(mWebContents.getUrl());
    294         }
    295         ((FrameLayout) findViewById(R.id.contentview_holder)).addView(cv,
    296                 new FrameLayout.LayoutParams(
    297                         FrameLayout.LayoutParams.MATCH_PARENT,
    298                         FrameLayout.LayoutParams.MATCH_PARENT));
    299         cv.requestFocus();
    300         mContentViewRenderView.setCurrentContentViewCore(mContentViewCore);
    301     }
    302 
    303     /**
    304      * Enable/Disable navigation(Prev/Next) button if navigation is allowed/disallowed
    305      * in respective direction.
    306      * @param controlId Id of button to update
    307      * @param enabled enable/disable value
    308      */
    309     @CalledByNative
    310     private void enableUiControl(int controlId, boolean enabled) {
    311         if (controlId == 0) mPrevButton.setEnabled(enabled);
    312         else if (controlId == 1) mNextButton.setEnabled(enabled);
    313         else if (controlId == 2) {
    314             mStopButton.setVisibility(enabled ? VISIBLE : GONE);
    315             mReloadButton.setVisibility(enabled ? GONE : VISIBLE);
    316         }
    317     }
    318 
    319     /**
    320      * @return The {@link ViewGroup} currently shown by this Shell.
    321      */
    322     public ViewGroup getContentView() {
    323         return mContentViewCore.getContainerView();
    324     }
    325 
    326     /**
    327      * @return The {@link ContentViewCore} currently managing the view shown by this Shell.
    328      */
    329     public ContentViewCore getContentViewCore() {
    330         return mContentViewCore;
    331     }
    332 
    333     private void setKeyboardVisibilityForUrl(boolean visible) {
    334         InputMethodManager imm = (InputMethodManager) getContext().getSystemService(
    335                 Context.INPUT_METHOD_SERVICE);
    336         if (visible) {
    337             imm.showSoftInput(mUrlTextView, InputMethodManager.SHOW_IMPLICIT);
    338         } else {
    339             imm.hideSoftInputFromWindow(mUrlTextView.getWindowToken(), 0);
    340         }
    341     }
    342 
    343     private static native void nativeCloseShell(long shellPtr);
    344 }
    345