Java tutorial
/* * Copyright (C) 2009 University of Washington * * 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 org.odk.collect.android.activities; import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.database.ContentObserver; import android.database.Cursor; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.preference.PreferenceManager; import android.support.v4.view.ViewConfigurationCompat; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.text.InputType; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewConfiguration; import android.view.WindowManager; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.EditText; import android.widget.TextView; import com.google.android.gms.analytics.GoogleAnalytics; import org.odk.collect.android.R; import org.odk.collect.android.application.Collect; import org.odk.collect.android.dao.InstancesDao; import org.odk.collect.android.preferences.AboutPreferencesActivity; import org.odk.collect.android.preferences.AdminKeys; import org.odk.collect.android.preferences.AdminPreferencesActivity; import org.odk.collect.android.preferences.AutoSendPreferenceMigrator; import org.odk.collect.android.preferences.PreferenceKeys; import org.odk.collect.android.preferences.PreferencesActivity; import org.odk.collect.android.provider.InstanceProviderAPI.InstanceColumns; import org.odk.collect.android.utilities.ApplicationConstants; import org.odk.collect.android.utilities.AuthDialogUtility; import org.odk.collect.android.utilities.PlayServicesUtil; import org.odk.collect.android.utilities.ToastUtils; import org.odk.collect.android.utilities.SharedPreferencesUtils; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.lang.ref.WeakReference; import java.util.Map; import java.util.Map.Entry; import timber.log.Timber; /** * Responsible for displaying buttons to launch the major activities. Launches * some activities based on returns of others. * * @author Carl Hartung (carlhartung@gmail.com) * @author Yaw Anokwa (yanokwa@gmail.com) */ public class MainMenuActivity extends AppCompatActivity { private static final int PASSWORD_DIALOG = 1; // menu options private static final int MENU_ABOUT = Menu.FIRST; private static final int MENU_PREFERENCES = Menu.FIRST + 1; private static final int MENU_ADMIN = Menu.FIRST + 2; private static final boolean EXIT = true; // buttons private Button enterDataButton; private Button manageFilesButton; private Button sendDataButton; private Button viewSentFormsButton; private Button reviewDataButton; private Button getFormsButton; private View reviewSpacer; private View getFormsSpacer; private AlertDialog alertDialog; private SharedPreferences adminPreferences; private int completedCount; private int savedCount; private int viewSentCount; private Cursor finalizedCursor; private Cursor savedCursor; private Cursor viewSentCursor; private IncomingHandler handler = new IncomingHandler(this); private MyContentObserver contentObserver = new MyContentObserver(); // private static boolean DO_NOT_EXIT = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main_menu); initToolbar(); // enter data button. expects a result. enterDataButton = (Button) findViewById(R.id.enter_data); enterDataButton.setText(getString(R.string.enter_data_button)); enterDataButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Collect.getInstance().getActivityLogger().logAction(this, "fillBlankForm", "click"); Intent i = new Intent(getApplicationContext(), FormChooserList.class); startActivity(i); } }); // review data button. expects a result. reviewDataButton = (Button) findViewById(R.id.review_data); reviewDataButton.setText(getString(R.string.review_data_button)); reviewDataButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Collect.getInstance().getActivityLogger().logAction(this, ApplicationConstants.FormModes.EDIT_SAVED, "click"); Intent i = new Intent(getApplicationContext(), InstanceChooserList.class); i.putExtra(ApplicationConstants.BundleKeys.FORM_MODE, ApplicationConstants.FormModes.EDIT_SAVED); startActivity(i); } }); // send data button. expects a result. sendDataButton = (Button) findViewById(R.id.send_data); sendDataButton.setText(getString(R.string.send_data_button)); sendDataButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Collect.getInstance().getActivityLogger().logAction(this, "uploadForms", "click"); Intent i = new Intent(getApplicationContext(), InstanceUploaderList.class); startActivity(i); } }); //View sent forms viewSentFormsButton = (Button) findViewById(R.id.view_sent_forms); viewSentFormsButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Collect.getInstance().getActivityLogger().logAction(this, ApplicationConstants.FormModes.VIEW_SENT, "click"); Intent i = new Intent(getApplicationContext(), InstanceChooserList.class); i.putExtra(ApplicationConstants.BundleKeys.FORM_MODE, ApplicationConstants.FormModes.VIEW_SENT); startActivity(i); } }); // manage forms button. no result expected. getFormsButton = (Button) findViewById(R.id.get_forms); getFormsButton.setText(getString(R.string.get_forms)); getFormsButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Collect.getInstance().getActivityLogger().logAction(this, "downloadBlankForms", "click"); SharedPreferences sharedPreferences = PreferenceManager .getDefaultSharedPreferences(MainMenuActivity.this); String protocol = sharedPreferences.getString(PreferenceKeys.KEY_PROTOCOL, getString(R.string.protocol_odk_default)); Intent i = null; if (protocol.equalsIgnoreCase(getString(R.string.protocol_google_sheets))) { if (PlayServicesUtil.isGooglePlayServicesAvailable(MainMenuActivity.this)) { i = new Intent(getApplicationContext(), GoogleDriveActivity.class); } else { PlayServicesUtil.showGooglePlayServicesAvailabilityErrorDialog(MainMenuActivity.this); return; } } else { i = new Intent(getApplicationContext(), FormDownloadList.class); } startActivity(i); } }); // manage forms button. no result expected. manageFilesButton = (Button) findViewById(R.id.manage_forms); manageFilesButton.setText(getString(R.string.manage_files)); manageFilesButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Collect.getInstance().getActivityLogger().logAction(this, "deleteSavedForms", "click"); Intent i = new Intent(getApplicationContext(), FileManagerTabs.class); startActivity(i); } }); // must be at the beginning of any activity that can be called from an // external intent Timber.i("Starting up, creating directories"); try { Collect.createODKDirs(); } catch (RuntimeException e) { createErrorDialog(e.getMessage(), EXIT); return; } { // dynamically construct the "ODK Collect vA.B" string TextView mainMenuMessageLabel = (TextView) findViewById(R.id.main_menu_header); mainMenuMessageLabel.setText(Collect.getInstance().getVersionedAppName()); } File f = new File(Collect.ODK_ROOT + "/collect.settings"); File j = new File(Collect.ODK_ROOT + "/collect.settings.json"); // Give JSON file preference if (j.exists()) { SharedPreferencesUtils sharedPrefs = new SharedPreferencesUtils(); boolean success = sharedPrefs.loadSharedPreferencesFromJSONFile(j); if (success) { ToastUtils.showLongToast(R.string.settings_successfully_loaded_file_notification); j.delete(); // Delete settings file to prevent overwrite of settings from JSON file on next startup if (f.exists()) { f.delete(); } } else { ToastUtils.showLongToast(R.string.corrupt_settings_file_notification); } } else if (f.exists()) { boolean success = loadSharedPreferencesFromFile(f); if (success) { ToastUtils.showLongToast(R.string.settings_successfully_loaded_file_notification); f.delete(); } else { ToastUtils.showLongToast(R.string.corrupt_settings_file_notification); } } reviewSpacer = findViewById(R.id.review_spacer); getFormsSpacer = findViewById(R.id.get_forms_spacer); adminPreferences = this.getSharedPreferences(AdminPreferencesActivity.ADMIN_PREFERENCES, 0); InstancesDao instancesDao = new InstancesDao(); // count for finalized instances try { finalizedCursor = instancesDao.getFinalizedInstancesCursor(); } catch (Exception e) { createErrorDialog(e.getMessage(), EXIT); return; } if (finalizedCursor != null) { startManagingCursor(finalizedCursor); } completedCount = finalizedCursor != null ? finalizedCursor.getCount() : 0; getContentResolver().registerContentObserver(InstanceColumns.CONTENT_URI, true, contentObserver); // finalizedCursor.registerContentObserver(contentObserver); // count for saved instances try { savedCursor = instancesDao.getUnsentInstancesCursor(); } catch (Exception e) { createErrorDialog(e.getMessage(), EXIT); return; } if (savedCursor != null) { startManagingCursor(savedCursor); } savedCount = savedCursor != null ? savedCursor.getCount() : 0; //count for view sent form try { viewSentCursor = instancesDao.getSentInstancesCursor(); } catch (Exception e) { createErrorDialog(e.getMessage(), EXIT); return; } if (viewSentCursor != null) { startManagingCursor(viewSentCursor); } viewSentCount = viewSentCursor != null ? viewSentCursor.getCount() : 0; updateButtons(); setupGoogleAnalytics(); } private void initToolbar() { Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); toolbar.setTitle(getString(R.string.main_menu)); boolean hasHardwareMenu = ViewConfigurationCompat .hasPermanentMenuKey(ViewConfiguration.get(getApplicationContext())); if (!hasHardwareMenu) { setSupportActionBar(toolbar); } } @Override protected void onResume() { super.onResume(); SharedPreferences sharedPreferences = this.getSharedPreferences(AdminPreferencesActivity.ADMIN_PREFERENCES, 0); boolean edit = sharedPreferences.getBoolean(AdminKeys.KEY_EDIT_SAVED, true); if (!edit) { if (reviewDataButton != null) { reviewDataButton.setVisibility(View.GONE); } if (reviewSpacer != null) { reviewSpacer.setVisibility(View.GONE); } } else { if (reviewDataButton != null) { reviewDataButton.setVisibility(View.VISIBLE); } if (reviewSpacer != null) { reviewSpacer.setVisibility(View.VISIBLE); } } boolean send = sharedPreferences.getBoolean(AdminKeys.KEY_SEND_FINALIZED, true); if (!send) { if (sendDataButton != null) { sendDataButton.setVisibility(View.GONE); } } else { if (sendDataButton != null) { sendDataButton.setVisibility(View.VISIBLE); } } boolean viewSent = sharedPreferences.getBoolean(AdminKeys.KEY_VIEW_SENT, true); if (!viewSent) { if (viewSentFormsButton != null) { viewSentFormsButton.setVisibility(View.GONE); } } else { if (viewSentFormsButton != null) { viewSentFormsButton.setVisibility(View.VISIBLE); } } boolean getBlank = sharedPreferences.getBoolean(AdminKeys.KEY_GET_BLANK, true); if (!getBlank) { if (getFormsButton != null) { getFormsButton.setVisibility(View.GONE); } if (getFormsSpacer != null) { getFormsSpacer.setVisibility(View.GONE); } } else { if (getFormsButton != null) { getFormsButton.setVisibility(View.VISIBLE); } if (getFormsSpacer != null) { getFormsSpacer.setVisibility(View.VISIBLE); } } boolean deleteSaved = sharedPreferences.getBoolean(AdminKeys.KEY_DELETE_SAVED, true); if (!deleteSaved) { if (manageFilesButton != null) { manageFilesButton.setVisibility(View.GONE); } } else { if (manageFilesButton != null) { manageFilesButton.setVisibility(View.VISIBLE); } } ((Collect) getApplication()).getDefaultTracker().enableAutoActivityTracking(true); } @Override protected void onPause() { super.onPause(); if (alertDialog != null && alertDialog.isShowing()) { alertDialog.dismiss(); } } @Override protected void onStart() { super.onStart(); Collect.getInstance().getActivityLogger().logOnStart(this); } @Override protected void onStop() { Collect.getInstance().getActivityLogger().logOnStop(this); super.onStop(); } @Override public boolean onCreateOptionsMenu(Menu menu) { Collect.getInstance().getActivityLogger().logAction(this, "onCreateOptionsMenu", "show"); super.onCreateOptionsMenu(menu); menu.add(0, MENU_ABOUT, 0, R.string.about_preferences).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); menu.add(0, MENU_PREFERENCES, 0, R.string.general_preferences).setIcon(R.drawable.ic_menu_preferences) .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); menu.add(0, MENU_ADMIN, 0, R.string.admin_preferences).setIcon(R.drawable.ic_menu_login) .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case MENU_ABOUT: Collect.getInstance().getActivityLogger().logAction(this, "onOptionsItemSelected", "MENU_ABOUT"); Intent aboutIntent = new Intent(this, AboutPreferencesActivity.class); startActivity(aboutIntent); return true; case MENU_PREFERENCES: Collect.getInstance().getActivityLogger().logAction(this, "onOptionsItemSelected", "MENU_PREFERENCES"); Intent ig = new Intent(this, PreferencesActivity.class); startActivity(ig); return true; case MENU_ADMIN: Collect.getInstance().getActivityLogger().logAction(this, "onOptionsItemSelected", "MENU_ADMIN"); String pw = adminPreferences.getString(AdminKeys.KEY_ADMIN_PW, ""); if ("".equalsIgnoreCase(pw)) { Intent i = new Intent(getApplicationContext(), AdminPreferencesActivity.class); startActivity(i); } else { showDialog(PASSWORD_DIALOG); Collect.getInstance().getActivityLogger().logAction(this, "createAdminPasswordDialog", "show"); } return true; } return super.onOptionsItemSelected(item); } private void createErrorDialog(String errorMsg, final boolean shouldExit) { Collect.getInstance().getActivityLogger().logAction(this, "createErrorDialog", "show"); alertDialog = new AlertDialog.Builder(this).create(); alertDialog.setIcon(android.R.drawable.ic_dialog_info); alertDialog.setMessage(errorMsg); DialogInterface.OnClickListener errorListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int i) { switch (i) { case DialogInterface.BUTTON_POSITIVE: Collect.getInstance().getActivityLogger().logAction(this, "createErrorDialog", shouldExit ? "exitApplication" : "OK"); if (shouldExit) { finish(); } break; } } }; alertDialog.setCancelable(false); alertDialog.setButton(getString(R.string.ok), errorListener); alertDialog.show(); } @Override protected Dialog onCreateDialog(int id) { switch (id) { case PASSWORD_DIALOG: AlertDialog.Builder builder = new AlertDialog.Builder(this); final AlertDialog passwordDialog = builder.create(); passwordDialog.setTitle(getString(R.string.enter_admin_password)); LayoutInflater inflater = this.getLayoutInflater(); View dialogView = inflater.inflate(R.layout.dialogbox_layout, null); passwordDialog.setView(dialogView, 20, 10, 20, 10); final CheckBox checkBox = (CheckBox) dialogView.findViewById(R.id.checkBox); final EditText input = (EditText) dialogView.findViewById(R.id.editText); checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean b) { if (!checkBox.isChecked()) { input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); } else { input.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD); } } }); passwordDialog.setButton(AlertDialog.BUTTON_POSITIVE, getString(R.string.ok), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { String value = input.getText().toString(); String pw = adminPreferences.getString(AdminKeys.KEY_ADMIN_PW, ""); if (pw.compareTo(value) == 0) { Intent i = new Intent(getApplicationContext(), AdminPreferencesActivity.class); startActivity(i); input.setText(""); passwordDialog.dismiss(); } else { ToastUtils.showShortToast(R.string.admin_password_incorrect); Collect.getInstance().getActivityLogger().logAction(this, "adminPasswordDialog", "PASSWORD_INCORRECT"); } } }); passwordDialog.setButton(AlertDialog.BUTTON_NEGATIVE, getString(R.string.cancel), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { Collect.getInstance().getActivityLogger().logAction(this, "adminPasswordDialog", "cancel"); input.setText(""); return; } }); passwordDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); return passwordDialog; } return null; } // This flag must be set each time the app starts up private void setupGoogleAnalytics() { SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(Collect.getInstance()); boolean isAnalyticsEnabled = settings.getBoolean(PreferenceKeys.KEY_ANALYTICS, true); GoogleAnalytics googleAnalytics = GoogleAnalytics.getInstance(getApplicationContext()); googleAnalytics.setAppOptOut(!isAnalyticsEnabled); } private void updateButtons() { if (finalizedCursor != null && !finalizedCursor.isClosed()) { finalizedCursor.requery(); completedCount = finalizedCursor.getCount(); if (completedCount > 0) { sendDataButton.setText(getString(R.string.send_data_button, String.valueOf(completedCount))); } else { sendDataButton.setText(getString(R.string.send_data)); } } else { sendDataButton.setText(getString(R.string.send_data)); Timber.w("Cannot update \"Send Finalized\" button label since the database is closed. " + "Perhaps the app is running in the background?"); } if (savedCursor != null && !savedCursor.isClosed()) { savedCursor.requery(); savedCount = savedCursor.getCount(); if (savedCount > 0) { reviewDataButton.setText(getString(R.string.review_data_button, String.valueOf(savedCount))); } else { reviewDataButton.setText(getString(R.string.review_data)); } } else { reviewDataButton.setText(getString(R.string.review_data)); Timber.w("Cannot update \"Edit Form\" button label since the database is closed. " + "Perhaps the app is running in the background?"); } if (viewSentCursor != null && !viewSentCursor.isClosed()) { viewSentCursor.requery(); viewSentCount = viewSentCursor.getCount(); if (viewSentCount > 0) { viewSentFormsButton .setText(getString(R.string.view_sent_forms_button, String.valueOf(viewSentCount))); } else { viewSentFormsButton.setText(getString(R.string.view_sent_forms)); } } else { viewSentFormsButton.setText(getString(R.string.view_sent_forms)); Timber.w("Cannot update \"View Sent\" button label since the database is closed. " + "Perhaps the app is running in the background?"); } } private boolean loadSharedPreferencesFromFile(File src) { // this should probably be in a thread if it ever gets big boolean res = false; ObjectInputStream input = null; try { input = new ObjectInputStream(new FileInputStream(src)); Editor prefEdit = PreferenceManager.getDefaultSharedPreferences(this).edit(); prefEdit.clear(); // first object is preferences Map<String, ?> entries = (Map<String, ?>) input.readObject(); AutoSendPreferenceMigrator.migrate(entries); for (Entry<String, ?> entry : entries.entrySet()) { Object v = entry.getValue(); String key = entry.getKey(); if (v instanceof Boolean) { prefEdit.putBoolean(key, (Boolean) v); } else if (v instanceof Float) { prefEdit.putFloat(key, (Float) v); } else if (v instanceof Integer) { prefEdit.putInt(key, (Integer) v); } else if (v instanceof Long) { prefEdit.putLong(key, (Long) v); } else if (v instanceof String) { prefEdit.putString(key, ((String) v)); } } prefEdit.apply(); AuthDialogUtility.setWebCredentialsFromPreferences(this); // second object is admin options Editor adminEdit = getSharedPreferences(AdminPreferencesActivity.ADMIN_PREFERENCES, 0).edit(); adminEdit.clear(); // first object is preferences Map<String, ?> adminEntries = (Map<String, ?>) input.readObject(); for (Entry<String, ?> entry : adminEntries.entrySet()) { Object v = entry.getValue(); String key = entry.getKey(); if (v instanceof Boolean) { adminEdit.putBoolean(key, (Boolean) v); } else if (v instanceof Float) { adminEdit.putFloat(key, (Float) v); } else if (v instanceof Integer) { adminEdit.putInt(key, (Integer) v); } else if (v instanceof Long) { adminEdit.putLong(key, (Long) v); } else if (v instanceof String) { adminEdit.putString(key, ((String) v)); } } adminEdit.apply(); res = true; } catch (IOException | ClassNotFoundException e) { Timber.e(e, "Exception while loading preferences from file due to : %s ", e.getMessage()); } finally { try { if (input != null) { input.close(); } } catch (IOException ex) { Timber.e(ex, "Exception thrown while closing an input stream due to: %s ", ex.getMessage()); } } return res; } /* * Used to prevent memory leaks */ static class IncomingHandler extends Handler { private final WeakReference<MainMenuActivity> target; IncomingHandler(MainMenuActivity target) { this.target = new WeakReference<MainMenuActivity>(target); } @Override public void handleMessage(Message msg) { MainMenuActivity target = this.target.get(); if (target != null) { target.updateButtons(); } } } /** * notifies us that something changed */ private class MyContentObserver extends ContentObserver { public MyContentObserver() { super(null); } @Override public void onChange(boolean selfChange) { super.onChange(selfChange); handler.sendEmptyMessage(0); } } }