1 // Copyright (c) 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.inputmethod.EditorInfo; 14 import android.view.inputmethod.InputMethodManager; 15 import android.widget.EditText; 16 import android.widget.FrameLayout; 17 import android.widget.ImageButton; 18 import android.widget.LinearLayout; 19 import android.widget.TextView; 20 import android.widget.TextView.OnEditorActionListener; 21 22 import org.chromium.base.CalledByNative; 23 import org.chromium.base.JNINamespace; 24 import org.chromium.content.browser.ContentView; 25 import org.chromium.content.browser.ContentViewRenderView; 26 import org.chromium.content.browser.LoadUrlParams; 27 import org.chromium.ui.WindowAndroid; 28 29 /** 30 * Container for the various UI components that make up a shell window. 31 */ 32 @JNINamespace("content") 33 public class Shell extends LinearLayout { 34 35 private static final long COMPLETED_PROGRESS_TIMEOUT_MS = 200; 36 37 private Runnable mClearProgressRunnable = new Runnable() { 38 @Override 39 public void run() { 40 mProgressDrawable.setLevel(0); 41 } 42 }; 43 44 // TODO(jrg): a mContentView.destroy() call is needed, both upstream and downstream. 45 private ContentView mContentView; 46 private EditText mUrlTextView; 47 private ImageButton mPrevButton; 48 private ImageButton mNextButton; 49 50 private ClipDrawable mProgressDrawable; 51 52 private ContentViewRenderView mContentViewRenderView; 53 private WindowAndroid mWindow; 54 55 private boolean mLoading = false; 56 57 /** 58 * Constructor for inflating via XML. 59 */ 60 public Shell(Context context, AttributeSet attrs) { 61 super(context, attrs); 62 } 63 64 /** 65 * Set the SurfaceView being renderered to as soon as it is available. 66 */ 67 public void setContentViewRenderView(ContentViewRenderView contentViewRenderView) { 68 FrameLayout contentViewHolder = (FrameLayout) findViewById(R.id.contentview_holder); 69 if (contentViewRenderView == null) { 70 if (mContentViewRenderView != null) { 71 contentViewHolder.removeView(mContentViewRenderView); 72 } 73 } else { 74 contentViewHolder.addView(contentViewRenderView, 75 new FrameLayout.LayoutParams( 76 FrameLayout.LayoutParams.MATCH_PARENT, 77 FrameLayout.LayoutParams.MATCH_PARENT)); 78 } 79 mContentViewRenderView = contentViewRenderView; 80 } 81 82 /** 83 * @param window The owning window for this shell. 84 */ 85 public void setWindow(WindowAndroid window) { 86 mWindow = window; 87 } 88 89 /** 90 * @return Whether or not the Shell is loading content. 91 */ 92 public boolean isLoading() { 93 return mLoading; 94 } 95 96 @Override 97 protected void onFinishInflate() { 98 super.onFinishInflate(); 99 100 mProgressDrawable = (ClipDrawable) findViewById(R.id.toolbar).getBackground(); 101 initializeUrlField(); 102 initializeNavigationButtons(); 103 } 104 105 private void initializeUrlField() { 106 mUrlTextView = (EditText) findViewById(R.id.url); 107 mUrlTextView.setOnEditorActionListener(new OnEditorActionListener() { 108 @Override 109 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 110 if ((actionId != EditorInfo.IME_ACTION_GO) && (event == null || 111 event.getKeyCode() != KeyEvent.KEYCODE_ENTER || 112 event.getAction() != KeyEvent.ACTION_DOWN)) { 113 return false; 114 } 115 loadUrl(mUrlTextView.getText().toString()); 116 setKeyboardVisibilityForUrl(false); 117 mContentView.requestFocus(); 118 return true; 119 } 120 }); 121 mUrlTextView.setOnFocusChangeListener(new OnFocusChangeListener() { 122 @Override 123 public void onFocusChange(View v, boolean hasFocus) { 124 setKeyboardVisibilityForUrl(hasFocus); 125 mNextButton.setVisibility(hasFocus ? GONE : VISIBLE); 126 mPrevButton.setVisibility(hasFocus ? GONE : VISIBLE); 127 if (!hasFocus) { 128 mUrlTextView.setText(mContentView.getUrl()); 129 } 130 } 131 }); 132 } 133 134 /** 135 * Loads an URL. This will perform minimal amounts of sanitizing of the URL to attempt to 136 * make it valid. 137 * 138 * @param url The URL to be loaded by the shell. 139 */ 140 public void loadUrl(String url) { 141 if (url == null) return; 142 143 if (TextUtils.equals(url, mContentView.getUrl())) { 144 mContentView.reload(); 145 } else { 146 mContentView.loadUrl(new LoadUrlParams(sanitizeUrl(url))); 147 } 148 mUrlTextView.clearFocus(); 149 // TODO(aurimas): Remove this when crbug.com/174541 is fixed. 150 mContentView.clearFocus(); 151 mContentView.requestFocus(); 152 } 153 154 /** 155 * Given an URL, this performs minimal sanitizing to ensure it will be valid. 156 * @param url The url to be sanitized. 157 * @return The sanitized URL. 158 */ 159 public static String sanitizeUrl(String url) { 160 if (url == null) return url; 161 if (url.startsWith("www.") || url.indexOf(":") == -1) url = "http://" + url; 162 return url; 163 } 164 165 private void initializeNavigationButtons() { 166 mPrevButton = (ImageButton) findViewById(R.id.prev); 167 mPrevButton.setOnClickListener(new OnClickListener() { 168 @Override 169 public void onClick(View v) { 170 if (mContentView.canGoBack()) mContentView.goBack(); 171 } 172 }); 173 174 mNextButton = (ImageButton) findViewById(R.id.next); 175 mNextButton.setOnClickListener(new OnClickListener() { 176 @Override 177 public void onClick(View v) { 178 if (mContentView.canGoForward()) mContentView.goForward(); 179 } 180 }); 181 } 182 183 @SuppressWarnings("unused") 184 @CalledByNative 185 private void onUpdateUrl(String url) { 186 mUrlTextView.setText(url); 187 } 188 189 @SuppressWarnings("unused") 190 @CalledByNative 191 private void onLoadProgressChanged(double progress) { 192 removeCallbacks(mClearProgressRunnable); 193 mProgressDrawable.setLevel((int) (10000.0 * progress)); 194 if (progress == 1.0) postDelayed(mClearProgressRunnable, COMPLETED_PROGRESS_TIMEOUT_MS); 195 } 196 197 @CalledByNative 198 private void toggleFullscreenModeForTab(boolean enterFullscreen) { 199 } 200 201 @CalledByNative 202 private boolean isFullscreenForTabOrPending() { 203 return false; 204 } 205 206 @SuppressWarnings("unused") 207 @CalledByNative 208 private void setIsLoading(boolean loading) { 209 mLoading = loading; 210 } 211 212 /** 213 * Initializes the ContentView based on the native tab contents pointer passed in. 214 * @param nativeTabContents The pointer to the native tab contents object. 215 */ 216 @SuppressWarnings("unused") 217 @CalledByNative 218 private void initFromNativeTabContents(int nativeTabContents) { 219 mContentView = ContentView.newInstance(getContext(), nativeTabContents, mWindow); 220 if (mContentView.getUrl() != null) mUrlTextView.setText(mContentView.getUrl()); 221 ((FrameLayout) findViewById(R.id.contentview_holder)).addView(mContentView, 222 new FrameLayout.LayoutParams( 223 FrameLayout.LayoutParams.MATCH_PARENT, 224 FrameLayout.LayoutParams.MATCH_PARENT)); 225 mContentView.requestFocus(); 226 mContentViewRenderView.setCurrentContentView(mContentView); 227 } 228 229 /** 230 * @return The {@link ContentView} currently shown by this Shell. 231 */ 232 public ContentView getContentView() { 233 return mContentView; 234 } 235 236 private void setKeyboardVisibilityForUrl(boolean visible) { 237 InputMethodManager imm = (InputMethodManager) getContext().getSystemService( 238 Context.INPUT_METHOD_SERVICE); 239 if (visible) { 240 imm.showSoftInput(mUrlTextView, InputMethodManager.SHOW_IMPLICIT); 241 } else { 242 imm.hideSoftInputFromWindow(mUrlTextView.getWindowToken(), 0); 243 } 244 } 245 } 246