123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521 |
- /*
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you 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 org.apache.cordova;
- import java.util.ArrayList;
- import java.util.Locale;
- import org.json.JSONException;
- import org.json.JSONObject;
- import android.app.Activity;
- import android.app.AlertDialog;
- import android.annotation.SuppressLint;
- import android.content.DialogInterface;
- import android.content.Intent;
- import android.content.res.Configuration;
- import android.graphics.Color;
- import android.media.AudioManager;
- import android.os.Build;
- import android.os.Bundle;
- import android.view.Menu;
- import android.view.MenuItem;
- import android.view.View;
- import android.view.ViewGroup;
- import android.view.Window;
- import android.view.WindowManager;
- import android.webkit.WebViewClient;
- import android.widget.FrameLayout;
- /**
- * This class is the main Android activity that represents the Cordova
- * application. It should be extended by the user to load the specific
- * html file that contains the application.
- *
- * As an example:
- *
- * <pre>
- * package org.apache.cordova.examples;
- *
- * import android.os.Bundle;
- * import org.apache.cordova.*;
- *
- * public class Example extends CordovaActivity {
- * @Override
- * public void onCreate(Bundle savedInstanceState) {
- * super.onCreate(savedInstanceState);
- * super.init();
- * // Load your application
- * loadUrl(launchUrl);
- * }
- * }
- * </pre>
- *
- * Cordova xml configuration: Cordova uses a configuration file at
- * res/xml/config.xml to specify its settings. See "The config.xml File"
- * guide in cordova-docs at http://cordova.apache.org/docs for the documentation
- * for the configuration. The use of the set*Property() methods is
- * deprecated in favor of the config.xml file.
- *
- */
- public class CordovaActivity extends Activity {
- public static String TAG = "CordovaActivity";
- // The webview for our app
- protected CordovaWebView appView;
- private static int ACTIVITY_STARTING = 0;
- private static int ACTIVITY_RUNNING = 1;
- private static int ACTIVITY_EXITING = 2;
- // Keep app running when pause is received. (default = true)
- // If true, then the JavaScript and native code continue to run in the background
- // when another application (activity) is started.
- protected boolean keepRunning = true;
- // Flag to keep immersive mode if set to fullscreen
- protected boolean immersiveMode;
- // Read from config.xml:
- protected CordovaPreferences preferences;
- protected String launchUrl;
- protected ArrayList<PluginEntry> pluginEntries;
- protected CordovaInterfaceImpl cordovaInterface;
- /**
- * Called when the activity is first created.
- */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- // need to activate preferences before super.onCreate to avoid "requestFeature() must be called before adding content" exception
- loadConfig();
- String logLevel = preferences.getString("loglevel", "ERROR");
- LOG.setLogLevel(logLevel);
- LOG.i(TAG, "Apache Cordova native platform version " + CordovaWebView.CORDOVA_VERSION + " is starting");
- LOG.d(TAG, "CordovaActivity.onCreate()");
- if (!preferences.getBoolean("ShowTitle", false)) {
- getWindow().requestFeature(Window.FEATURE_NO_TITLE);
- }
- if (preferences.getBoolean("SetFullscreen", false)) {
- LOG.d(TAG, "The SetFullscreen configuration is deprecated in favor of Fullscreen, and will be removed in a future version.");
- preferences.set("Fullscreen", true);
- }
- if (preferences.getBoolean("Fullscreen", false)) {
- // NOTE: use the FullscreenNotImmersive configuration key to set the activity in a REAL full screen
- // (as was the case in previous cordova versions)
- if (!preferences.getBoolean("FullscreenNotImmersive", false)) {
- immersiveMode = true;
- } else {
- getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
- WindowManager.LayoutParams.FLAG_FULLSCREEN);
- }
- } else {
- getWindow().setFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN,
- WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
- }
- super.onCreate(savedInstanceState);
- cordovaInterface = makeCordovaInterface();
- if (savedInstanceState != null) {
- cordovaInterface.restoreInstanceState(savedInstanceState);
- }
- }
- protected void init() {
- appView = makeWebView();
- createViews();
- if (!appView.isInitialized()) {
- appView.init(cordovaInterface, pluginEntries, preferences);
- }
- cordovaInterface.onCordovaInit(appView.getPluginManager());
- // Wire the hardware volume controls to control media if desired.
- String volumePref = preferences.getString("DefaultVolumeStream", "");
- if ("media".equals(volumePref.toLowerCase(Locale.ENGLISH))) {
- setVolumeControlStream(AudioManager.STREAM_MUSIC);
- }
- }
- @SuppressWarnings("deprecation")
- protected void loadConfig() {
- ConfigXmlParser parser = new ConfigXmlParser();
- parser.parse(this);
- preferences = parser.getPreferences();
- preferences.setPreferencesBundle(getIntent().getExtras());
- launchUrl = parser.getLaunchUrl();
- pluginEntries = parser.getPluginEntries();
- Config.parser = parser;
- }
- //Suppressing warnings in AndroidStudio
- @SuppressWarnings({"deprecation", "ResourceType"})
- protected void createViews() {
- //Why are we setting a constant as the ID? This should be investigated
- appView.getView().setId(100);
- appView.getView().setLayoutParams(new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
- setContentView(appView.getView());
- if (preferences.contains("BackgroundColor")) {
- try {
- int backgroundColor = preferences.getInteger("BackgroundColor", Color.BLACK);
- // Background of activity:
- appView.getView().setBackgroundColor(backgroundColor);
- }
- catch (NumberFormatException e){
- e.printStackTrace();
- }
- }
- appView.getView().requestFocusFromTouch();
- }
- /**
- * Construct the default web view object.
- * <p/>
- * Override this to customize the webview that is used.
- */
- protected CordovaWebView makeWebView() {
- return new CordovaWebViewImpl(makeWebViewEngine());
- }
- protected CordovaWebViewEngine makeWebViewEngine() {
- return CordovaWebViewImpl.createEngine(this, preferences);
- }
- protected CordovaInterfaceImpl makeCordovaInterface() {
- return new CordovaInterfaceImpl(this) {
- @Override
- public Object onMessage(String id, Object data) {
- // Plumb this to CordovaActivity.onMessage for backwards compatibility
- return CordovaActivity.this.onMessage(id, data);
- }
- };
- }
- /**
- * Load the url into the webview.
- */
- public void loadUrl(String url) {
- if (appView == null) {
- init();
- }
- // If keepRunning
- this.keepRunning = preferences.getBoolean("KeepRunning", true);
- appView.loadUrlIntoView(url, true);
- }
- /**
- * Called when the system is about to start resuming a previous activity.
- */
- @Override
- protected void onPause() {
- super.onPause();
- LOG.d(TAG, "Paused the activity.");
- if (this.appView != null) {
- // CB-9382 If there is an activity that started for result and main activity is waiting for callback
- // result, we shoudn't stop WebView Javascript timers, as activity for result might be using them
- boolean keepRunning = this.keepRunning || this.cordovaInterface.activityResultCallback != null;
- this.appView.handlePause(keepRunning);
- }
- }
- /**
- * Called when the activity receives a new intent
- */
- @Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- //Forward to plugins
- if (this.appView != null)
- this.appView.onNewIntent(intent);
- }
- /**
- * Called when the activity will start interacting with the user.
- */
- @Override
- protected void onResume() {
- super.onResume();
- LOG.d(TAG, "Resumed the activity.");
- if (this.appView == null) {
- return;
- }
- if (! this.getWindow().getDecorView().hasFocus()) {
- // Force window to have focus, so application always
- // receive user input. Workaround for some devices (Samsung Galaxy Note 3 at least)
- this.getWindow().getDecorView().requestFocus();
- }
- this.appView.handleResume(this.keepRunning);
- }
- /**
- * Called when the activity is no longer visible to the user.
- */
- @Override
- protected void onStop() {
- super.onStop();
- LOG.d(TAG, "Stopped the activity.");
- if (this.appView == null) {
- return;
- }
- this.appView.handleStop();
- }
- /**
- * Called when the activity is becoming visible to the user.
- */
- @Override
- protected void onStart() {
- super.onStart();
- LOG.d(TAG, "Started the activity.");
- if (this.appView == null) {
- return;
- }
- this.appView.handleStart();
- }
- /**
- * The final call you receive before your activity is destroyed.
- */
- @Override
- public void onDestroy() {
- LOG.d(TAG, "CordovaActivity.onDestroy()");
- super.onDestroy();
- if (this.appView != null) {
- appView.handleDestroy();
- }
- }
- /**
- * Called when view focus is changed
- */
- @SuppressLint("InlinedApi")
- @Override
- public void onWindowFocusChanged(boolean hasFocus) {
- super.onWindowFocusChanged(hasFocus);
- if (hasFocus && immersiveMode) {
- final int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
- | View.SYSTEM_UI_FLAG_FULLSCREEN
- | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
- getWindow().getDecorView().setSystemUiVisibility(uiOptions);
- }
- }
- @SuppressLint("NewApi")
- @Override
- public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
- // Capture requestCode here so that it is captured in the setActivityResultCallback() case.
- cordovaInterface.setActivityResultRequestCode(requestCode);
- super.startActivityForResult(intent, requestCode, options);
- }
- /**
- * Called when an activity you launched exits, giving you the requestCode you started it with,
- * the resultCode it returned, and any additional data from it.
- *
- * @param requestCode The request code originally supplied to startActivityForResult(),
- * allowing you to identify who this result came from.
- * @param resultCode The integer result code returned by the child activity through its setResult().
- * @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
- */
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
- LOG.d(TAG, "Incoming Result. Request code = " + requestCode);
- super.onActivityResult(requestCode, resultCode, intent);
- cordovaInterface.onActivityResult(requestCode, resultCode, intent);
- }
- /**
- * Report an error to the host application. These errors are unrecoverable (i.e. the main resource is unavailable).
- * The errorCode parameter corresponds to one of the ERROR_* constants.
- *
- * @param errorCode The error code corresponding to an ERROR_* value.
- * @param description A String describing the error.
- * @param failingUrl The url that failed to load.
- */
- public void onReceivedError(final int errorCode, final String description, final String failingUrl) {
- final CordovaActivity me = this;
- // If errorUrl specified, then load it
- final String errorUrl = preferences.getString("errorUrl", null);
- if ((errorUrl != null) && (!failingUrl.equals(errorUrl)) && (appView != null)) {
- // Load URL on UI thread
- me.runOnUiThread(new Runnable() {
- public void run() {
- me.appView.showWebPage(errorUrl, false, true, null);
- }
- });
- }
- // If not, then display error dialog
- else {
- final boolean exit = !(errorCode == WebViewClient.ERROR_HOST_LOOKUP);
- me.runOnUiThread(new Runnable() {
- public void run() {
- if (exit) {
- me.appView.getView().setVisibility(View.GONE);
- me.displayError("Application Error", description + " (" + failingUrl + ")", "OK", exit);
- }
- }
- });
- }
- }
- /**
- * Display an error dialog and optionally exit application.
- */
- public void displayError(final String title, final String message, final String button, final boolean exit) {
- final CordovaActivity me = this;
- me.runOnUiThread(new Runnable() {
- public void run() {
- try {
- AlertDialog.Builder dlg = new AlertDialog.Builder(me);
- dlg.setMessage(message);
- dlg.setTitle(title);
- dlg.setCancelable(false);
- dlg.setPositiveButton(button,
- new AlertDialog.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- if (exit) {
- finish();
- }
- }
- });
- dlg.create();
- dlg.show();
- } catch (Exception e) {
- finish();
- }
- }
- });
- }
- /*
- * Hook in Cordova for menu plugins
- */
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- if (appView != null) {
- appView.getPluginManager().postMessage("onCreateOptionsMenu", menu);
- }
- return super.onCreateOptionsMenu(menu);
- }
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- if (appView != null) {
- appView.getPluginManager().postMessage("onPrepareOptionsMenu", menu);
- }
- return true;
- }
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (appView != null) {
- appView.getPluginManager().postMessage("onOptionsItemSelected", item);
- }
- return true;
- }
- /**
- * Called when a message is sent to plugin.
- *
- * @param id The message id
- * @param data The message data
- * @return Object or null
- */
- public Object onMessage(String id, Object data) {
- if ("onReceivedError".equals(id)) {
- JSONObject d = (JSONObject) data;
- try {
- this.onReceivedError(d.getInt("errorCode"), d.getString("description"), d.getString("url"));
- } catch (JSONException e) {
- e.printStackTrace();
- }
- } else if ("exit".equals(id)) {
- finish();
- }
- return null;
- }
- protected void onSaveInstanceState(Bundle outState) {
- cordovaInterface.onSaveInstanceState(outState);
- super.onSaveInstanceState(outState);
- }
- /**
- * Called by the system when the device configuration changes while your activity is running.
- *
- * @param newConfig The new device configuration
- */
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- if (this.appView == null) {
- return;
- }
- PluginManager pm = this.appView.getPluginManager();
- if (pm != null) {
- pm.onConfigurationChanged(newConfig);
- }
- }
- /**
- * Called by the system when the user grants permissions
- *
- * @param requestCode
- * @param permissions
- * @param grantResults
- */
- @Override
- public void onRequestPermissionsResult(int requestCode, String permissions[],
- int[] grantResults) {
- try
- {
- cordovaInterface.onRequestPermissionResult(requestCode, permissions, grantResults);
- }
- catch (JSONException e)
- {
- LOG.d(TAG, "JSONException: Parameters fed into the method are not valid");
- e.printStackTrace();
- }
- }
- }
|