/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.webruntime; import android.app.Activity; import android.content.Intent; import android.content.ComponentName; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.Resources; import android.net.Uri; import android.os.Bundle; import android.util.Log; import android.view.KeyEvent; import android.view.Menu; import android.view.View; import android.view.Window; import android.webkit.GeolocationPermissions; import android.webkit.WebChromeClient; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.ImageView; import android.widget.Toast; import com.android.internal.R; import java.net.MalformedURLException; import java.net.URL; /** * The runtime used to display installed web applications. * @hide */ public class WebRuntimeActivity extends Activity { private final static String LOGTAG = "WebRuntimeActivity"; private WebView mWebView; private URL mBaseUrl; private ImageView mSplashScreen; public static class SensitiveFeatures { // All of the sensitive features private boolean mGeolocation; // On Android, the Browser doesn't prompt for database access, so we don't require an // explicit permission here in the WebRuntimeActivity, and there's no Android system // permission required for it either. //private boolean mDatabase; public boolean getGeolocation() { return mGeolocation; } public void setGeolocation(boolean geolocation) { mGeolocation = geolocation; } } /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Can't get meta data using getApplicationInfo() as it doesn't pass GET_META_DATA PackageManager packageManager = getPackageManager(); ComponentName componentName = new ComponentName(this, getClass()); ActivityInfo activityInfo = null; try { activityInfo = packageManager.getActivityInfo(componentName, PackageManager.GET_META_DATA); } catch (PackageManager.NameNotFoundException e) { Log.d(LOGTAG, "Failed to find component"); return; } if (activityInfo == null) { Log.d(LOGTAG, "Failed to get activity info"); return; } Bundle metaData = activityInfo.metaData; if (metaData == null) { Log.d(LOGTAG, "No meta data"); return; } String url = metaData.getString("android.webruntime.url"); if (url == null) { Log.d(LOGTAG, "No URL"); return; } try { mBaseUrl = new URL(url); } catch (MalformedURLException e) { Log.d(LOGTAG, "Invalid URL"); } // All false by default, and reading non-existent bundle properties gives false too. final SensitiveFeatures sensitiveFeatures = new SensitiveFeatures(); sensitiveFeatures.setGeolocation(metaData.getBoolean("android.webruntime.SensitiveFeaturesGeolocation")); getWindow().requestFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.web_runtime); mWebView = (WebView) findViewById(R.id.webview); mSplashScreen = (ImageView) findViewById(R.id.splashscreen); mSplashScreen.setImageResource( getResources().getIdentifier("splash_screen", "drawable", getPackageName())); mWebView.getSettings().setJavaScriptEnabled(true); mWebView.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { try { URL newOrigin = new URL(url); if (areSameOrigin(mBaseUrl, newOrigin)) { // If simple same origin test passes, load in the webview. return false; } } catch(MalformedURLException e) { // Don't load anything if this wasn't a proper URL. return true; } // Otherwise this is a URL that is not same origin so pass it to the // Browser to load. startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); return true; } @Override public void onPageFinished(WebView view, String url) { if (mSplashScreen != null && mSplashScreen.getVisibility() == View.VISIBLE) { mSplashScreen.setVisibility(View.GONE); mSplashScreen = null; } } }); // Use a custom WebChromeClient with geolocation permissions handling. mWebView.setWebChromeClient(new WebChromeClient() { public void onGeolocationPermissionsShowPrompt( String origin, GeolocationPermissions.Callback callback) { // Allow this origin if it has Geolocation permissions, otherwise deny. boolean allowed = false; if (sensitiveFeatures.getGeolocation()) { try { URL originUrl = new URL(origin); allowed = areSameOrigin(mBaseUrl, originUrl); } catch(MalformedURLException e) { } } callback.invoke(origin, allowed, false); } }); // Set the DB location. Optional. Geolocation works without DBs. mWebView.getSettings().setGeolocationDatabasePath( getDir("geolocation", MODE_PRIVATE).getPath()); String title = metaData.getString("android.webruntime.title"); // We turned off the title bar to go full screen so display the // webapp's title as a toast. if (title != null) { Toast.makeText(this, title, Toast.LENGTH_SHORT).show(); } // Load the webapp's base URL. mWebView.loadUrl(url); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK && mWebView.canGoBack()) { mWebView.goBack(); return true; } return super.onKeyDown(keyCode, event); } @Override public boolean onCreateOptionsMenu(Menu menu) { menu.add(0, 0, 0, "Menu item 1"); menu.add(0, 1, 0, "Menu item 2"); return true; } private static boolean areSameOrigin(URL a, URL b) { int aPort = a.getPort() == -1 ? a.getDefaultPort() : a.getPort(); int bPort = b.getPort() == -1 ? b.getDefaultPort() : b.getPort(); return a.getProtocol().equals(b.getProtocol()) && aPort == bPort && a.getHost().equals(b.getHost()); } }