Java tutorial
/** * Copyright (C) 2015 RasPi Check Contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package de.eidottermihi.rpicheck.activity; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.database.Cursor; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.AsyncTask; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.v4.app.DialogFragment; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.app.ActionBar; import android.util.SparseArray; import android.util.TypedValue; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.SpinnerAdapter; import android.widget.TableLayout; import android.widget.TableRow; import android.widget.TextView; import android.widget.Toast; import com.google.common.base.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.security.Security; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import de.eidottermihi.raspicheck.BuildConfig; import de.eidottermihi.raspicheck.R; import de.eidottermihi.rpicheck.activity.helper.Constants; import de.eidottermihi.rpicheck.activity.helper.FormatHelper; import de.eidottermihi.rpicheck.activity.helper.LoggingHelper; import de.eidottermihi.rpicheck.adapter.DeviceSpinnerAdapter; import de.eidottermihi.rpicheck.beans.QueryBean; import de.eidottermihi.rpicheck.beans.ShutdownResult; import de.eidottermihi.rpicheck.db.DeviceDbHelper; import de.eidottermihi.rpicheck.db.RaspberryDeviceBean; import de.eidottermihi.rpicheck.fragment.PassphraseDialog; import de.eidottermihi.rpicheck.fragment.PassphraseDialog.PassphraseDialogListener; import de.eidottermihi.rpicheck.fragment.QueryErrorMessagesDialog; import de.eidottermihi.rpicheck.fragment.QueryExceptionDialog; import de.eidottermihi.rpicheck.fragment.RebootDialogFragment; import de.eidottermihi.rpicheck.fragment.RebootDialogFragment.ShutdownDialogListener; import de.eidottermihi.rpicheck.ssh.LoadAveragePeriod; import de.eidottermihi.rpicheck.ssh.beans.DiskUsageBean; import de.eidottermihi.rpicheck.ssh.beans.Exported; import de.eidottermihi.rpicheck.ssh.beans.NetworkInterfaceInformation; import de.eidottermihi.rpicheck.ssh.beans.ProcessBean; import de.eidottermihi.rpicheck.ssh.beans.WlanBean; import de.eidottermihi.rpicheck.ssh.impl.RaspiQueryException; import de.fhconfig.android.library.injection.annotation.XmlLayout; import de.fhconfig.android.library.injection.annotation.XmlView; import de.fhconfig.android.library.ui.injection.InjectionActionBarActivity; import sheetrock.panda.changelog.ChangeLog; @XmlLayout(R.layout.activity_main) public class MainActivity extends InjectionActionBarActivity implements ActionBar.OnNavigationListener, ShutdownDialogListener, PassphraseDialogListener, AsyncQueryDataUpdate, AsyncShutdownUpdate { private static final Logger LOGGER = LoggerFactory.getLogger(MainActivity.class); private static final String CURRENT_DEVICE = "currentDevice"; private static final String ALL_DEVICES = "allDevices"; private static final String KEY_PREF_REFRESH_BY_ACTION_COUNT = "refreshCountByAction"; private static boolean isOnBackground; private Intent settingsIntent; private Intent newRaspiIntent; private Intent editRaspiIntent; private Intent commandIntent; @XmlView(R.id.commandButton) private Button commandButton; // assigning textviews to fields @XmlView(R.id.armFreqText) private TextView armFreqText; @XmlView(R.id.coreFreqText) private TextView coreFreqText; @XmlView(R.id.coreVoltText) private TextView coreVoltText; @XmlView(R.id.coreTempText) private TextView coreTempText; @XmlView(R.id.firmwareText) private TextView firmwareText; @XmlView(R.id.lastUpdateText) private TextView lastUpdateText; @XmlView(R.id.uptimeText) private TextView uptimeText; @XmlView(R.id.averageLoadText) private TextView averageLoadText; @XmlView(R.id.totalMemoryText) private TextView totalMemoryText; @XmlView(R.id.freeMemoryText) private TextView freeMemoryText; @XmlView(R.id.cpuSerialText) private TextView serialNoText; @XmlView(R.id.distriText) private TextView distriText; @XmlView(R.id.diskTable) private TableLayout diskTable; @XmlView(R.id.processTable) private TableLayout processTable; @XmlView(R.id.networkTable) private TableLayout networkTable; @XmlView(R.id.swipeRefreshLayout) private SwipeRefreshLayout swipeRefreshLayout; private SharedPreferences sharedPrefs; private DeviceDbHelper deviceDb; private RaspberryDeviceBean currentDevice; private SparseArray<RaspberryDeviceBean> allDevices; private Cursor deviceCursor; private SpinnerAdapter spinadapter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // assigning Shared Preferences PreferenceManager.setDefaultValues(this, R.xml.preferences, false); sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); // init intents settingsIntent = new Intent(MainActivity.this, SettingsActivity.class); newRaspiIntent = new Intent(MainActivity.this, NewRaspiActivity.class); editRaspiIntent = new Intent(MainActivity.this, EditRaspiActivity.class); commandIntent = new Intent(MainActivity.this, CustomCommandActivity.class); // assigning refreshable root scrollview initSwipeRefreshLayout(); boolean isDebugLogging = sharedPrefs.getBoolean(SettingsActivity.KEY_PREF_DEBUG_LOGGING, false); LoggingHelper.changeLogger(isDebugLogging); // Changelog final ChangeLog changeLog = new ChangeLog(this); if (changeLog.firstRun()) { changeLog.getLogDialog().show(); } // init device database deviceDb = new DeviceDbHelper(this); new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { deviceCursor = deviceDb.getFullDeviceCursor(); return null; } @Override protected void onPostExecute(Void r) { if (deviceCursor.getCount() == 0) { MainActivity.this.startActivityForResult(newRaspiIntent, NewRaspiActivity.REQUEST_SAVE); } else { // init spinner initSpinner(); } } }.execute(); } private void initSwipeRefreshLayout() { swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { LOGGER.trace("Query initiated by PullToRefresh."); doQuery(true); } }); TypedValue accentColor = new TypedValue(); TypedValue primaryColor = new TypedValue(); if (getTheme().resolveAttribute(R.attr.colorAccent, accentColor, true) && getTheme().resolveAttribute(R.attr.colorPrimary, primaryColor, true)) { swipeRefreshLayout.setColorSchemeResources(accentColor.resourceId, primaryColor.resourceId); } } @Override protected void onResume() { super.onResume(); isOnBackground = false; } @Override protected void onPause() { super.onPause(); isOnBackground = true; } /** * Reset the view. */ private void resetView() { coreTempText.setText(""); armFreqText.setText(""); coreFreqText.setText(""); coreVoltText.setText(""); firmwareText.setText(""); uptimeText.setText(""); averageLoadText.setText(""); totalMemoryText.setText(""); freeMemoryText.setText(""); distriText.setText(""); serialNoText.setText(""); lastUpdateText.setText(""); // tables updateDiskTable(null); updateNetworkTable(null); updateProcessTable(null); // remove share action this.supportInvalidateOptionsMenu(); } private void updateQueryDataInView(QueryBean result) { final String tempScale = sharedPrefs.getString(SettingsActivity.KEY_PREF_TEMPERATURE_SCALE, getString(R.string.pref_temperature_scala_default)); coreTempText.setText(FormatHelper.formatTemperature( currentDevice.getLastQueryData().getVcgencmdInfo().getCpuTemperature(), tempScale)); final String freqScale = sharedPrefs.getString(SettingsActivity.KEY_PREF_FREQUENCY_UNIT, getString(R.string.pref_frequency_unit_default)); armFreqText.setText(FormatHelper .formatFrequency(currentDevice.getLastQueryData().getVcgencmdInfo().getArmFrequency(), freqScale)); coreFreqText.setText(FormatHelper .formatFrequency(currentDevice.getLastQueryData().getVcgencmdInfo().getCoreFrequency(), freqScale)); coreVoltText.setText( FormatHelper.formatDecimal(currentDevice.getLastQueryData().getVcgencmdInfo().getCoreVolts())); firmwareText.setText(result.getVcgencmdInfo().getVersion()); lastUpdateText.setText(SimpleDateFormat.getDateTimeInstance().format(result.getLastUpdate())); // uptime and average load may contain errors if (result.getAvgLoad() != null) { averageLoadText.setText(result.getAvgLoad()); } if (result.getStartup() != null) { uptimeText.setText(result.getStartup()); } if (result.getFreeMem() != null) { freeMemoryText.setText(result.getFreeMem().humanReadableByteCount(false)); } if (result.getTotalMem() != null) { totalMemoryText.setText(result.getTotalMem().humanReadableByteCount(false)); } serialNoText.setText(result.getSerialNo()); distriText.setText(result.getDistri()); // update tables updateNetworkTable(result); updateDiskTable(result); updateProcessTable(result); this.handleQueryError(result.getErrorMessages()); this.supportInvalidateOptionsMenu(); } /** * Shows a dialog containing the ErrorMessages. * * @param errorMessages the messages */ private void handleQueryError(List<String> errorMessages) { final ArrayList<String> messages = new ArrayList<>(errorMessages); if (errorMessages.size() > 0 && !isOnBackground) { LOGGER.debug("Showing query error messages."); Bundle args = new Bundle(); args.putStringArrayList(QueryErrorMessagesDialog.KEY_ERROR_MESSAGES, messages); final QueryErrorMessagesDialog messageDialog = new QueryErrorMessagesDialog(); messageDialog.setArguments(args); messageDialog.show(getSupportFragmentManager(), "QueryErrorMessagesDialog"); } } private void updateNetworkTable(QueryBean result) { // remove rows except header networkTable.removeViews(1, networkTable.getChildCount() - 1); if (result != null && result.getNetworkInfo() != null) { for (NetworkInterfaceInformation interfaceInformation : result.getNetworkInfo()) { networkTable.addView(createNetworkRow(interfaceInformation)); } } } private View createNetworkRow(NetworkInterfaceInformation interfaceInformation) { final TableRow tempRow = new TableRow(this); tempRow.addView(createTextView(interfaceInformation.getName())); CharSequence statusText; if (interfaceInformation.isHasCarrier()) { statusText = getText(R.string.network_status_up); } else { statusText = getText(R.string.network_status_down); } tempRow.addView(createTextView(statusText.toString())); if (interfaceInformation.getIpAdress() != null) { tempRow.addView(createTextView(interfaceInformation.getIpAdress())); } else { tempRow.addView(createTextView(" - ")); } if (interfaceInformation.getWlanInfo() != null) { final WlanBean wlan = interfaceInformation.getWlanInfo(); tempRow.addView(createTextView(FormatHelper.formatPercentage(wlan.getSignalLevel()))); tempRow.addView(createTextView(FormatHelper.formatPercentage(wlan.getLinkQuality()))); } else { tempRow.addView(createTextView(" - ")); tempRow.addView(createTextView(" - ")); } return tempRow; } private void updateProcessTable(QueryBean result) { // remove current rows except header row processTable.removeViews(1, processTable.getChildCount() - 1); if (result != null && result.getProcesses() != null) { for (ProcessBean processBean : result.getProcesses()) { processTable.addView(createProcessRow(processBean)); } } } private View createProcessRow(ProcessBean processBean) { final TableRow tempRow = new TableRow(this); tempRow.addView(createTextView(processBean.getpId() + "")); tempRow.addView(createTextView(processBean.getTty())); tempRow.addView(createTextView(processBean.getCpuTime())); tempRow.addView(createTextView(processBean.getCommand())); return tempRow; } private View createDiskRow(DiskUsageBean disk) { final TableRow tempRow = new TableRow(this); tempRow.addView(createTextView(disk.getFileSystem())); tempRow.addView(createTextView(disk.getSize())); tempRow.addView(createTextView(disk.getAvailable())); tempRow.addView(createTextView(disk.getUsedPercent())); tempRow.addView(createTextView(disk.getMountedOn())); return tempRow; } private View createTextView(String text) { final TextView tempText = new TextView(this); tempText.setText(text); float pix = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, getResources().getDisplayMetrics()); tempText.setPadding((int) (pix), (int) (pix), 0, 0); return tempText; } private void updateDiskTable(QueryBean result) { // remove current rows except header row diskTable.removeViews(1, diskTable.getChildCount() - 1); if (result != null && result.getDisks() != null) { for (DiskUsageBean diskUsageBean : result.getDisks()) { // add row to table diskTable.addView(createDiskRow(diskUsageBean)); } } } private void initSpinner() { new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { deviceCursor = deviceDb.getFullDeviceCursor(); LOGGER.debug("Device cursor rows: " + deviceCursor.getCount()); return null; } @Override protected void onPostExecute(Void r) { // only show spinner if theres already a device to show if (deviceCursor.getCount() > 0) { // make adapter //spinadapter = new SimpleCursorAdapter(MainActivity.this, // android.R.layout.simple_spinner_dropdown_item, // deviceCursor, new String[]{"name", "_id"}, // new int[]{android.R.id.text1}); spinadapter = new DeviceSpinnerAdapter(MainActivity.this, deviceCursor, true); //spinadapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); getSupportActionBar().setListNavigationCallbacks(spinadapter, MainActivity.this); getSupportActionBar().setDisplayShowTitleEnabled(false); commandButton.setVisibility(View.VISIBLE); } else { getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); getSupportActionBar().setDisplayShowTitleEnabled(true); currentDevice = null; // disable edit/restart/delete action menu items supportInvalidateOptionsMenu(); commandButton.setVisibility(View.GONE); } } }.execute(); } /** * Shows a dialog with a detailed error message. * * @param exception */ private void handleQueryException(final RaspiQueryException exception) { final String errorMessage = this.mapExceptionToErrorMessage(exception); // only show dialog when app is not in background if (!isOnBackground) { LOGGER.debug("Query caused exception. Showing dialog."); // build dialog Bundle dialogArgs = new Bundle(); dialogArgs.putString(QueryExceptionDialog.MESSAGE_KEY, errorMessage); QueryExceptionDialog dialogFragment = new QueryExceptionDialog(); dialogFragment.setArguments(dialogArgs); dialogFragment.show(getSupportFragmentManager(), "QueryExceptionDialog"); } } private String mapExceptionToErrorMessage(RaspiQueryException exception) { String message = null; switch (exception.getReasonCode()) { case RaspiQueryException.REASON_CONNECTION_FAILED: message = getString(R.string.connection_failed); break; case RaspiQueryException.REASON_AUTHENTIFICATION_FAILED: message = getString(R.string.authentication_failed); break; case RaspiQueryException.REASON_TRANSPORT_EXCEPTION: message = getString(R.string.transport_exception); break; case RaspiQueryException.REASON_IO_EXCEPTION: message = getString(R.string.unexpected_exception); break; case RaspiQueryException.REASON_VCGENCMD_NOT_FOUND: message = getString(R.string.exception_vcgencmd); break; default: message = getString(R.string.weird_exception); break; } return message; } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_main, menu); // set delete, edit and reboot visible if there is a current device boolean currDevice = currentDevice != null; menu.findItem(R.id.menu_delete).setVisible(currDevice); menu.findItem(R.id.menu_edit_raspi).setVisible(currDevice); menu.findItem(R.id.menu_reboot).setVisible(currDevice); boolean showingQueryData = currDevice && currentDevice.getLastQueryData() != null; menu.findItem(R.id.menu_share).setVisible(showingQueryData); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_settings: this.startActivity(settingsIntent); break; case R.id.menu_new_raspi: this.startActivityForResult(newRaspiIntent, NewRaspiActivity.REQUEST_SAVE); break; case R.id.menu_delete: this.deleteCurrentDevice(); break; case R.id.menu_edit_raspi: final Bundle extras = new Bundle(); extras.putInt(Constants.EXTRA_DEVICE_ID, currentDevice.getId()); editRaspiIntent.putExtras(extras); this.startActivityForResult(editRaspiIntent, EditRaspiActivity.REQUEST_EDIT); break; case R.id.menu_reboot: this.showRebootDialog(); break; case R.id.menu_refresh: swipeRefreshLayout.setRefreshing(true); this.doQuery(false); break; case R.id.menu_share: this.shareQueryData(); break; } return super.onOptionsItemSelected(item); } private void shareQueryData() { LOGGER.debug("Create sharing intent for current query data."); final Intent sharingIntent = new Intent(Intent.ACTION_SEND); sharingIntent.setType("text/plain"); sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, "RasPi Check Status '" + currentDevice.getName() + "'"); sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, buildShareText(currentDevice)); startActivity(Intent.createChooser(sharingIntent, "Share via")); } private String buildShareText(@NonNull RaspberryDeviceBean device) { return getString(R.string.share_body, device.getName(), device.getHost(), new SimpleDateFormat().format(device.getLastQueryData().getLastUpdate()), buildShareTextBody(device.getLastQueryData()), BuildConfig.VERSION_NAME); } private String buildShareTextBody(@NonNull QueryBean lastQueryData) { Map<String, String> informations = new LinkedHashMap<>(); visitExportedMethods(lastQueryData, "", informations); StringBuilder sb = new StringBuilder(); for (Map.Entry<String, String> infoEntry : informations.entrySet()) { sb.append(infoEntry.getKey()).append("=").append(infoEntry.getValue()).append("\n"); } return sb.toString(); } private void visitExportedMethods(Object object, String keyPrefix, Map<String, String> informationMap) { for (Method method : object.getClass().getMethods()) { if (method.isAnnotationPresent(Exported.class) && method.getParameterTypes().length == 0) { String key = method.getAnnotation(Exported.class).value(); if ("".equals(key)) { key = method.getName(); if (key.startsWith("get")) { key = key.substring(3).toLowerCase(); } } try { final Object value = method.invoke(object, new Object[] {}); if (value != null) { if (value instanceof Boolean || value instanceof String || value instanceof Double || value instanceof Long || value instanceof Integer) { informationMap.put(keyPrefix + key, value.toString()); } else if (value instanceof Collection) { Collection collection = (List) value; int counter = 0; for (Object val : collection) { visitExportedMethods(val, String.format("%s[%s].", key, counter++), informationMap); } } else { visitExportedMethods(value, key + ".", informationMap); } } } catch (IllegalAccessException e) { } catch (InvocationTargetException e) { } } } } /** * Shows 3 toasts if refresh is happening via action button and not pull to refresh */ private void showPullToRefreshHint() { int count = sharedPrefs.getInt(KEY_PREF_REFRESH_BY_ACTION_COUNT, 0); if (count < 3) { Toast.makeText(this, getString(R.string.hint_pulltorefresh), Toast.LENGTH_LONG).show(); sharedPrefs.edit().putInt(KEY_PREF_REFRESH_BY_ACTION_COUNT, ++count).apply(); } } private void showRebootDialog() { LOGGER.trace("Showing reboot dialog."); DialogFragment rebootDialog = new RebootDialogFragment(); rebootDialog.show(getSupportFragmentManager(), "reboot"); } private void doRebootOrHalt(String type) { LOGGER.info("Doing {} on {}...", type, currentDevice.getName()); ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); if (networkInfo != null && networkInfo.isConnected()) { // get connection settings from shared preferences final String host = currentDevice.getHost(); final String user = currentDevice.getUser(); final String port = currentDevice.getPort() + ""; final String sudoPass = currentDevice.getSudoPass(); if (currentDevice.getAuthMethod().equals(NewRaspiAuthActivity.SPINNER_AUTH_METHODS[0])) { // ssh password final String pass = currentDevice.getPass(); new SSHShutdownTask(this).execute(host, user, pass, port, sudoPass, type, null, null); } else if (currentDevice.getAuthMethod().equals(NewRaspiAuthActivity.SPINNER_AUTH_METHODS[1])) { // keyfile final String keyfilePath = currentDevice.getKeyfilePath(); if (keyfilePath != null) { final File privateKey = new File(keyfilePath); if (privateKey.exists()) { new SSHShutdownTask(this).execute(host, user, null, port, sudoPass, type, keyfilePath, null); } else { Toast.makeText(this, "Cannot find keyfile at location: " + keyfilePath, Toast.LENGTH_LONG); } } else { Toast.makeText(this, "No keyfile specified!", Toast.LENGTH_LONG); } } else if (currentDevice.getAuthMethod().equals(NewRaspiAuthActivity.SPINNER_AUTH_METHODS[2])) { // keyfile and passphrase final String keyfilePath = currentDevice.getKeyfilePath(); if (keyfilePath != null) { final File privateKey = new File(keyfilePath); if (privateKey.exists()) { if (!Strings.isNullOrEmpty(currentDevice.getKeyfilePass())) { final String passphrase = currentDevice.getKeyfilePass(); new SSHShutdownTask(this).execute(host, user, null, port, sudoPass, type, keyfilePath, passphrase); } else { final String dialogType = type.equals(Constants.TYPE_REBOOT) ? PassphraseDialog.SSH_SHUTDOWN : PassphraseDialog.SSH_HALT; final DialogFragment passphraseDialog = new PassphraseDialog(); final Bundle args = new Bundle(); args.putString(PassphraseDialog.KEY_TYPE, dialogType); passphraseDialog.setArguments(args); passphraseDialog.setCancelable(false); passphraseDialog.show(getSupportFragmentManager(), "passphrase"); } } else { Toast.makeText(this, "Cannot find keyfile at location: " + keyfilePath, Toast.LENGTH_LONG); } } else { Toast.makeText(this, "No keyfile specified!", Toast.LENGTH_LONG); } } } else { Toast.makeText(this, R.string.no_connection, Toast.LENGTH_SHORT).show(); } } private void deleteCurrentDevice() { LOGGER.info("Deleting pi {}.", currentDevice.getName()); deviceDb.delete(currentDevice.getId()); if (allDevices != null) { allDevices.delete(currentDevice.getId()); } initSpinner(); } private void doQuery(boolean initByPullToRefresh) { if (currentDevice == null) { // no device available, show hint for user Toast.makeText(this, R.string.no_device_available, Toast.LENGTH_LONG).show(); // stop refresh animation from pull-to-refresh swipeRefreshLayout.setRefreshing(false); return; } ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); if (networkInfo != null && networkInfo.isConnected()) { // get connection settings from shared preferences String host = currentDevice.getHost(); String user = currentDevice.getUser(); String port = currentDevice.getPort() + ""; String pass = null; // reading process preference final Boolean hideRoot = sharedPrefs.getBoolean(SettingsActivity.KEY_PREF_QUERY_HIDE_ROOT_PROCESSES, true); String keyPath = null; String keyPass = null; boolean canConnect = false; // check authentification method final String authMethod = currentDevice.getAuthMethod(); if (authMethod.equals(NewRaspiAuthActivity.SPINNER_AUTH_METHODS[0])) { // only ssh password pass = currentDevice.getPass(); if (pass != null) { canConnect = true; } else { Toast.makeText(this, R.string.no_password_specified, Toast.LENGTH_LONG).show(); } } else if (authMethod.equals(NewRaspiAuthActivity.SPINNER_AUTH_METHODS[1])) { // keyfile must be present final String keyfilePath = currentDevice.getKeyfilePath(); if (keyfilePath != null) { final File privateKey = new File(keyfilePath); if (privateKey.exists()) { keyPath = keyfilePath; canConnect = true; } else { Toast.makeText(this, "Cannot find keyfile at location: " + keyfilePath, Toast.LENGTH_LONG) .show(); } } else { Toast.makeText(this, "No keyfile specified!", Toast.LENGTH_LONG).show(); } } else if (authMethod.equals(NewRaspiAuthActivity.SPINNER_AUTH_METHODS[2])) { // keyfile and keypass must be present final String keyfilePath = currentDevice.getKeyfilePath(); if (keyfilePath != null) { final File privateKey = new File(keyfilePath); if (privateKey.exists()) { keyPath = keyfilePath; final String keyfilePass = currentDevice.getKeyfilePass(); if (keyfilePass != null) { canConnect = true; keyPass = keyfilePass; } else { final DialogFragment newFragment = new PassphraseDialog(); final Bundle args = new Bundle(); args.putString(PassphraseDialog.KEY_TYPE, PassphraseDialog.SSH_QUERY); newFragment.setArguments(args); newFragment.setCancelable(false); newFragment.show(getSupportFragmentManager(), "passphrase"); canConnect = false; } } else { Toast.makeText(this, "Cannot find keyfile at location: " + keyfilePath, Toast.LENGTH_LONG) .show(); } } else { Toast.makeText(this, "No keyfile specified!", Toast.LENGTH_LONG).show(); } } if (host == null) { Toast.makeText(this, R.string.no_hostname_specified, Toast.LENGTH_LONG).show(); canConnect = false; } else if (user == null) { Toast.makeText(this, R.string.no_username_specified, Toast.LENGTH_LONG).show(); canConnect = false; } if (canConnect) { // disable pullToRefresh (if refresh initiated by action bar) if (!initByPullToRefresh) { // show hint for pull-to-refresh this.showPullToRefreshHint(); } // execute query new SSHQueryTask(this, getLoadAveragePreference()).execute(host, user, pass, port, hideRoot.toString(), keyPath, keyPass); } } else { Toast.makeText(this, R.string.no_connection, Toast.LENGTH_SHORT).show(); // stop refresh animation from pull-to-refresh swipeRefreshLayout.setRefreshing(false); } } private LoadAveragePeriod getLoadAveragePreference() { final String loadAvgPrefString = sharedPrefs.getString("pref_load_avg", "FIVE_MINUTES"); LoadAveragePeriod period; switch (loadAvgPrefString) { case "ONE_MINUTE": period = LoadAveragePeriod.ONE_MINUTE; break; case "FIVE_MINUTES": period = LoadAveragePeriod.FIVE_MINUTES; break; case "FIFTEEN_MINUTES": period = LoadAveragePeriod.FIFTEEN_MINUTES; break; default: period = LoadAveragePeriod.FIVE_MINUTES; break; } LOGGER.debug("Load average preference: {}", period); return period; } @Override public boolean onNavigationItemSelected(final int itemPosition, long itemId) { LOGGER.debug("Spinner item selected: pos=" + itemPosition + ", id=" + itemId); new AsyncTask<Long, Void, RaspberryDeviceBean>() { @Override protected RaspberryDeviceBean doInBackground(Long... params) { // get device with id return deviceDb.read(params[0]); } @Override protected void onPostExecute(RaspberryDeviceBean read) { if (currentDevice == null) { currentDevice = read; } else { // set current device only when device has changed (query // data get // lost otherwise) if (read.getId() != currentDevice.getId()) { LOGGER.debug("Switch from device id {} to device id {}.", currentDevice.getId(), read.getId()); currentDevice = read; // switched to other device // check if last query data for new device is present boolean lastQueryPresent = false; if (allDevices != null) { RaspberryDeviceBean deviceBean = allDevices.get(currentDevice.getId()); if (deviceBean != null) { if (deviceBean.getLastQueryData() != null && deviceBean.getLastQueryData().getException() == null) { currentDevice.setLastQueryData(deviceBean.getLastQueryData()); updateQueryDataInView(currentDevice.getLastQueryData()); lastQueryPresent = true; } } } if (!lastQueryPresent) { resetView(); } } else { // device was maybe updated if (currentDevice.getLastQueryData() != null) { final QueryBean data = currentDevice.getLastQueryData(); currentDevice = read; currentDevice.setLastQueryData(data); } else { currentDevice = read; } } } if (currentDevice != null) { currentDevice.setSpinnerPosition(itemPosition); } // refresh options menu supportInvalidateOptionsMenu(); // if current device == null (if only device was deleted), start // new // raspi activity if (currentDevice == null) { Toast.makeText(MainActivity.this, R.string.please_add_a_raspberry_pi, Toast.LENGTH_LONG).show(); startActivity(newRaspiIntent); } } }.execute(itemId); return true; } @Override protected void onSaveInstanceState(Bundle outState) { // saving query data of current device if (currentDevice != null) { LOGGER.debug("Saving instance state (current device)"); outState.putSerializable(CURRENT_DEVICE, currentDevice); if (allDevices == null) { LOGGER.debug("Saving new instance of all devices."); allDevices = new SparseArray<RaspberryDeviceBean>(); allDevices.put(currentDevice.getId(), currentDevice); } else { LOGGER.debug("Adding current device to all devices."); allDevices.put(currentDevice.getId(), currentDevice); } } if (allDevices != null) { outState.putSparseParcelableArray(ALL_DEVICES, allDevices); } outState.putString("bug:fix", "no empty outstate"); super.onSaveInstanceState(outState); } @Override protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); if (savedInstanceState.getSerializable(CURRENT_DEVICE) != null) { LOGGER.debug("Restoring device.."); currentDevice = (RaspberryDeviceBean) savedInstanceState.getSerializable(CURRENT_DEVICE); // restoring tables LOGGER.debug("Setting spinner to show last Pi."); this.getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); this.getSupportActionBar().setSelectedNavigationItem(currentDevice.getSpinnerPosition()); if (currentDevice.getLastQueryData() != null && currentDevice.getLastQueryData().getException() == null) { LOGGER.debug("Restoring query data.."); this.updateQueryDataInView(currentDevice.getLastQueryData()); } else { LOGGER.debug("No last query data present."); this.resetView(); } } if (savedInstanceState.getSparseParcelableArray(ALL_DEVICES) != null) { LOGGER.debug("Restoring all devices."); allDevices = savedInstanceState.getSparseParcelableArray(ALL_DEVICES); } } @Override public void onHaltClick(DialogInterface dialog) { LOGGER.trace("ShutdownDialog: Halt chosen."); this.doRebootOrHalt(Constants.TYPE_HALT); } @Override public void onRebootClick(DialogInterface dialog) { LOGGER.trace("ShutdownDialog: Reboot chosen."); this.doRebootOrHalt(Constants.TYPE_REBOOT); } @Override public void onPassphraseOKClick(DialogFragment dialog, String passphrase, boolean savePassphrase, String type) { if (savePassphrase) { // save passphrase in db LOGGER.debug("Saving passphrase for device {}.", currentDevice.getName()); currentDevice.setKeyfilePass(passphrase); new Thread() { @Override public void run() { deviceDb.update(currentDevice); } }.start(); } if (type.equals(PassphraseDialog.SSH_QUERY)) { // connect final Boolean hideRoot = sharedPrefs.getBoolean(SettingsActivity.KEY_PREF_QUERY_HIDE_ROOT_PROCESSES, true); new SSHQueryTask(this, getLoadAveragePreference()).execute(currentDevice.getHost(), currentDevice.getUser(), null, currentDevice.getPort() + "", hideRoot.toString(), currentDevice.getKeyfilePath(), passphrase); } else if (type.equals(PassphraseDialog.SSH_SHUTDOWN)) { new SSHShutdownTask(this).execute(currentDevice.getHost(), currentDevice.getUser(), null, currentDevice.getPort() + "", currentDevice.getSudoPass(), Constants.TYPE_REBOOT, currentDevice.getKeyfilePath(), passphrase); } else if (type.equals(PassphraseDialog.SSH_HALT)) { new SSHShutdownTask(this).execute(currentDevice.getHost(), currentDevice.getUser(), null, currentDevice.getPort() + "", currentDevice.getSudoPass(), Constants.TYPE_HALT, currentDevice.getKeyfilePath(), passphrase); } } @Override public void onPassphraseCancelClick() { // update and reset pullToRefresh swipeRefreshLayout.setRefreshing(false); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { switch (requestCode) { case NewRaspiActivity.REQUEST_SAVE: initSpinner(); break; case EditRaspiActivity.REQUEST_EDIT: initSpinner(); break; default: break; } } /** * Gets called when Command Button is clicked. Starts activity for custom * Commands. * * @param view */ public void onCommandButtonClick(View view) { switch (view.getId()) { case R.id.commandButton: Bundle currPi = new Bundle(); currPi.putSerializable("pi", currentDevice); commandIntent.putExtras(currPi); this.startActivity(commandIntent); break; default: break; } } @Override protected void onDestroy() { super.onDestroy(); if (deviceDb != null) { deviceDb.close(); } } @Override public void onQueryFinished(QueryBean result) { currentDevice.setLastQueryData(result); // update and reset pullToRefresh swipeRefreshLayout.setRefreshing(false); if (result.getException() == null) { // update view data this.updateQueryDataInView(result); // update entry in allDevices-Map if (allDevices != null) { allDevices.put(this.currentDevice.getId(), this.currentDevice); } else { allDevices = new SparseArray<>(); allDevices.put(this.currentDevice.getId(), this.currentDevice); } } else { this.handleQueryException(result.getException()); } } @Override public void onQueryProgress(int progress) { //progressBar.setProgress(progress); } @Override public void onShutdownFinished(ShutdownResult result) { if (result.getType().equals(Constants.TYPE_REBOOT)) { if (result.getExcpetion() == null) { Toast.makeText(this, R.string.reboot_success, Toast.LENGTH_LONG).show(); } else { Toast.makeText(this, R.string.reboot_fail, Toast.LENGTH_LONG).show(); } } else if (result.getType().equals(Constants.TYPE_HALT)) { if (result.getExcpetion() == null) { Toast.makeText(this, R.string.halt_success, Toast.LENGTH_LONG).show(); } else { Toast.makeText(this, R.string.halt_fail, Toast.LENGTH_LONG).show(); } } } }