/* ############################################################################### # # # Copyright (C) 2011-2016 OpenMEAP, Inc. # # Credits to Jonathan Schang & Rob Thacher # # # # Released under the LGPLv3 # # # # OpenMEAP is free software: you can redistribute it and/or modify # # it under the terms of the GNU Lesser General Public License as published # # by the Free Software Foundation, either version 3 of the License, or # # (at your option) any later version. # # # # OpenMEAP is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # # GNU Lesser General Public License for more details. # # # # You should have received a copy of the GNU Lesser General Public License # # along with OpenMEAP. If not, see <http://www.gnu.org/licenses/>. # # # ############################################################################### */ package com.openmeap.android; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Properties; import android.app.Activity; import android.app.Dialog; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.net.Uri; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.Window; import android.webkit.GeolocationPermissions; import android.webkit.JsResult; import android.webkit.WebChromeClient; import android.webkit.GeolocationPermissions.Callback; import android.webkit.WebStorage.QuotaUpdater; import android.webkit.WebViewClient; import android.widget.Button; import android.widget.CheckBox; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import com.openmeap.constants.FormConstants; import com.openmeap.digest.DigestInputStreamFactory; import com.openmeap.digest.Md5DigestInputStream; import com.openmeap.digest.Sha1DigestInputStream; import com.openmeap.http.CredentialsProviderFactory; import com.openmeap.http.HttpRequestExecuter; import com.openmeap.http.HttpRequestExecuterFactory; import com.openmeap.http.HttpRequestExecuterImpl; import com.openmeap.thinclient.FirstRunCheck; import com.openmeap.thinclient.LoginFormCallback; import com.openmeap.thinclient.LoginFormLauncher; import com.openmeap.thinclient.OmMainActivity; import com.openmeap.thinclient.OmWebView; import com.openmeap.thinclient.Preferences; import com.openmeap.thinclient.SLICConfig; import com.openmeap.thinclient.javascript.JsApiCoreImpl; import com.openmeap.thinclient.javascript.Orientation; import com.openmeap.thinclient.update.UpdateHandler; import com.openmeap.util.Utils; public class MainActivity extends Activity implements OmMainActivity, LoginFormLauncher { private static String SOURCE_ENCODING = FormConstants.CHAR_ENC_DEFAULT; private static String DIRECTORY_INDEX = "index.html"; private static final int LOGIN_DIALOG = 1; private AndroidSLICConfig config = null; private UpdateHandler updateHandler = null; private WebView webView = null; private LocalStorageImpl storage = null; private LoginFormCallback loginFormCallback = null; private boolean readyForUpdateCheck = false; /** * Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Get rid of the android title bar requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.main); CredentialsProviderFactory .setDefaultCredentialsProviderFactory(new OmSlicCredentialsProvider.Factory( this)); HttpRequestExecuterFactory .setDefaultType(HttpRequestExecuterImpl.class); DigestInputStreamFactory.setDigestInputStreamForName("md5", Md5DigestInputStream.class); DigestInputStreamFactory.setDigestInputStreamForName("sha1", Sha1DigestInputStream.class); // setup the SLICConfig instance Preferences prefs = new SharedPreferencesImpl(getSharedPreferences( SLICConfig.PREFERENCES_FILE, MODE_PRIVATE)); try { Properties props = new Properties(); props.load(getAssets().open(SLICConfig.PROPERTIES_FILE)); config = new AndroidSLICConfig(this, prefs, props); } catch (IOException ioe) { // this is a deal breaker...if we cannot read the properties file, // then the application is fail throw new RuntimeException("The primary configuration file (" + SLICConfig.PROPERTIES_FILE + ") could not be opened."); } // perform our first-run-check Object o = getSystemService(Context.WIFI_SERVICE); if (o instanceof WifiManager) { WifiManager wifiManager = (WifiManager) o; WifiInfo wifiInfo = wifiManager.getConnectionInfo(); if (wifiInfo != null && wifiInfo.getMacAddress() != null) { Runnable firstRunCheck = new FirstRunCheck(config, wifiInfo.getMacAddress(), HttpRequestExecuterFactory.newDefault()); new Thread(firstRunCheck).start(); } } if (config.isDevelopmentMode()) { System.setProperty(HttpRequestExecuter.SSL_PEER_NOVERIFY_PROPERTY, "true"); } else { System.setProperty(HttpRequestExecuter.SSL_PEER_NOVERIFY_PROPERTY, "false"); } storage = new LocalStorageImpl(this); updateHandler = new UpdateHandler(this, config, storage); // Calls the title from client.properties // setupWindowTitle(); } public void launchLoginForm(LoginFormCallback credProv) { this.loginFormCallback = credProv; this.runOnUiThread(new Runnable() { public void run() { showDialog(LOGIN_DIALOG); } }); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // We do nothing here. We're only handling this to keep orientation // or keyboard hiding from causing the WebView activity to restart. } public void restart() { Intent i = getBaseContext().getPackageManager() .getLaunchIntentForPackage(getBaseContext().getPackageName()); i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(i); } public AndroidSLICConfig getConfig() { return config; } public LocalStorageImpl getStorage() { return storage; } public UpdateHandler getUpdateHandler() { return updateHandler; } public Preferences getPreferences(String name) { return new SharedPreferencesImpl(this.getSharedPreferences(name, 0)); } public Orientation getOrientation() { Configuration config = getResources().getConfiguration(); return config.orientation == Configuration.ORIENTATION_LANDSCAPE ? Orientation.LANDSCAPE : config.orientation == Configuration.ORIENTATION_PORTRAIT ? Orientation.PORTRAIT : config.orientation == Configuration.ORIENTATION_SQUARE ? Orientation.SQUARE : Orientation.UNDEFINED; } public void doToast(String mesg, boolean isLong) { Context context = getApplicationContext(); CharSequence text = mesg; int duration = isLong ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT; Toast toast = Toast.makeText(context, text, duration); toast.show(); } public void setTitle(String title) { super.setTitle(title); } /* * PROTECTED METHODS HERE */ @Override protected void onSaveInstanceState(Bundle outState) { if (webView != null) { webView.saveState(outState); } } @Override protected void onResume() { super.onResume(); if (webView != null) { webView.performOnResume(); } this.initialize(); } @Override protected void onPause() { if (webView != null) { webView.performOnPause(); } super.onPause(); } @Override protected Dialog onCreateDialog(int id) { Dialog dialog = null; switch (id) { case LOGIN_DIALOG: dialog = createLoginFormDialog(); break; } return dialog; } protected void initialize() { FileContentProvider.setProviderAuthority(config.getProviderAuthority()); storage.setupSystemProperties(); updateHandler.initialize(webView); } /** * Loads in the content of the index document page to load into the WebView * * @return The content of the index document * @throws IOException */ public String getRootWebPageContent() throws IOException { InputStream inputStream = null; // storage location will be null until the first successful update if (config.shouldUseAssetsOrSdCard()) { if (config.assetsOnExternalStorage()) { String path = config.getAssetsBaseUrl() + DIRECTORY_INDEX; inputStream = getContentResolver().openInputStream( Uri.parse(path)); } else { String rootPath = config.getAssetsRootPath() + DIRECTORY_INDEX; inputStream = getAssets().open(rootPath); } } else inputStream = openFileInput(FileContentProvider .getInternalStorageFileName(DIRECTORY_INDEX)); return Utils.readInputStream(inputStream, SOURCE_ENCODING); } public String getIndexFilePath() { return FileContentProvider.getInternalStorageFileName(DIRECTORY_INDEX); } /** * Sets up the window title, per the properties * * private void setupWindowTitle() { if( config.getApplicationTitle()!=null * ) { if( config.getApplicationTitle().equals("off") ) { * requestWindowFeature(Window.FEATURE_NO_TITLE); } else { * setTitle(config.getApplicationTitle()); } } else * setTitle(config.getApplicationName()); } */ /** * Creates the default WebView where we'll run javascript and render content */ public WebView createDefaultWebView() { WebView webView = new com.openmeap.android.WebView(this, this); // make sure javascript and our api is available to the webview JsApiCoreImpl jsApi = new JsApiCoreImpl(this, webView, updateHandler); webView.addJavascriptInterface(jsApi, JS_API_NAME); webView.getSettings().setJavaScriptEnabled(true); webView.setWebViewClient(new WebViewClient() { public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; } }); //making web database enabled. webView.getSettings().setDatabaseEnabled(true); //making Dom storage enabled. webView.getSettings().setDomStorageEnabled(true); //requesting to create directory with name "localstorage" in /data/data/.../App_localstorage, //so that, localstorage related data files saved into that directory. String databasePath = this.getApplicationContext().getDir("localstorage", Context.MODE_PRIVATE).getPath(); //setting local storage database path. webView.getSettings().setDatabasePath("/data/data/" + webView.getContext().getPackageName() + "/databases/"); // enable navigator.geolocation webView.getSettings().setGeolocationEnabled(true); webView.getSettings().setGeolocationDatabasePath("/data/data/" + webView.getContext().getPackageName() + "/databases/"); // removes vertical and horizontal scroll bars webView.setVerticalScrollBarEnabled(false); webView.setHorizontalScrollBarEnabled(false); //WebChromeClient class is set, so that the overridden methods are executed, //when something that might impact a browser UI happens. webView.setWebChromeClient(new WebChromeClient() { @Override public void onGeolocationPermissionsShowPrompt(String origin, Callback callback) { callback.invoke(origin, true, false); } @Override public void onExceededDatabaseQuota(String url, String databaseIdentifier, long quota, long estimatedDatabaseSize, long totalQuota, QuotaUpdater quotaUpdater) { // TODO Auto-generated method stub super.onExceededDatabaseQuota(url, databaseIdentifier, quota, estimatedDatabaseSize, totalQuota, quotaUpdater); } }); // make sure the web view fills the viewable area webView.setLayoutParams(new LinearLayout.LayoutParams( android.view.ViewGroup.LayoutParams.FILL_PARENT, android.view.ViewGroup.LayoutParams.WRAP_CONTENT)); return webView; } private Dialog createLoginFormDialog() { Context mContext = this; final Dialog dialog = new Dialog(mContext); LayoutInflater inflater = (LayoutInflater) mContext .getSystemService(LAYOUT_INFLATER_SERVICE); final View layout = inflater.inflate(R.layout.login_form, (ViewGroup) findViewById(R.id.login_form_container)); dialog.setContentView(layout); dialog.setTitle("Authentication Prompt"); String infoText = loginFormCallback.getInfoText(); TextView infoTextView = (TextView) layout.findViewById(R.id.info); infoTextView.setText(infoText); Button proceedButton = (Button) layout.findViewById(R.id.proceed); proceedButton.setOnClickListener(new OnClickListener() { public void onClick(View view) { EditText passwordText = (EditText) layout .findViewById(R.id.password); EditText usernameText = (EditText) layout .findViewById(R.id.username); CheckBox rememberBox = (CheckBox) layout .findViewById(R.id.remember); loginFormCallback.onProceed(usernameText.getEditableText() .toString(), passwordText.getEditableText().toString(), rememberBox.isChecked()); loginFormCallback = null; if (!rememberBox.isChecked()) { usernameText.setText(""); passwordText.setText(""); } dialog.dismiss(); } }); Button cancelButton = (Button) layout.findViewById(R.id.cancel); cancelButton.setOnClickListener(new OnClickListener() { public void onClick(View view) { EditText passwordText = (EditText) layout .findViewById(R.id.password); EditText usernameText = (EditText) layout .findViewById(R.id.username); CheckBox rememberBox = (CheckBox) layout .findViewById(R.id.remember); loginFormCallback.onCancel(); loginFormCallback = null; if (!rememberBox.isChecked()) { usernameText.setText(""); passwordText.setText(""); } dialog.dismiss(); } }); EditText usernameText = (EditText) layout.findViewById(R.id.username); usernameText.requestFocus(); return dialog; } public void setContentView(OmWebView webView) { runOnUiThread(new Runnable() { OmWebView webView; public void run() { setContentView((View) webView); } public Runnable construct(OmWebView webView) { this.webView = webView; return this; } }.construct(webView)); } public void setWebView(OmWebView webView) { this.webView = (WebView) webView; } public void setReadyForUpdateCheck(boolean ready) { this.readyForUpdateCheck = ready; } public boolean getReadyForUpdateCheck() { return readyForUpdateCheck; } }