/** * MIT License * * Copyright (c) 2019 RasPi Check Contributors * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package de.eidottermihi.rpicheck.widget; import android.app.AlarmManager; import android.app.PendingIntent; import android.appwidget.AppWidgetManager; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; import android.os.Bundle; import android.support.design.widget.TextInputLayout; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.CheckBox; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.Spinner; import android.widget.Toast; import com.google.common.base.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import de.eidottermihi.raspicheck.R; import de.eidottermihi.rpicheck.activity.helper.LoggingHelper; import de.eidottermihi.rpicheck.adapter.DeviceSpinnerAdapter; import de.eidottermihi.rpicheck.db.DeviceDbHelper; import de.eidottermihi.rpicheck.db.RaspberryDeviceBean; import io.freefair.android.injection.annotation.InjectView; import io.freefair.android.injection.annotation.XmlLayout; import io.freefair.android.injection.annotation.XmlMenu; import io.freefair.android.injection.app.InjectionAppCompatActivity; /** * The configuration screen for the {@link de.eidottermihi.rpicheck.widget.OverclockingWidget OverclockingWidget} AppWidget. */ @XmlLayout(R.layout.overclocking_widget_configure) @XmlMenu(R.menu.activity_overclocking_widget_configure) public class OverclockingWidgetConfigureActivity extends InjectionAppCompatActivity implements AdapterView.OnItemSelectedListener { public static final String ACTION_WIDGET_UPDATE_ONE = "updateOneWidget"; public static final String PREF_SHOW_TEMP_SUFFIX = "_temp"; public static final String PREF_SHOW_ARM_SUFFIX = "_arm"; public static final String PREF_SHOW_LOAD_SUFFIX = "_load"; public static final String PREF_SHOW_MEMORY_SUFFIX = "_memory"; private static final Logger LOGGER = LoggerFactory.getLogger(OverclockingWidgetConfigureActivity.class); private static final String PREFS_NAME = "de.eidottermihi.rpicheck.widget.OverclockingWidget"; private static final String PREF_PREFIX_KEY = "appwidget_"; private static final String PREF_UPDATE_ONLY_ON_WIFI = "_onlywifi"; private static final String URI_SCHEME = "raspicheck"; private static final String PREF_UPDATE_INTERVAL_SUFFIX = "_interval"; private static final String UPDATE_YES = "yes"; private static final String UPDATE_WIFI = "wifi"; private static final String UPDATE_NO = "no"; // corresponds to array/widget_auto_update private static final String[] autoUpdate = {UPDATE_YES, UPDATE_WIFI, UPDATE_NO}; int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID; @InjectView(R.id.widgetPiSpinner) private Spinner widgetPiSpinner; @InjectView(R.id.textEditUpdateInterval) private EditText textEditUpdateInterval; @InjectView(R.id.widgetUpdateSpinner) private Spinner widgetUpdateSpinner; @InjectView(R.id.widgetUpdateIntervalSpinner) private Spinner widgetUpdateIntervalSpinner; @InjectView(R.id.linLayoutCustomUpdateInterval) private TextInputLayout linLayoutCustomInterval; @InjectView(R.id.checkBoxArm) private CheckBox checkBoxArm; @InjectView(R.id.checkBoxLoad) private CheckBox checkBoxLoad; @InjectView(R.id.checkBoxTemp) private CheckBox checkBoxTemp; @InjectView(R.id.checkBoxRam) private CheckBox checkBoxRam; @InjectView(R.id.linLayoutUpdateInterval) private LinearLayout linLayoutUpdateInterval; private DeviceDbHelper deviceDbHelper; private int[] updateIntervalsMinutes; public OverclockingWidgetConfigureActivity() { super(); } /** * @param context * @param appWidgetId * @param deviceId ID of the chosen device */ static void saveChosenDevicePref(Context context, int appWidgetId, Long deviceId, int updateInterval, boolean onlyOnWifi, boolean showTemp, boolean showArm, boolean showLoad, boolean showMemory) { LOGGER.info("Saving new OverclockingWidget. Settings - Update interval: {} - Only on Wifi: {}" + " - TEMP: {} - ARM: {} - LOAD: {} - RAM: {}", updateInterval, onlyOnWifi, showTemp, showArm, showLoad, showMemory); SharedPreferences.Editor prefs = context.getSharedPreferences(PREFS_NAME, 0).edit(); prefs.putLong(PREF_PREFIX_KEY + appWidgetId, deviceId); prefs.putInt(prefKey(PREF_UPDATE_INTERVAL_SUFFIX, appWidgetId), updateInterval); prefs.putBoolean(prefKey(PREF_SHOW_TEMP_SUFFIX, appWidgetId), showTemp); prefs.putBoolean(prefKey(PREF_SHOW_ARM_SUFFIX, appWidgetId), showArm); prefs.putBoolean(prefKey(PREF_SHOW_LOAD_SUFFIX, appWidgetId), showLoad); prefs.putBoolean(prefKey(PREF_SHOW_MEMORY_SUFFIX, appWidgetId), showMemory); prefs.putBoolean(prefKey(PREF_UPDATE_ONLY_ON_WIFI, appWidgetId), onlyOnWifi); prefs.apply(); } static String prefKey(String key, int appWidgetId) { return PREF_PREFIX_KEY + appWidgetId + key; } static boolean loadShowStatus(Context context, String key, int appWidgetId) { SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, 0); return prefs.getBoolean(prefKey(key, appWidgetId), true); } static Long loadDeviceId(Context context, int appWidgetId) { SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, 0); Long deviceId = prefs.getLong(PREF_PREFIX_KEY + appWidgetId, 0); if (deviceId != 0) { return deviceId; } else { return null; } } static boolean loadOnlyOnWifi(Context context, int appWidgetId) { SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, 0); return prefs.getBoolean(prefKey(PREF_UPDATE_ONLY_ON_WIFI, appWidgetId), false); } static int loadUpdateInterval(Context context, int appWidgetId) { SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, 0); return prefs.getInt(prefKey(PREF_UPDATE_INTERVAL_SUFFIX, appWidgetId), 5); } /** * @param appWidgetId the app widget id * @param context the context * @return if this widget has associated preferences (it it fully configured then) */ static boolean isInitiated(int appWidgetId, Context context) { SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, 0); return prefs.contains(PREF_PREFIX_KEY + appWidgetId); } static void deleteDevicePref(Context context, int appWidgetId) { SharedPreferences.Editor prefs = context.getSharedPreferences(PREFS_NAME, 0).edit(); prefs.remove(PREF_PREFIX_KEY + appWidgetId); prefs.remove(prefKey(PREF_UPDATE_INTERVAL_SUFFIX, appWidgetId)); prefs.remove(prefKey(PREF_SHOW_LOAD_SUFFIX, appWidgetId)); prefs.remove(prefKey(PREF_SHOW_ARM_SUFFIX, appWidgetId)); prefs.remove(prefKey(PREF_SHOW_TEMP_SUFFIX, appWidgetId)); prefs.remove(prefKey(PREF_UPDATE_ONLY_ON_WIFI, appWidgetId)); prefs.apply(); } static void settingScheduledAlarm(Context context, int appWidgetId) { LOGGER.debug("Check if Widget[ID={}] needs a new Alarm.", appWidgetId); if (isInitiated(appWidgetId, context)) { int updateIntervalInMinutes = OverclockingWidgetConfigureActivity.loadUpdateInterval(context, appWidgetId); if (updateIntervalInMinutes > 0) { long updateIntervalMillis = updateIntervalInMinutes * 60 * 1000; // Setting alarm via AlarmManager AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); PendingIntent pintent = getServicePendingIntent(context, appWidgetId, getPendingIntentUri(appWidgetId), ACTION_WIDGET_UPDATE_ONE); alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + updateIntervalMillis, updateIntervalMillis, pintent); LOGGER.debug("Added alarm for periodic updates of Widget[ID={}], update interval: {} ms.", appWidgetId, updateIntervalMillis); } else { LOGGER.debug("No periodic updates for Widget[ID={}].", appWidgetId); } } else { LOGGER.debug("Widget[ID={}] not fully initiated, no alarm needed.", appWidgetId); } } public static void removeAlarm(Context c, int appWidgetId) { LOGGER.debug("Removing alarm for Widget[ID={}].", appWidgetId); final Uri intentUri = getPendingIntentUri(appWidgetId); final PendingIntent pendingIntent = getServicePendingIntent(c, appWidgetId, intentUri, ACTION_WIDGET_UPDATE_ONE); final AlarmManager alarmManager = (AlarmManager) c.getSystemService(Context.ALARM_SERVICE); alarmManager.cancel(pendingIntent); } protected static PendingIntent getServicePendingIntent(Context context, int appWidgetId, Uri uri, String action) { final Intent intent = new Intent(context, WidgetUpdateService.class); intent.setAction(action); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); intent.setData(uri); return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); } protected static Uri getPendingIntentUri(int appWidgetId) { return Uri.withAppendedPath(Uri.parse(URI_SCHEME + "://widget/id/"), String.valueOf(appWidgetId)); } @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); // init logging LoggingHelper.initLogging(OverclockingWidgetConfigureActivity.this); // Set the result to CANCELED. This will cause the widget host to cancel // out of the widget placement if the user presses the back button. setResult(RESULT_CANCELED); // Find the widget id from the intent. Intent intent = getIntent(); Bundle extras = intent.getExtras(); if (extras != null) { mAppWidgetId = extras.getInt( AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); } // If this activity was started with an intent without an app widget ID, finish with an error. if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { finish(); return; } this.getSupportActionBar().setTitle(getString(R.string.widget_configure_title)); deviceDbHelper = new DeviceDbHelper(this); final int deviceCount = initSpinners(); if (deviceCount == 0) { // show Toast to add a device first Toast.makeText(this, getString(R.string.widget_add_no_device), Toast.LENGTH_LONG).show(); finish(); return; } linLayoutCustomInterval.setVisibility(View.GONE); } /** * @return the device count */ private int initSpinners() { // Device Spinner final DeviceSpinnerAdapter deviceSpinnerAdapter = new DeviceSpinnerAdapter(OverclockingWidgetConfigureActivity.this, deviceDbHelper.getFullDeviceCursor(), true); widgetPiSpinner.setAdapter(deviceSpinnerAdapter); // Auto update final ArrayAdapter<CharSequence> autoUpdateAdapter = ArrayAdapter.createFromResource(OverclockingWidgetConfigureActivity.this, R.array.widget_auto_updates, android.R.layout.simple_spinner_item); autoUpdateAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); widgetUpdateSpinner.setAdapter(autoUpdateAdapter); widgetUpdateSpinner.setOnItemSelectedListener(this); // Update interval this.updateIntervalsMinutes = this.getResources().getIntArray(R.array.widget_update_intervals_values); final ArrayAdapter<CharSequence> updateIntervalAdapter = ArrayAdapter.createFromResource(OverclockingWidgetConfigureActivity.this, R.array.widget_update_intervals, android.R.layout.simple_spinner_item); updateIntervalAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); widgetUpdateIntervalSpinner.setAdapter(updateIntervalAdapter); widgetUpdateIntervalSpinner.setOnItemSelectedListener(this); return deviceSpinnerAdapter.getCount(); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_save: final Context context = OverclockingWidgetConfigureActivity.this; long selectedItemId = widgetPiSpinner.getSelectedItemId(); LOGGER.info("Selected Device - Item ID = {}", selectedItemId); RaspberryDeviceBean deviceBean = deviceDbHelper.read(selectedItemId); if (deviceBean.usesAuthentificationMethod(RaspberryDeviceBean.AUTH_PUBLIC_KEY_WITH_PASSWORD) && deviceBean.getKeyfilePass() == null) { Toast.makeText(context, getString(R.string.widget_key_pass_error), Toast.LENGTH_LONG).show(); return super.onOptionsItemSelected(item); } int updateIntervalInMinutes = 0; boolean onlyOnWifi = false; if (!autoUpdate[widgetUpdateSpinner.getSelectedItemPosition()].equals(UPDATE_NO)) { if (updateIntervalsMinutes[widgetUpdateIntervalSpinner.getSelectedItemPosition()] == -1) { String s = textEditUpdateInterval.getText().toString().trim(); if (Strings.isNullOrEmpty(s)) { textEditUpdateInterval.setError(getString(R.string.widget_update_interval_error)); return super.onOptionsItemSelected(item); } updateIntervalInMinutes = Integer.parseInt(s); if (updateIntervalInMinutes == 0) { textEditUpdateInterval.setError(getString(R.string.widget_update_interval_zero)); return super.onOptionsItemSelected(item); } } else { updateIntervalInMinutes = updateIntervalsMinutes[widgetUpdateIntervalSpinner.getSelectedItemPosition()]; } } if (autoUpdate[widgetUpdateSpinner.getSelectedItemPosition()].equals(UPDATE_WIFI)) { onlyOnWifi = true; } // save Device ID in prefs saveChosenDevicePref(context, mAppWidgetId, selectedItemId, updateIntervalInMinutes, onlyOnWifi, checkBoxTemp.isChecked(), checkBoxArm.isChecked(), checkBoxLoad.isChecked(), checkBoxRam.isChecked()); settingScheduledAlarm(context, mAppWidgetId); // It is the responsibility of the configuration activity to update the app widget OverclockingWidget.updateAppWidget(context, mAppWidgetId, deviceDbHelper, false); // Make sure we pass back the original appWidgetId Intent resultValue = new Intent(); resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId); setResult(RESULT_OK, resultValue); finish(); default: return super.onOptionsItemSelected(item); } } @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { final int updateId = widgetUpdateSpinner.getId(); final int updateIntervalId = widgetUpdateIntervalSpinner.getId(); if (parent.getId() == updateId) { LOGGER.debug("Update-Spinner: Item pos {}, id {} selected.", position, id); if (autoUpdate[position].equals(UPDATE_NO)) { linLayoutUpdateInterval.setVisibility(View.GONE); } else { linLayoutUpdateInterval.setVisibility(View.VISIBLE); } } else if (parent.getId() == updateIntervalId) { LOGGER.debug("Interval-Spinner: Item pos {}, id {} selected.", position, id); int updateIntervalInMinutes = updateIntervalsMinutes[position]; if (updateIntervalInMinutes == -1) { // custom time interval linLayoutCustomInterval.setVisibility(View.VISIBLE); } else { linLayoutCustomInterval.setVisibility(View.GONE); } } } @Override public void onNothingSelected(AdapterView<?> parent) { // nothing to do } }