/* * Copyright 2013 Thomas Hoffmann * * 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 de.j4velin.wifiAutoOff; import android.Manifest; import android.annotation.SuppressLint; import android.app.AlertDialog; import android.app.Dialog; import android.app.TimePickerDialog; import android.app.TimePickerDialog.OnTimeSetListener; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnCancelListener; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; import android.net.wifi.WifiManager; import android.os.BatteryManager; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.preference.CheckBoxPreference; import android.preference.Preference; import android.preference.Preference.OnPreferenceChangeListener; import android.preference.PreferenceActivity; import android.preference.PreferenceManager; import android.preference.PreferenceScreen; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.support.annotation.LayoutRes; import android.support.v4.content.PermissionChecker; import android.support.v4.view.MenuItemCompat; import android.support.v7.app.AppCompatDelegate; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.text.InputType; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.EditText; import android.widget.TimePicker; import android.widget.Toast; import java.util.List; public class Preferences extends PreferenceActivity { private final static int[] time_values = {5, 15, 30, 60, 120, 300, 600}; private StatusPreference status; private AppCompatDelegate mDelegate; /** * whether the location settings should be enabled by the enable/disable app switch */ private boolean disableLocationSettings = false; private final Handler handler = new Handler(); private final Runnable signalUpdater = new Runnable() { @Override public void run() { if (status.updateSignal()) handler.postDelayed(signalUpdater, 1000); } }; private final BroadcastReceiver stateChangedReceiver = new BroadcastReceiver() { @Override public void onReceive(final Context context, final Intent intent) { handler.removeCallbacks(signalUpdater); status.update(); if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(intent.getAction())) { final NetworkInfo nwi = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); if (nwi == null) return; if (nwi.isConnected()) { // seems to still take some time until NetworkInfo::isConnected returns true handler.postDelayed(new Runnable() { @Override public void run() { status.update(); } }, 2000); handler.postDelayed(signalUpdater, 2000); } } } }; @SuppressLint("NewApi") @Override public boolean onCreateOptionsMenu(final Menu menu) { getMenuInflater().inflate(R.menu.menu, menu); boolean isEnabled = getPackageManager() .getComponentEnabledSetting(new ComponentName(Preferences.this, Receiver.class)) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED; if (Build.VERSION.SDK_INT < 14) { menu.findItem(R.id.enable) .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(final MenuItem menuItem) { boolean isEnabled = getPackageManager().getComponentEnabledSetting( new ComponentName(Preferences.this, Receiver.class)) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED; changeEnableState(!isEnabled); return true; } }); } else { CompoundButton enable = (CompoundButton) MenuItemCompat.getActionView(menu.findItem(R.id.enable)); enable.setChecked(isEnabled); enable.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { changeEnableState(isChecked); } }); } // disable initially if not checked if (!isEnabled) { enableSettings(false); } return true; } @SuppressWarnings("deprecation") private void enableSettings(final boolean enable) { PreferenceScreen ps = getPreferenceScreen(); // start at 1 to skip "status" preference for (int i = 1; i < ps.getPreferenceCount(); i++) { ps.getPreference(i).setEnabled(enable); } if (enable && disableLocationSettings) { // disable locations again if disableLocationSettings is set findPreference("locations").setEnabled(false); } } private void changeEnableState(final boolean enable) { enableSettings(enable); getPackageManager() .setComponentEnabledSetting(new ComponentName(Preferences.this, Receiver.class), enable ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); if (!enable) stopService(new Intent(Preferences.this, ScreenChangeDetector.class)); getPackageManager().setComponentEnabledSetting( new ComponentName(Preferences.this, ScreenChangeDetector.class), enable ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); } @Override public boolean onOptionsItemSelected(final MenuItem item) { // action bar overflow menu switch (item.getItemId()) { case R.id.enable: break; case R.id.action_wifi_adv: try { startActivity(new Intent(Settings.ACTION_WIFI_IP_SETTINGS) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); } catch (Exception e) { Toast.makeText(this, R.string.settings_not_found_, Toast.LENGTH_SHORT).show(); } break; case R.id.action_apps: try { startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=pub:j4velin")) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); } catch (ActivityNotFoundException anf) { try { startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse( "https://play.google.com/store/apps/developer?id=j4velin")) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); } catch (ActivityNotFoundException anf2) { Toast.makeText(this, "No browser found to load https://play.google.com/store/apps/developer?id=j4velin", Toast.LENGTH_LONG).show(); } } break; case R.id.action_donate: try { startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://j4velin.de/donate.php")) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); } catch (ActivityNotFoundException anf) { Toast.makeText(this, "No browser found to load http://j4velin.de/donate.php", Toast.LENGTH_LONG).show(); } break; default: return super.onOptionsItemSelected(item); } return true; } /** * Checks if the WiFi sleep policy will keep WiFi when the screen goes off * * @param c Context * @return true if WiFi will be kept on during sleep */ @SuppressWarnings("deprecation") private static boolean keepWiFiOn(Context c) { try { return ((android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1 && APILevel17Wrapper.sleepPolicySetToNever(c)) || (Settings.System.WIFI_SLEEP_POLICY_NEVER == Settings.System .getInt(c.getContentResolver(), Settings.System.WIFI_SLEEP_POLICY))); } catch (SettingNotFoundException e) { e.printStackTrace(); } return true; } @Override public void onResume() { super.onResume(); final CheckBoxPreference no_network_off = (CheckBoxPreference) findPreference("off_no_network"); no_network_off.setSummary(getString(R.string.for_at_least, PreferenceManager.getDefaultSharedPreferences(this) .getInt("no_network_timeout", Receiver.TIMEOUT_NO_NETWORK))); handler.postDelayed(signalUpdater, 1000); IntentFilter ifilter = new IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION); ifilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); registerReceiver(stateChangedReceiver, ifilter); } @Override public void onPause() { super.onPause(); Start.start(this); handler.removeCallbacks(signalUpdater); unregisterReceiver(stateChangedReceiver); } @SuppressWarnings("deprecation") @Override public void onCreate(final Bundle savedInstanceState) { getDelegate().installViewFactory(); getDelegate().onCreate(savedInstanceState); super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferences); if (BuildConfig.DEBUG && Build.VERSION.SDK_INT >= 23 && PermissionChecker .checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PermissionChecker.PERMISSION_GRANTED) { requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0); } status = (StatusPreference) findPreference("status"); status.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(final Preference preference) { WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE); boolean connected = ((ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE)) .getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnected(); if (wm.isWifiEnabled() && !connected) { try { startActivity(new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); } catch (Exception e) { Toast.makeText(Preferences.this, R.string.settings_not_found_, Toast.LENGTH_SHORT).show(); } } else if (!wm.isWifiEnabled()) { try { wm.setWifiEnabled(true); } catch (SecurityException ex) { ex.printStackTrace(); Toast.makeText(Preferences.this, "No permission to enable WiFi", Toast.LENGTH_SHORT).show(); } } else { try { startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); } catch (Exception e) { Toast.makeText(Preferences.this, R.string.settings_not_found_, Toast.LENGTH_SHORT).show(); } } return true; } }); final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); final CheckBoxPreference screen_off = (CheckBoxPreference) findPreference("off_screen_off"); screen_off.setSummary(getString(R.string.for_at_least, prefs.getInt("screen_off_timeout", Receiver.TIMEOUT_SCREEN_OFF))); if (!keepWiFiOn(this)) { screen_off.setChecked(false); } screen_off.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(final Preference preference, final Object newValue) { if ((Boolean) newValue) { if (!keepWiFiOn(Preferences.this)) { new AlertDialog.Builder(Preferences.this).setMessage(R.string.sleep_policy) .setPositiveButton(R.string.adv_wifi_settings, new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { try { startActivity(new Intent( Settings.ACTION_WIFI_IP_SETTINGS) .addFlags( Intent.FLAG_ACTIVITY_NEW_TASK)); } catch (Exception e) { Toast.makeText(Preferences.this, R.string.settings_not_found_, Toast.LENGTH_SHORT).show(); } } }) .setNegativeButton(android.R.string.cancel, new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }).create().show(); return false; } if (android.os.Build.VERSION.SDK_INT >= 11) { APILevel11Wrapper.showNumberPicker(Preferences.this, prefs, screen_off, R.string.for_at_least, 1, 60, getString(R.string.minutes_before_turning_off_wifi_), "screen_off_timeout", Receiver.TIMEOUT_SCREEN_OFF, false); } else { showPre11NumberPicker(Preferences.this, prefs, screen_off, R.string.for_at_least, 1, 60, getString(R.string.minutes_before_turning_off_wifi_), "screen_off_timeout", Receiver.TIMEOUT_SCREEN_OFF, false); } } return true; } }); findPreference("off_no_network") .setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(final Preference preference, final Object newValue) { if ((Boolean) newValue) { if (android.os.Build.VERSION.SDK_INT >= 11) { APILevel11Wrapper .showNumberPicker(Preferences.this, prefs, preference, R.string.for_at_least, 1, 60, getString( R.string.minutes_before_turning_off_wifi_), "no_network_timeout", Receiver.TIMEOUT_NO_NETWORK, false); } else { showPre11NumberPicker(Preferences.this, prefs, preference, R.string.for_at_least, 1, 60, getString(R.string.minutes_before_turning_off_wifi_), "no_network_timeout", Receiver.TIMEOUT_NO_NETWORK, false); } } return true; } }); final CheckBoxPreference on_at = (CheckBoxPreference) findPreference("on_at"); on_at.setTitle( getString(R.string.at_summary, prefs.getString("on_at_time", Receiver.ON_AT_TIME))); on_at.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(final Preference preference, final Object newValue) { if ((Boolean) newValue) { String[] time = prefs.getString("on_at_time", Receiver.ON_AT_TIME).split(":"); final TimePickerDialog dialog = new TimePickerDialog(Preferences.this, new OnTimeSetListener() { @Override public void onTimeSet(TimePicker view, int hourOfDay, int minute) { prefs.edit().putString("on_at_time", hourOfDay + ":" + (minute < 10 ? "0" + minute : minute)) .commit(); on_at.setTitle(getString(R.string.at_summary, hourOfDay + ":" + (minute < 10 ? "0" + minute : minute))); } }, Integer.parseInt(time[0]), Integer.parseInt(time[1]), true); dialog.setOnCancelListener(new OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { on_at.setChecked(false); } }); dialog.setTitle(getString(R.string.turn_wifi_on_at_)); dialog.show(); } return true; } }); final CheckBoxPreference off_at = (CheckBoxPreference) findPreference("off_at"); off_at.setTitle(getString(R.string.at_summary, prefs.getString("off_at_time", Receiver.OFF_AT_TIME))); off_at.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(final Preference preference, final Object newValue) { if ((Boolean) newValue) { String[] time = prefs.getString("off_at_time", Receiver.OFF_AT_TIME).split(":"); final TimePickerDialog dialog = new TimePickerDialog(Preferences.this, new OnTimeSetListener() { @Override public void onTimeSet(TimePicker view, int hourOfDay, int minute) { prefs.edit().putString("off_at_time", hourOfDay + ":" + (minute < 10 ? "0" + minute : minute)) .commit(); off_at.setTitle(getString(R.string.at_summary, hourOfDay + ":" + (minute < 10 ? "0" + minute : minute))); } }, Integer.parseInt(time[0]), Integer.parseInt(time[1]), true); dialog.setOnCancelListener(new OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { off_at.setChecked(false); } }); dialog.setTitle(getString(R.string.turn_wifi_off_at_)); dialog.show(); } return true; } }); final Preference on_every = findPreference("on_every"); final String[] time_names = getResources().getStringArray(R.array.time_names); // default 2 hours on_every.setTitle( getString(R.string.every_summary, prefs.getString("on_every_str", time_names[4]))); on_every.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(final Preference preference, final Object newValue) { if ((Boolean) newValue) { AlertDialog.Builder builder = new AlertDialog.Builder(Preferences.this); builder.setTitle(R.string.turn_wifi_on_every) .setItems(time_names, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { prefs.edit().putInt("on_every_time_min", time_values[which]) .putString("on_every_str", time_names[which]).commit(); on_every.setTitle( getString(R.string.every_summary, time_names[which])); } }); builder.create().show(); } return true; } }); Preference locations = findPreference("locations"); if (BuildConfig.FLAVOR.equals("play")) { if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_LOCATION_NETWORK)) { locations.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(final Preference preference) { startActivity(new Intent(Preferences.this, Locations.class)); return true; } }); } else { locations.setEnabled(false); disableLocationSettings = true; } } else { locations.setSummary("Not available in F-Droid version"); locations.setEnabled(false); disableLocationSettings = true; } final Preference power = findPreference("power_connected"); power.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(final Preference preference, final Object newValue) { if ((boolean) newValue) { Intent battery = registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); if (battery != null && battery.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) > 0) { // already connected to external power prefs.edit().putBoolean("ignore_screen_off", true).commit(); } } else { prefs.edit().putBoolean("ignore_screen_off", false).commit(); } return true; } }); findPreference("log") .setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(final Preference preference) { getLogDialog().show(); return true; } }); } private static void showPre11NumberPicker(final Context c, final SharedPreferences prefs, final Preference p, final int summary, final int min, final int max, final String title, final String setting, final int def, final boolean changeTitle) { final EditText np = new EditText(c); np.setInputType(InputType.TYPE_CLASS_NUMBER); np.setText(String.valueOf(prefs.getInt(setting, def))); new AlertDialog.Builder(c).setTitle(title).setView(np) .setPositiveButton(android.R.string.ok, new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { int number = -1; try { number = Integer.parseInt(np.getText().toString()); } catch (Exception e) { } if (number >= min && number <= max) { prefs.edit().putInt(setting, number).commit(); if (changeTitle) p.setTitle(c.getString(summary, number)); else p.setSummary(c.getString(summary, number)); } else { Toast.makeText(c, c.getString(R.string.invalid_input_number_has_to_be_, min, max), Toast.LENGTH_SHORT).show(); } } }).create().show(); } private Dialog getLogDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setPositiveButton(android.R.string.ok, new OnClickListener() { @Override public void onClick(final DialogInterface dialogInterface, int i) { dialogInterface.dismiss(); } }); List<Log.Item> items = Log.getLog(this, 50); if (items.isEmpty()) { builder.setMessage(R.string.log_empty); } else { RecyclerView list = new RecyclerView(this); list.setAdapter(new LogAdapter(items)); list.setLayoutManager(new LinearLayoutManager(this)); builder.setView(list); builder.setNegativeButton(R.string.delete_log, new OnClickListener() { @Override public void onClick(final DialogInterface dialogInterface, int i) { Log.deleteOldLogs(Preferences.this, 0); dialogInterface.dismiss(); } }); } return builder.create(); } private AppCompatDelegate getDelegate() { if (mDelegate == null) { mDelegate = AppCompatDelegate.create(this, null); } return mDelegate; } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); getDelegate().onPostCreate(savedInstanceState); } @Override public MenuInflater getMenuInflater() { return getDelegate().getMenuInflater(); } @Override public void setContentView(@LayoutRes int layoutResID) { getDelegate().setContentView(layoutResID); } @Override public void setContentView(View view) { getDelegate().setContentView(view); } @Override public void setContentView(View view, ViewGroup.LayoutParams params) { getDelegate().setContentView(view, params); } @Override public void addContentView(View view, ViewGroup.LayoutParams params) { getDelegate().addContentView(view, params); } @Override protected void onPostResume() { super.onPostResume(); getDelegate().onPostResume(); } @Override protected void onTitleChanged(CharSequence title, int color) { super.onTitleChanged(title, color); getDelegate().setTitle(title); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); getDelegate().onConfigurationChanged(newConfig); } @Override protected void onStop() { super.onStop(); getDelegate().onStop(); } @Override protected void onDestroy() { super.onDestroy(); getDelegate().onDestroy(); } public void invalidateOptionsMenu() { getDelegate().invalidateOptionsMenu(); } }