AI2 Component  (Version nb184)
WebViewer.java
Go to the documentation of this file.
1 // -*- mode: java; c-basic-offset: 2; -*-
2 // Copyright 2009-2011 Google, All Rights reserved
3 // Copyright 2011-2020 MIT, All rights reserved
4 // Released under the Apache License, Version 2.0
5 // http://www.apache.org/licenses/LICENSE-2.0
6 
7 package com.google.appinventor.components.runtime;
8 
9 import android.Manifest;
10 import android.graphics.Bitmap;
11 import android.view.MotionEvent;
12 import android.view.View;
13 import android.webkit.CookieManager;
14 import android.webkit.JavascriptInterface;
15 import android.webkit.WebView;
16 import android.webkit.WebViewClient;
17 
29 
34 
35 
81 @DesignerComponent(version = YaVersion.WEBVIEWER_COMPONENT_VERSION,
82  category = ComponentCategory.USERINTERFACE,
83  description = "Component for viewing Web pages. The Home URL can be " +
84  "specified in the Designer or in the Blocks Editor. The view can be set " +
85  "to follow links when they are tapped, and users can fill in Web forms. " +
86  "Warning: This is not a full browser. For example, pressing the phone's " +
87  "hardware Back key will exit the app, rather than move back in the " +
88  "browser history." +
89  "<p />You can use the WebViewer.WebViewString property to communicate " +
90  "between your app and Javascript code running in the Webviewer page. " +
91  "In the app, you get and set WebViewString. " +
92  "In the WebViewer, you include Javascript that references the window.AppInventor " +
93  "object, using the methoods </em getWebViewString()</em> and <em>setWebViewString(text)</em>. " +
94  "<p />For example, if the WebViewer opens to a page that contains the Javascript command " +
95  "<br /> <em>document.write(\"The answer is\" + window.AppInventor.getWebViewString());</em> " +
96  "<br />and if you set WebView.WebVewString to \"hello\", then the web page will show " +
97  "</br ><em>The answer is hello</em>. " +
98  "<br />And if the Web page contains Javascript that executes the command " +
99  "<br /><em>window.AppInventor.setWebViewString(\"hello from Javascript\")</em>, " +
100  "<br />then the value of the WebViewString property will be " +
101  "<br /><em>hello from Javascript</em>. ")
102 
103 // TODO(halabelson): Integrate control of the Back key, when we provide it
104 
105 @SimpleObject
106 @UsesPermissions(permissionNames = "android.permission.INTERNET")
107 public final class WebViewer extends AndroidViewComponent {
108 
109  private final WebView webview;
110 
111  // URL for the WebViewer to load initially
112  private String homeUrl;
113 
114  // whether or not to follow links when they are tapped
115  private boolean followLinks = true;
116 
117  // Whether or not to prompt for permission in the WebViewer
118  private boolean prompt = true;
119 
120  // ignore SSL Errors (mostly certificate errors. When set
121  // self signed certificates should work.
122 
123  private boolean ignoreSslErrors = false;
124 
125  // allows passing strings to javascript
126  WebViewInterface wvInterface;
127 
128  // Flag to mark whether we have received permission to read external storage
129  private boolean havePermission = false;
130 
136  public WebViewer(ComponentContainer container) {
137  super(container);
138 
139  webview = new WebView(container.$context());
140  resetWebViewClient(); // Set up the web view client
141  webview.getSettings().setJavaScriptEnabled(true);
142  webview.setFocusable(true);
143  // adds a way to send strings to the javascript
144  wvInterface = new WebViewInterface();
145  webview.addJavascriptInterface(wvInterface, "AppInventor");
146  // enable pinch zooming and zoom controls
147  webview.getSettings().setBuiltInZoomControls(true);
148 
150  EclairUtil.setupWebViewGeoLoc(this, webview, container.$context());
151 
152  container.$add(this);
153 
154  webview.setOnTouchListener(new View.OnTouchListener() {
155  @Override
156  public boolean onTouch(View v, MotionEvent event) {
157  switch (event.getAction()) {
158  case MotionEvent.ACTION_DOWN:
159  case MotionEvent.ACTION_UP:
160  if (!v.hasFocus()) {
161  v.requestFocus();
162  }
163  break;
164  }
165  return false;
166  }
167  });
168 
169  // set the initial default properties. Height and Width
170  // will be fill-parent, which will be the default for the web viewer.
171 
172  HomeUrl("");
173  Width(LENGTH_FILL_PARENT);
174  Height(LENGTH_FILL_PARENT);
175  }
176 
183  @SimpleProperty(description = "Gets the WebView's String, which is viewable through " +
184  "Javascript in the WebView as the window.AppInventor object",
185  category = PropertyCategory.BEHAVIOR)
186  public String WebViewString() {
187  return wvInterface.getWebViewString();
188  }
189 
196  public void WebViewString(String newString) {
197  wvInterface.setWebViewStringFromBlocks(newString);
198  }
199 
200  @Override
201  public View getView() {
202  return webview;
203  }
204 
205  // Create a class so we can override the default link following behavior.
206  // The handler doesn't do anything on its own. But returning true means that
207  // this do nothing will override the default WebView behavior. Returning
208  // false means to let the WebView handle the Url. In other words, returning
209  // true will not follow the link, and returning false will follow the link.
210  private class WebViewerClient extends WebViewClient {
211  @Override
212  public boolean shouldOverrideUrlLoading(WebView view, String url) {
213  return !followLinks;
214  }
215 
216  @Override
217  public void onPageStarted(WebView view, String url, Bitmap favicon) {
218  BeforePageLoad(url);
219  }
220 
221  @Override
222  public void onPageFinished(WebView view, String url) {
223  PageLoaded(url);
224  }
225 
226  @Override
227  public void onReceivedError(WebView view, final int errorCode, final String description, final String failingUrl) {
228  container.$form().runOnUiThread(new Runnable() {
229  public void run() {
230  ErrorOccurred(errorCode, description, failingUrl);
231  }
232  });
233  }
234  }
235 
236  // Components don't normally override Width and Height, but we do it here so that
237  // the automatic width and height will be fill parent.
238 
243  @Override
244  @SimpleProperty()
245  public void Width(int width) {
246  if (width == LENGTH_PREFERRED) {
247  width = LENGTH_FILL_PARENT;
248  }
249  super.Width(width);
250  }
251 
256  @Override
257  @SimpleProperty()
258  public void Height(int height) {
259  if (height == LENGTH_PREFERRED) {
260  height = LENGTH_FILL_PARENT;
261  }
262  super.Height(height);
263  }
264 
265 
272  description = "URL of the page the WebViewer should initially open to. " +
273  "Setting this will load the page.",
274  category = PropertyCategory.BEHAVIOR)
275  public String HomeUrl() {
276  return homeUrl;
277  }
278 
286  defaultValue = "")
287  @SimpleProperty()
288  public void HomeUrl(String url) {
289  homeUrl = url;
290  // clear the history, since changing Home is a kind of reset
291  webview.clearHistory();
292  loadUrl("HomeUrl", homeUrl);
293  }
294 
302  description = "URL of the page currently viewed. This could be different from the " +
303  "Home URL if new pages were visited by following links.",
304  category = PropertyCategory.BEHAVIOR)
305  public String CurrentUrl() {
306  return (webview.getUrl() == null) ? "" : webview.getUrl();
307  }
308 
315  description = "Title of the page currently viewed",
316  category = PropertyCategory.BEHAVIOR)
317  public String CurrentPageTitle() {
318  return (webview.getTitle() == null) ? "" : webview.getTitle();
319  }
320 
321 
326  description = "Determines whether to follow links when they are tapped in the WebViewer. " +
327  "If you follow links, you can use GoBack and GoForward to navigate the browser history. ",
328  category = PropertyCategory.BEHAVIOR)
329  public boolean FollowLinks() {
330  return followLinks;
331  }
332 
333 
340  defaultValue = "True")
341  @SimpleProperty()
342  public void FollowLinks(boolean follow) {
343  followLinks = follow;
344  resetWebViewClient();
345  }
346 
355  description = "Determine whether or not to ignore SSL errors. Set to true to ignore " +
356  "errors. Use this to accept self signed certificates from websites.",
357  category = PropertyCategory.BEHAVIOR)
358  public boolean IgnoreSslErrors() {
359  return ignoreSslErrors;
360  }
361 
369  defaultValue = "False")
370  @SimpleProperty()
371  public void IgnoreSslErrors(boolean ignoreSslErrors) {
372  this.ignoreSslErrors = ignoreSslErrors;
373  resetWebViewClient();
374  }
375 
381  description = "Loads the home URL page. This happens automatically when " +
382  "the home URL is changed.")
383  public void GoHome() {
384  loadUrl("GoHome", homeUrl);
385  }
386 
391  description = "Go back to the previous page in the history list. " +
392  "Does nothing if there is no previous page.")
393  public void GoBack() {
394  if (webview.canGoBack()) {
395  webview.goBack();
396  }
397  }
398 
403  description = "Go forward to the next page in the history list. " +
404  "Does nothing if there is no next page.")
405  public void GoForward() {
406  if (webview.canGoForward()) {
407  webview.goForward();
408  }
409  }
410 
415  description = "Returns true if the WebViewer can go forward in the history list.")
416  public boolean CanGoForward() {
417  return webview.canGoForward();
418  }
419 
420 
425  description = "Returns true if the WebViewer can go back in the history list.")
426  public boolean CanGoBack() {
427  return webview.canGoBack();
428  }
429 
430 
435  description = "Load the page at the given URL.")
436  public void GoToUrl(String url) {
437  loadUrl("GoToUrl", url);
438  }
439 
444  description = "Stop loading a page.")
445  public void StopLoading() {
446  webview.stopLoading();
447  }
448 
453  description = "Reload the current page.")
454  public void Reload() {
455  webview.reload();
456  }
457 
465  defaultValue = "False")
466  @SimpleProperty(userVisible = false,
467  description = "Whether or not to give the application permission to use the Javascript geolocation API. " +
468  "This property is available only in the designer.")
469  public void UsesLocation(boolean uses) {
470  // We don't actually do anything here (the work is in the MockWebViewer)
471  }
472 
480  @SimpleProperty(description = "If True, then prompt the user of the WebView to give permission to access the geolocation API. " +
481  "If False, then assume permission is granted.")
482  public boolean PromptforPermission() {
483  return prompt;
484  }
485 
496  defaultValue = "True")
497  @SimpleProperty(userVisible = true)
498  public void PromptforPermission(boolean prompt) {
499  this.prompt = prompt;
500  }
501 
511  @SimpleFunction(description = "Clear stored location permissions.")
512  public void ClearLocations() {
515  }
516 
517  private void resetWebViewClient() {
519  webview.setWebViewClient(FroyoUtil.getWebViewClient(ignoreSslErrors, followLinks, container.$form(), this));
520  } else {
521  webview.setWebViewClient(new WebViewerClient());
522  }
523  }
524 
530  @SimpleFunction(description = "Clear WebView caches.")
531  public void ClearCaches() {
532  webview.clearCache(true);
533  }
534 
539  @SimpleFunction(description = "Clear WebView cookies.")
540  public void ClearCookies() {
541  CookieManager cookieManager = CookieManager.getInstance();
543  cookieManager.removeAllCookies(null);
544  } else {
545  cookieManager.removeAllCookie();
546  }
547  }
548 
552  @SimpleFunction(description = "Run JavaScript in the current page.")
553  public void RunJavaScript(String js) {
554  // evaluateJavascript() was added in API 19
555  // and is therefore not used here for compatibility purposes.
556  webview.loadUrl("javascript:(function(){" + js + "})()");
557  }
558 
564  @SimpleEvent(description = "When the JavaScript calls AppInventor.setWebViewString this event is run.")
565  public void WebViewStringChange(String value) {
566  EventDispatcher.dispatchEvent(this, "WebViewStringChange", value);
567  }
568 
569  @SimpleEvent(description = "When a page is about to load this event is run.")
570  public void BeforePageLoad(String url) {
571  EventDispatcher.dispatchEvent(this, "BeforePageLoad", url);
572  }
573 
574  @SimpleEvent(description = "When a page is finished loading this event is run.")
575  public void PageLoaded(String url) {
576  EventDispatcher.dispatchEvent(this, "PageLoaded", url);
577  }
578 
579  @SimpleEvent(description = "When an error occurs this event is run.")
580  public void ErrorOccurred(int errorCode, String description, String failingUrl) {
581  EventDispatcher.dispatchEvent(this, "ErrorOccurred", errorCode, description, failingUrl);
582  }
583 
584  private void loadUrl(final String caller, final String url) {
585  if (!havePermission && MediaUtil.isExternalFileUrl(container.$form(), url)) {
586  container.$form().askPermission(Manifest.permission.READ_EXTERNAL_STORAGE,
588  @Override
589  public void HandlePermissionResponse(String permission, boolean granted) {
590  if (granted) {
591  havePermission = true;
592  webview.loadUrl(url);
593  } else {
594  container.$form().dispatchPermissionDeniedEvent(WebViewer.this, caller,
595  Manifest.permission.READ_EXTERNAL_STORAGE);
596  }
597  }
598  });
599  return;
600  }
601  webview.loadUrl(url);
602  }
603 
608  public class WebViewInterface {
609  String webViewString;
610 
612  WebViewInterface() {
613  webViewString = " ";
614  }
615 
621  @JavascriptInterface
622  public String getWebViewString() {
623  return webViewString;
624  }
625 
629  @JavascriptInterface
630  public void setWebViewString(final String newString) {
631  webViewString = newString;
632 
633  container.$form().runOnUiThread(new Runnable() {
634  public void run() {
635  WebViewStringChange(newString);
636  }
637  });
638  }
639 
640  public void setWebViewStringFromBlocks(final String newString) {
641  webViewString = newString;
642  }
643 
644  }
645 }
646 
com.google.appinventor.components.runtime.EventDispatcher
Definition: EventDispatcher.java:22
com.google.appinventor.components.runtime.util.SdkLevel.LEVEL_LOLLIPOP
static final int LEVEL_LOLLIPOP
Definition: SdkLevel.java:35
com.google.appinventor.components.runtime.WebViewer.WebViewInterface.setWebViewString
void setWebViewString(final String newString)
Definition: WebViewer.java:630
com.google.appinventor.components.annotations.SimpleFunction
Definition: SimpleFunction.java:23
com.google.appinventor.components.runtime.util.EclairUtil.setupWebViewGeoLoc
static void setupWebViewGeoLoc(final WebViewer caller, WebView webview, final Activity activity)
Definition: EclairUtil.java:56
com.google.appinventor.components.runtime.WebViewer.WebViewInterface
Definition: WebViewer.java:608
com.google.appinventor.components.runtime.util
-*- mode: java; c-basic-offset: 2; -*-
Definition: AccountChooser.java:7
com.google.appinventor.components.common.YaVersion
Definition: YaVersion.java:14
com.google.appinventor.components.annotations.DesignerProperty
Definition: DesignerProperty.java:25
com.google.appinventor.components.common.PropertyTypeConstants.PROPERTY_TYPE_STRING
static final String PROPERTY_TYPE_STRING
Definition: PropertyTypeConstants.java:237
com.google.appinventor.components
com.google.appinventor.components.common.PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN
static final String PROPERTY_TYPE_BOOLEAN
Definition: PropertyTypeConstants.java:35
com.google.appinventor.components.runtime.util.MediaUtil
Definition: MediaUtil.java:53
com.google.appinventor.components.annotations.DesignerComponent
Definition: DesignerComponent.java:22
com.google.appinventor.components.annotations.SimpleEvent
Definition: SimpleEvent.java:20
com.google.appinventor.components.runtime.WebViewer.WebViewInterface.getWebViewString
String getWebViewString()
Definition: WebViewer.java:622
com.google.appinventor.components.annotations.PropertyCategory.BEHAVIOR
BEHAVIOR
Definition: PropertyCategory.java:15
com.google.appinventor.components.runtime.util.SdkLevel.LEVEL_ECLAIR
static final int LEVEL_ECLAIR
Definition: SdkLevel.java:22
com.google.appinventor.components.runtime.ComponentContainer.$add
void $add(AndroidViewComponent component)
com.google.appinventor.components.runtime.util.FroyoUtil
Definition: FroyoUtil.java:28
com.google.appinventor.components.runtime.util.MediaUtil.isExternalFileUrl
static boolean isExternalFileUrl(String mediaPath)
Definition: MediaUtil.java:182
com.google.appinventor.components.annotations.UsesPermissions
Definition: UsesPermissions.java:21
com.google.appinventor.components.runtime.PermissionResultHandler
Definition: PermissionResultHandler.java:15
com.google.appinventor.components.runtime.EventDispatcher.dispatchEvent
static boolean dispatchEvent(Component component, String eventName, Object...args)
Definition: EventDispatcher.java:188
com.google.appinventor.components.runtime.util.SdkLevel
Definition: SdkLevel.java:19
com.google.appinventor.components.annotations.SimpleProperty
Definition: SimpleProperty.java:23
com.google.appinventor.components.runtime.util.FroyoUtil.getWebViewClient
static WebViewClient getWebViewClient(final boolean ignoreErrors, final boolean followLinks, final Form form, final Component component)
Definition: FroyoUtil.java:128
com.google.appinventor.components.annotations.PropertyCategory
Definition: PropertyCategory.java:13
com.google.appinventor.components.runtime.WebViewer
Definition: WebViewer.java:107
com.google.appinventor.components.runtime.util.EclairUtil.clearWebViewGeoLoc
static void clearWebViewGeoLoc()
Definition: EclairUtil.java:100
com.google.appinventor.components.runtime.ComponentContainer
Definition: ComponentContainer.java:16
com.google.appinventor.components.runtime.util.SdkLevel.getLevel
static int getLevel()
Definition: SdkLevel.java:45
com.google.appinventor.components.runtime
Copyright 2009-2011 Google, All Rights reserved.
Definition: AccelerometerSensor.java:8
com.google.appinventor.components.common
Definition: ComponentCategory.java:7
com.google.appinventor.components.common.ComponentCategory
Definition: ComponentCategory.java:48
com.google.appinventor.components.runtime.util.SdkLevel.LEVEL_FROYO
static final int LEVEL_FROYO
Definition: SdkLevel.java:25
com.google.appinventor.components.runtime.WebViewer.WebViewInterface.setWebViewStringFromBlocks
void setWebViewStringFromBlocks(final String newString)
Definition: WebViewer.java:640
com.google.appinventor.components.annotations.SimpleObject
Definition: SimpleObject.java:23
com.google.appinventor.components.runtime.WebViewer.getView
View getView()
Definition: WebViewer.java:201
com.google
com
com.google.appinventor.components.runtime.WebViewer.WebViewer
WebViewer(ComponentContainer container)
Definition: WebViewer.java:136
com.google.appinventor.components.runtime.ComponentContainer.$context
Activity $context()
com.google.appinventor.components.runtime.AndroidViewComponent
Definition: AndroidViewComponent.java:27
com.google.appinventor.components.runtime.util.EclairUtil
Definition: EclairUtil.java:31
com.google.appinventor.components.common.PropertyTypeConstants
Definition: PropertyTypeConstants.java:14
com.google.appinventor.components.annotations
com.google.appinventor