Java tutorial
/* 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 com.webileapps.fragments; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Configuration; import android.graphics.Color; import android.media.AudioManager; import android.net.http.SslError; import android.os.Bundle; import android.app.Fragment; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.webkit.SslErrorHandler; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.FrameLayout; import org.apache.cordova.BuildConfig; import org.apache.cordova.ConfigXmlParser; import org.apache.cordova.CordovaInterfaceImpl; import org.apache.cordova.CordovaPreferences; import org.apache.cordova.CordovaWebView; import org.apache.cordova.CordovaWebViewEngine; import org.apache.cordova.CordovaWebViewImpl; import org.apache.cordova.LOG; import org.apache.cordova.PluginEntry; import org.apache.cordova.PluginManager; import org.apache.cordova.engine.SystemWebViewClient; import org.apache.cordova.engine.SystemWebViewEngine; import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; import java.util.Locale; /** * 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 CordovaFragment extends Fragment { public static String TAG = "CordovaFragment"; // The webview for our app protected CordovaWebView appView; public CordovaWebView getAppView() { return appView; } // 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; // Read from config.xml: protected CordovaPreferences preferences; protected String launchUrl; protected ArrayList<PluginEntry> pluginEntries; protected CordovaInterfaceImpl cordovaInterface; private View contentView; public View getContentView() { return contentView; } public void setContentView(View contentView) { this.contentView = contentView; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (contentView == null) { init(); } loadUrl(launchUrl); return contentView; } /** * Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { LOG.i(TAG, "Apache Cordova native platform version " + CordovaWebView.CORDOVA_VERSION + " is starting"); LOG.d(TAG, "CordovaActivity.onCreate()"); // need to activate preferences before super.onCreate to avoid "requestFeature() must be called before adding content" exception loadConfig(); if (getArguments() != null) { launchUrl = getArguments().getString("url"); } Log.d(TAG, " launchURL : " + launchUrl); /** if(!preferences.getBoolean("ShowTitle", false)) { getActivity().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."); getActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); } else if (preferences.getBoolean("Fullscreen", false)) { getActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); } else { getActivity().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))) { getActivity().setVolumeControlStream(AudioManager.STREAM_MUSIC); } if (BuildConfig.DEBUG) { ((WebView) appView.getView()) .setWebViewClient(new SystemWebViewClient((SystemWebViewEngine) appView.getEngine()) { @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { handler.proceed(); } }); } } @SuppressWarnings("deprecation") protected void loadConfig() { ConfigXmlParser parser = new ConfigXmlParser(); parser.parse(this.getActivity()); preferences = parser.getPreferences(); preferences.setPreferencesBundle(getActivity().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")) { int backgroundColor = preferences.getInteger("BackgroundColor", Color.BLACK); // Background of activity: appView.getView().setBackgroundColor(backgroundColor); } appView.getView().requestFocusFromTouch(); } /** * Construct the default web view object. * * Override this to customize the webview that is used. */ protected CordovaWebView makeWebView() { return new CordovaWebViewImpl(makeWebViewEngine()); } protected CordovaWebViewEngine makeWebViewEngine() { return CordovaWebViewImpl.createEngine(this.getActivity(), preferences); } protected CordovaInterfaceImpl makeCordovaInterface() { return new CordovaInterfaceImpl(getActivity()) { @Override public Object onMessage(String id, Object data) { // Plumb this to CordovaActivity.onMessage for backwards compatibility return CordovaFragment.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 activity will start interacting with the user. */ @Override public void onResume() { super.onResume(); LOG.d(TAG, "Resumed the activity."); if (this.appView == null) { return; } // Force window to have focus, so application always // receive user input. Workaround for some devices (Samsung Galaxy Note 3 at least) this.getActivity().getWindow().getDecorView().requestFocus(); this.appView.handleResume(this.keepRunning); } /** * Called when the activity is no longer visible to the user. */ @Override public 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 public 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(); } } @Override public void startActivityForResult(Intent intent, int requestCode) { // Capture requestCode here so that it is captured in the setActivityResultCallback() case. cordovaInterface.setActivityResultRequestCode(requestCode); super.startActivityForResult(intent, requestCode); } /** * 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 public 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 CordovaFragment 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.getActivity().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.getActivity().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 CordovaFragment me = this; me.getActivity().runOnUiThread(new Runnable() { public void run() { try { AlertDialog.Builder dlg = new AlertDialog.Builder(me.getActivity()); 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) { getActivity().finish(); } } }); dlg.create(); dlg.show(); } catch (Exception e) { getActivity().finish(); } } }); } /* * Hook in Cordova for menu plugins */ @Override public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) { if (appView != null) { appView.getPluginManager().postMessage("onCreateOptionsMenu", menu); } super.onCreateOptionsMenu(menu, menuInflater); } @Override public void onPrepareOptionsMenu(Menu menu) { if (appView != null) { appView.getPluginManager().postMessage("onPrepareOptionsMenu", menu); } } @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)) { // getActivity().finish(); } return null; } public 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(); } } }