summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/com/android/settings/AccessibilitySettings.java44
-rw-r--r--src/com/android/settings/ApplicationSettings.java40
-rw-r--r--src/com/android/settings/BrightnessPreference.java4
-rw-r--r--src/com/android/settings/ChooseLockGeneric.java46
-rw-r--r--src/com/android/settings/ChooseLockPassword.java6
-rw-r--r--src/com/android/settings/ChooseLockSettingsHelper.java8
-rw-r--r--src/com/android/settings/ConfirmLockPassword.java33
-rw-r--r--src/com/android/settings/ConfirmLockPattern.java7
-rw-r--r--src/com/android/settings/CredentialStorage.java492
-rw-r--r--src/com/android/settings/CryptKeeper.java10
-rw-r--r--src/com/android/settings/CryptKeeperConfirm.java2
-rw-r--r--src/com/android/settings/CryptKeeperSettings.java16
-rw-r--r--src/com/android/settings/DataUsageAppDetail.java309
-rw-r--r--src/com/android/settings/DataUsageSummary.java1090
-rw-r--r--src/com/android/settings/DateTimeSettingsSetupWizard.java4
-rw-r--r--src/com/android/settings/DefaultRingtonePreference.java1
-rw-r--r--src/com/android/settings/DisplaySettings.java103
-rw-r--r--src/com/android/settings/DreamComponentPreference.java139
-rw-r--r--src/com/android/settings/DreamSettings.java128
-rw-r--r--src/com/android/settings/DreamTesterPreference.java75
-rw-r--r--src/com/android/settings/GoogleLocationSettingHelper.java1
-rw-r--r--src/com/android/settings/LocationSettings.java193
-rw-r--r--src/com/android/settings/PointerSpeedPreference.java4
-rw-r--r--src/com/android/settings/RadioInfo.java24
-rw-r--r--src/com/android/settings/SecuritySettings.java146
-rw-r--r--src/com/android/settings/Settings.java319
-rw-r--r--src/com/android/settings/SettingsCheckBoxPreference.java91
-rw-r--r--src/com/android/settings/SettingsLicenseActivity.java3
-rw-r--r--src/com/android/settings/SubSettings.java24
-rw-r--r--src/com/android/settings/TestingSettingsBroadcastReceiver.java1
-rw-r--r--src/com/android/settings/TextToSpeechSettings.java638
-rw-r--r--src/com/android/settings/UserDictionarySettings.java94
-rw-r--r--src/com/android/settings/Utils.java22
-rw-r--r--src/com/android/settings/WallpaperTypeSettings.java59
-rw-r--r--src/com/android/settings/WirelessSettings.java42
-rw-r--r--src/com/android/settings/accounts/AccountPreferenceBase.java14
-rw-r--r--src/com/android/settings/accounts/ChooseAccountActivity.java10
-rw-r--r--src/com/android/settings/applications/ApplicationsState.java8
-rw-r--r--src/com/android/settings/applications/InstalledAppDetails.java4
-rw-r--r--src/com/android/settings/bluetooth/AdvancedBluetoothSettings.java96
-rw-r--r--src/com/android/settings/bluetooth/BluetoothEnabler.java84
-rw-r--r--src/com/android/settings/bluetooth/BluetoothSettings.java179
-rw-r--r--src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java42
-rw-r--r--src/com/android/settings/bluetooth/DevicePickerFragment.java2
-rw-r--r--src/com/android/settings/bluetooth/DeviceProfilesSettings.java4
-rw-r--r--src/com/android/settings/deviceinfo/PercentageBarChart.java19
-rw-r--r--src/com/android/settings/deviceinfo/UsageBarPreference.java6
-rw-r--r--src/com/android/settings/fuelgauge/BatteryHistoryChart.java17
-rw-r--r--src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java30
-rw-r--r--src/com/android/settings/inputmethod/InputMethodAndSubtypeEnabler.java26
-rw-r--r--src/com/android/settings/inputmethod/InputMethodConfig.java6
-rw-r--r--src/com/android/settings/inputmethod/InputMethodDialogActivity.java31
-rw-r--r--src/com/android/settings/inputmethod/UserDictionaryList.java109
-rw-r--r--src/com/android/settings/net/NetworkPolicyEditor.java162
-rw-r--r--src/com/android/settings/vpn/VpnSettings.java7
-rw-r--r--src/com/android/settings/widget/ChartAxis.java35
-rw-r--r--src/com/android/settings/widget/ChartGridView.java79
-rw-r--r--src/com/android/settings/widget/ChartNetworkSeriesView.java197
-rw-r--r--src/com/android/settings/widget/ChartSweepView.java184
-rw-r--r--src/com/android/settings/widget/ChartView.java121
-rw-r--r--src/com/android/settings/widget/DataUsageChartView.java321
-rw-r--r--src/com/android/settings/widget/InvertedChartAxis.java64
-rw-r--r--src/com/android/settings/wifi/AdvancedSettings.java75
-rw-r--r--src/com/android/settings/wifi/AdvancedWifiSettings.java110
-rw-r--r--src/com/android/settings/wifi/WifiConfigController.java26
-rw-r--r--src/com/android/settings/wifi/WifiEnabler.java103
-rw-r--r--src/com/android/settings/wifi/WifiSettings.java226
-rw-r--r--src/com/android/settings/wifi/WifiSettingsForSetupWizardXL.java64
68 files changed, 5465 insertions, 1214 deletions
diff --git a/src/com/android/settings/AccessibilitySettings.java b/src/com/android/settings/AccessibilitySettings.java
index 826410d..9a0db5d 100644
--- a/src/com/android/settings/AccessibilitySettings.java
+++ b/src/com/android/settings/AccessibilitySettings.java
@@ -16,6 +16,7 @@
package com.android.settings;
+import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.Service;
@@ -78,7 +79,7 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
private CheckBoxPreference mToggleAccessibilityCheckBox;
private CheckBoxPreference mToggleScriptInjectionCheckBox;
- private CheckBoxPreference mToggleAccessibilityServiceCheckBox;
+ private SettingsCheckBoxPreference mToggleAccessibilityServiceCheckBox;
private PreferenceCategory mPowerButtonCategory;
private CheckBoxPreference mPowerButtonEndsCallCheckBox;
@@ -87,8 +88,8 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
private ListPreference mLongPressTimeoutListPreference;
- private Map<String, ServiceInfo> mAccessibilityServices =
- new LinkedHashMap<String, ServiceInfo>();
+ private Map<String, AccessibilityServiceInfo> mAccessibilityServices =
+ new LinkedHashMap<String, AccessibilityServiceInfo>();
private TextUtils.SimpleStringSplitter mStringColonSplitter =
new TextUtils.SimpleStringSplitter(':');
@@ -157,7 +158,7 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
}
}
- Map<String, ServiceInfo> accessibilityServices = mAccessibilityServices;
+ Map<String, AccessibilityServiceInfo> accessibilityServices = mAccessibilityServices;
for (String key : accessibilityServices.keySet()) {
CheckBoxPreference preference = (CheckBoxPreference) findPreference(key);
@@ -230,9 +231,9 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
throw new IllegalArgumentException(
KEY_TOGGLE_ACCESSIBILITY_SERVICE_CHECKBOX
+ " must be mapped to an instance of a "
- + CheckBoxPreference.class.getName());
+ + SettingsCheckBoxPreference.class.getName());
}
- mToggleAccessibilityServiceCheckBox = (CheckBoxPreference) preference;
+ mToggleAccessibilityServiceCheckBox = (SettingsCheckBoxPreference) preference;
}
}
@@ -274,7 +275,7 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
} else if (TOGGLE_ACCESSIBILITY_SCRIPT_INJECTION_CHECKBOX.equals(key)) {
handleToggleAccessibilityScriptInjection((CheckBoxPreference) preference);
} else if (preference instanceof CheckBoxPreference) {
- handleEnableAccessibilityServiceStateChange((CheckBoxPreference) preference);
+ handleEnableAccessibilityServiceStateChange((SettingsCheckBoxPreference) preference);
}
return super.onPreferenceTreeClick(preferenceScreen, preference);
@@ -318,7 +319,8 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
*
* @param preference The preference.
*/
- private void handleEnableAccessibilityServiceStateChange(CheckBoxPreference preference) {
+ private void handleEnableAccessibilityServiceStateChange(
+ SettingsCheckBoxPreference preference) {
if (preference.isChecked()) {
mToggleAccessibilityServiceCheckBox = preference;
// set right enabled state since the user may press back
@@ -357,7 +359,8 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
AccessibilityManager accessibilityManager =
(AccessibilityManager) getSystemService(Service.ACCESSIBILITY_SERVICE);
- List<ServiceInfo> installedServices = accessibilityManager.getAccessibilityServiceList();
+ List<AccessibilityServiceInfo> installedServices =
+ accessibilityManager.getInstalledAccessibilityServiceList();
if (installedServices.isEmpty()) {
getPreferenceScreen().removePreference(mAccessibilityServicesCategory);
@@ -367,12 +370,22 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
getPreferenceScreen().addPreference(mAccessibilityServicesCategory);
for (int i = 0, count = installedServices.size(); i < count; ++i) {
- ServiceInfo serviceInfo = installedServices.get(i);
- String key = serviceInfo.packageName + "/" + serviceInfo.name;
-
- if (mAccessibilityServices.put(key, serviceInfo) == null) {
- CheckBoxPreference preference = new CheckBoxPreference(getActivity());
+ AccessibilityServiceInfo accessibilityServiceInfo = installedServices.get(i);
+ String key = accessibilityServiceInfo.getId();
+
+ if (mAccessibilityServices.put(key, accessibilityServiceInfo) == null) {
+ String settingsActivityName = accessibilityServiceInfo.getSettingsActivityName();
+ Intent settingsIntent = null;
+ if (!TextUtils.isEmpty(settingsActivityName)) {
+ String packageName = accessibilityServiceInfo.getResolveInfo()
+ .serviceInfo.packageName;
+ settingsIntent = new Intent(Intent.ACTION_MAIN);
+ settingsIntent.setClassName(packageName, settingsActivityName);
+ }
+ SettingsCheckBoxPreference preference = new SettingsCheckBoxPreference(
+ getActivity(), settingsIntent);
preference.setKey(key);
+ ServiceInfo serviceInfo = accessibilityServiceInfo.getResolveInfo().serviceInfo;
preference.setTitle(serviceInfo.loadLabel(getActivity().getPackageManager()));
mAccessibilityServicesCategory.addPreference(preference);
}
@@ -424,7 +437,8 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
.setMessage(getResources().getString(
R.string.accessibility_service_security_warning,
mAccessibilityServices.get(mToggleAccessibilityServiceCheckBox.getKey())
- .applicationInfo.loadLabel(getActivity().getPackageManager())))
+ .getResolveInfo().serviceInfo.applicationInfo.loadLabel(
+ getActivity().getPackageManager())))
.setCancelable(true)
.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
diff --git a/src/com/android/settings/ApplicationSettings.java b/src/com/android/settings/ApplicationSettings.java
index da417ec..15eb840 100644
--- a/src/com/android/settings/ApplicationSettings.java
+++ b/src/com/android/settings/ApplicationSettings.java
@@ -18,6 +18,7 @@ package com.android.settings;
import android.app.AlertDialog;
import android.content.DialogInterface;
+import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
@@ -26,11 +27,13 @@ import android.preference.Preference;
import android.preference.PreferenceScreen;
import android.preference.Preference.OnPreferenceChangeListener;
import android.provider.Settings;
+import android.util.Log;
public class ApplicationSettings extends SettingsPreferenceFragment implements
DialogInterface.OnClickListener {
private static final String KEY_TOGGLE_INSTALL_APPLICATIONS = "toggle_install_applications";
+ private static final String KEY_TOGGLE_ADVANCED_SETTINGS = "toggle_advanced_settings";
private static final String KEY_APP_INSTALL_LOCATION = "app_install_location";
// App installation location. Default is ask the user.
@@ -43,9 +46,8 @@ public class ApplicationSettings extends SettingsPreferenceFragment implements
private static final String APP_INSTALL_AUTO_ID = "auto";
private CheckBoxPreference mToggleAppInstallation;
-
+ private CheckBoxPreference mToggleAdvancedSettings;
private ListPreference mInstallLocation;
-
private DialogInterface mWarnInstallApps;
@Override
@@ -54,9 +56,20 @@ public class ApplicationSettings extends SettingsPreferenceFragment implements
addPreferencesFromResource(R.xml.application_settings);
- mToggleAppInstallation = (CheckBoxPreference) findPreference(KEY_TOGGLE_INSTALL_APPLICATIONS);
+ mToggleAppInstallation = (CheckBoxPreference)findPreference(
+ KEY_TOGGLE_INSTALL_APPLICATIONS);
mToggleAppInstallation.setChecked(isNonMarketAppsAllowed());
+ mToggleAdvancedSettings = (CheckBoxPreference)findPreference(
+ KEY_TOGGLE_ADVANCED_SETTINGS);
+ mToggleAdvancedSettings.setChecked(isAdvancedSettingsEnabled());
+ getPreferenceScreen().removePreference(mToggleAdvancedSettings);
+
+ // not ready for prime time yet
+ if (false) {
+ getPreferenceScreen().removePreference(mInstallLocation);
+ }
+
mInstallLocation = (ListPreference) findPreference(KEY_APP_INSTALL_LOCATION);
// Is app default install location set?
boolean userSetInstLocation = (Settings.System.getInt(getContentResolver(),
@@ -110,6 +123,9 @@ public class ApplicationSettings extends SettingsPreferenceFragment implements
} else {
setNonMarketAppsAllowed(false);
}
+ } else if (preference == mToggleAdvancedSettings) {
+ boolean value = mToggleAdvancedSettings.isChecked();
+ setAdvancedSettingsEnabled(value);
}
return super.onPreferenceTreeClick(preferenceScreen, preference);
@@ -127,7 +143,23 @@ public class ApplicationSettings extends SettingsPreferenceFragment implements
Settings.Secure.putInt(getContentResolver(), Settings.Secure.INSTALL_NON_MARKET_APPS,
enabled ? 1 : 0);
}
-
+
+ private boolean isAdvancedSettingsEnabled() {
+ return Settings.System.getInt(getContentResolver(),
+ Settings.System.ADVANCED_SETTINGS,
+ Settings.System.ADVANCED_SETTINGS_DEFAULT) > 0;
+ }
+
+ private void setAdvancedSettingsEnabled(boolean enabled) {
+ int value = enabled ? 1 : 0;
+ // Change the system setting
+ Settings.Secure.putInt(getContentResolver(), Settings.System.ADVANCED_SETTINGS, value);
+ // TODO: the settings thing should broadcast this for thread safety purposes.
+ Intent intent = new Intent(Intent.ACTION_ADVANCED_SETTINGS_CHANGED);
+ intent.putExtra("state", value);
+ getActivity().sendBroadcast(intent);
+ }
+
private boolean isNonMarketAppsAllowed() {
return Settings.Secure.getInt(getContentResolver(),
Settings.Secure.INSTALL_NON_MARKET_APPS, 0) > 0;
diff --git a/src/com/android/settings/BrightnessPreference.java b/src/com/android/settings/BrightnessPreference.java
index 9bbb66a..df50ada 100644
--- a/src/com/android/settings/BrightnessPreference.java
+++ b/src/com/android/settings/BrightnessPreference.java
@@ -26,7 +26,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.preference.SeekBarPreference;
+import android.preference.SeekBarDialogPreference;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.AttributeSet;
@@ -35,7 +35,7 @@ import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.SeekBar;
-public class BrightnessPreference extends SeekBarPreference implements
+public class BrightnessPreference extends SeekBarDialogPreference implements
SeekBar.OnSeekBarChangeListener, CheckBox.OnCheckedChangeListener {
private SeekBar mSeekBar;
diff --git a/src/com/android/settings/ChooseLockGeneric.java b/src/com/android/settings/ChooseLockGeneric.java
index 118bc6f..8311c4a 100644
--- a/src/com/android/settings/ChooseLockGeneric.java
+++ b/src/com/android/settings/ChooseLockGeneric.java
@@ -27,6 +27,7 @@ import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.preference.PreferenceScreen;
+import android.security.KeyStore;
public class ChooseLockGeneric extends PreferenceActivity {
@@ -48,9 +49,11 @@ public class ChooseLockGeneric extends PreferenceActivity {
private static final int CONFIRM_EXISTING_REQUEST = 100;
private static final String PASSWORD_CONFIRMED = "password_confirmed";
private static final String CONFIRM_CREDENTIALS = "confirm_credentials";
+ public static final String MINIMUM_QUALITY_KEY = "minimum_quality";
private ChooseLockSettingsHelper mChooseLockSettingsHelper;
private DevicePolicyManager mDPM;
+ private KeyStore mKeyStore;
private boolean mPasswordConfirmed = false;
@Override
@@ -58,6 +61,7 @@ public class ChooseLockGeneric extends PreferenceActivity {
super.onCreate(savedInstanceState);
mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
+ mKeyStore = KeyStore.getInstance();
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this.getActivity());
if (savedInstanceState != null) {
@@ -126,8 +130,8 @@ public class ChooseLockGeneric extends PreferenceActivity {
.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, -1);
if (quality == -1) {
// If caller didn't specify password quality, show UI and allow the user to choose.
- quality = mDPM.getPasswordQuality(null);
- quality = upgradeQualityForEncryption(quality);
+ quality = getActivity().getIntent().getIntExtra(MINIMUM_QUALITY_KEY, -1);
+ quality = upgradeQuality(quality);
final PreferenceScreen prefScreen = getPreferenceScreen();
if (prefScreen != null) {
prefScreen.removeAll();
@@ -135,11 +139,26 @@ public class ChooseLockGeneric extends PreferenceActivity {
addPreferencesFromResource(R.xml.security_settings_picker);
disableUnusablePreferences(quality);
} else {
- quality = upgradeQualityForEncryption(quality);
updateUnlockMethodAndFinish(quality, false);
}
}
+ private int upgradeQuality(int quality) {
+ quality = upgradeQualityForDPM(quality);
+ quality = upgradeQualityForEncryption(quality);
+ quality = upgradeQualityForKeyStore(quality);
+ return quality;
+ }
+
+ private int upgradeQualityForDPM(int quality) {
+ // Compare min allowed password quality
+ int minQuality = mDPM.getPasswordQuality(null);
+ if (quality < minQuality) {
+ quality = minQuality;
+ }
+ return quality;
+ }
+
/**
* Mix in "encryption minimums" to any given quality value. This prevents users
* from downgrading the pattern/pin/password to a level below the minimums.
@@ -152,8 +171,17 @@ public class ChooseLockGeneric extends PreferenceActivity {
boolean encrypted = (encryptionStatus == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE)
|| (encryptionStatus == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVATING);
if (encrypted) {
- if (quality < DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) {
- quality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+ if (quality < CryptKeeperSettings.MIN_PASSWORD_QUALITY) {
+ quality = CryptKeeperSettings.MIN_PASSWORD_QUALITY;
+ }
+ }
+ return quality;
+ }
+
+ private int upgradeQualityForKeyStore(int quality) {
+ if (!mKeyStore.isEmpty()) {
+ if (quality < CredentialStorage.MIN_PASSWORD_QUALITY) {
+ quality = CredentialStorage.MIN_PASSWORD_QUALITY;
}
}
return quality;
@@ -208,13 +236,7 @@ public class ChooseLockGeneric extends PreferenceActivity {
throw new IllegalStateException("Tried to update password without confirming it");
}
- // Compare min allowed password quality and launch appropriate security setting method
- int minQuality = mDPM.getPasswordQuality(null);
- if (quality < minQuality) {
- quality = minQuality;
- }
- quality = upgradeQualityForEncryption(quality);
-
+ quality = upgradeQuality(quality);
if (quality >= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) {
int minLength = mDPM.getPasswordMinimumLength(null);
if (minLength < MIN_PASSWORD_LENGTH) {
diff --git a/src/com/android/settings/ChooseLockPassword.java b/src/com/android/settings/ChooseLockPassword.java
index a0f2346..96255eb 100644
--- a/src/com/android/settings/ChooseLockPassword.java
+++ b/src/com/android/settings/ChooseLockPassword.java
@@ -405,8 +405,10 @@ public class ChooseLockPassword extends PreferenceActivity {
}
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
- // Check if this was the result of hitting the enter key
- if (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_DOWN) {
+ // Check if this was the result of hitting the enter or "done" key
+ if (actionId == EditorInfo.IME_NULL
+ || actionId == EditorInfo.IME_ACTION_DONE
+ || actionId == EditorInfo.IME_ACTION_NEXT) {
handleNext();
return true;
}
diff --git a/src/com/android/settings/ChooseLockSettingsHelper.java b/src/com/android/settings/ChooseLockSettingsHelper.java
index d31fe3b..a069712 100644
--- a/src/com/android/settings/ChooseLockSettingsHelper.java
+++ b/src/com/android/settings/ChooseLockSettingsHelper.java
@@ -23,7 +23,10 @@ import android.app.Fragment;
import android.app.admin.DevicePolicyManager;
import android.content.Intent;
-public class ChooseLockSettingsHelper {
+public final class ChooseLockSettingsHelper {
+
+ static final String EXTRA_KEY_PASSWORD = "password";
+
private LockPatternUtils mLockPatternUtils;
private Activity mActivity;
private Fragment mFragment;
@@ -49,8 +52,7 @@ public class ChooseLockSettingsHelper {
* @return true if one exists and we launched an activity to confirm it
* @see #onActivityResult(int, int, android.content.Intent)
*/
- protected boolean launchConfirmationActivity(int request,
- CharSequence message, CharSequence details) {
+ boolean launchConfirmationActivity(int request, CharSequence message, CharSequence details) {
boolean launched = false;
switch (mLockPatternUtils.getKeyguardStoredPasswordQuality()) {
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
diff --git a/src/com/android/settings/ConfirmLockPassword.java b/src/com/android/settings/ConfirmLockPassword.java
index 3f4a4f3..1229046 100644
--- a/src/com/android/settings/ConfirmLockPassword.java
+++ b/src/com/android/settings/ConfirmLockPassword.java
@@ -27,13 +27,16 @@ import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceActivity;
+import android.text.Editable;
import android.text.InputType;
+import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
+import android.widget.Button;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
@@ -58,7 +61,7 @@ public class ConfirmLockPassword extends PreferenceActivity {
}
public static class ConfirmLockPasswordFragment extends Fragment implements OnClickListener,
- OnEditorActionListener {
+ OnEditorActionListener, TextWatcher {
private static final long ERROR_MESSAGE_TIMEOUT = 3000;
private TextView mPasswordEntry;
private LockPatternUtils mLockPatternUtils;
@@ -66,6 +69,7 @@ public class ConfirmLockPassword extends PreferenceActivity {
private Handler mHandler = new Handler();
private PasswordEntryKeyboardHelper mKeyboardHelper;
private PasswordEntryKeyboardView mKeyboardView;
+ private Button mContinueButton;
// required constructor for fragments
@@ -87,9 +91,14 @@ public class ConfirmLockPassword extends PreferenceActivity {
// Disable IME on our window since we provide our own keyboard
view.findViewById(R.id.cancel_button).setOnClickListener(this);
- view.findViewById(R.id.next_button).setOnClickListener(this);
+ mContinueButton = (Button) view.findViewById(R.id.next_button);
+ mContinueButton.setOnClickListener(this);
+ mContinueButton.setEnabled(false); // disable until the user enters at least one char
+
mPasswordEntry = (TextView) view.findViewById(R.id.password_entry);
mPasswordEntry.setOnEditorActionListener(this);
+ mPasswordEntry.addTextChangedListener(this);
+
mKeyboardView = (PasswordEntryKeyboardView) view.findViewById(R.id.keyboard);
mHeaderText = (TextView) view.findViewById(R.id.headerText);
final boolean isAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == storedQuality
@@ -140,7 +149,7 @@ public class ConfirmLockPassword extends PreferenceActivity {
if (mLockPatternUtils.checkPassword(pin)) {
Intent intent = new Intent();
- intent.putExtra("password", pin);
+ intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, pin);
getActivity().setResult(RESULT_OK, intent);
getActivity().finish();
@@ -172,13 +181,27 @@ public class ConfirmLockPassword extends PreferenceActivity {
}, ERROR_MESSAGE_TIMEOUT);
}
+ // {@link OnEditorActionListener} methods.
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
- // Check if this was the result of hitting the enter key
- if (actionId == EditorInfo.IME_NULL) {
+ // Check if this was the result of hitting the enter or "done" key
+ if (actionId == EditorInfo.IME_NULL
+ || actionId == EditorInfo.IME_ACTION_DONE
+ || actionId == EditorInfo.IME_ACTION_NEXT) {
handleNext();
return true;
}
return false;
}
+
+ // {@link TextWatcher} methods.
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ public void afterTextChanged(Editable s) {
+ mContinueButton.setEnabled(mPasswordEntry.getText().length() > 0);
+ }
}
}
diff --git a/src/com/android/settings/ConfirmLockPattern.java b/src/com/android/settings/ConfirmLockPattern.java
index 0653d3f..2892930 100644
--- a/src/com/android/settings/ConfirmLockPattern.java
+++ b/src/com/android/settings/ConfirmLockPattern.java
@@ -256,7 +256,12 @@ public class ConfirmLockPattern extends PreferenceActivity {
public void onPatternDetected(List<LockPatternView.Cell> pattern) {
if (mLockPatternUtils.checkPattern(pattern)) {
- getActivity().setResult(Activity.RESULT_OK);
+
+ Intent intent = new Intent();
+ intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD,
+ LockPatternUtils.patternToString(pattern));
+
+ getActivity().setResult(Activity.RESULT_OK, intent);
getActivity().finish();
} else {
if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL &&
diff --git a/src/com/android/settings/CredentialStorage.java b/src/com/android/settings/CredentialStorage.java
index 9d5a603..e246fce 100644
--- a/src/com/android/settings/CredentialStorage.java
+++ b/src/com/android/settings/CredentialStorage.java
@@ -18,213 +18,417 @@ package com.android.settings;
import android.app.Activity;
import android.app.AlertDialog;
+import android.app.admin.DevicePolicyManager;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.res.Resources;
+import android.os.AsyncTask;
import android.os.Bundle;
+import android.os.RemoteException;
+import android.security.KeyChain.KeyChainConnection;
+import android.security.KeyChain;
import android.security.KeyStore;
import android.text.Editable;
+import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
+import com.android.internal.widget.LockPatternUtils;
-import java.io.UnsupportedEncodingException;
+/**
+ * CredentialStorage handles KeyStore reset, unlock, and install.
+ *
+ * CredentialStorage has a pretty convoluted state machine to migrate
+ * from the old style separate keystore password to a new key guard
+ * based password, as well as to deal with setting up the key guard if
+ * necessary.
+ *
+ * KeyStore: UNINITALIZED
+ * KeyGuard: OFF
+ * Action: set up key guard
+ * Notes: factory state
+ *
+ * KeyStore: UNINITALIZED
+ * KeyGuard: ON
+ * Action: confirm key guard
+ * Notes: user had key guard but no keystore and upgraded from pre-ICS
+ * OR user had key guard and pre-ICS keystore password which was then reset
+ *
+ * KeyStore: LOCKED
+ * KeyGuard: OFF/ON
+ * Action: old unlock dialog
+ * Notes: assume old password, need to use it to unlock.
+ * if unlock, ensure key guard before install.
+ * if reset, treat as UNINITALIZED/OFF
+ *
+ * KeyStore: UNLOCKED
+ * KeyGuard: OFF
+ * Action: set up key guard
+ * Notes: ensure key guard, then proceed
+ *
+ * KeyStore: UNLOCKED
+ * keyguard: ON
+ * Action: normal unlock/install
+ * Notes: this is the common case
+ */
+public final class CredentialStorage extends Activity {
-public class CredentialStorage extends Activity implements TextWatcher,
- DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
+ private static final String TAG = "CredentialStorage";
public static final String ACTION_UNLOCK = "com.android.credentials.UNLOCK";
- public static final String ACTION_SET_PASSWORD = "com.android.credentials.SET_PASSWORD";
public static final String ACTION_INSTALL = "com.android.credentials.INSTALL";
public static final String ACTION_RESET = "com.android.credentials.RESET";
- private static final String TAG = "CredentialStorage";
+ // This is the minimum acceptable password quality. If the current password quality is
+ // lower than this, keystore should not be activated.
+ static final int MIN_PASSWORD_QUALITY = DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
- private KeyStore mKeyStore = KeyStore.getInstance();
- private boolean mSubmit = false;
- private Bundle mBundle;
+ private static final int CONFIRM_KEY_GUARD_REQUEST = 1;
- private TextView mOldPassword;
- private TextView mNewPassword;
- private TextView mConfirmPassword;
- private TextView mError;
- private Button mButton;
+ private final KeyStore mKeyStore = KeyStore.getInstance();
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+ /**
+ * When non-null, the bundle containing credentials to install.
+ */
+ private Bundle mInstallBundle;
+
+ /**
+ * After unsuccessful KeyStore.unlock, the number of unlock
+ * attempts remaining before the KeyStore will reset itself.
+ *
+ * Reset to -1 on successful unlock or reset.
+ */
+ private int mRetriesRemaining = -1;
+
+ @Override protected void onResume() {
+ super.onResume();
Intent intent = getIntent();
String action = intent.getAction();
- int state = mKeyStore.test();
if (ACTION_RESET.equals(action)) {
- showResetDialog();
- } else if (ACTION_SET_PASSWORD.equals(action)) {
- showPasswordDialog(state == KeyStore.UNINITIALIZED);
+ new ResetDialog();
} else {
if (ACTION_INSTALL.equals(action) &&
"com.android.certinstaller".equals(getCallingPackage())) {
- mBundle = intent.getExtras();
- }
- if (state == KeyStore.UNINITIALIZED) {
- showPasswordDialog(true);
- } else if (state == KeyStore.LOCKED) {
- showUnlockDialog();
- } else {
- install();
- finish();
+ mInstallBundle = intent.getExtras();
}
+ // ACTION_UNLOCK also handled here in addition to ACTION_INSTALL
+ handleUnlockOrInstall();
}
}
- private void install() {
- if (mBundle != null && !mBundle.isEmpty()) {
- try {
- for (String key : mBundle.keySet()) {
- byte[] value = mBundle.getByteArray(key);
- if (value != null && !mKeyStore.put(key.getBytes("UTF-8"), value)) {
- Log.e(TAG, "Failed to install " + key);
- return;
- }
+ /**
+ * Based on the current state of the KeyStore and key guard, try to
+ * make progress on unlocking or installing to the keystore.
+ */
+ private void handleUnlockOrInstall() {
+ // something already decided we are done, do not proceed
+ if (isFinishing()) {
+ return;
+ }
+ switch (mKeyStore.state()) {
+ case UNINITIALIZED: {
+ ensureKeyGuard();
+ return;
+ }
+ case LOCKED: {
+ new UnlockDialog();
+ return;
+ }
+ case UNLOCKED: {
+ if (!checkKeyGuardQuality()) {
+ new ConfigureKeyGuardDialog();
+ return;
}
- setResult(RESULT_OK);
- } catch (UnsupportedEncodingException e) {
- // Should never happen.
- throw new RuntimeException(e);
+ installIfAvailable();
+ finish();
+ return;
}
}
}
- private void showResetDialog() {
- AlertDialog dialog = new AlertDialog.Builder(this)
- .setTitle(android.R.string.dialog_alert_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(R.string.credentials_reset_hint)
- .setNeutralButton(android.R.string.ok, this)
- .setNegativeButton(android.R.string.cancel, this)
- .create();
- dialog.setOnDismissListener(this);
- dialog.show();
+ /**
+ * Make sure the user enters the key guard to set or change the
+ * keystore password. This can be used in UNINITIALIZED to set the
+ * keystore password or UNLOCKED to change the password (as is the
+ * case after unlocking with an old-style password).
+ */
+ private void ensureKeyGuard() {
+ if (!checkKeyGuardQuality()) {
+ // key guard not setup, doing so will initialize keystore
+ new ConfigureKeyGuardDialog();
+ // will return to onResume after Activity
+ return;
+ }
+ // force key guard confirmation
+ if (confirmKeyGuard()) {
+ // will return password value via onActivityResult
+ return;
+ }
+ finish();
}
- private void showPasswordDialog(boolean firstTime) {
- View view = View.inflate(this, R.layout.credentials_dialog, null);
+ /**
+ * Returns true if the currently set key guard matches our minimum quality requirements.
+ */
+ private boolean checkKeyGuardQuality() {
+ int quality = new LockPatternUtils(this).getActivePasswordQuality();
+ return (quality >= MIN_PASSWORD_QUALITY);
+ }
- ((TextView) view.findViewById(R.id.hint)).setText(R.string.credentials_password_hint);
- if (!firstTime) {
- view.findViewById(R.id.old_password_prompt).setVisibility(View.VISIBLE);
- mOldPassword = (TextView) view.findViewById(R.id.old_password);
- mOldPassword.setVisibility(View.VISIBLE);
- mOldPassword.addTextChangedListener(this);
+ /**
+ * Install credentials if available, otherwise do nothing.
+ */
+ private void installIfAvailable() {
+ if (mInstallBundle != null && !mInstallBundle.isEmpty()) {
+ Bundle bundle = mInstallBundle;
+ mInstallBundle = null;
+ for (String key : bundle.keySet()) {
+ byte[] value = bundle.getByteArray(key);
+ if (value != null && !mKeyStore.put(key, value)) {
+ Log.e(TAG, "Failed to install " + key);
+ return;
+ }
+ }
+ setResult(RESULT_OK);
}
- view.findViewById(R.id.new_passwords).setVisibility(View.VISIBLE);
- mNewPassword = (TextView) view.findViewById(R.id.new_password);
- mNewPassword.addTextChangedListener(this);
- mConfirmPassword = (TextView) view.findViewById(R.id.confirm_password);
- mConfirmPassword.addTextChangedListener(this);
- mError = (TextView) view.findViewById(R.id.error);
-
- AlertDialog dialog = new AlertDialog.Builder(this)
- .setView(view)
- .setTitle(R.string.credentials_set_password)
- .setPositiveButton(android.R.string.ok, this)
- .setNegativeButton(android.R.string.cancel, this)
- .create();
- dialog.setOnDismissListener(this);
- dialog.show();
- mButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
- mButton.setEnabled(false);
}
- private void showUnlockDialog() {
- View view = View.inflate(this, R.layout.credentials_dialog, null);
-
- ((TextView) view.findViewById(R.id.hint)).setText(R.string.credentials_unlock_hint);
- mOldPassword = (TextView) view.findViewById(R.id.old_password);
- mOldPassword.setVisibility(View.VISIBLE);
- mOldPassword.addTextChangedListener(this);
- mError = (TextView) view.findViewById(R.id.error);
-
- AlertDialog dialog = new AlertDialog.Builder(this)
- .setView(view)
- .setTitle(R.string.credentials_unlock)
- .setPositiveButton(android.R.string.ok, this)
- .setNegativeButton(android.R.string.cancel, this)
- .create();
- dialog.setOnDismissListener(this);
- dialog.show();
- mButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
- mButton.setEnabled(false);
- }
+ /**
+ * Prompt for reset confirmation, resetting on confirmation, finishing otherwise.
+ */
+ private class ResetDialog
+ implements DialogInterface.OnClickListener, DialogInterface.OnDismissListener
+ {
+ private boolean mResetConfirmed;
- public void afterTextChanged(Editable editable) {
- if ((mOldPassword == null || mOldPassword.getText().length() > 0) &&
- (mNewPassword == null || mNewPassword.getText().length() >= 8) &&
- (mConfirmPassword == null || mConfirmPassword.getText().length() >= 8)) {
- mButton.setEnabled(true);
- } else {
- mButton.setEnabled(false);
+ private ResetDialog() {
+ AlertDialog dialog = new AlertDialog.Builder(CredentialStorage.this)
+ .setTitle(android.R.string.dialog_alert_title)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setMessage(R.string.credentials_reset_hint)
+ .setPositiveButton(android.R.string.ok, this)
+ .setNegativeButton(android.R.string.cancel, this)
+ .create();
+ dialog.setOnDismissListener(this);
+ dialog.show();
}
- }
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
+ @Override public void onClick(DialogInterface dialog, int button) {
+ mResetConfirmed = (button == DialogInterface.BUTTON_POSITIVE);
+ }
- public void onTextChanged(CharSequence s,int start, int before, int count) {
+ @Override public void onDismiss(DialogInterface dialog) {
+ if (mResetConfirmed) {
+ mResetConfirmed = false;
+ new ResetKeyStoreAndKeyChain().execute();
+ return;
+ }
+ finish();
+ }
}
- public void onClick(DialogInterface dialog, int button) {
- mSubmit = (button == DialogInterface.BUTTON_POSITIVE);
- if (button == DialogInterface.BUTTON_NEUTRAL) {
+ /**
+ * Background task to handle reset of both keystore and user installed CAs.
+ */
+ private class ResetKeyStoreAndKeyChain extends AsyncTask<Void, Void, Boolean> {
+
+ @Override protected Boolean doInBackground(Void... unused) {
+
mKeyStore.reset();
- Toast.makeText(this, R.string.credentials_erased, Toast.LENGTH_SHORT).show();
+
+ try {
+ KeyChainConnection keyChainConnection = KeyChain.bind(CredentialStorage.this);
+ try {
+ return keyChainConnection.getService().reset();
+ } catch (RemoteException e) {
+ return false;
+ } finally {
+ keyChainConnection.close();
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return false;
+ }
+ }
+
+ @Override protected void onPostExecute(Boolean success) {
+ if (success) {
+ Toast.makeText(CredentialStorage.this,
+ R.string.credentials_erased, Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(CredentialStorage.this,
+ R.string.credentials_not_erased, Toast.LENGTH_SHORT).show();
+ }
+ finish();
}
}
- public void onDismiss(DialogInterface dialog) {
- if (mSubmit) {
- mSubmit = false;
- mError.setVisibility(View.VISIBLE);
+ /**
+ * Prompt for key guard configuration confirmation.
+ */
+ private class ConfigureKeyGuardDialog
+ implements DialogInterface.OnClickListener, DialogInterface.OnDismissListener
+ {
+ private boolean mConfigureConfirmed;
- if (mNewPassword == null) {
- mKeyStore.unlock(mOldPassword.getText().toString());
- } else {
- String newPassword = mNewPassword.getText().toString();
- String confirmPassword = mConfirmPassword.getText().toString();
+ private ConfigureKeyGuardDialog() {
+ AlertDialog dialog = new AlertDialog.Builder(CredentialStorage.this)
+ .setTitle(android.R.string.dialog_alert_title)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setMessage(R.string.credentials_configure_lock_screen_hint)
+ .setPositiveButton(android.R.string.ok, this)
+ .setNegativeButton(android.R.string.cancel, this)
+ .create();
+ dialog.setOnDismissListener(this);
+ dialog.show();
+ }
+
+ @Override public void onClick(DialogInterface dialog, int button) {
+ mConfigureConfirmed = (button == DialogInterface.BUTTON_POSITIVE);
+ }
- if (!newPassword.equals(confirmPassword)) {
- mError.setText(R.string.credentials_passwords_mismatch);
- ((AlertDialog) dialog).show();
+ @Override public void onDismiss(DialogInterface dialog) {
+ if (mConfigureConfirmed) {
+ mConfigureConfirmed = false;
+ Intent intent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD);
+ intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY,
+ MIN_PASSWORD_QUALITY);
+ startActivity(intent);
+ return;
+ }
+ finish();
+ }
+ }
+
+ /**
+ * Confirm existing key guard, returning password via onActivityResult.
+ */
+ private boolean confirmKeyGuard() {
+ Resources res = getResources();
+ boolean launched = new ChooseLockSettingsHelper(this)
+ .launchConfirmationActivity(CONFIRM_KEY_GUARD_REQUEST,
+ res.getText(R.string.master_clear_gesture_prompt),
+ res.getText(R.string.master_clear_gesture_explanation));
+ return launched;
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ /**
+ * Receive key guard password initiated by confirmKeyGuard.
+ */
+ if (requestCode == CONFIRM_KEY_GUARD_REQUEST) {
+ if (resultCode == Activity.RESULT_OK) {
+ String password = data.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
+ if (!TextUtils.isEmpty(password)) {
+ // success
+ mKeyStore.password(password);
+ // return to onResume
return;
- } else if (mOldPassword == null) {
- mKeyStore.password(newPassword);
- } else {
- mKeyStore.password(mOldPassword.getText().toString(), newPassword);
}
}
+ // failed confirmation, bail
+ finish();
+ }
+ }
+
+ /**
+ * Prompt for unlock with old-style password.
+ *
+ * On successful unlock, ensure migration to key guard before continuing.
+ * On unsuccessful unlock, retry by calling handleUnlockOrInstall.
+ */
+ private class UnlockDialog implements TextWatcher,
+ DialogInterface.OnClickListener, DialogInterface.OnDismissListener
+ {
+ private boolean mUnlockConfirmed;
+
+ private final Button mButton;
+ private final TextView mOldPassword;
+ private final TextView mError;
+
+ private UnlockDialog() {
+ View view = View.inflate(CredentialStorage.this, R.layout.credentials_dialog, null);
+
+ CharSequence text;
+ if (mRetriesRemaining == -1) {
+ text = getResources().getText(R.string.credentials_unlock_hint);
+ } else if (mRetriesRemaining > 3) {
+ text = getResources().getText(R.string.credentials_wrong_password);
+ } else if (mRetriesRemaining == 1) {
+ text = getResources().getText(R.string.credentials_reset_warning);
+ } else {
+ text = getString(R.string.credentials_reset_warning_plural, mRetriesRemaining);
+ }
- int error = mKeyStore.getLastError();
- if (error == KeyStore.NO_ERROR) {
- Toast.makeText(this, R.string.credentials_enabled, Toast.LENGTH_SHORT).show();
- install();
- } else if (error == KeyStore.UNINITIALIZED) {
- Toast.makeText(this, R.string.credentials_erased, Toast.LENGTH_SHORT).show();
- } else if (error >= KeyStore.WRONG_PASSWORD) {
- int count = error - KeyStore.WRONG_PASSWORD + 1;
- if (count > 3) {
- mError.setText(R.string.credentials_wrong_password);
- } else if (count == 1) {
- mError.setText(R.string.credentials_reset_warning);
- } else {
- mError.setText(getString(R.string.credentials_reset_warning_plural, count));
+ ((TextView) view.findViewById(R.id.hint)).setText(text);
+ mOldPassword = (TextView) view.findViewById(R.id.old_password);
+ mOldPassword.setVisibility(View.VISIBLE);
+ mOldPassword.addTextChangedListener(this);
+ mError = (TextView) view.findViewById(R.id.error);
+
+ AlertDialog dialog = new AlertDialog.Builder(CredentialStorage.this)
+ .setView(view)
+ .setTitle(R.string.credentials_unlock)
+ .setPositiveButton(android.R.string.ok, this)
+ .setNegativeButton(android.R.string.cancel, this)
+ .create();
+ dialog.setOnDismissListener(this);
+ dialog.show();
+ mButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
+ mButton.setEnabled(false);
+ }
+
+ @Override public void afterTextChanged(Editable editable) {
+ mButton.setEnabled(mOldPassword == null || mOldPassword.getText().length() > 0);
+ }
+
+ @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override public void onTextChanged(CharSequence s,int start, int before, int count) {
+ }
+
+ @Override public void onClick(DialogInterface dialog, int button) {
+ mUnlockConfirmed = (button == DialogInterface.BUTTON_POSITIVE);
+ }
+
+ @Override public void onDismiss(DialogInterface dialog) {
+ if (mUnlockConfirmed) {
+ mUnlockConfirmed = false;
+ mError.setVisibility(View.VISIBLE);
+ mKeyStore.unlock(mOldPassword.getText().toString());
+ int error = mKeyStore.getLastError();
+ if (error == KeyStore.NO_ERROR) {
+ mRetriesRemaining = -1;
+ Toast.makeText(CredentialStorage.this,
+ R.string.credentials_enabled,
+ Toast.LENGTH_SHORT).show();
+ // aha, now we are unlocked, switch to key guard.
+ // we'll end up back in onResume to install
+ ensureKeyGuard();
+ } else if (error == KeyStore.UNINITIALIZED) {
+ mRetriesRemaining = -1;
+ Toast.makeText(CredentialStorage.this,
+ R.string.credentials_erased,
+ Toast.LENGTH_SHORT).show();
+ // we are reset, we can now set new password with key guard
+ handleUnlockOrInstall();
+ } else if (error >= KeyStore.WRONG_PASSWORD) {
+ // we need to try again
+ mRetriesRemaining = error - KeyStore.WRONG_PASSWORD + 1;
+ handleUnlockOrInstall();
}
- ((AlertDialog) dialog).show();
return;
}
+ finish();
}
- finish();
}
}
diff --git a/src/com/android/settings/CryptKeeper.java b/src/com/android/settings/CryptKeeper.java
index edf00d5..20bf7ce 100644
--- a/src/com/android/settings/CryptKeeper.java
+++ b/src/com/android/settings/CryptKeeper.java
@@ -342,9 +342,11 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
KeyboardView keyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard);
- PasswordEntryKeyboardHelper keyboardHelper = new PasswordEntryKeyboardHelper(this,
- keyboardView, mPasswordEntry, false);
- keyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA);
+ if (keyboardView != null) {
+ PasswordEntryKeyboardHelper keyboardHelper = new PasswordEntryKeyboardHelper(this,
+ keyboardView, mPasswordEntry, false);
+ keyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA);
+ }
}
private IMountService getMountService() {
@@ -357,7 +359,7 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
- if (actionId == EditorInfo.IME_NULL) {
+ if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE) {
// Get the password
String password = v.getText().toString();
diff --git a/src/com/android/settings/CryptKeeperConfirm.java b/src/com/android/settings/CryptKeeperConfirm.java
index ba8ce10..d177cb4 100644
--- a/src/com/android/settings/CryptKeeperConfirm.java
+++ b/src/com/android/settings/CryptKeeperConfirm.java
@@ -62,6 +62,8 @@ public class CryptKeeperConfirm extends Fragment {
public void run() {
IBinder service = ServiceManager.getService("mount");
if (service == null) {
+ Log.e("CryptKeeper", "Failed to find the mount service");
+ finish();
return;
}
diff --git a/src/com/android/settings/CryptKeeperSettings.java b/src/com/android/settings/CryptKeeperSettings.java
index 10fa8ac..a9002fa 100644
--- a/src/com/android/settings/CryptKeeperSettings.java
+++ b/src/com/android/settings/CryptKeeperSettings.java
@@ -37,16 +37,6 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
-/**
- * Confirm and execute a reset of the device to a clean "just out of the box"
- * state. Multiple confirmations are required: first, a general "are you sure
- * you want to do this?" prompt, followed by a keyguard pattern trace if the user
- * has defined one, followed by a final strongly-worded "THIS WILL ERASE EVERYTHING
- * ON THE PHONE" prompt. If at any time the phone is allowed to go to sleep, is
- * locked, et cetera, then the confirmation sequence is abandoned.
- *
- * This is the initial screen.
- */
public class CryptKeeperSettings extends Fragment {
private static final String TAG = "CryptKeeper";
@@ -54,7 +44,7 @@ public class CryptKeeperSettings extends Fragment {
// This is the minimum acceptable password quality. If the current password quality is
// lower than this, encryption should not be activated.
- private static final int MIN_PASSWORD_QUALITY = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+ static final int MIN_PASSWORD_QUALITY = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
// Minimum battery charge level (in percent) to launch encryption. If the battery charge is
// lower than this, encryption should not be activated.
@@ -163,7 +153,7 @@ public class CryptKeeperSettings extends Fragment {
*/
private boolean runKeyguardConfirmation(int request) {
// 1. Confirm that we have a sufficient PIN/Password to continue
- int quality = new LockPatternUtils(getActivity()).getKeyguardStoredPasswordQuality();
+ int quality = new LockPatternUtils(getActivity()).getActivePasswordQuality();
if (quality < MIN_PASSWORD_QUALITY) {
return false;
}
@@ -186,7 +176,7 @@ public class CryptKeeperSettings extends Fragment {
// If the user entered a valid keyguard trace, present the final
// confirmation prompt; otherwise, go back to the initial state.
if (resultCode == Activity.RESULT_OK && data != null) {
- String password = data.getStringExtra("password");
+ String password = data.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
if (!TextUtils.isEmpty(password)) {
showFinalConfirmation(password);
}
diff --git a/src/com/android/settings/DataUsageAppDetail.java b/src/com/android/settings/DataUsageAppDetail.java
new file mode 100644
index 0000000..6294ad3
--- /dev/null
+++ b/src/com/android/settings/DataUsageAppDetail.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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 com.android.settings;
+
+import static android.net.NetworkPolicyManager.POLICY_NONE;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
+import static com.android.settings.DataUsageSummary.getHistoryBounds;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.Color;
+import android.net.INetworkPolicyManager;
+import android.net.INetworkStatsService;
+import android.net.NetworkPolicyManager;
+import android.net.NetworkStats;
+import android.net.NetworkStatsHistory;
+import android.net.NetworkTemplate;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.preference.CheckBoxPreference;
+import android.preference.Preference;
+import android.text.format.DateUtils;
+import android.text.format.Formatter;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.settings.widget.DataUsageChartView;
+import com.android.settings.widget.DataUsageChartView.DataUsageChartListener;
+
+public class DataUsageAppDetail extends Fragment {
+ private static final String TAG = "DataUsage";
+ private static final boolean LOGD = true;
+
+ public static final String EXTRA_UID = "uid";
+ public static final String EXTRA_NETWORK_TEMPLATE = "networkTemplate";
+
+ private int mUid;
+ private NetworkTemplate mTemplate;
+
+ private Intent mAppSettingsIntent;
+
+ private static final String TAG_CONFIRM_RESTRICT = "confirmRestrict";
+
+ private INetworkStatsService mStatsService;
+ private INetworkPolicyManager mPolicyService;
+
+ private CheckBoxPreference mRestrictBackground;
+ private View mRestrictBackgroundView;
+
+ private FrameLayout mChartContainer;
+ private TextView mTitle;
+ private TextView mText1;
+ private Button mAppSettings;
+ private LinearLayout mSwitches;
+
+ private DataUsageChartView mChart;
+ private NetworkStatsHistory mHistory;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mStatsService = INetworkStatsService.Stub.asInterface(
+ ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+ mPolicyService = INetworkPolicyManager.Stub.asInterface(
+ ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+
+ final Context context = inflater.getContext();
+ final View view = inflater.inflate(R.layout.data_usage_detail, container, false);
+
+ mChartContainer = (FrameLayout) view.findViewById(R.id.chart_container);
+ mTitle = (TextView) view.findViewById(android.R.id.title);
+ mText1 = (TextView) view.findViewById(android.R.id.text1);
+ mAppSettings = (Button) view.findViewById(R.id.data_usage_app_settings);
+ mSwitches = (LinearLayout) view.findViewById(R.id.switches);
+
+ mRestrictBackground = new CheckBoxPreference(context);
+ mRestrictBackground.setTitle(R.string.data_usage_app_restrict_background);
+ mRestrictBackground.setSummary(R.string.data_usage_app_restrict_background_summary);
+
+ // kick refresh once to force-create views
+ refreshPreferenceViews();
+
+ mSwitches.addView(mRestrictBackgroundView);
+ mRestrictBackgroundView.setOnClickListener(mRestrictBackgroundListener);
+
+ mAppSettings.setOnClickListener(mAppSettingsListener);
+
+ mChart = new DataUsageChartView(context);
+ mChartContainer.addView(mChart);
+
+ mChart.setListener(mChartListener);
+ mChart.setChartColor(Color.parseColor("#d88d3a"), Color.parseColor("#c0ba7f3e"),
+ Color.parseColor("#88566abc"));
+
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ updateBody();
+ }
+
+ private void updateBody() {
+ final PackageManager pm = getActivity().getPackageManager();
+
+ mUid = getArguments().getInt(EXTRA_UID);
+ mTemplate = getArguments().getParcelable(EXTRA_NETWORK_TEMPLATE);
+
+ mTitle.setText(pm.getNameForUid(mUid));
+
+ // enable settings button when package provides it
+ // TODO: target torwards entire UID instead of just first package
+ final String[] packageNames = pm.getPackagesForUid(mUid);
+ if (packageNames != null && packageNames.length > 0) {
+ mAppSettingsIntent = new Intent(Intent.ACTION_MANAGE_NETWORK_USAGE);
+ mAppSettingsIntent.setPackage(packageNames[0]);
+ mAppSettingsIntent.addCategory(Intent.CATEGORY_DEFAULT);
+
+ final boolean matchFound = pm.resolveActivity(mAppSettingsIntent, 0) != null;
+ mAppSettings.setEnabled(matchFound);
+
+ } else {
+ mAppSettingsIntent = null;
+ mAppSettings.setEnabled(false);
+ }
+
+ try {
+ // load stats for current uid and template
+ // TODO: read template from extras
+ mHistory = mStatsService.getHistoryForUid(mTemplate, mUid, NetworkStats.TAG_NONE);
+ } catch (RemoteException e) {
+ // since we can't do much without history, and we don't want to
+ // leave with half-baked UI, we bail hard.
+ throw new RuntimeException("problem reading network stats", e);
+ }
+
+ // bind chart to historical stats
+ mChart.bindNetworkStats(mHistory);
+
+ // show entire history known
+ final long[] bounds = getHistoryBounds(mHistory);
+ mChart.setVisibleRange(bounds[0], bounds[1] + DateUtils.WEEK_IN_MILLIS, bounds[1]);
+ updateDetailData();
+
+ final Context context = getActivity();
+ if (NetworkPolicyManager.isUidValidForPolicy(context, mUid)) {
+ mRestrictBackgroundView.setVisibility(View.VISIBLE);
+
+ final int uidPolicy;
+ try {
+ uidPolicy = mPolicyService.getUidPolicy(mUid);
+ } catch (RemoteException e) {
+ // since we can't do much without policy, we bail hard.
+ throw new RuntimeException("problem reading network policy", e);
+ }
+
+ // update policy checkbox
+ final boolean restrictBackground = (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
+ mRestrictBackground.setChecked(restrictBackground);
+
+ // kick preference views so they rebind from changes above
+ refreshPreferenceViews();
+
+ } else {
+ mRestrictBackgroundView.setVisibility(View.GONE);
+ }
+ }
+
+ private void updateDetailData() {
+ if (LOGD) Log.d(TAG, "updateDetailData()");
+
+ final Context context = mChart.getContext();
+ final long[] range = mChart.getInspectRange();
+ final long[] total = mHistory.getTotalData(range[0], range[1], null);
+ final long totalCombined = total[0] + total[1];
+ mText1.setText(Formatter.formatFileSize(context, totalCombined));
+ }
+
+ private void setRestrictBackground(boolean restrictBackground) {
+ if (LOGD) Log.d(TAG, "setRestrictBackground()");
+ try {
+ mPolicyService.setUidPolicy(
+ mUid, restrictBackground ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE);
+ } catch (RemoteException e) {
+ throw new RuntimeException("unable to save policy", e);
+ }
+
+ mRestrictBackground.setChecked(restrictBackground);
+ refreshPreferenceViews();
+ }
+
+ /**
+ * Force rebind of hijacked {@link Preference} views.
+ */
+ private void refreshPreferenceViews() {
+ mRestrictBackgroundView = mRestrictBackground.getView(mRestrictBackgroundView, mSwitches);
+ }
+
+ private DataUsageChartListener mChartListener = new DataUsageChartListener() {
+ /** {@inheritDoc} */
+ public void onInspectRangeChanged() {
+ if (LOGD) Log.d(TAG, "onInspectRangeChanged()");
+ updateDetailData();
+ }
+
+ /** {@inheritDoc} */
+ public void onWarningChanged() {
+ // ignored
+ }
+
+ /** {@inheritDoc} */
+ public void onLimitChanged() {
+ // ignored
+ }
+ };
+
+ private OnClickListener mAppSettingsListener = new OnClickListener() {
+ /** {@inheritDoc} */
+ public void onClick(View v) {
+ // TODO: target torwards entire UID instead of just first package
+ startActivity(mAppSettingsIntent);
+ }
+ };
+
+ private OnClickListener mRestrictBackgroundListener = new OnClickListener() {
+ /** {@inheritDoc} */
+ public void onClick(View v) {
+ final boolean restrictBackground = !mRestrictBackground.isChecked();
+
+ if (restrictBackground) {
+ // enabling restriction; show confirmation dialog which
+ // eventually calls setRestrictBackground() once user confirms.
+ ConfirmRestrictFragment.show(DataUsageAppDetail.this);
+ } else {
+ setRestrictBackground(false);
+ }
+ }
+ };
+
+ /**
+ * Dialog to request user confirmation before setting
+ * {@link #POLICY_REJECT_METERED_BACKGROUND}.
+ */
+ public static class ConfirmRestrictFragment extends DialogFragment {
+ public static void show(DataUsageAppDetail parent) {
+ final ConfirmRestrictFragment dialog = new ConfirmRestrictFragment();
+ dialog.setTargetFragment(parent, 0);
+ dialog.show(parent.getFragmentManager(), TAG_CONFIRM_RESTRICT);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Context context = getActivity();
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.data_usage_app_restrict_dialog_title);
+ builder.setMessage(R.string.data_usage_app_restrict_dialog);
+
+ builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ final DataUsageAppDetail target = (DataUsageAppDetail) getTargetFragment();
+ if (target != null) {
+ target.setRestrictBackground(true);
+ }
+ }
+ });
+ builder.setNegativeButton(android.R.string.cancel, null);
+
+ return builder.create();
+ }
+ }
+
+}
diff --git a/src/com/android/settings/DataUsageSummary.java b/src/com/android/settings/DataUsageSummary.java
new file mode 100644
index 0000000..ef2282a
--- /dev/null
+++ b/src/com/android/settings/DataUsageSummary.java
@@ -0,0 +1,1090 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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 com.android.settings;
+
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_WIMAX;
+import static android.net.NetworkPolicy.LIMIT_DISABLED;
+import static android.net.NetworkPolicyManager.ACTION_DATA_USAGE_LIMIT;
+import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
+import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
+import static android.net.NetworkPolicyManager.computeNextCycleBoundary;
+import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER;
+import static android.net.NetworkTemplate.MATCH_MOBILE_4G;
+import static android.net.NetworkTemplate.MATCH_MOBILE_ALL;
+import static android.net.NetworkTemplate.MATCH_WIFI;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.ConnectivityManager;
+import android.net.INetworkPolicyManager;
+import android.net.INetworkStatsService;
+import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
+import android.net.NetworkStats;
+import android.net.NetworkStatsHistory;
+import android.net.NetworkTemplate;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.text.format.Formatter;
+import android.text.format.Time;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
+import android.widget.BaseAdapter;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.NumberPicker;
+import android.widget.Spinner;
+import android.widget.Switch;
+import android.widget.TabHost;
+import android.widget.TabHost.OnTabChangeListener;
+import android.widget.TabHost.TabContentFactory;
+import android.widget.TabHost.TabSpec;
+import android.widget.TabWidget;
+import android.widget.TextView;
+
+import com.android.internal.telephony.Phone;
+import com.android.settings.net.NetworkPolicyEditor;
+import com.android.settings.widget.DataUsageChartView;
+import com.android.settings.widget.DataUsageChartView.DataUsageChartListener;
+import com.google.android.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Locale;
+
+/**
+ * Panel show data usage history across various networks, including options to
+ * inspect based on usage cycle and control through {@link NetworkPolicy}.
+ */
+public class DataUsageSummary extends Fragment {
+ private static final String TAG = "DataUsage";
+ private static final boolean LOGD = true;
+
+ private static final int TEMPLATE_INVALID = -1;
+
+ private static final String TAB_3G = "3g";
+ private static final String TAB_4G = "4g";
+ private static final String TAB_MOBILE = "mobile";
+ private static final String TAB_WIFI = "wifi";
+
+ private static final String TAG_CONFIRM_LIMIT = "confirmLimit";
+ private static final String TAG_CYCLE_EDITOR = "cycleEditor";
+ private static final String TAG_POLICY_LIMIT = "policyLimit";
+
+ private static final long KB_IN_BYTES = 1024;
+ private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
+ private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
+
+ private INetworkStatsService mStatsService;
+ private INetworkPolicyManager mPolicyService;
+ private ConnectivityManager mConnService;
+
+ private static final String PREF_FILE = "data_usage";
+ private static final String PREF_SHOW_WIFI = "show_wifi";
+
+ private SharedPreferences mPrefs;
+
+ private TabHost mTabHost;
+ private TabWidget mTabWidget;
+ private ListView mListView;
+ private DataUsageAdapter mAdapter;
+
+ private View mHeader;
+ private LinearLayout mSwitches;
+
+ private Switch mDataEnabled;
+ private CheckBox mDisableAtLimit;
+ private View mDataEnabledView;
+ private View mDisableAtLimitView;
+
+ private DataUsageChartView mChart;
+
+ private Spinner mCycleSpinner;
+ private CycleAdapter mCycleAdapter;
+
+ private boolean mShowWifi = false;
+
+ private NetworkTemplate mTemplate = null;
+
+ private NetworkPolicyEditor mPolicyEditor;
+ private NetworkStatsHistory mHistory;
+
+ private String mIntentTab = null;
+
+ /** Flag used to ignore listeners during binding. */
+ private boolean mBinding;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mStatsService = INetworkStatsService.Stub.asInterface(
+ ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+ mPolicyService = INetworkPolicyManager.Stub.asInterface(
+ ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
+ mConnService = (ConnectivityManager) getActivity().getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ mPrefs = getActivity().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
+
+ mPolicyEditor = new NetworkPolicyEditor(mPolicyService);
+ mPolicyEditor.read();
+
+ mShowWifi = mPrefs.getBoolean(PREF_SHOW_WIFI, false);
+
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+
+ final Context context = inflater.getContext();
+ final View view = inflater.inflate(R.layout.data_usage_summary, container, false);
+
+ mTabHost = (TabHost) view.findViewById(android.R.id.tabhost);
+ mTabWidget = (TabWidget) view.findViewById(android.R.id.tabs);
+ mListView = (ListView) view.findViewById(android.R.id.list);
+
+ mTabHost.setup();
+ mTabHost.setOnTabChangedListener(mTabListener);
+
+ mHeader = inflater.inflate(R.layout.data_usage_header, mListView, false);
+ mListView.addHeaderView(mHeader, null, false);
+
+ mDataEnabled = new Switch(inflater.getContext());
+ mDataEnabledView = inflatePreference(inflater, mSwitches, mDataEnabled);
+ mDataEnabled.setOnCheckedChangeListener(mDataEnabledListener);
+
+ mDisableAtLimit = new CheckBox(inflater.getContext());
+ mDisableAtLimitView = inflatePreference(inflater, mSwitches, mDisableAtLimit);
+ mDisableAtLimitView.setOnClickListener(mDisableAtLimitListener);
+
+ mSwitches = (LinearLayout) mHeader.findViewById(R.id.switches);
+ mSwitches.addView(mDataEnabledView);
+ mSwitches.addView(mDisableAtLimitView);
+
+ mCycleSpinner = (Spinner) mHeader.findViewById(R.id.cycles);
+ mCycleAdapter = new CycleAdapter(context);
+ mCycleSpinner.setAdapter(mCycleAdapter);
+ mCycleSpinner.setOnItemSelectedListener(mCycleListener);
+
+ final int chartHeight = getResources().getDimensionPixelSize(
+ R.dimen.data_usage_chart_height);
+ mChart = new DataUsageChartView(context);
+ mChart.setListener(mChartListener);
+ mChart.setLayoutParams(new AbsListView.LayoutParams(MATCH_PARENT, chartHeight));
+ mListView.addHeaderView(mChart, null, false);
+
+ mAdapter = new DataUsageAdapter();
+ mListView.setOnItemClickListener(mListListener);
+ mListView.setAdapter(mAdapter);
+
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ // pick default tab based on incoming intent
+ final Intent intent = getActivity().getIntent();
+ mIntentTab = computeTabFromIntent(intent);
+
+ // this kicks off chain reaction which creates tabs, binds the body to
+ // selected network, and binds chart, cycles and detail list.
+ updateTabs();
+
+ // template and tab has been selected; show dialog if limit passed
+ final String action = intent.getAction();
+ if (ACTION_DATA_USAGE_LIMIT.equals(action)) {
+ PolicyLimitFragment.show(this);
+ }
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ inflater.inflate(R.menu.data_usage, menu);
+ }
+
+ @Override
+ public void onPrepareOptionsMenu(Menu menu) {
+ final Context context = getActivity();
+
+ final MenuItem split4g = menu.findItem(R.id.action_split_4g);
+ split4g.setVisible(hasMobile4gRadio(context));
+ split4g.setChecked(isMobilePolicySplit());
+
+ final MenuItem showWifi = menu.findItem(R.id.action_show_wifi);
+ showWifi.setVisible(hasMobileRadio(context) && hasWifiRadio(context));
+ showWifi.setChecked(mShowWifi);
+
+ final MenuItem settings = menu.findItem(R.id.action_settings);
+ settings.setVisible(split4g.isVisible() || showWifi.isVisible());
+
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_split_4g: {
+ final boolean mobileSplit = !item.isChecked();
+ setMobilePolicySplit(mobileSplit);
+ item.setChecked(isMobilePolicySplit());
+ updateTabs();
+ return true;
+ }
+ case R.id.action_show_wifi: {
+ mShowWifi = !item.isChecked();
+ mPrefs.edit().putBoolean(PREF_SHOW_WIFI, mShowWifi).apply();
+ item.setChecked(mShowWifi);
+ updateTabs();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+
+ mDataEnabledView = null;
+ mDisableAtLimitView = null;
+ }
+
+ /**
+ * Rebuild all tabs based on {@link NetworkPolicyEditor} and
+ * {@link #mShowWifi}, hiding the tabs entirely when applicable. Selects
+ * first tab, and kicks off a full rebind of body contents.
+ */
+ private void updateTabs() {
+ final Context context = getActivity();
+ mTabHost.clearAllTabs();
+
+ final boolean mobileSplit = isMobilePolicySplit();
+ if (mobileSplit && hasMobile4gRadio(context)) {
+ mTabHost.addTab(buildTabSpec(TAB_3G, R.string.data_usage_tab_3g));
+ mTabHost.addTab(buildTabSpec(TAB_4G, R.string.data_usage_tab_4g));
+ }
+
+ if (mShowWifi && hasWifiRadio(context) && hasMobileRadio(context)) {
+ if (!mobileSplit) {
+ mTabHost.addTab(buildTabSpec(TAB_MOBILE, R.string.data_usage_tab_mobile));
+ }
+ mTabHost.addTab(buildTabSpec(TAB_WIFI, R.string.data_usage_tab_wifi));
+ }
+
+ final boolean hasTabs = mTabWidget.getTabCount() > 0;
+ mTabWidget.setVisibility(hasTabs ? View.VISIBLE : View.GONE);
+ if (hasTabs) {
+ if (mIntentTab != null) {
+ // select default tab, which will kick off updateBody()
+ mTabHost.setCurrentTabByTag(mIntentTab);
+ } else {
+ // select first tab, which will kick off updateBody()
+ mTabHost.setCurrentTab(0);
+ }
+ } else {
+ // no tabs visible; update body manually
+ updateBody();
+ }
+ }
+
+ /**
+ * Factory that provide empty {@link View} to make {@link TabHost} happy.
+ */
+ private TabContentFactory mEmptyTabContent = new TabContentFactory() {
+ /** {@inheritDoc} */
+ public View createTabContent(String tag) {
+ return new View(mTabHost.getContext());
+ }
+ };
+
+ /**
+ * Build {@link TabSpec} with thin indicator, and empty content.
+ */
+ private TabSpec buildTabSpec(String tag, int titleRes) {
+ final LayoutInflater inflater = LayoutInflater.from(mTabWidget.getContext());
+ final View indicator = inflater.inflate(
+ R.layout.tab_indicator_thin_holo, mTabWidget, false);
+ final TextView title = (TextView) indicator.findViewById(android.R.id.title);
+ title.setText(titleRes);
+ return mTabHost.newTabSpec(tag).setIndicator(indicator).setContent(mEmptyTabContent);
+ }
+
+ private OnTabChangeListener mTabListener = new OnTabChangeListener() {
+ /** {@inheritDoc} */
+ public void onTabChanged(String tabId) {
+ // user changed tab; update body
+ updateBody();
+ }
+ };
+
+ /**
+ * Update body content based on current tab. Loads
+ * {@link NetworkStatsHistory} and {@link NetworkPolicy} from system, and
+ * binds them to visible controls.
+ */
+ private void updateBody() {
+ mBinding = true;
+
+ final Context context = getActivity();
+ final String tabTag = mTabHost.getCurrentTabTag();
+
+ final String currentTab;
+ if (tabTag != null) {
+ currentTab = tabTag;
+ } else if (hasMobileRadio(context)) {
+ currentTab = TAB_MOBILE;
+ } else if (hasWifiRadio(context)) {
+ currentTab = TAB_WIFI;
+ } else {
+ throw new IllegalStateException("no mobile or wifi radios");
+ }
+
+ if (LOGD) Log.d(TAG, "updateBody() with currentTab=" + currentTab);
+
+ if (TAB_WIFI.equals(currentTab)) {
+ // wifi doesn't have any controls
+ mDataEnabledView.setVisibility(View.GONE);
+ mDisableAtLimitView.setVisibility(View.GONE);
+ mTemplate = new NetworkTemplate(MATCH_WIFI, null);
+
+ } else {
+ // make sure we show for non-wifi
+ mDataEnabledView.setVisibility(View.VISIBLE);
+ mDisableAtLimitView.setVisibility(View.VISIBLE);
+ }
+
+ final String subscriberId = getActiveSubscriberId(context);
+ if (TAB_MOBILE.equals(currentTab)) {
+ setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_mobile);
+ setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_mobile_limit);
+ mDataEnabled.setChecked(mConnService.getMobileDataEnabled());
+ mTemplate = new NetworkTemplate(MATCH_MOBILE_ALL, subscriberId);
+
+ } else if (TAB_3G.equals(currentTab)) {
+ setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_3g);
+ setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_3g_limit);
+ // TODO: bind mDataEnabled to 3G radio state
+ mTemplate = new NetworkTemplate(MATCH_MOBILE_3G_LOWER, subscriberId);
+
+ } else if (TAB_4G.equals(currentTab)) {
+ setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_4g);
+ setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_4g_limit);
+ // TODO: bind mDataEnabled to 4G radio state
+ mTemplate = new NetworkTemplate(MATCH_MOBILE_4G, subscriberId);
+ }
+
+ try {
+ // load stats for current template
+ mHistory = mStatsService.getHistoryForNetwork(mTemplate);
+ } catch (RemoteException e) {
+ // since we can't do much without policy or history, and we don't
+ // want to leave with half-baked UI, we bail hard.
+ throw new RuntimeException("problem reading network policy or stats", e);
+ }
+
+ // bind chart to historical stats
+ mChart.bindNetworkStats(mHistory);
+
+ updatePolicy(true);
+
+ // force scroll to top of body
+ mListView.smoothScrollToPosition(0);
+
+ mBinding = false;
+ }
+
+ private void setPolicyCycleDay(int cycleDay) {
+ if (LOGD) Log.d(TAG, "setPolicyCycleDay()");
+ mPolicyEditor.setPolicyCycleDay(mTemplate, cycleDay);
+ updatePolicy(true);
+ }
+
+ private void setPolicyWarningBytes(long warningBytes) {
+ if (LOGD) Log.d(TAG, "setPolicyWarningBytes()");
+ mPolicyEditor.setPolicyWarningBytes(mTemplate, warningBytes);
+ updatePolicy(false);
+ }
+
+ private void setPolicyLimitBytes(long limitBytes) {
+ if (LOGD) Log.d(TAG, "setPolicyLimitBytes()");
+ mPolicyEditor.setPolicyLimitBytes(mTemplate, limitBytes);
+ updatePolicy(false);
+ }
+
+ /**
+ * Update chart sweeps and cycle list to reflect {@link NetworkPolicy} for
+ * current {@link #mTemplate}.
+ */
+ private void updatePolicy(boolean refreshCycle) {
+ final NetworkPolicy policy = mPolicyEditor.getPolicy(mTemplate);
+
+ // reflect policy limit in checkbox
+ mDisableAtLimit.setChecked(policy != null && policy.limitBytes != LIMIT_DISABLED);
+ mChart.bindNetworkPolicy(policy);
+
+ if (refreshCycle) {
+ // generate cycle list based on policy and available history
+ updateCycleList(policy);
+ }
+ }
+
+ /**
+ * Return full time bounds (earliest and latest time recorded) of the given
+ * {@link NetworkStatsHistory}.
+ */
+ public static long[] getHistoryBounds(NetworkStatsHistory history) {
+ final long currentTime = System.currentTimeMillis();
+
+ long start = currentTime;
+ long end = currentTime;
+ if (history.bucketCount > 0) {
+ start = history.bucketStart[0];
+ end = history.bucketStart[history.bucketCount - 1];
+ }
+
+ return new long[] { start, end };
+ }
+
+ /**
+ * Rebuild {@link #mCycleAdapter} based on {@link NetworkPolicy#cycleDay}
+ * and available {@link NetworkStatsHistory} data. Always selects the newest
+ * item, updating the inspection range on {@link #mChart}.
+ */
+ private void updateCycleList(NetworkPolicy policy) {
+ mCycleAdapter.clear();
+
+ final Context context = mCycleSpinner.getContext();
+
+ final long[] bounds = getHistoryBounds(mHistory);
+ final long historyStart = bounds[0];
+ final long historyEnd = bounds[1];
+
+ if (policy != null) {
+ // find the next cycle boundary
+ long cycleEnd = computeNextCycleBoundary(historyEnd, policy);
+
+ int guardCount = 0;
+
+ // walk backwards, generating all valid cycle ranges
+ while (cycleEnd > historyStart) {
+ final long cycleStart = computeLastCycleBoundary(cycleEnd, policy);
+ Log.d(TAG, "generating cs=" + cycleStart + " to ce=" + cycleEnd + " waiting for hs="
+ + historyStart);
+ mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
+ cycleEnd = cycleStart;
+
+ // TODO: remove this guard once we have better testing
+ if (guardCount++ > 50) {
+ Log.wtf(TAG, "stuck generating ranges for bounds=" + Arrays.toString(bounds)
+ + " and policy=" + policy);
+ }
+ }
+
+ // one last cycle entry to modify policy cycle day
+ mCycleAdapter.add(new CycleChangeItem(context));
+
+ } else {
+ // no valid cycle; show all data
+ // TODO: offer simple ranges like "last week" etc
+ mCycleAdapter.add(new CycleItem(context, historyStart, historyEnd));
+
+ }
+
+ // force pick the current cycle (first item)
+ mCycleSpinner.setSelection(0);
+ mCycleListener.onItemSelected(mCycleSpinner, null, 0, 0);
+ }
+
+ private OnCheckedChangeListener mDataEnabledListener = new OnCheckedChangeListener() {
+ /** {@inheritDoc} */
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (mBinding) return;
+
+ final boolean dataEnabled = isChecked;
+ mDataEnabled.setChecked(dataEnabled);
+
+ switch (mTemplate.getMatchRule()) {
+ case MATCH_MOBILE_ALL: {
+ mConnService.setMobileDataEnabled(dataEnabled);
+ }
+ }
+ }
+ };
+
+ private View.OnClickListener mDisableAtLimitListener = new View.OnClickListener() {
+ /** {@inheritDoc} */
+ public void onClick(View v) {
+ final boolean disableAtLimit = !mDisableAtLimit.isChecked();
+ if (disableAtLimit) {
+ // enabling limit; show confirmation dialog which eventually
+ // calls setPolicyLimitBytes() once user confirms.
+ ConfirmLimitFragment.show(DataUsageSummary.this);
+ } else {
+ setPolicyLimitBytes(LIMIT_DISABLED);
+ }
+ }
+ };
+
+ private OnItemClickListener mListListener = new OnItemClickListener() {
+ /** {@inheritDoc} */
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ final AppUsageItem app = (AppUsageItem) parent.getItemAtPosition(position);
+
+ final Bundle args = new Bundle();
+ args.putParcelable(DataUsageAppDetail.EXTRA_NETWORK_TEMPLATE, mTemplate);
+ args.putInt(DataUsageAppDetail.EXTRA_UID, app.uid);
+
+ final PreferenceActivity activity = (PreferenceActivity) getActivity();
+ activity.startPreferencePanel(DataUsageAppDetail.class.getName(), args,
+ R.string.data_usage_summary_title, null, null, 0);
+ }
+ };
+
+ private OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
+ /** {@inheritDoc} */
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ final CycleItem cycle = (CycleItem) parent.getItemAtPosition(position);
+ if (cycle instanceof CycleChangeItem) {
+ // show cycle editor; will eventually call setPolicyCycleDay()
+ // when user finishes editing.
+ CycleEditorFragment.show(DataUsageSummary.this);
+
+ // reset spinner to something other than "change cycle..."
+ mCycleSpinner.setSelection(0);
+
+ } else {
+ if (LOGD) {
+ Log.d(TAG, "showing cycle " + cycle + ", start=" + cycle.start + ", end="
+ + cycle.end + "]");
+ }
+
+ // update chart to show selected cycle, and update detail data
+ // to match updated sweep bounds.
+ final long[] bounds = getHistoryBounds(mHistory);
+ mChart.setVisibleRange(cycle.start, cycle.end, bounds[1]);
+
+ updateDetailData();
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void onNothingSelected(AdapterView<?> parent) {
+ // ignored
+ }
+ };
+
+ /**
+ * Update {@link #mAdapter} with sorted list of applications data usage,
+ * based on current inspection from {@link #mChart}.
+ */
+ private void updateDetailData() {
+ if (LOGD) Log.d(TAG, "updateDetailData()");
+
+ new AsyncTask<Void, Void, NetworkStats>() {
+ @Override
+ protected NetworkStats doInBackground(Void... params) {
+ try {
+ final long[] range = mChart.getInspectRange();
+ return mStatsService.getSummaryForAllUid(mTemplate, range[0], range[1], false);
+ } catch (RemoteException e) {
+ Log.w(TAG, "problem reading stats");
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(NetworkStats stats) {
+ if (stats != null) {
+ mAdapter.bindStats(stats);
+ }
+ }
+ }.execute();
+ }
+
+ private boolean isMobilePolicySplit() {
+ final String subscriberId = getActiveSubscriberId(getActivity());
+ return mPolicyEditor.isMobilePolicySplit(subscriberId);
+ }
+
+ private void setMobilePolicySplit(boolean split) {
+ final String subscriberId = getActiveSubscriberId(getActivity());
+ mPolicyEditor.setMobilePolicySplit(subscriberId, split);
+ }
+
+ private static String getActiveSubscriberId(Context context) {
+ final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
+ Context.TELEPHONY_SERVICE);
+ return telephony.getSubscriberId();
+ }
+
+ private DataUsageChartListener mChartListener = new DataUsageChartListener() {
+ /** {@inheritDoc} */
+ public void onInspectRangeChanged() {
+ if (LOGD) Log.d(TAG, "onInspectRangeChanged()");
+ updateDetailData();
+ }
+
+ /** {@inheritDoc} */
+ public void onWarningChanged() {
+ setPolicyWarningBytes(mChart.getWarningBytes());
+ }
+
+ /** {@inheritDoc} */
+ public void onLimitChanged() {
+ setPolicyLimitBytes(mChart.getLimitBytes());
+ }
+ };
+
+
+ /**
+ * List item that reflects a specific data usage cycle.
+ */
+ public static class CycleItem {
+ public CharSequence label;
+ public long start;
+ public long end;
+
+ private static final StringBuilder sBuilder = new StringBuilder(50);
+ private static final java.util.Formatter sFormatter = new java.util.Formatter(
+ sBuilder, Locale.getDefault());
+
+ CycleItem(CharSequence label) {
+ this.label = label;
+ }
+
+ public CycleItem(Context context, long start, long end) {
+ this.label = formatDateRangeUtc(context, start, end);
+ this.start = start;
+ this.end = end;
+ }
+
+ private static String formatDateRangeUtc(Context context, long start, long end) {
+ synchronized (sBuilder) {
+ sBuilder.setLength(0);
+ return DateUtils.formatDateRange(context, sFormatter, start, end,
+ DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH,
+ Time.TIMEZONE_UTC).toString();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return label.toString();
+ }
+ }
+
+ /**
+ * Special-case data usage cycle that triggers dialog to change
+ * {@link NetworkPolicy#cycleDay}.
+ */
+ public static class CycleChangeItem extends CycleItem {
+ public CycleChangeItem(Context context) {
+ super(context.getString(R.string.data_usage_change_cycle));
+ }
+ }
+
+ public static class CycleAdapter extends ArrayAdapter<CycleItem> {
+ public CycleAdapter(Context context) {
+ super(context, android.R.layout.simple_spinner_item);
+ setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ }
+ }
+
+ private static class AppUsageItem implements Comparable<AppUsageItem> {
+ public int uid;
+ public long total;
+
+ /** {@inheritDoc} */
+ public int compareTo(AppUsageItem another) {
+ return Long.compare(another.total, total);
+ }
+ }
+
+ /**
+ * Adapter of applications, sorted by total usage descending.
+ */
+ public static class DataUsageAdapter extends BaseAdapter {
+ private ArrayList<AppUsageItem> mItems = Lists.newArrayList();
+
+ public void bindStats(NetworkStats stats) {
+ mItems.clear();
+
+ for (int i = 0; i < stats.size; i++) {
+ final long total = stats.rx[i] + stats.tx[i];
+ final AppUsageItem item = new AppUsageItem();
+ item.uid = stats.uid[i];
+ item.total = total;
+ mItems.add(item);
+ }
+
+ Collections.sort(mItems);
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public int getCount() {
+ return mItems.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mItems.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = LayoutInflater.from(parent.getContext()).inflate(
+ android.R.layout.simple_list_item_2, parent, false);
+ }
+
+ final Context context = parent.getContext();
+ final PackageManager pm = context.getPackageManager();
+
+ final TextView text1 = (TextView) convertView.findViewById(android.R.id.text1);
+ final TextView text2 = (TextView) convertView.findViewById(android.R.id.text2);
+
+ final AppUsageItem item = mItems.get(position);
+ text1.setText(resolveLabelForUid(pm, item.uid));
+ text2.setText(Formatter.formatFileSize(context, item.total));
+
+ return convertView;
+ }
+
+ }
+
+ /**
+ * Dialog to request user confirmation before setting
+ * {@link NetworkPolicy#limitBytes}.
+ */
+ public static class ConfirmLimitFragment extends DialogFragment {
+ public static final String EXTRA_MESSAGE_ID = "messageId";
+ public static final String EXTRA_LIMIT_BYTES = "limitBytes";
+
+ public static void show(DataUsageSummary parent) {
+ final Bundle args = new Bundle();
+
+ // TODO: customize default limits based on network template
+ switch (parent.mTemplate.getMatchRule()) {
+ case MATCH_MOBILE_3G_LOWER: {
+ args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_3g);
+ args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
+ break;
+ }
+ case MATCH_MOBILE_4G: {
+ args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_4g);
+ args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
+ break;
+ }
+ case MATCH_MOBILE_ALL: {
+ args.putInt(EXTRA_MESSAGE_ID, R.string.data_usage_limit_dialog_mobile);
+ args.putLong(EXTRA_LIMIT_BYTES, 5 * GB_IN_BYTES);
+ break;
+ }
+ }
+
+ final ConfirmLimitFragment dialog = new ConfirmLimitFragment();
+ dialog.setArguments(args);
+ dialog.setTargetFragment(parent, 0);
+ dialog.show(parent.getFragmentManager(), TAG_CONFIRM_LIMIT);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Context context = getActivity();
+
+ final int messageId = getArguments().getInt(EXTRA_MESSAGE_ID);
+ final long limitBytes = getArguments().getLong(EXTRA_LIMIT_BYTES);
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.data_usage_limit_dialog_title);
+ builder.setMessage(messageId);
+
+ builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
+ if (target != null) {
+ target.setPolicyLimitBytes(limitBytes);
+ }
+ }
+ });
+
+ return builder.create();
+ }
+ }
+
+ /**
+ * Dialog to edit {@link NetworkPolicy#cycleDay}.
+ */
+ public static class CycleEditorFragment extends DialogFragment {
+ public static final String EXTRA_CYCLE_DAY = "cycleDay";
+
+ public static void show(DataUsageSummary parent) {
+ final NetworkPolicy policy = parent.mPolicyEditor.getPolicy(parent.mTemplate);
+ final Bundle args = new Bundle();
+ args.putInt(CycleEditorFragment.EXTRA_CYCLE_DAY, policy.cycleDay);
+
+ final CycleEditorFragment dialog = new CycleEditorFragment();
+ dialog.setArguments(args);
+ dialog.setTargetFragment(parent, 0);
+ dialog.show(parent.getFragmentManager(), TAG_CYCLE_EDITOR);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Context context = getActivity();
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
+
+ final View view = dialogInflater.inflate(R.layout.data_usage_cycle_editor, null, false);
+ final NumberPicker cycleDayPicker = (NumberPicker) view.findViewById(R.id.cycle_day);
+
+ final int oldCycleDay = getArguments().getInt(EXTRA_CYCLE_DAY, 1);
+
+ cycleDayPicker.setMinValue(1);
+ cycleDayPicker.setMaxValue(31);
+ cycleDayPicker.setValue(oldCycleDay);
+ cycleDayPicker.setWrapSelectorWheel(true);
+
+ builder.setTitle(R.string.data_usage_cycle_editor_title);
+ builder.setView(view);
+
+ builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ final int cycleDay = cycleDayPicker.getValue();
+ final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
+ if (target != null) {
+ target.setPolicyCycleDay(cycleDay);
+ }
+ }
+ });
+
+ return builder.create();
+ }
+ }
+
+ /**
+ * Dialog explaining that {@link NetworkPolicy#limitBytes} has been passed,
+ * and giving the user an option to bypass.
+ */
+ public static class PolicyLimitFragment extends DialogFragment {
+ public static final String EXTRA_TITLE_ID = "titleId";
+
+ public static void show(DataUsageSummary parent) {
+ final Bundle args = new Bundle();
+
+ switch (parent.mTemplate.getMatchRule()) {
+ case MATCH_MOBILE_3G_LOWER: {
+ args.putInt(EXTRA_TITLE_ID, R.string.data_usage_disabled_dialog_3g_title);
+ break;
+ }
+ case MATCH_MOBILE_4G: {
+ args.putInt(EXTRA_TITLE_ID, R.string.data_usage_disabled_dialog_4g_title);
+ break;
+ }
+ case MATCH_MOBILE_ALL: {
+ args.putInt(EXTRA_TITLE_ID, R.string.data_usage_disabled_dialog_mobile_title);
+ break;
+ }
+ }
+
+ final PolicyLimitFragment dialog = new PolicyLimitFragment();
+ dialog.setArguments(args);
+ dialog.setTargetFragment(parent, 0);
+ dialog.show(parent.getFragmentManager(), TAG_POLICY_LIMIT);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Context context = getActivity();
+
+ final int titleId = getArguments().getInt(EXTRA_TITLE_ID);
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(titleId);
+ builder.setMessage(R.string.data_usage_disabled_dialog);
+
+ builder.setPositiveButton(android.R.string.ok, null);
+ builder.setNegativeButton(R.string.data_usage_disabled_dialog_enable,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
+ if (target != null) {
+ // TODO: consider "allow 100mb more data", or
+ // only bypass limit for current cycle.
+ target.setPolicyLimitBytes(LIMIT_DISABLED);
+ }
+ }
+ });
+
+ return builder.create();
+ }
+ }
+
+ /**
+ * Compute default tab that should be selected, based on
+ * {@link NetworkPolicyManager#EXTRA_NETWORK_TEMPLATE} extra.
+ */
+ private static String computeTabFromIntent(Intent intent) {
+ final int networkTemplate = intent.getIntExtra(EXTRA_NETWORK_TEMPLATE, TEMPLATE_INVALID);
+ switch (networkTemplate) {
+ case MATCH_MOBILE_3G_LOWER:
+ return TAB_3G;
+ case MATCH_MOBILE_4G:
+ return TAB_4G;
+ case MATCH_MOBILE_ALL:
+ return TAB_MOBILE;
+ case MATCH_WIFI:
+ return TAB_WIFI;
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Resolve best descriptive label for the given UID.
+ */
+ public static CharSequence resolveLabelForUid(PackageManager pm, int uid) {
+ final String[] packageNames = pm.getPackagesForUid(uid);
+ final int length = packageNames != null ? packageNames.length : 0;
+
+ CharSequence label = pm.getNameForUid(uid);
+ try {
+ if (length == 1) {
+ final ApplicationInfo info = pm.getApplicationInfo(packageNames[0], 0);
+ label = info.loadLabel(pm);
+ } else if (length > 1) {
+ for (String packageName : packageNames) {
+ final PackageInfo info = pm.getPackageInfo(packageName, 0);
+ if (info.sharedUserLabel != 0) {
+ label = pm.getText(packageName, info.sharedUserLabel, info.applicationInfo);
+ if (!TextUtils.isEmpty(label)) {
+ break;
+ }
+ }
+ }
+ }
+ } catch (NameNotFoundException e) {
+ }
+
+ if (TextUtils.isEmpty(label)) {
+ label = Integer.toString(uid);
+ }
+ return label;
+ }
+
+ /**
+ * Test if device has a mobile data radio.
+ */
+ private static boolean hasMobileRadio(Context context) {
+ final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+
+ // mobile devices should have MOBILE network tracker regardless of
+ // connection status.
+ return conn.getNetworkInfo(TYPE_MOBILE) != null;
+ }
+
+ /**
+ * Test if device has a mobile 4G data radio.
+ */
+ private static boolean hasMobile4gRadio(Context context) {
+ final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
+ Context.TELEPHONY_SERVICE);
+
+ // WiMAX devices should have WiMAX network tracker regardless of
+ // connection status.
+ final boolean hasWimax = conn.getNetworkInfo(TYPE_WIMAX) != null;
+ final boolean hasLte = telephony.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE;
+ return hasWimax || hasLte;
+ }
+
+ /**
+ * Test if device has a Wi-Fi data radio.
+ */
+ private static boolean hasWifiRadio(Context context) {
+ return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
+ }
+
+ /**
+ * Inflate a {@link Preference} style layout, adding the given {@link View}
+ * widget into {@link android.R.id#widget_frame}.
+ */
+ private static View inflatePreference(LayoutInflater inflater, ViewGroup root, View widget) {
+ final View view = inflater.inflate(R.layout.preference, root, false);
+ final LinearLayout widgetFrame = (LinearLayout) view.findViewById(
+ android.R.id.widget_frame);
+ widgetFrame.addView(widget, new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+ return view;
+ }
+
+ /**
+ * Set {@link android.R.id#title} for a preference view inflated with
+ * {@link #inflatePreference(LayoutInflater, View, View)}.
+ */
+ private static void setPreferenceTitle(View parent, int resId) {
+ final TextView title = (TextView) parent.findViewById(android.R.id.title);
+ title.setText(resId);
+ }
+
+}
diff --git a/src/com/android/settings/DateTimeSettingsSetupWizard.java b/src/com/android/settings/DateTimeSettingsSetupWizard.java
index 4ff7fc8..e63153e 100644
--- a/src/com/android/settings/DateTimeSettingsSetupWizard.java
+++ b/src/com/android/settings/DateTimeSettingsSetupWizard.java
@@ -31,7 +31,6 @@ import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
-import android.text.format.DateFormat;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
@@ -46,6 +45,7 @@ import android.widget.DatePicker;
import android.widget.LinearLayout;
import android.widget.ListPopupWindow;
import android.widget.SimpleAdapter;
+import android.widget.TextView;
import android.widget.TimePicker;
import java.util.Calendar;
@@ -136,8 +136,6 @@ public class DateTimeSettingsSetupWizard extends Activity
mAutoDateTimeButton = (CompoundButton)findViewById(R.id.date_time_auto_button);
mAutoDateTimeButton.setChecked(autoDateTimeEnabled);
- mAutoDateTimeButton.setText(autoDateTimeEnabled ? R.string.date_time_auto_summaryOn :
- R.string.date_time_auto_summaryOff);
mAutoDateTimeButton.setOnCheckedChangeListener(this);
mTimePicker = (TimePicker)findViewById(R.id.time_picker);
diff --git a/src/com/android/settings/DefaultRingtonePreference.java b/src/com/android/settings/DefaultRingtonePreference.java
index 0933d62..0801b1f 100644
--- a/src/com/android/settings/DefaultRingtonePreference.java
+++ b/src/com/android/settings/DefaultRingtonePreference.java
@@ -23,7 +23,6 @@ import android.media.RingtoneManager;
import android.net.Uri;
import android.preference.RingtonePreference;
import android.util.AttributeSet;
-import android.util.Config;
import android.util.Log;
public class DefaultRingtonePreference extends RingtonePreference {
diff --git a/src/com/android/settings/DisplaySettings.java b/src/com/android/settings/DisplaySettings.java
index cdb0147..682184e 100644
--- a/src/com/android/settings/DisplaySettings.java
+++ b/src/com/android/settings/DisplaySettings.java
@@ -18,9 +18,17 @@ package com.android.settings;
import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
+import android.app.ActivityManagerNative;
import android.app.admin.DevicePolicyManager;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.res.Configuration;
+
+import android.app.ActivityManagerNative;
+import android.app.admin.DevicePolicyManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Configuration;
import android.database.ContentObserver;
import android.os.Bundle;
import android.os.Handler;
@@ -46,11 +54,15 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
private static final String KEY_SCREEN_TIMEOUT = "screen_timeout";
private static final String KEY_ANIMATIONS = "animations";
private static final String KEY_ACCELEROMETER = "accelerometer";
+ private static final String KEY_FONT_SIZE = "font_size";
private ListPreference mAnimations;
private CheckBoxPreference mAccelerometer;
private float[] mAnimationScales;
+ private ListPreference mFontSizePref;
+ private final Configuration mCurConfig = new Configuration();
+
private IWindowManager mWindowManager;
private ListPreference mScreenTimeoutPreference;
@@ -70,6 +82,12 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
addPreferencesFromResource(R.xml.display_settings);
+ // Fetch this once before attaching a listener for changes.
+ try {
+ mAnimationScales = mWindowManager.getAnimationScales();
+ } catch (RemoteException e) {
+ // Shouldn't happen and not much can be done anyway.
+ }
mAnimations = (ListPreference) findPreference(KEY_ANIMATIONS);
mAnimations.setOnPreferenceChangeListener(this);
mAccelerometer = (CheckBoxPreference) findPreference(KEY_ACCELEROMETER);
@@ -81,22 +99,42 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
mScreenTimeoutPreference.setValue(String.valueOf(currentTimeout));
mScreenTimeoutPreference.setOnPreferenceChangeListener(this);
disableUnusableTimeouts(mScreenTimeoutPreference);
- updateTimeoutPreferenceDescription(resolver, currentTimeout);
+ updateTimeoutPreferenceDescription(resolver, mScreenTimeoutPreference,
+ R.string.screen_timeout_summary, currentTimeout);
+
+ mFontSizePref = (ListPreference) findPreference(KEY_FONT_SIZE);
+ mFontSizePref.setOnPreferenceChangeListener(this);
}
- private void updateTimeoutPreferenceDescription(ContentResolver resolver, long currentTimeout) {
- final CharSequence[] entries = mScreenTimeoutPreference.getEntries();
- final CharSequence[] values = mScreenTimeoutPreference.getEntryValues();
- int best = 0;
- for (int i = 0; i < values.length; i++) {
- long timeout = Long.valueOf(values[i].toString());
- if (currentTimeout >= timeout) {
- best = i;
+ private void updateTimeoutPreferenceDescription(
+ ContentResolver resolver,
+ ListPreference pref,
+ int summaryStrings,
+ long currentTimeout) {
+ updateTimeoutPreferenceDescription(resolver, pref, summaryStrings, 0, currentTimeout);
+ }
+ private void updateTimeoutPreferenceDescription(
+ ContentResolver resolver,
+ ListPreference pref,
+ int summaryStrings,
+ int zeroString,
+ long currentTimeout) {
+ String summary;
+ if (currentTimeout == 0) {
+ summary = pref.getContext().getString(zeroString);
+ } else {
+ final CharSequence[] entries = pref.getEntries();
+ final CharSequence[] values = pref.getEntryValues();
+ int best = 0;
+ for (int i = 0; i < values.length; i++) {
+ long timeout = Long.valueOf(values[i].toString());
+ if (currentTimeout >= timeout) {
+ best = i;
+ }
}
+ summary = pref.getContext().getString(summaryStrings, entries[best]);
}
- String summary = mScreenTimeoutPreference.getContext()
- .getString(R.string.screen_timeout_summary, entries[best]);
- mScreenTimeoutPreference.setSummary(summary);
+ pref.setSummary(summary);
}
private void disableUnusableTimeouts(ListPreference screenTimeoutPreference) {
@@ -135,6 +173,29 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
screenTimeoutPreference.setEnabled(revisedEntries.size() > 0);
}
+ int floatToIndex(float val, int resid) {
+ String[] indices = getResources().getStringArray(resid);
+ float lastVal = Float.parseFloat(indices[0]);
+ for (int i=1; i<indices.length; i++) {
+ float thisVal = Float.parseFloat(indices[i]);
+ if (val < (lastVal + (thisVal-lastVal)*.5f)) {
+ return i-1;
+ }
+ lastVal = thisVal;
+ }
+ return indices.length-1;
+ }
+
+ public void readFontSizePreference(ListPreference pref) {
+ try {
+ mCurConfig.updateFrom(
+ ActivityManagerNative.getDefault().getConfiguration());
+ } catch (RemoteException e) {
+ }
+ pref.setValueIndex(floatToIndex(mCurConfig.fontScale,
+ R.array.entryvalues_font_size));
+ }
+
@Override
public void onResume() {
super.onResume();
@@ -157,6 +218,7 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
try {
mAnimationScales = mWindowManager.getAnimationScales();
} catch (RemoteException e) {
+ // Shouldn't happen and not much can be done anyway.
}
if (mAnimationScales != null) {
if (mAnimationScales.length >= 1) {
@@ -179,6 +241,7 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
mAnimations.setValueIndex(idx);
updateAnimationsSummary(mAnimations.getValue());
updateAccelerometerRotationCheckbox();
+ readFontSizePreference(mFontSizePref);
}
private void updateAccelerometerRotationCheckbox() {
@@ -200,6 +263,14 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
}
}
+ public void writeFontSizePreference(Object objValue) {
+ try {
+ mCurConfig.fontScale = Float.parseFloat(objValue.toString());
+ ActivityManagerNative.getDefault().updateConfiguration(mCurConfig);
+ } catch (RemoteException e) {
+ }
+ }
+
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
if (preference == mAccelerometer) {
@@ -207,7 +278,7 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
Settings.System.ACCELEROMETER_ROTATION,
mAccelerometer.isChecked() ? 1 : 0);
}
- return true;
+ return super.onPreferenceTreeClick(preferenceScreen, preference);
}
public boolean onPreferenceChange(Preference preference, Object objValue) {
@@ -236,11 +307,15 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
try {
Settings.System.putInt(getContentResolver(),
SCREEN_OFF_TIMEOUT, value);
- updateTimeoutPreferenceDescription(getContentResolver(), value);
+ updateTimeoutPreferenceDescription(getContentResolver(), mScreenTimeoutPreference,
+ R.string.screen_timeout_summary, value);
} catch (NumberFormatException e) {
Log.e(TAG, "could not persist screen timeout setting", e);
}
}
+ if (KEY_FONT_SIZE.equals(key)) {
+ writeFontSizePreference(objValue);
+ }
return true;
}
diff --git a/src/com/android/settings/DreamComponentPreference.java b/src/com/android/settings/DreamComponentPreference.java
new file mode 100644
index 0000000..3bc0eb4
--- /dev/null
+++ b/src/com/android/settings/DreamComponentPreference.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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 com.android.settings;
+
+import static android.provider.Settings.Secure.DREAM_COMPONENT;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.preference.Preference;
+import android.provider.Settings;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.ListAdapter;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DreamComponentPreference extends Preference {
+ private static final String TAG = "DreamComponentPreference";
+
+ private final PackageManager pm;
+ private final ContentResolver resolver;
+
+ public DreamComponentPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ pm = getContext().getPackageManager();
+ resolver = getContext().getContentResolver();
+
+ refreshFromSettings();
+ }
+
+ private void refreshFromSettings() {
+ String component = Settings.Secure.getString(resolver, DREAM_COMPONENT);
+ if (component != null) {
+ ComponentName cn = ComponentName.unflattenFromString(component);
+ try {
+ setSummary(pm.getActivityInfo(cn, 0).loadLabel(pm));
+ } catch (PackageManager.NameNotFoundException ex) {
+ setSummary("(unknown)");
+ }
+ }
+ }
+
+ public class DreamListAdapter extends BaseAdapter implements ListAdapter {
+ private ArrayList<ResolveInfo> results;
+ private final LayoutInflater inflater;
+
+ public DreamListAdapter(Context context) {
+ Intent choosy = new Intent(Intent.ACTION_MAIN)
+ .addCategory("android.intent.category.DREAM");
+
+ inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ results = new ArrayList<ResolveInfo>(pm.queryIntentActivities(choosy, 0));
+ }
+
+ @Override
+ public int getCount() {
+ return results.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return results.get(position);
+ }
+
+ @Override
+ public long getItemId (int position) {
+ return (long) position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View row = (convertView != null)
+ ? convertView
+ : inflater.inflate(R.layout.dream_picker_row, parent, false);
+ ResolveInfo ri = results.get(position);
+ ((TextView)row.findViewById(R.id.title)).setText(ri.loadLabel(pm));
+ ((ImageView)row.findViewById(R.id.icon)).setImageDrawable(ri.loadIcon(pm));
+ return row;
+ }
+ }
+
+ @Override
+ protected void onClick() {
+ final DreamListAdapter list = new DreamListAdapter(getContext());
+ AlertDialog alert = new AlertDialog.Builder(getContext())
+ .setAdapter(
+ list,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ ResolveInfo ri = (ResolveInfo)list.getItem(which);
+ ActivityInfo act = ri.activityInfo;
+ ComponentName cn = new ComponentName(
+ act.applicationInfo.packageName,
+ act.name);
+ Intent intent = new Intent(Intent.ACTION_MAIN).setComponent(cn);
+
+ setSummary(ri.loadLabel(pm));
+ //getContext().startActivity(intent);
+
+ Settings.Secure.putString(resolver, DREAM_COMPONENT, cn.flattenToString());
+ }
+ })
+ .create();
+ alert.show();
+ }
+}
diff --git a/src/com/android/settings/DreamSettings.java b/src/com/android/settings/DreamSettings.java
new file mode 100644
index 0000000..7ddab31
--- /dev/null
+++ b/src/com/android/settings/DreamSettings.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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 com.android.settings;
+
+import static android.provider.Settings.Secure.DREAM_TIMEOUT;
+
+import android.app.ActivityManagerNative;
+import android.app.admin.DevicePolicyManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.database.ContentObserver;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.preference.CheckBoxPreference;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.IWindowManager;
+
+import java.util.ArrayList;
+
+public class DreamSettings extends SettingsPreferenceFragment implements
+ Preference.OnPreferenceChangeListener {
+ private static final String TAG = "DreamSettings";
+
+ private static final String KEY_DREAM_TIMEOUT = "dream_timeout";
+
+ private ListPreference mScreenTimeoutPreference;
+ private ListPreference mDreamTimeoutPreference;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ContentResolver resolver = getActivity().getContentResolver();
+
+ addPreferencesFromResource(R.xml.dream_settings);
+
+ mDreamTimeoutPreference = (ListPreference) findPreference(KEY_DREAM_TIMEOUT);
+ final long currentSaverTimeout = Settings.Secure.getLong(resolver, DREAM_TIMEOUT,
+ 0);
+ mDreamTimeoutPreference.setValue(String.valueOf(currentSaverTimeout));
+ mDreamTimeoutPreference.setOnPreferenceChangeListener(this);
+ updateTimeoutPreferenceDescription(resolver, mDreamTimeoutPreference,
+ R.string.dream_timeout_summary,
+ R.string.dream_timeout_zero_summary,
+ currentSaverTimeout);
+ }
+
+ private void updateTimeoutPreferenceDescription(
+ ContentResolver resolver,
+ ListPreference pref,
+ int summaryStrings,
+ long currentTimeout) {
+ updateTimeoutPreferenceDescription(resolver, pref, summaryStrings, 0, currentTimeout);
+ }
+ private void updateTimeoutPreferenceDescription(
+ ContentResolver resolver,
+ ListPreference pref,
+ int summaryStrings,
+ int zeroString,
+ long currentTimeout) {
+ String summary;
+ if (currentTimeout == 0) {
+ summary = pref.getContext().getString(zeroString);
+ } else {
+ final CharSequence[] entries = pref.getEntries();
+ final CharSequence[] values = pref.getEntryValues();
+ int best = 0;
+ for (int i = 0; i < values.length; i++) {
+ long timeout = Long.valueOf(values[i].toString());
+ if (currentTimeout >= timeout) {
+ best = i;
+ }
+ }
+ summary = pref.getContext().getString(summaryStrings, entries[best]);
+ }
+ pref.setSummary(summary);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ }
+
+ public boolean onPreferenceChange(Preference preference, Object objValue) {
+ final String key = preference.getKey();
+ if (KEY_DREAM_TIMEOUT.equals(key)) {
+ int value = Integer.parseInt((String) objValue);
+ try {
+ Settings.Secure.putInt(getContentResolver(),
+ DREAM_TIMEOUT, value);
+ updateTimeoutPreferenceDescription(getContentResolver(),
+ mDreamTimeoutPreference,
+ R.string.dream_timeout_summary,
+ R.string.dream_timeout_zero_summary,
+ value);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "could not persist dream timeout setting", e);
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/src/com/android/settings/DreamTesterPreference.java b/src/com/android/settings/DreamTesterPreference.java
new file mode 100644
index 0000000..7ff5667
--- /dev/null
+++ b/src/com/android/settings/DreamTesterPreference.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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 com.android.settings;
+
+import static android.provider.Settings.Secure.DREAM_COMPONENT;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.preference.Preference;
+import android.provider.Settings;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.ListAdapter;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DreamTesterPreference extends Preference {
+ private static final String TAG = "DreamTesterPreference";
+
+ private final PackageManager pm;
+ private final ContentResolver resolver;
+
+ public DreamTesterPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ pm = getContext().getPackageManager();
+ resolver = getContext().getContentResolver();
+ }
+
+ @Override
+ protected void onClick() {
+ String component = Settings.Secure.getString(resolver, DREAM_COMPONENT);
+ if (component != null) {
+ ComponentName cn = ComponentName.unflattenFromString(component);
+ Intent intent = new Intent(Intent.ACTION_MAIN)
+ .setComponent(cn)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+ | Intent.FLAG_ACTIVITY_SINGLE_TOP)
+ .putExtra("android.dreams.TEST", true);
+ getContext().startActivity(intent);
+ }
+ }
+}
diff --git a/src/com/android/settings/GoogleLocationSettingHelper.java b/src/com/android/settings/GoogleLocationSettingHelper.java
index 0d4861e..be4a02c 100644
--- a/src/com/android/settings/GoogleLocationSettingHelper.java
+++ b/src/com/android/settings/GoogleLocationSettingHelper.java
@@ -122,6 +122,7 @@ public class GoogleLocationSettingHelper {
try {
context.startActivity(i);
} catch (ActivityNotFoundException e) {
+ Log.e("GoogleLocationSettingHelper", "Problem while starting GSF location activity");
}
}
diff --git a/src/com/android/settings/LocationSettings.java b/src/com/android/settings/LocationSettings.java
new file mode 100644
index 0000000..645e6d7
--- /dev/null
+++ b/src/com/android/settings/LocationSettings.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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 com.android.settings;
+
+
+import android.content.ContentQueryMap;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.database.Cursor;
+import android.location.LocationManager;
+import android.preference.CheckBoxPreference;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceChangeListener;
+import android.preference.PreferenceScreen;
+import android.provider.Settings;
+
+import java.util.Observable;
+import java.util.Observer;
+
+/**
+ * Gesture lock pattern settings.
+ */
+public class LocationSettings extends SettingsPreferenceFragment
+ implements OnPreferenceChangeListener {
+
+ // Location Settings
+ private static final String KEY_LOCATION_NETWORK = "location_network";
+ private static final String KEY_LOCATION_GPS = "location_gps";
+ private static final String KEY_ASSISTED_GPS = "assisted_gps";
+ private static final String KEY_USE_LOCATION = "location_use_for_services";
+
+ private CheckBoxPreference mNetwork;
+ private CheckBoxPreference mGps;
+ private CheckBoxPreference mAssistedGps;
+ private CheckBoxPreference mUseLocation;
+
+ // These provide support for receiving notification when Location Manager settings change.
+ // This is necessary because the Network Location Provider can change settings
+ // if the user does not confirm enabling the provider.
+ private ContentQueryMap mContentQueryMap;
+
+ private Observer mSettingsObserver;
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ // listen for Location Manager settings changes
+ Cursor settingsCursor = getContentResolver().query(Settings.Secure.CONTENT_URI, null,
+ "(" + Settings.System.NAME + "=?)",
+ new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED},
+ null);
+ mContentQueryMap = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, null);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (mSettingsObserver != null) {
+ mContentQueryMap.deleteObserver(mSettingsObserver);
+ }
+ }
+
+ private PreferenceScreen createPreferenceHierarchy() {
+ PreferenceScreen root = getPreferenceScreen();
+ if (root != null) {
+ root.removeAll();
+ }
+ addPreferencesFromResource(R.xml.location_settings);
+ root = getPreferenceScreen();
+
+ mNetwork = (CheckBoxPreference) root.findPreference(KEY_LOCATION_NETWORK);
+ mGps = (CheckBoxPreference) root.findPreference(KEY_LOCATION_GPS);
+ mAssistedGps = (CheckBoxPreference) root.findPreference(KEY_ASSISTED_GPS);
+ if (GoogleLocationSettingHelper.isAvailable(getActivity())) {
+ // GSF present, Add setting for 'Use My Location'
+ CheckBoxPreference useLocation = new CheckBoxPreference(getActivity());
+ useLocation.setKey(KEY_USE_LOCATION);
+ useLocation.setTitle(R.string.use_location_title);
+ useLocation.setSummaryOn(R.string.use_location_summary_enabled);
+ useLocation.setSummaryOff(R.string.use_location_summary_disabled);
+ useLocation.setChecked(
+ GoogleLocationSettingHelper.getUseLocationForServices(getActivity())
+ == GoogleLocationSettingHelper.USE_LOCATION_FOR_SERVICES_ON);
+ useLocation.setPersistent(false);
+ useLocation.setOnPreferenceChangeListener(this);
+ getPreferenceScreen().addPreference(useLocation);
+ mUseLocation = useLocation;
+ }
+
+ // Change the summary for wifi-only devices
+ if (Utils.isWifiOnly()) {
+ mNetwork.setSummaryOn(R.string.location_neighborhood_level_wifi);
+ }
+
+ return root;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ // Make sure we reload the preference hierarchy since some of these settings
+ // depend on others...
+ createPreferenceHierarchy();
+ updateLocationToggles();
+
+ if (mSettingsObserver == null) {
+ mSettingsObserver = new Observer() {
+ public void update(Observable o, Object arg) {
+ updateLocationToggles();
+ }
+ };
+ mContentQueryMap.addObserver(mSettingsObserver);
+ }
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
+
+ if (preference == mNetwork) {
+ Settings.Secure.setLocationProviderEnabled(getContentResolver(),
+ LocationManager.NETWORK_PROVIDER, mNetwork.isChecked());
+ } else if (preference == mGps) {
+ boolean enabled = mGps.isChecked();
+ Settings.Secure.setLocationProviderEnabled(getContentResolver(),
+ LocationManager.GPS_PROVIDER, enabled);
+ if (mAssistedGps != null) {
+ mAssistedGps.setEnabled(enabled);
+ }
+ } else if (preference == mAssistedGps) {
+ Settings.Secure.putInt(getContentResolver(), Settings.Secure.ASSISTED_GPS_ENABLED,
+ mAssistedGps.isChecked() ? 1 : 0);
+ } else {
+ // If we didn't handle it, let preferences handle it.
+ return super.onPreferenceTreeClick(preferenceScreen, preference);
+ }
+
+ return true;
+ }
+
+ /*
+ * Creates toggles for each available location provider
+ */
+ private void updateLocationToggles() {
+ ContentResolver res = getContentResolver();
+ boolean gpsEnabled = Settings.Secure.isLocationProviderEnabled(
+ res, LocationManager.GPS_PROVIDER);
+ mNetwork.setChecked(Settings.Secure.isLocationProviderEnabled(
+ res, LocationManager.NETWORK_PROVIDER));
+ mGps.setChecked(gpsEnabled);
+ if (mAssistedGps != null) {
+ mAssistedGps.setChecked(Settings.Secure.getInt(res,
+ Settings.Secure.ASSISTED_GPS_ENABLED, 2) == 1);
+ mAssistedGps.setEnabled(gpsEnabled);
+ }
+ }
+
+ /**
+ * see confirmPatternThenDisableAndClear
+ */
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ createPreferenceHierarchy();
+ }
+
+ public boolean onPreferenceChange(Preference preference, Object value) {
+ if (preference == mUseLocation) {
+ boolean newValue = (value == null ? false : (Boolean) value);
+ GoogleLocationSettingHelper.setUseLocationForServices(getActivity(), newValue);
+ // We don't want to change the value immediately here, since the user may click
+ // disagree in the dialog that pops up. When the activity we just launched exits, this
+ // activity will be restated and the new value re-read, so the checkbox will get its
+ // new value then.
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/com/android/settings/PointerSpeedPreference.java b/src/com/android/settings/PointerSpeedPreference.java
index e9704fd..6a6a71f 100644
--- a/src/com/android/settings/PointerSpeedPreference.java
+++ b/src/com/android/settings/PointerSpeedPreference.java
@@ -25,7 +25,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.preference.SeekBarPreference;
+import android.preference.SeekBarDialogPreference;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.AttributeSet;
@@ -33,7 +33,7 @@ import android.view.IWindowManager;
import android.view.View;
import android.widget.SeekBar;
-public class PointerSpeedPreference extends SeekBarPreference implements
+public class PointerSpeedPreference extends SeekBarDialogPreference implements
SeekBar.OnSeekBarChangeListener {
private SeekBar mSeekBar;
diff --git a/src/com/android/settings/RadioInfo.java b/src/com/android/settings/RadioInfo.java
index ba07fb5..d45616e 100644
--- a/src/com/android/settings/RadioInfo.java
+++ b/src/com/android/settings/RadioInfo.java
@@ -24,11 +24,11 @@ import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.LinkProperties;
+import android.net.TrafficStats;
import android.net.Uri;
import android.os.AsyncResult;
import android.os.Bundle;
import android.os.Handler;
-import android.os.INetStatService;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -133,7 +133,6 @@ public class RadioInfo extends Activity {
private TelephonyManager mTelephonyManager;
private Phone phone = null;
private PhoneStateIntentReceiver mPhoneStateReceiver;
- private INetStatService netstat;
private String mPingIpAddrResult;
private String mPingHostnameResult;
@@ -311,8 +310,6 @@ public class RadioInfo extends Activity {
phone.getNeighboringCids(
mHandler.obtainMessage(EVENT_QUERY_NEIGHBORING_CIDS_DONE));
- netstat = INetStatService.Stub.asInterface(ServiceManager.getService("netstat"));
-
CellLocation.requestLocationUpdate();
}
@@ -625,19 +622,16 @@ public class RadioInfo extends Activity {
private final void updateDataStats2() {
Resources r = getResources();
- try {
- long txPackets = netstat.getMobileTxPackets();
- long rxPackets = netstat.getMobileRxPackets();
- long txBytes = netstat.getMobileTxBytes();
- long rxBytes = netstat.getMobileRxBytes();
+ long txPackets = TrafficStats.getMobileTxPackets();
+ long rxPackets = TrafficStats.getMobileRxPackets();
+ long txBytes = TrafficStats.getMobileTxBytes();
+ long rxBytes = TrafficStats.getMobileRxBytes();
- String packets = r.getString(R.string.radioInfo_display_packets);
- String bytes = r.getString(R.string.radioInfo_display_bytes);
+ String packets = r.getString(R.string.radioInfo_display_packets);
+ String bytes = r.getString(R.string.radioInfo_display_bytes);
- sent.setText(txPackets + " " + packets + ", " + txBytes + " " + bytes);
- received.setText(rxPackets + " " + packets + ", " + rxBytes + " " + bytes);
- } catch (RemoteException e) {
- }
+ sent.setText(txPackets + " " + packets + ", " + txBytes + " " + bytes);
+ received.setText(rxPackets + " " + packets + ", " + rxBytes + " " + bytes);
}
/**
diff --git a/src/com/android/settings/SecuritySettings.java b/src/com/android/settings/SecuritySettings.java
index dc4c42b..fc91e78 100644
--- a/src/com/android/settings/SecuritySettings.java
+++ b/src/com/android/settings/SecuritySettings.java
@@ -19,15 +19,9 @@ package com.android.settings;
import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
-import com.android.internal.widget.LockPatternUtils;
-
import android.app.admin.DevicePolicyManager;
-import android.content.ContentQueryMap;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.database.Cursor;
-import android.location.LocationManager;
import android.os.Bundle;
import android.os.Vibrator;
import android.preference.CheckBoxPreference;
@@ -41,9 +35,9 @@ import android.security.KeyStore;
import android.telephony.TelephonyManager;
import android.util.Log;
+import com.android.internal.widget.LockPatternUtils;
+
import java.util.ArrayList;
-import java.util.Observable;
-import java.util.Observer;
/**
* Gesture lock pattern settings.
@@ -60,45 +54,22 @@ public class SecuritySettings extends SettingsPreferenceFragment
private static final String KEY_LOCK_AFTER_TIMEOUT = "lock_after_timeout";
private static final int SET_OR_CHANGE_LOCK_METHOD_REQUEST = 123;
- // Location Settings
- private static final String KEY_LOCATION_CATEGORY = "location_category";
- private static final String KEY_LOCATION_NETWORK = "location_network";
- private static final String KEY_LOCATION_GPS = "location_gps";
- private static final String KEY_ASSISTED_GPS = "assisted_gps";
- private static final String KEY_USE_LOCATION = "location_use_for_services";
-
// Misc Settings
private static final String KEY_SIM_LOCK = "sim_lock";
private static final String KEY_SHOW_PASSWORD = "show_password";
- private static final String KEY_ENABLE_CREDENTIALS = "enable_credentials";
private static final String KEY_RESET_CREDENTIALS = "reset_credentials";
- private static final String TAG = "SecuritySettings";
-
- private CheckBoxPreference mNetwork;
- private CheckBoxPreference mGps;
- private CheckBoxPreference mAssistedGps;
- private CheckBoxPreference mUseLocation;
-
DevicePolicyManager mDPM;
- // These provide support for receiving notification when Location Manager settings change.
- // This is necessary because the Network Location Provider can change settings
- // if the user does not confirm enabling the provider.
- private ContentQueryMap mContentQueryMap;
-
private ChooseLockSettingsHelper mChooseLockSettingsHelper;
private LockPatternUtils mLockPatternUtils;
private ListPreference mLockAfter;
- private Observer mSettingsObserver;
-
private CheckBoxPreference mVisiblePattern;
private CheckBoxPreference mTactileFeedback;
private CheckBoxPreference mShowPassword;
- private CheckBoxPreference mEnableCredentials;
private Preference mResetCredentials;
@Override
@@ -112,25 +83,6 @@ public class SecuritySettings extends SettingsPreferenceFragment
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity());
}
- @Override
- public void onStart() {
- super.onStart();
- // listen for Location Manager settings changes
- Cursor settingsCursor = getContentResolver().query(Settings.Secure.CONTENT_URI, null,
- "(" + Settings.System.NAME + "=?)",
- new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED},
- null);
- mContentQueryMap = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, null);
- }
-
- @Override
- public void onStop() {
- super.onStop();
- if (mSettingsObserver != null) {
- mContentQueryMap.deleteObserver(mSettingsObserver);
- }
- }
-
private PreferenceScreen createPreferenceHierarchy() {
PreferenceScreen root = getPreferenceScreen();
if (root != null) {
@@ -139,32 +91,6 @@ public class SecuritySettings extends SettingsPreferenceFragment
addPreferencesFromResource(R.xml.security_settings);
root = getPreferenceScreen();
- mNetwork = (CheckBoxPreference) root.findPreference(KEY_LOCATION_NETWORK);
- mGps = (CheckBoxPreference) root.findPreference(KEY_LOCATION_GPS);
- mAssistedGps = (CheckBoxPreference) root.findPreference(KEY_ASSISTED_GPS);
- if (GoogleLocationSettingHelper.isAvailable(getActivity())) {
- // GSF present, Add setting for 'Use My Location'
- PreferenceGroup locationCat =
- (PreferenceGroup) root.findPreference(KEY_LOCATION_CATEGORY);
- CheckBoxPreference useLocation = new CheckBoxPreference(getActivity());
- useLocation.setKey(KEY_USE_LOCATION);
- useLocation.setTitle(R.string.use_location_title);
- useLocation.setSummaryOn(R.string.use_location_summary_enabled);
- useLocation.setSummaryOff(R.string.use_location_summary_disabled);
- useLocation.setChecked(
- GoogleLocationSettingHelper.getUseLocationForServices(getActivity())
- == GoogleLocationSettingHelper.USE_LOCATION_FOR_SERVICES_ON);
- useLocation.setPersistent(false);
- useLocation.setOnPreferenceChangeListener(this);
- locationCat.addPreference(useLocation);
- mUseLocation = useLocation;
- }
-
- // Change the summary for wifi-only devices
- if (Utils.isWifiOnly()) {
- mNetwork.setSummaryOn(R.string.location_neighborhood_level_wifi);
- }
-
// Add options for lock/unlock screen
int resid = 0;
if (!mLockPatternUtils.isSecure()) {
@@ -239,8 +165,6 @@ public class SecuritySettings extends SettingsPreferenceFragment
mShowPassword = (CheckBoxPreference) root.findPreference(KEY_SHOW_PASSWORD);
// Credential storage
- mEnableCredentials = (CheckBoxPreference) root.findPreference(KEY_ENABLE_CREDENTIALS);
- mEnableCredentials.setOnPreferenceChangeListener(this);
mResetCredentials = root.findPreference(KEY_RESET_CREDENTIALS);
return root;
@@ -315,16 +239,6 @@ public class SecuritySettings extends SettingsPreferenceFragment
// Make sure we reload the preference hierarchy since some of these settings
// depend on others...
createPreferenceHierarchy();
- updateLocationToggles();
-
- if (mSettingsObserver == null) {
- mSettingsObserver = new Observer() {
- public void update(Observable o, Object arg) {
- updateLocationToggles();
- }
- };
- mContentQueryMap.addObserver(mSettingsObserver);
- }
final LockPatternUtils lockPatternUtils = mChooseLockSettingsHelper.utils();
if (mVisiblePattern != null) {
@@ -337,15 +251,12 @@ public class SecuritySettings extends SettingsPreferenceFragment
mShowPassword.setChecked(Settings.System.getInt(getContentResolver(),
Settings.System.TEXT_SHOW_PASSWORD, 1) != 0);
- int state = KeyStore.getInstance().test();
- mEnableCredentials.setChecked(state == KeyStore.NO_ERROR);
- mEnableCredentials.setEnabled(state != KeyStore.UNINITIALIZED);
- mResetCredentials.setEnabled(state != KeyStore.UNINITIALIZED);
+ KeyStore.State state = KeyStore.getInstance().state();
+ mResetCredentials.setEnabled(state != KeyStore.State.UNINITIALIZED);
}
@Override
- public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
- Preference preference) {
+ public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
final String key = preference.getKey();
final LockPatternUtils lockPatternUtils = mChooseLockSettingsHelper.utils();
@@ -361,19 +272,6 @@ public class SecuritySettings extends SettingsPreferenceFragment
} else if (preference == mShowPassword) {
Settings.System.putInt(getContentResolver(), Settings.System.TEXT_SHOW_PASSWORD,
mShowPassword.isChecked() ? 1 : 0);
- } else if (preference == mNetwork) {
- Settings.Secure.setLocationProviderEnabled(getContentResolver(),
- LocationManager.NETWORK_PROVIDER, mNetwork.isChecked());
- } else if (preference == mGps) {
- boolean enabled = mGps.isChecked();
- Settings.Secure.setLocationProviderEnabled(getContentResolver(),
- LocationManager.GPS_PROVIDER, enabled);
- if (mAssistedGps != null) {
- mAssistedGps.setEnabled(enabled);
- }
- } else if (preference == mAssistedGps) {
- Settings.Secure.putInt(getContentResolver(), Settings.Secure.ASSISTED_GPS_ENABLED,
- mAssistedGps.isChecked() ? 1 : 0);
} else {
// If we didn't handle it, let preferences handle it.
return super.onPreferenceTreeClick(preferenceScreen, preference);
@@ -382,29 +280,12 @@ public class SecuritySettings extends SettingsPreferenceFragment
return true;
}
- /*
- * Creates toggles for each available location provider
- */
- private void updateLocationToggles() {
- ContentResolver res = getContentResolver();
- boolean gpsEnabled = Settings.Secure.isLocationProviderEnabled(
- res, LocationManager.GPS_PROVIDER);
- mNetwork.setChecked(Settings.Secure.isLocationProviderEnabled(
- res, LocationManager.NETWORK_PROVIDER));
- mGps.setChecked(gpsEnabled);
- if (mAssistedGps != null) {
- mAssistedGps.setChecked(Settings.Secure.getInt(res,
- Settings.Secure.ASSISTED_GPS_ENABLED, 2) == 1);
- mAssistedGps.setEnabled(gpsEnabled);
- }
- }
-
private boolean isToggled(Preference pref) {
return ((CheckBoxPreference) pref).isChecked();
}
/**
- * @see #confirmPatternThenDisableAndClear
+ * see confirmPatternThenDisableAndClear
*/
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
@@ -422,21 +303,6 @@ public class SecuritySettings extends SettingsPreferenceFragment
Log.e("SecuritySettings", "could not persist lockAfter timeout setting", e);
}
updateLockAfterPreferenceSummary();
- } else if (preference == mUseLocation) {
- boolean newValue = (value == null ? false : (Boolean) value);
- GoogleLocationSettingHelper.setUseLocationForServices(getActivity(), newValue);
- // We don't want to change the value immediately here, since the user may click
- // disagree in the dialog that pops up. When the activity we just launched exits, this
- // activity will be restated and the new value re-read, so the checkbox will get its
- // new value then.
- return false;
- } else if (preference == mEnableCredentials) {
- if (value != null && (Boolean) value) {
- getActivity().startActivity(new Intent(CredentialStorage.ACTION_UNLOCK));
- return false;
- } else {
- KeyStore.getInstance().lock();
- }
}
return true;
}
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 2532747..cbdd37b 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -17,17 +17,30 @@
package com.android.settings;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.preference.PreferenceActivity;
+import android.text.TextUtils;
import android.util.Log;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.ListAdapter;
+import android.widget.Switch;
+import android.widget.TextView;
+import com.android.settings.bluetooth.BluetoothEnabler;
+import com.android.settings.wifi.WifiEnabler;
+
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -36,10 +49,11 @@ import java.util.List;
*/
public class Settings extends PreferenceActivity implements ButtonBarHandler {
+ private static final String LOG_TAG = "Settings";
private static final String META_DATA_KEY_HEADER_ID =
- "com.android.settings.TOP_LEVEL_HEADER_ID";
+ "com.android.settings.TOP_LEVEL_HEADER_ID";
private static final String META_DATA_KEY_FRAGMENT_CLASS =
- "com.android.settings.FRAGMENT_CLASS";
+ "com.android.settings.FRAGMENT_CLASS";
private static final String META_DATA_KEY_PARENT_TITLE =
"com.android.settings.PARENT_FRAGMENT_TITLE";
private static final String META_DATA_KEY_PARENT_FRAGMENT_CLASS =
@@ -58,6 +72,7 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
// TODO: Update Call Settings based on airplane mode state.
protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
+ private List<Header> mHeaders;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -107,6 +122,26 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
}
}
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ ListAdapter listAdapter = getListAdapter();
+ if (listAdapter instanceof HeaderAdapter) {
+ ((HeaderAdapter) listAdapter).resume();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ ListAdapter listAdapter = getListAdapter();
+ if (listAdapter instanceof HeaderAdapter) {
+ ((HeaderAdapter) listAdapter).pause();
+ }
+ }
+
private void switchToHeaderLocal(Header header) {
mInLocalHeaderSwitch = true;
switchToHeader(header);
@@ -148,7 +183,7 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
mParentHeader.title = parentInfo.metaData.getString(META_DATA_KEY_PARENT_TITLE);
}
} catch (NameNotFoundException nnfe) {
- Log.w("Settings", "Could not find parent activity : " + className);
+ Log.w(LOG_TAG, "Could not find parent activity : " + className);
}
}
@@ -174,21 +209,22 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
@Override
public Intent getIntent() {
- String startingFragment = getStartingFragmentClass(super.getIntent());
+ Intent superIntent = super.getIntent();
+ String startingFragment = getStartingFragmentClass(superIntent);
if (startingFragment != null && !onIsMultiPane()) {
- Intent modIntent = new Intent(super.getIntent());
+ Intent modIntent = new Intent(superIntent);
modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
- Bundle args = super.getIntent().getExtras();
+ Bundle args = superIntent.getExtras();
if (args != null) {
args = new Bundle(args);
} else {
args = new Bundle();
}
- args.putParcelable("intent", super.getIntent());
- modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, super.getIntent().getExtras());
+ args.putParcelable("intent", superIntent);
+ modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
return modIntent;
}
- return super.getIntent();
+ return superIntent;
}
/**
@@ -204,7 +240,7 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
if ("com.android.settings.ManageApplications".equals(intentClass)
|| "com.android.settings.RunningServices".equals(intentClass)
|| "com.android.settings.applications.StorageUse".equals(intentClass)) {
- // Old name of manage apps.
+ // Old names of manage apps.
intentClass = com.android.settings.applications.ManageApplications.class.getName();
}
@@ -226,17 +262,39 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
mCurrentHeader = header;
return header;
}
- return super.onGetInitialHeader();
+
+ // Find first non-category header
+ int position = 0;
+ while (position < mHeaders.size()) {
+ Header header = mHeaders.get(position);
+ if (HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY)
+ return header;
+ position++;
+ }
+
+ Log.e(LOG_TAG, "Unable to find a non-category header");
+ return null;
}
+ @Override
+ public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args,
+ int titleRes, int shortTitleRes) {
+ Intent intent = super.onBuildStartFragmentIntent(fragmentName, args,
+ titleRes, shortTitleRes);
+ intent.setClass(this, SubSettings.class);
+ return intent;
+ }
+
/**
* Populate the activity with the top-level headers.
*/
@Override
- public void onBuildHeaders(List<Header> target) {
- loadHeadersFromResource(R.xml.settings_headers, target);
+ public void onBuildHeaders(List<Header> headers) {
+ loadHeadersFromResource(R.xml.settings_headers, headers);
- updateHeaderList(target);
+ updateHeaderList(headers);
+
+ mHeaders = headers;
}
private void updateHeaderList(List<Header> target) {
@@ -253,7 +311,18 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
} else if (id == R.id.call_settings) {
if (!Utils.isVoiceCapable(this))
target.remove(header);
+ } else if (id == R.id.wifi_settings) {
+ // Remove WiFi Settings if WiFi service is not available.
+ if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ target.remove(header);
+ }
+ } else if (id == R.id.bluetooth_settings) {
+ // Remove Bluetooth Settings if Bluetooth service is not available.
+ if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
+ target.remove(header);
+ }
}
+
// Increment if the current one wasn't removed by the Utils code.
if (target.get(i) == header) {
// Hold on to the first header, when we need to reset to the top-level
@@ -287,6 +356,7 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
}
}
} catch (NameNotFoundException nnfe) {
+ // No recovery
}
}
@@ -300,38 +370,197 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
return super.getNextButton();
}
+ private static class HeaderAdapter extends ArrayAdapter<Header> {
+ static final int HEADER_TYPE_CATEGORY = 0;
+ static final int HEADER_TYPE_NORMAL = 1;
+ static final int HEADER_TYPE_SWITCH = 2;
+ private static final int HEADER_TYPE_COUNT = HEADER_TYPE_SWITCH + 1;
+
+ private final WifiEnabler mWifiEnabler;
+ private final BluetoothEnabler mBluetoothEnabler;
+
+ private static class HeaderViewHolder {
+ ImageView icon;
+ TextView title;
+ TextView summary;
+ Switch switch_;
+ }
+
+ private LayoutInflater mInflater;
+
+ static int getHeaderType(Header header) {
+ if (header.fragment == null && header.intent == null) {
+ return HEADER_TYPE_CATEGORY;
+ } else if (header.id == R.id.wifi_settings || header.id == R.id.bluetooth_settings) {
+ return HEADER_TYPE_SWITCH;
+ } else {
+ return HEADER_TYPE_NORMAL;
+ }
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ Header header = getItem(position);
+ return getHeaderType(header);
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ return false; // because of categories
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return getItemViewType(position) != HEADER_TYPE_CATEGORY;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return HEADER_TYPE_COUNT;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ public HeaderAdapter(Context context, List<Header> objects) {
+ super(context, 0, objects);
+ mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ // These Switches are provided as placeholder until the adapter replaces these with actual
+ // Switches inflated from their layouts. Must be done before adapter is set in super
+ mWifiEnabler = new WifiEnabler(context, new Switch(context));
+ mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context));
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ HeaderViewHolder holder;
+ Header header = getItem(position);
+ int headerType = getHeaderType(header);
+ View view = null;
+
+ if (convertView == null) {
+ holder = new HeaderViewHolder();
+ switch (headerType) {
+ case HEADER_TYPE_CATEGORY:
+ view = new TextView(getContext(), null, android.R.attr.listSeparatorTextViewStyle);
+ holder.title = (TextView) view;
+ break;
+
+ case HEADER_TYPE_SWITCH:
+ view = mInflater.inflate(R.layout.preference_header_switch_item, parent, false);
+ holder.icon = (ImageView) view.findViewById(R.id.icon);
+ holder.title = (TextView) view.findViewById(com.android.internal.R.id.title);
+ holder.summary = (TextView) view.findViewById(com.android.internal.R.id.summary);
+ holder.switch_ = (Switch) view.findViewById(R.id.switchWidget);
+ break;
+
+ case HEADER_TYPE_NORMAL:
+ view = mInflater.inflate(com.android.internal.R.layout.preference_header_item, parent, false);
+ holder.icon = (ImageView) view.findViewById(com.android.internal.R.id.icon);
+ holder.title = (TextView) view.findViewById(com.android.internal.R.id.title);
+ holder.summary = (TextView) view.findViewById(com.android.internal.R.id.summary);
+ break;
+ }
+ view.setTag(holder);
+ } else {
+ view = convertView;
+ holder = (HeaderViewHolder) view.getTag();
+ }
+
+ // All view fields must be updated every time, because the view may be recycled
+ switch (headerType) {
+ case HEADER_TYPE_CATEGORY:
+ holder.title.setText(header.getTitle(getContext().getResources()));
+ break;
+
+ case HEADER_TYPE_SWITCH:
+ // Would need a different treatment if the main menu had more switches
+ if (header.id == R.id.wifi_settings) {
+ mWifiEnabler.setSwitch(holder.switch_);
+ } else {
+ mBluetoothEnabler.setSwitch(holder.switch_);
+ }
+ // No break, fall through on purpose to update common fields
+
+ //$FALL-THROUGH$
+ case HEADER_TYPE_NORMAL:
+ holder.icon.setImageResource(header.iconRes);
+ holder.title.setText(header.getTitle(getContext().getResources()));
+ CharSequence summary = header.getSummary(getContext().getResources());
+ if (!TextUtils.isEmpty(summary)) {
+ holder.summary.setVisibility(View.VISIBLE);
+ holder.summary.setText(summary);
+ } else {
+ holder.summary.setVisibility(View.GONE);
+ }
+ break;
+ }
+
+ return view;
+ }
+
+ public void resume() {
+ mWifiEnabler.resume();
+ mBluetoothEnabler.resume();
+ }
+
+ public void pause() {
+ mWifiEnabler.pause();
+ mBluetoothEnabler.pause();
+ }
+ }
+
+ @Override
+ public void setListAdapter(ListAdapter adapter) {
+ if (mHeaders == null) {
+ mHeaders = new ArrayList<Header>();
+ // When the saved state provides the list of headers, onBuildHeaders is not called
+ // Copy the list of Headers from the adapter, preserving their order
+ for (int i = 0; i < adapter.getCount(); i++) {
+ mHeaders.add((Header) adapter.getItem(i));
+ }
+ }
+
+ // Ignore the adapter provided by PreferenceActivity and substitute ours instead
+ super.setListAdapter(new HeaderAdapter(this, mHeaders));
+ }
+
/*
* Settings subclasses for launching independently.
*/
-
- public static class BluetoothSettingsActivity extends Settings { }
- public static class WirelessSettingsActivity extends Settings { }
- public static class TetherSettingsActivity extends Settings { }
- public static class VpnSettingsActivity extends Settings { }
- public static class DateTimeSettingsActivity extends Settings { }
- public static class StorageSettingsActivity extends Settings { }
- public static class WifiSettingsActivity extends Settings { }
- public static class InputMethodAndLanguageSettingsActivity extends Settings { }
- public static class InputMethodConfigActivity extends Settings { }
- public static class InputMethodAndSubtypeEnablerActivity extends Settings { }
- public static class LocalePickerActivity extends Settings { }
- public static class UserDictionarySettingsActivity extends Settings { }
- public static class SoundSettingsActivity extends Settings { }
- public static class DisplaySettingsActivity extends Settings { }
- public static class DeviceInfoSettingsActivity extends Settings { }
- public static class ApplicationSettingsActivity extends Settings { }
- public static class ManageApplicationsActivity extends Settings { }
- public static class StorageUseActivity extends Settings { }
- public static class DevelopmentSettingsActivity extends Settings { }
- public static class AccessibilitySettingsActivity extends Settings { }
- public static class SecuritySettingsActivity extends Settings { }
- public static class PrivacySettingsActivity extends Settings { }
- public static class DockSettingsActivity extends Settings { }
- public static class RunningServicesActivity extends Settings { }
- public static class ManageAccountsSettingsActivity extends Settings { }
- public static class PowerUsageSummaryActivity extends Settings { }
- public static class AccountSyncSettingsActivity extends Settings { }
- public static class AccountSyncSettingsInAddAccountActivity extends Settings { }
- public static class CryptKeeperSettingsActivity extends Settings { }
- public static class DeviceAdminSettingsActivity extends Settings { }
+ public static class BluetoothSettingsActivity extends Settings { /* empty */ }
+ public static class WirelessSettingsActivity extends Settings { /* empty */ }
+ public static class TetherSettingsActivity extends Settings { /* empty */ }
+ public static class VpnSettingsActivity extends Settings { /* empty */ }
+ public static class DateTimeSettingsActivity extends Settings { /* empty */ }
+ public static class StorageSettingsActivity extends Settings { /* empty */ }
+ public static class WifiSettingsActivity extends Settings { /* empty */ }
+ public static class InputMethodAndLanguageSettingsActivity extends Settings { /* empty */ }
+ public static class InputMethodConfigActivity extends Settings { /* empty */ }
+ public static class InputMethodAndSubtypeEnablerActivity extends Settings { /* empty */ }
+ public static class LocalePickerActivity extends Settings { /* empty */ }
+ public static class UserDictionarySettingsActivity extends Settings { /* empty */ }
+ public static class SoundSettingsActivity extends Settings { /* empty */ }
+ public static class DisplaySettingsActivity extends Settings { /* empty */ }
+ public static class DeviceInfoSettingsActivity extends Settings { /* empty */ }
+ public static class ApplicationSettingsActivity extends Settings { /* empty */ }
+ public static class ManageApplicationsActivity extends Settings { /* empty */ }
+ public static class StorageUseActivity extends Settings { /* empty */ }
+ public static class DevelopmentSettingsActivity extends Settings { /* empty */ }
+ public static class AccessibilitySettingsActivity extends Settings { /* empty */ }
+ public static class SecuritySettingsActivity extends Settings { /* empty */ }
+ public static class LocationSettingsActivity extends Settings { /* empty */ }
+ public static class PrivacySettingsActivity extends Settings { /* empty */ }
+ public static class DockSettingsActivity extends Settings { /* empty */ }
+ public static class RunningServicesActivity extends Settings { /* empty */ }
+ public static class ManageAccountsSettingsActivity extends Settings { /* empty */ }
+ public static class PowerUsageSummaryActivity extends Settings { /* empty */ }
+ public static class AccountSyncSettingsActivity extends Settings { /* empty */ }
+ public static class AccountSyncSettingsInAddAccountActivity extends Settings { /* empty */ }
+ public static class CryptKeeperSettingsActivity extends Settings { /* empty */ }
+ public static class DeviceAdminSettingsActivity extends Settings { /* empty */ }
+ public static class DataUsageSummaryActivity extends Settings { /* empty */ }
}
diff --git a/src/com/android/settings/SettingsCheckBoxPreference.java b/src/com/android/settings/SettingsCheckBoxPreference.java
new file mode 100644
index 0000000..6acdfe8
--- /dev/null
+++ b/src/com/android/settings/SettingsCheckBoxPreference.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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 com.android.settings;
+
+import android.content.Context;
+import android.content.Intent;
+import android.preference.CheckBoxPreference;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+
+/**
+ * CheckBox preference that optionally shows an icon for launching a settings
+ * {@link android.app.Activity}. The settings activity, if intent for launching
+ * it was provided, can be stared only if the CheckBox in is checked.
+ */
+public class SettingsCheckBoxPreference extends CheckBoxPreference {
+
+ // Integer.MIN_VALUE means not initalized
+ private static int sDimAlpha = Integer.MIN_VALUE;
+
+ private final Intent mSettingsIntent;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param context Context for accessing resources.
+ * @param settingsIntent Intent to use as settings for the item represented by
+ * this preference. Pass <code>null</code> if there is no associated
+ * settings activity.
+ */
+ public SettingsCheckBoxPreference(Context context, Intent settingsIntent) {
+ super(context);
+
+ if (sDimAlpha == Integer.MIN_VALUE) {
+ TypedValue outValue = new TypedValue();
+ context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, outValue, true);
+ sDimAlpha = (int) (outValue.getFloat() * 255);
+ }
+
+ mSettingsIntent = settingsIntent;
+ setWidgetLayoutResource(R.layout.preference_settings_checkbox_widget);
+ }
+
+ @Override
+ protected void onBindView(View view) {
+ super.onBindView(view);
+
+ ImageView settingsButton = (ImageView) view.findViewById(R.id.settings_button);
+ if (settingsButton == null) {
+ return;
+ }
+ if (mSettingsIntent != null) {
+ CheckBox checkbox = (CheckBox) view.findViewById(com.android.internal.R.id.checkbox);
+ if (checkbox == null) {
+ return;
+ }
+ if (checkbox.isChecked()) {
+ settingsButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View view) {
+ getContext().startActivity(mSettingsIntent);
+ }
+ });
+ }
+ settingsButton.setVisibility(View.VISIBLE);
+ if (checkbox.isChecked() && isEnabled()) {
+ settingsButton.setAlpha(255);
+ } else {
+ settingsButton.setAlpha(sDimAlpha);
+ }
+ } else {
+ settingsButton.setVisibility(View.GONE);
+ }
+ }
+}
diff --git a/src/com/android/settings/SettingsLicenseActivity.java b/src/com/android/settings/SettingsLicenseActivity.java
index 99828ce..2960180 100644
--- a/src/com/android/settings/SettingsLicenseActivity.java
+++ b/src/com/android/settings/SettingsLicenseActivity.java
@@ -21,7 +21,6 @@ import android.os.Handler;
import android.os.Message;
import android.os.SystemProperties;
import android.text.TextUtils;
-import android.util.Config;
import android.util.Log;
import android.webkit.WebView;
import android.webkit.WebViewClient;
@@ -46,7 +45,7 @@ import java.util.zip.GZIPInputStream;
public class SettingsLicenseActivity extends Activity {
private static final String TAG = "SettingsLicenseActivity";
- private static final boolean LOGV = false || Config.LOGV;
+ private static final boolean LOGV = false || false;
private static final String DEFAULT_LICENSE_PATH = "/system/etc/NOTICE.html.gz";
private static final String PROPERTY_LICENSE_PATH = "ro.config.license_path";
diff --git a/src/com/android/settings/SubSettings.java b/src/com/android/settings/SubSettings.java
new file mode 100644
index 0000000..9cd3c31
--- /dev/null
+++ b/src/com/android/settings/SubSettings.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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 com.android.settings;
+
+/**
+ * Stub class for showing sub-settings; we can't use the main Settings class
+ * since for our app it is a special singleTask class.
+ */
+public class SubSettings extends Settings {
+}
diff --git a/src/com/android/settings/TestingSettingsBroadcastReceiver.java b/src/com/android/settings/TestingSettingsBroadcastReceiver.java
index c6cd7e1..cea12c5 100644
--- a/src/com/android/settings/TestingSettingsBroadcastReceiver.java
+++ b/src/com/android/settings/TestingSettingsBroadcastReceiver.java
@@ -6,7 +6,6 @@ import static android.provider.Telephony.Intents.SECRET_CODE_ACTION;
import android.content.Context;
import android.content.Intent;
import android.content.BroadcastReceiver;
-import android.util.Config;
import android.util.Log;
import android.view.KeyEvent;
diff --git a/src/com/android/settings/TextToSpeechSettings.java b/src/com/android/settings/TextToSpeechSettings.java
index 62edac9..f7945d8 100644
--- a/src/com/android/settings/TextToSpeechSettings.java
+++ b/src/com/android/settings/TextToSpeechSettings.java
@@ -21,28 +21,26 @@ import static android.provider.Settings.Secure.TTS_DEFAULT_LANG;
import static android.provider.Settings.Secure.TTS_DEFAULT_RATE;
import static android.provider.Settings.Secure.TTS_DEFAULT_SYNTH;
import static android.provider.Settings.Secure.TTS_DEFAULT_VARIANT;
-import static android.provider.Settings.Secure.TTS_ENABLED_PLUGINS;
import static android.provider.Settings.Secure.TTS_USE_DEFAULTS;
-import android.app.Activity;
import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.os.Bundle;
-import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
import android.preference.Preference;
-import android.preference.PreferenceGroup;
-import android.preference.PreferenceScreen;
import android.preference.Preference.OnPreferenceClickListener;
+import android.preference.PreferenceGroup;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.speech.tts.TextToSpeech;
+import android.speech.tts.TextToSpeech.EngineInfo;
+import android.speech.tts.TtsEngines;
+import android.text.TextUtils;
import android.util.Log;
import java.util.ArrayList;
@@ -56,7 +54,6 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment implements
private static final String TAG = "TextToSpeechSettings";
- private static final String SYSTEM_TTS = "com.svox.pico";
private static final String KEY_TTS_PLAY_EXAMPLE = "tts_play_example";
private static final String KEY_TTS_INSTALL_DATA = "tts_install_data";
private static final String KEY_TTS_USE_DEFAULT = "toggle_use_default_tts_settings";
@@ -65,6 +62,7 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment implements
private static final String KEY_TTS_DEFAULT_COUNTRY = "tts_default_country";
private static final String KEY_TTS_DEFAULT_VARIANT = "tts_default_variant";
private static final String KEY_TTS_DEFAULT_SYNTH = "tts_default_synth";
+ private static final String KEY_TTS_ENGINE_SETTINGS = "tts_engine_settings";
private static final String KEY_PLUGIN_ENABLED_PREFIX = "ENABLED_";
private static final String KEY_PLUGIN_SETTINGS_PREFIX = "SETTINGS_";
@@ -76,19 +74,18 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment implements
private static final String LOCALE_DELIMITER = "-";
- private static final String FALLBACK_TTS_DEFAULT_SYNTH =
- TextToSpeech.Engine.DEFAULT_SYNTH;
+ private Preference mPlayExample = null;
+
+ private ListPreference mDefaultRatePref = null;
+ private ListPreference mDefaultLocPref = null;
+ private ListPreference mDefaultSynthPref = null;
+
+ private Preference mInstallData = null;
+ private Preference mEngineSettings = null;
- private Preference mPlayExample = null;
- private Preference mInstallData = null;
- private CheckBoxPreference mUseDefaultPref = null;
- private ListPreference mDefaultRatePref = null;
- private ListPreference mDefaultLocPref = null;
- private ListPreference mDefaultSynthPref = null;
private String mDefaultLanguage = null;
private String mDefaultCountry = null;
private String mDefaultLocVariant = null;
- private String mDefaultEng = "";
private int mDefaultRate = TextToSpeech.Engine.DEFAULT_RATE;
// Index of the current string to use for the demo.
@@ -98,6 +95,7 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment implements
private boolean mVoicesMissing = false;
private TextToSpeech mTts = null;
+ private TtsEngines mEnginesHelper = null;
private boolean mTtsStarted = false;
/**
@@ -112,10 +110,7 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment implements
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.tts_settings);
- final Activity activity = getActivity();
- addEngineSpecificSettings(activity);
-
- activity.setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
+ getActivity().setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
mEnableDemo = false;
mTtsStarted = false;
@@ -125,10 +120,25 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment implements
mDefaultCountry = currentLocale.getISO3Country();
mDefaultLocVariant = currentLocale.getVariant();
- mTts = new TextToSpeech(activity, this);
- initClickers();
- }
+ mPlayExample = findPreference(KEY_TTS_PLAY_EXAMPLE);
+ mPlayExample.setOnPreferenceClickListener(this);
+ mInstallData = findPreference(KEY_TTS_INSTALL_DATA);
+ mInstallData.setOnPreferenceClickListener(this);
+
+ mDefaultSynthPref = (ListPreference) findPreference(KEY_TTS_DEFAULT_SYNTH);
+ mDefaultRatePref = (ListPreference) findPreference(KEY_TTS_DEFAULT_RATE);
+ mDefaultLocPref = (ListPreference) findPreference(KEY_TTS_DEFAULT_LANG);
+
+ mEngineSettings = findPreference(KEY_TTS_ENGINE_SETTINGS);
+ mEngineSettings.setEnabled(false);
+
+ mTts = new TextToSpeech(getActivity().getApplicationContext(), this);
+ mEnginesHelper = new TtsEngines(getActivity().getApplicationContext());
+
+ initDefaultSettings();
+ initEngineSpecificSettings();
+ }
@Override
public void onStart() {
@@ -142,7 +152,6 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment implements
}
}
-
@Override
public void onDestroy() {
super.onDestroy();
@@ -165,97 +174,39 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment implements
}
}
- private void addEngineSpecificSettings(Context context) {
- PreferenceGroup enginesCategory = (PreferenceGroup) findPreference("tts_engines_section");
- Intent intent = new Intent("android.intent.action.START_TTS_ENGINE");
- ResolveInfo[] enginesArray = new ResolveInfo[0];
- PackageManager pm = getPackageManager();
- enginesArray = pm.queryIntentActivities(intent, 0).toArray(enginesArray);
- for (int i = 0; i < enginesArray.length; i++) {
- String prefKey = "";
- final String pluginPackageName = enginesArray[i].activityInfo.packageName;
- if (!enginesArray[i].activityInfo.packageName.equals(SYSTEM_TTS)) {
- CheckBoxPreference chkbxPref = new CheckBoxPreference(context);
- prefKey = KEY_PLUGIN_ENABLED_PREFIX + pluginPackageName;
- chkbxPref.setKey(prefKey);
- chkbxPref.setTitle(enginesArray[i].loadLabel(pm));
- enginesCategory.addPreference(chkbxPref);
- }
- if (pluginHasSettings(pluginPackageName)) {
- Preference pref = new Preference(context);
- prefKey = KEY_PLUGIN_SETTINGS_PREFIX + pluginPackageName;
- pref.setKey(prefKey);
- pref.setTitle(enginesArray[i].loadLabel(pm));
- CharSequence settingsLabel = getResources().getString(
- R.string.tts_engine_name_settings, enginesArray[i].loadLabel(pm));
- pref.setSummary(settingsLabel);
- pref.setOnPreferenceClickListener(new OnPreferenceClickListener(){
- public boolean onPreferenceClick(Preference preference){
- Intent i = new Intent();
- i.setClassName(pluginPackageName,
- pluginPackageName + ".EngineSettings");
- startActivity(i);
- return true;
- }
- });
- enginesCategory.addPreference(pref);
- }
- }
- }
-
- private boolean pluginHasSettings(String pluginPackageName) {
- PackageManager pm = getPackageManager();
- Intent i = new Intent();
- i.setClassName(pluginPackageName, pluginPackageName + ".EngineSettings");
- if (pm.resolveActivity(i, PackageManager.MATCH_DEFAULT_ONLY) != null){
- return true;
- }
- return false;
- }
+ private void initEngineSpecificSettings() {
+ final String engineName = mEnginesHelper.getDefaultEngine();
+ final EngineInfo engine = mEnginesHelper.getEngineInfo(engineName);
+ mEngineSettings.setTitle(getResources().getString(R.string.tts_engine_settings_title,
+ engine.label));
- private void initClickers() {
- mPlayExample = findPreference(KEY_TTS_PLAY_EXAMPLE);
- mPlayExample.setOnPreferenceClickListener(this);
+ final Intent settingsIntent = mEnginesHelper.getSettingsIntent(engineName);
+ if (settingsIntent != null) {
+ mEngineSettings.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ public boolean onPreferenceClick(Preference preference) {
+ startActivity(settingsIntent);
+ return true;
+ }
+ });
+ mEngineSettings.setEnabled(true);
+ } else {
+ mEngineSettings.setEnabled(false);
+ }
- mInstallData = findPreference(KEY_TTS_INSTALL_DATA);
- mInstallData.setOnPreferenceClickListener(this);
}
-
private void initDefaultSettings() {
ContentResolver resolver = getContentResolver();
// Find the default TTS values in the settings, initialize and store the
// settings if they are not found.
- // "Use Defaults"
- int useDefault = 0;
- mUseDefaultPref = (CheckBoxPreference) findPreference(KEY_TTS_USE_DEFAULT);
- try {
- useDefault = Settings.Secure.getInt(resolver, TTS_USE_DEFAULTS);
- } catch (SettingNotFoundException e) {
- // "use default" setting not found, initialize it
- useDefault = TextToSpeech.Engine.USE_DEFAULTS;
- Settings.Secure.putInt(resolver, TTS_USE_DEFAULTS, useDefault);
- }
- mUseDefaultPref.setChecked(useDefault == 1);
- mUseDefaultPref.setOnPreferenceChangeListener(this);
-
// Default synthesis engine
- mDefaultSynthPref = (ListPreference) findPreference(KEY_TTS_DEFAULT_SYNTH);
loadEngines();
mDefaultSynthPref.setOnPreferenceChangeListener(this);
- String engine = Settings.Secure.getString(resolver, TTS_DEFAULT_SYNTH);
- if (engine == null) {
- // TODO move FALLBACK_TTS_DEFAULT_SYNTH to TextToSpeech
- engine = FALLBACK_TTS_DEFAULT_SYNTH;
- Settings.Secure.putString(resolver, TTS_DEFAULT_SYNTH, engine);
- }
- mDefaultEng = engine;
// Default rate
- mDefaultRatePref = (ListPreference) findPreference(KEY_TTS_DEFAULT_RATE);
try {
mDefaultRate = Settings.Secure.getInt(resolver, TTS_DEFAULT_RATE);
} catch (SettingNotFoundException e) {
@@ -265,34 +216,27 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment implements
}
mDefaultRatePref.setValue(String.valueOf(mDefaultRate));
mDefaultRatePref.setOnPreferenceChangeListener(this);
- // apply the default rate so the TTS demo in the Settings screen uses it, even if
- // the use of default settings is not enforced
- mTts.setSpeechRate(mDefaultRate/100.0f);
// Default language / country / variant : these three values map to a single ListPref
// representing the matching Locale
- mDefaultLocPref = (ListPreference) findPreference(KEY_TTS_DEFAULT_LANG);
initDefaultLang();
mDefaultLocPref.setOnPreferenceChangeListener(this);
}
-
/**
* Ask the current default engine to launch the matching CHECK_TTS_DATA activity
* to check the required TTS files are properly installed.
*/
private void checkVoiceData() {
- PackageManager pm = getPackageManager();
- Intent intent = new Intent();
- intent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
- List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
- // query only the package that matches that of the default engine
- for (int i = 0; i < resolveInfos.size(); i++) {
- ActivityInfo currentActivityInfo = resolveInfos.get(i).activityInfo;
- if (mDefaultEng.equals(currentActivityInfo.packageName)) {
- intent.setClassName(mDefaultEng, currentActivityInfo.name);
- this.startActivityForResult(intent, VOICE_DATA_INTEGRITY_CHECK);
- }
+ String defaultEngine = mTts.getDefaultEngine();
+ if (TextUtils.isEmpty(defaultEngine)) return;
+ Intent intent = new Intent(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
+ intent.setPackage(defaultEngine);
+ try {
+ Log.v(TAG, "Checking voice data: " + intent.toUri(0));
+ startActivityForResult(intent, VOICE_DATA_INTEGRITY_CHECK);
+ } catch (ActivityNotFoundException ex) {
+ Log.e(TAG, "Failed to check TTS data, no acitivty found for " + intent + ")");
}
}
@@ -302,18 +246,16 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment implements
* so the required TTS files are properly installed.
*/
private void installVoiceData() {
- PackageManager pm = getPackageManager();
- Intent intent = new Intent();
- intent.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
+ String defaultEngine = mTts.getDefaultEngine();
+ if (TextUtils.isEmpty(defaultEngine)) return;
+ Intent intent = new Intent(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
- // query only the package that matches that of the default engine
- for (int i = 0; i < resolveInfos.size(); i++) {
- ActivityInfo currentActivityInfo = resolveInfos.get(i).activityInfo;
- if (mDefaultEng.equals(currentActivityInfo.packageName)) {
- intent.setClassName(mDefaultEng, currentActivityInfo.name);
- this.startActivity(intent);
- }
+ intent.setPackage(defaultEngine);
+ try {
+ Log.v(TAG, "Installing voice data: " + intent.toUri(0));
+ startActivity(intent);
+ } catch (ActivityNotFoundException ex) {
+ Log.e(TAG, "Failed to install TTS data, no acitivty found for " + intent + ")");
}
}
@@ -322,26 +264,22 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment implements
* spoken to the user.
*/
private void getSampleText() {
- PackageManager pm = getPackageManager();
- Intent intent = new Intent();
- // TODO (clchen): Replace Intent string with the actual
- // Intent defined in the list of platform Intents.
- intent.setAction("android.speech.tts.engine.GET_SAMPLE_TEXT");
+ String defaultEngine = mTts.getDefaultEngine();
+ if (TextUtils.isEmpty(defaultEngine)) return;
+ Intent intent = new Intent(TextToSpeech.Engine.ACTION_GET_SAMPLE_TEXT);
intent.putExtra("language", mDefaultLanguage);
intent.putExtra("country", mDefaultCountry);
intent.putExtra("variant", mDefaultLocVariant);
- List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
- // query only the package that matches that of the default engine
- for (int i = 0; i < resolveInfos.size(); i++) {
- ActivityInfo currentActivityInfo = resolveInfos.get(i).activityInfo;
- if (mDefaultEng.equals(currentActivityInfo.packageName)) {
- intent.setClassName(mDefaultEng, currentActivityInfo.name);
- this.startActivityForResult(intent, GET_SAMPLE_TEXT);
- }
+ intent.setPackage(defaultEngine);
+
+ try {
+ Log.v(TAG, "Getting sample text: " + intent.toUri(0));
+ startActivityForResult(intent, GET_SAMPLE_TEXT);
+ } catch (ActivityNotFoundException ex) {
+ Log.e(TAG, "Failed to get sample text, no acitivty found for " + intent + ")");
}
}
-
/**
* Called when the TTS engine is initialized.
*/
@@ -358,7 +296,6 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment implements
mDefaultLocVariant = new String();
}
mTts.setLanguage(new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
- initDefaultSettings();
updateWidgetState();
checkVoiceData();
mTtsStarted = true;
@@ -370,142 +307,155 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment implements
updateWidgetState();
}
-
/**
* Called when voice data integrity check returns
*/
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == VOICE_DATA_INTEGRITY_CHECK) {
- if (data == null){
- // The CHECK_TTS_DATA activity for the plugin did not run properly;
- // disable the preview and install controls and return.
- mEnableDemo = false;
- mVoicesMissing = false;
- updateWidgetState();
- return;
- }
- ArrayList<String> available =
- data.getStringArrayListExtra(TextToSpeech.Engine.EXTRA_AVAILABLE_VOICES);
- ArrayList<String> unavailable =
- data.getStringArrayListExtra(TextToSpeech.Engine.EXTRA_UNAVAILABLE_VOICES);
- if ((available == null) || (unavailable == null)){
- // The CHECK_TTS_DATA activity for the plugin did not run properly;
- // disable the preview and install controls and return.
- mEnableDemo = false;
- mVoicesMissing = false;
- updateWidgetState();
- return;
+ onVoiceDataIntegrityCheckDone(data);
+ } else if (requestCode == GET_SAMPLE_TEXT) {
+ onSampleTextReceived(resultCode, data);
+ }
+ }
+
+ private void onVoiceDataIntegrityCheckDone(Intent data) {
+ if (data == null){
+ Log.e(TAG, "TTS data check failed data = null");
+ // The CHECK_TTS_DATA activity for the plugin did not run properly;
+ // disable the preview and install controls and return.
+ mEnableDemo = false;
+ mVoicesMissing = false;
+ updateWidgetState();
+ return;
+ }
+ Log.v(TAG, "TTS data check completed, data = " + data.toUri(0));
+ ArrayList<String> available =
+ data.getStringArrayListExtra(TextToSpeech.Engine.EXTRA_AVAILABLE_VOICES);
+ ArrayList<String> unavailable =
+ data.getStringArrayListExtra(TextToSpeech.Engine.EXTRA_UNAVAILABLE_VOICES);
+ if (available == null || unavailable == null){
+ Log.e(TAG, "TTS data check failed (available == == null)");
+ // The CHECK_TTS_DATA activity for the plugin did not run properly;
+ // disable the preview and install controls and return.
+ mEnableDemo = false;
+ mVoicesMissing = false;
+ updateWidgetState();
+ return;
+ }
+ if (available.size() > 0){
+ if (mTts == null) {
+ mTts = new TextToSpeech(getActivity(), this);
}
- if (available.size() > 0){
- if (mTts == null) {
- mTts = new TextToSpeech(getActivity(), this);
- }
- ListPreference ttsLanguagePref =
- (ListPreference) findPreference("tts_default_lang");
- CharSequence[] entries = new CharSequence[available.size()];
- CharSequence[] entryValues = new CharSequence[available.size()];
- int selectedLanguageIndex = -1;
- String selectedLanguagePref = mDefaultLanguage;
- if (mDefaultCountry.length() > 0) {
- selectedLanguagePref = selectedLanguagePref + LOCALE_DELIMITER +
- mDefaultCountry;
- }
- if (mDefaultLocVariant.length() > 0) {
- selectedLanguagePref = selectedLanguagePref + LOCALE_DELIMITER +
- mDefaultLocVariant;
- }
- for (int i = 0; i < available.size(); i++) {
- String[] langCountryVariant = available.get(i).split("-");
- Locale loc = null;
- if (langCountryVariant.length == 1){
- loc = new Locale(langCountryVariant[0]);
- } else if (langCountryVariant.length == 2){
- loc = new Locale(langCountryVariant[0], langCountryVariant[1]);
- } else if (langCountryVariant.length == 3){
- loc = new Locale(langCountryVariant[0], langCountryVariant[1],
- langCountryVariant[2]);
- }
- if (loc != null){
- entries[i] = loc.getDisplayName();
- entryValues[i] = available.get(i);
- if (entryValues[i].equals(selectedLanguagePref)) {
- selectedLanguageIndex = i;
- }
- }
- }
- ttsLanguagePref.setEntries(entries);
- ttsLanguagePref.setEntryValues(entryValues);
- if (selectedLanguageIndex > -1) {
- ttsLanguagePref.setValueIndex(selectedLanguageIndex);
- }
- mEnableDemo = true;
- // Make sure that the default language can be used.
- int languageResult = mTts.setLanguage(
+
+ updateDefaultLocPref(available);
+
+ mEnableDemo = true;
+ // Make sure that the default language can be used.
+ int languageResult = mTts.setLanguage(
+ new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
+ if (languageResult < TextToSpeech.LANG_AVAILABLE){
+ Locale currentLocale = Locale.getDefault();
+ mDefaultLanguage = currentLocale.getISO3Language();
+ mDefaultCountry = currentLocale.getISO3Country();
+ mDefaultLocVariant = currentLocale.getVariant();
+ languageResult = mTts.setLanguage(
new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
+ // If the default Locale isn't supported, just choose the first available
+ // language so that there is at least something.
if (languageResult < TextToSpeech.LANG_AVAILABLE){
- Locale currentLocale = Locale.getDefault();
- mDefaultLanguage = currentLocale.getISO3Language();
- mDefaultCountry = currentLocale.getISO3Country();
- mDefaultLocVariant = currentLocale.getVariant();
- languageResult = mTts.setLanguage(
+ parseLocaleInfo(mDefaultLocPref.getEntryValues()[0].toString());
+ mTts.setLanguage(
new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
- // If the default Locale isn't supported, just choose the first available
- // language so that there is at least something.
- if (languageResult < TextToSpeech.LANG_AVAILABLE){
- parseLocaleInfo(ttsLanguagePref.getEntryValues()[0].toString());
- mTts.setLanguage(
- new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
- }
- ContentResolver resolver = getContentResolver();
- Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, mDefaultLanguage);
- Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, mDefaultCountry);
- Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, mDefaultLocVariant);
}
- } else {
- mEnableDemo = false;
+ ContentResolver resolver = getContentResolver();
+ Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, mDefaultLanguage);
+ Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, mDefaultCountry);
+ Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, mDefaultLocVariant);
}
+ } else {
+ mEnableDemo = false;
+ }
- if (unavailable.size() > 0){
- mVoicesMissing = true;
- } else {
- mVoicesMissing = false;
- }
+ if (unavailable.size() > 0){
+ mVoicesMissing = true;
+ } else {
+ mVoicesMissing = false;
+ }
- updateWidgetState();
- } else if (requestCode == GET_SAMPLE_TEXT) {
- if (resultCode == TextToSpeech.LANG_AVAILABLE) {
- String sample = getActivity().getString(R.string.tts_demo);
- if ((data != null) && (data.getStringExtra("sampleText") != null)) {
- sample = data.getStringExtra("sampleText");
- }
- if (mTts != null) {
- mTts.speak(sample, TextToSpeech.QUEUE_FLUSH, null);
+ updateWidgetState();
+ }
+
+ private void updateDefaultLocPref(ArrayList<String> availableLangs) {
+ CharSequence[] entries = new CharSequence[availableLangs.size()];
+ CharSequence[] entryValues = new CharSequence[availableLangs.size()];
+ int selectedLanguageIndex = -1;
+ String selectedLanguagePref = mDefaultLanguage;
+ if (mDefaultCountry.length() > 0) {
+ selectedLanguagePref = selectedLanguagePref + LOCALE_DELIMITER +
+ mDefaultCountry;
+ }
+ if (mDefaultLocVariant.length() > 0) {
+ selectedLanguagePref = selectedLanguagePref + LOCALE_DELIMITER +
+ mDefaultLocVariant;
+ }
+ for (int i = 0; i < availableLangs.size(); i++) {
+ String[] langCountryVariant = availableLangs.get(i).split("-");
+ Locale loc = null;
+ if (langCountryVariant.length == 1){
+ loc = new Locale(langCountryVariant[0]);
+ } else if (langCountryVariant.length == 2){
+ loc = new Locale(langCountryVariant[0], langCountryVariant[1]);
+ } else if (langCountryVariant.length == 3){
+ loc = new Locale(langCountryVariant[0], langCountryVariant[1],
+ langCountryVariant[2]);
+ }
+ if (loc != null){
+ entries[i] = loc.getDisplayName();
+ entryValues[i] = availableLangs.get(i);
+ if (entryValues[i].equals(selectedLanguagePref)) {
+ selectedLanguageIndex = i;
}
- } else {
- // TODO: Display an error here to the user.
- Log.e(TAG, "Did not have a sample string for the requested language");
}
}
+ mDefaultLocPref.setEntries(entries);
+ mDefaultLocPref.setEntryValues(entryValues);
+ if (selectedLanguageIndex > -1) {
+ mDefaultLocPref.setValueIndex(selectedLanguageIndex);
+ }
+ }
+
+ private void onSampleTextReceived(int resultCode, Intent data) {
+ if (resultCode == TextToSpeech.LANG_AVAILABLE) {
+ String sample = getActivity().getString(R.string.tts_demo);
+ if (data != null && data.getStringExtra("sampleText") != null) {
+ sample = data.getStringExtra("sampleText");
+ }
+ Log.v(TAG, "Got sample text: " + sample);
+ if (mTts != null) {
+ mTts.speak(sample, TextToSpeech.QUEUE_FLUSH, null);
+ }
+ } else {
+ // TODO: Display an error here to the user.
+ Log.e(TAG, "Did not have a sample string for the requested language");
+ }
}
public boolean onPreferenceChange(Preference preference, Object objValue) {
if (KEY_TTS_USE_DEFAULT.equals(preference.getKey())) {
// "Use Defaults"
- int value = (Boolean)objValue ? 1 : 0;
- Settings.Secure.putInt(getContentResolver(), TTS_USE_DEFAULTS,
- value);
- Log.i(TAG, "TTS use default settings is "+objValue.toString());
+ int value = ((Boolean) objValue) ? 1 : 0;
+ Settings.Secure.putInt(getContentResolver(), TTS_USE_DEFAULTS, value);
+ Log.i(TAG, "TTS 'use default' settings changed, now " + value);
} else if (KEY_TTS_DEFAULT_RATE.equals(preference.getKey())) {
// Default rate
mDefaultRate = Integer.parseInt((String) objValue);
try {
- Settings.Secure.putInt(getContentResolver(),
- TTS_DEFAULT_RATE, mDefaultRate);
+ Settings.Secure.putInt(getContentResolver(), TTS_DEFAULT_RATE, mDefaultRate);
if (mTts != null) {
- mTts.setSpeechRate(mDefaultRate/100.0f);
+ mTts.setSpeechRate(mDefaultRate / 100.0f);
}
- Log.i(TAG, "TTS default rate is " + mDefaultRate);
+ Log.v(TAG, "TTS default rate changed, now " + mDefaultRate);
} catch (NumberFormatException e) {
Log.e(TAG, "could not persist default TTS rate setting", e);
}
@@ -522,19 +472,24 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment implements
mTts.setLanguage(new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
}
int newIndex = mDefaultLocPref.findIndexOfValue((String)objValue);
- Log.v("Settings", " selected is " + newIndex);
+ Log.v(TAG, " selected is " + newIndex);
mDemoStringIndex = newIndex > -1 ? newIndex : 0;
} else if (KEY_TTS_DEFAULT_SYNTH.equals(preference.getKey())) {
- mDefaultEng = objValue.toString();
- Settings.Secure.putString(getContentResolver(), TTS_DEFAULT_SYNTH, mDefaultEng);
- if (mTts != null) {
- mTts.setEngineByPackageName(mDefaultEng);
- mEnableDemo = false;
- mVoicesMissing = false;
- updateWidgetState();
- checkVoiceData();
+ final String name = objValue.toString();
+ final EngineInfo info = mEnginesHelper.getEngineInfo(name);
+
+ if (info.system) {
+ // For system engines, do away with the alert dialog.
+ updateDefaultEngine(name);
+ initEngineSpecificSettings();
+ } else {
+ // For all other engines, display a warning message before
+ // turning them on.
+ displayDataAlert(preference, name);
}
- Log.v("Settings", "The default synth is: " + objValue.toString());
+
+ // We'll deal with updating the UI ourselves.
+ return false;
}
return true;
@@ -550,61 +505,18 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment implements
// the actual speaking
getSampleText();
return true;
- }
- if (preference == mInstallData) {
+ } else if (preference == mInstallData) {
installVoiceData();
// quit this activity so it needs to be restarted after installation of the voice data
finish();
return true;
}
- return false;
- }
-
- @Override
- public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
- if (Utils.isMonkeyRunning()) {
- return false;
- }
- if (preference instanceof CheckBoxPreference) {
- final CheckBoxPreference chkPref = (CheckBoxPreference) preference;
- if (!chkPref.getKey().equals(KEY_TTS_USE_DEFAULT)){
- if (chkPref.isChecked()) {
- chkPref.setChecked(false);
- AlertDialog d = (new AlertDialog.Builder(getActivity()))
- .setTitle(android.R.string.dialog_alert_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(
- getActivity().getString(R.string.tts_engine_security_warning,
- chkPref.getTitle()))
- .setCancelable(true)
- .setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- chkPref.setChecked(true);
- loadEngines();
- }
- })
- .setNegativeButton(android.R.string.cancel,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- }
- })
- .create();
- d.show();
- } else {
- loadEngines();
- }
- return true;
- }
- }
return false;
}
-
private void updateWidgetState() {
mPlayExample.setEnabled(mEnableDemo);
- mUseDefaultPref.setEnabled(mEnableDemo);
mDefaultRatePref.setEnabled(mEnableDemo);
mDefaultLocPref.setEnabled(mEnableDemo);
@@ -617,14 +529,18 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment implements
mDefaultLanguage = "";
mDefaultCountry = "";
mDefaultLocVariant = "";
- if (tokenizer.hasMoreTokens()) {
- mDefaultLanguage = tokenizer.nextToken().trim();
- }
- if (tokenizer.hasMoreTokens()) {
- mDefaultCountry = tokenizer.nextToken().trim();
- }
- if (tokenizer.hasMoreTokens()) {
- mDefaultLocVariant = tokenizer.nextToken().trim();
+
+ if (locale != null) {
+ String[] components = locale.split(LOCALE_DELIMITER);
+ if (components.length > 0) {
+ mDefaultLanguage = components[0];
+ }
+ if (components.length > 1) {
+ mDefaultCountry = components[1];
+ }
+ if (components.length > 2) {
+ mDefaultLocVariant = components[2];
+ }
}
}
@@ -716,50 +632,78 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment implements
Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, DEFAULT_VARIANT_VAL);
}
-
private void loadEngines() {
- mDefaultSynthPref = (ListPreference) findPreference(KEY_TTS_DEFAULT_SYNTH);
-
- // TODO (clchen): Try to see if it is possible to be more efficient here
- // and not search for plugins again.
- Intent intent = new Intent("android.intent.action.START_TTS_ENGINE");
- ResolveInfo[] enginesArray = new ResolveInfo[0];
- PackageManager pm = getPackageManager();
- enginesArray = pm.queryIntentActivities(intent, 0).toArray(enginesArray);
- ArrayList<CharSequence> entries = new ArrayList<CharSequence>();
- ArrayList<CharSequence> values = new ArrayList<CharSequence>();
- String enabledEngines = "";
- for (int i = 0; i < enginesArray.length; i++) {
- String pluginPackageName = enginesArray[i].activityInfo.packageName;
- if (pluginPackageName.equals(SYSTEM_TTS)) {
- entries.add(enginesArray[i].loadLabel(pm));
- values.add(pluginPackageName);
- } else {
- CheckBoxPreference pref = (CheckBoxPreference) findPreference(
- KEY_PLUGIN_ENABLED_PREFIX + pluginPackageName);
- if ((pref != null) && pref.isChecked()){
- entries.add(enginesArray[i].loadLabel(pm));
- values.add(pluginPackageName);
- enabledEngines = enabledEngines + pluginPackageName + " ";
- }
- }
+ List<EngineInfo> engines = mEnginesHelper.getEngines();
+ CharSequence entries[] = new CharSequence[engines.size()];
+ CharSequence values[] = new CharSequence[engines.size()];
+
+ final int count = engines.size();
+ for (int i = 0; i < count; ++i) {
+ final EngineInfo engine = engines.get(i);
+ entries[i] = engine.label;
+ values[i] = engine.name;
}
- ContentResolver resolver = getContentResolver();
- Settings.Secure.putString(resolver, TTS_ENABLED_PLUGINS, enabledEngines);
-
- CharSequence entriesArray[] = new CharSequence[entries.size()];
- CharSequence valuesArray[] = new CharSequence[values.size()];
- mDefaultSynthPref.setEntries(entries.toArray(entriesArray));
- mDefaultSynthPref.setEntryValues(values.toArray(valuesArray));
+ mDefaultSynthPref.setEntries(entries);
+ mDefaultSynthPref.setEntryValues(values);
// Set the selected engine based on the saved preference
String selectedEngine = Settings.Secure.getString(getContentResolver(), TTS_DEFAULT_SYNTH);
int selectedEngineIndex = mDefaultSynthPref.findIndexOfValue(selectedEngine);
if (selectedEngineIndex == -1){
- selectedEngineIndex = mDefaultSynthPref.findIndexOfValue(SYSTEM_TTS);
+ selectedEngineIndex = mDefaultSynthPref.findIndexOfValue(
+ mEnginesHelper.getHighestRankedEngineName());
+ }
+ if (selectedEngineIndex >= 0) {
+ mDefaultSynthPref.setValueIndex(selectedEngineIndex);
+ }
+ }
+
+ private void displayDataAlert(Preference pref, final String key) {
+ Log.v(TAG, "Displaying data alert for :" + key);
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setTitle(android.R.string.dialog_alert_title);
+ builder.setIcon(android.R.drawable.ic_dialog_alert);
+ builder.setMessage(getActivity().getString(
+ R.string.tts_engine_security_warning, pref.getTitle()));
+ builder.setCancelable(true);
+ builder.setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ updateDefaultEngine(key);
+ loadEngines();
+ initEngineSpecificSettings();
+ }
+ });
+ builder.setNegativeButton(android.R.string.cancel, null);
+
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ }
+
+ private void updateDefaultEngine(String engine) {
+ Log.v(TAG, "Updating default synth to : " + engine);
+ if (mTts != null) {
+ try {
+ mTts.shutdown();
+ mTts = null;
+ } catch (Exception e) {
+ Log.e(TAG, "Error shutting down TTS engine" + e);
+ }
}
- mDefaultSynthPref.setValueIndex(selectedEngineIndex);
+
+ mTts = new TextToSpeech(getActivity().getApplicationContext(), this, engine);
+ mEnableDemo = false;
+ mVoicesMissing = false;
+ updateWidgetState();
+ checkVoiceData();
+
+ // Finally, persist this value to settings.
+ Settings.Secure.putString(getContentResolver(), TTS_DEFAULT_SYNTH, engine);
+ // .. and update the UI.
+ mDefaultSynthPref.setValue(engine);
+
+ Log.v(TAG, "The default synth is now: " + engine);
}
}
diff --git a/src/com/android/settings/UserDictionarySettings.java b/src/com/android/settings/UserDictionarySettings.java
index b13126a..22112d7 100644
--- a/src/com/android/settings/UserDictionarySettings.java
+++ b/src/com/android/settings/UserDictionarySettings.java
@@ -46,6 +46,7 @@ import android.widget.TextView;
import com.android.settings.SettingsPreferenceFragment.SettingsDialogFragment;
+import java.util.Arrays;
import java.util.Locale;
public class UserDictionarySettings extends ListFragment implements DialogCreatable {
@@ -63,23 +64,29 @@ public class UserDictionarySettings extends ListFragment implements DialogCreata
// Either the locale is empty (means the word is applicable to all locales)
// or the word equals our current locale
- private static final String QUERY_SELECTION = UserDictionary.Words.LOCALE + "=? OR "
- + UserDictionary.Words.LOCALE + " is null";
+ private static final String QUERY_SELECTION =
+ UserDictionary.Words.LOCALE + "=?";
+ private static final String QUERY_SELECTION_ALL_LOCALES =
+ UserDictionary.Words.LOCALE + " is null";
private static final String DELETE_SELECTION = UserDictionary.Words.WORD + "=?";
private static final String EXTRA_WORD = "word";
-
+
private static final int OPTIONS_MENU_ADD = Menu.FIRST;
private static final int DIALOG_ADD_OR_EDIT = 0;
-
+
+ private static final int FREQUENCY_FOR_USER_DICTIONARY_ADDS = 250;
+
/** The word being edited in the dialog (null means the user is adding a word). */
private String mDialogEditingWord;
private View mView;
private Cursor mCursor;
-
+
+ protected String mLocale;
+
private boolean mAddedWordAlready;
private boolean mAutoReturn;
@@ -101,7 +108,25 @@ public class UserDictionarySettings extends ListFragment implements DialogCreata
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- mCursor = createCursor();
+ final Intent intent = getActivity().getIntent();
+ final String localeFromIntent =
+ null == intent ? null : intent.getStringExtra("locale");
+
+ final Bundle arguments = getArguments();
+ final String localeFromArguments =
+ null == arguments ? null : arguments.getString("locale");
+
+ final String locale;
+ if (null != localeFromArguments) {
+ locale = localeFromArguments;
+ } else if (null != localeFromIntent) {
+ locale = localeFromIntent;
+ } else {
+ locale = null;
+ }
+
+ mLocale = locale;
+ mCursor = createCursor(locale);
TextView emptyView = (TextView)mView.findViewById(R.id.empty);
emptyView.setText(R.string.user_dict_settings_empty_text);
@@ -117,12 +142,12 @@ public class UserDictionarySettings extends ListFragment implements DialogCreata
mAddedWordAlready = savedInstanceState.getBoolean(INSTANCE_KEY_ADDED_WORD, false);
}
}
-
+
@Override
public void onResume() {
super.onResume();
final Intent intent = getActivity().getIntent();
- if (!mAddedWordAlready
+ if (!mAddedWordAlready
&& intent.getAction().equals("com.android.settings.USER_DICTIONARY_INSERT")) {
final String word = intent.getStringExtra(EXTRA_WORD);
mAutoReturn = true;
@@ -139,12 +164,28 @@ public class UserDictionarySettings extends ListFragment implements DialogCreata
outState.putBoolean(INSTANCE_KEY_ADDED_WORD, mAddedWordAlready);
}
- private Cursor createCursor() {
- String currentLocale = Locale.getDefault().toString();
- // Case-insensitive sort
- return getActivity().managedQuery(UserDictionary.Words.CONTENT_URI, QUERY_PROJECTION,
- QUERY_SELECTION, new String[] { currentLocale },
- "UPPER(" + UserDictionary.Words.WORD + ")");
+ private Cursor createCursor(final String locale) {
+ // Locale can be any of:
+ // - The string representation of a locale, as returned by Locale#toString()
+ // - The empty string. This means we want a cursor returning words valid for all locales.
+ // - null. This means we want a cursor for the current locale, whatever this is.
+ // Note that this contrasts with the data inside the database, where NULL means "all
+ // locales" and there should never be an empty string. The confusion is called by the
+ // historical use of null for "all locales".
+ // TODO: it should be easy to make this more readable by making the special values
+ // human-readable, like "all_locales" and "current_locales" strings, provided they
+ // can be guaranteed not to match locales that may exist.
+ if ("".equals(locale)) {
+ // Case-insensitive sort
+ return getActivity().managedQuery(UserDictionary.Words.CONTENT_URI, QUERY_PROJECTION,
+ QUERY_SELECTION_ALL_LOCALES, null,
+ "UPPER(" + UserDictionary.Words.WORD + ")");
+ } else {
+ final String queryLocale = null != locale ? locale : Locale.getDefault().toString();
+ return getActivity().managedQuery(UserDictionary.Words.CONTENT_URI, QUERY_PROJECTION,
+ QUERY_SELECTION, new String[] { queryLocale },
+ "UPPER(" + UserDictionary.Words.WORD + ")");
+ }
}
private ListAdapter createAdapter() {
@@ -153,7 +194,7 @@ public class UserDictionarySettings extends ListFragment implements DialogCreata
new String[] { UserDictionary.Words.WORD, UserDictionary.Words._ID },
new int[] { android.R.id.text1, R.id.delete_button }, this);
}
-
+
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
String word = getWord(position);
@@ -235,13 +276,28 @@ public class UserDictionarySettings extends ListFragment implements DialogCreata
// The user was editing a word, so do a delete/add
deleteWord(mDialogEditingWord);
}
-
+
// Disallow duplicates
deleteWord(word);
-
+
// TODO: present UI for picking whether to add word to all locales, or current.
- UserDictionary.Words.addWord(getActivity(), word.toString(),
- 250, UserDictionary.Words.LOCALE_TYPE_ALL);
+ if (null == mLocale) {
+ // Null means insert with the default system locale.
+ UserDictionary.Words.addWord(getActivity(), word.toString(),
+ FREQUENCY_FOR_USER_DICTIONARY_ADDS, UserDictionary.Words.LOCALE_TYPE_CURRENT);
+ } else if ("".equals(mLocale)) {
+ // Empty string means insert for all languages.
+ UserDictionary.Words.addWord(getActivity(), word.toString(),
+ FREQUENCY_FOR_USER_DICTIONARY_ADDS, UserDictionary.Words.LOCALE_TYPE_ALL);
+ } else {
+ // TODO: fix the framework so that it can accept a locale when we add a word
+ // to the user dictionary instead of querying the system locale.
+ final Locale prevLocale = Locale.getDefault();
+ Locale.setDefault(Utils.createLocaleFromString(mLocale));
+ UserDictionary.Words.addWord(getActivity(), word.toString(),
+ FREQUENCY_FOR_USER_DICTIONARY_ADDS, UserDictionary.Words.LOCALE_TYPE_CURRENT);
+ Locale.setDefault(prevLocale);
+ }
if (!mCursor.requery()) {
throw new IllegalStateException("can't requery on already-closed cursor.");
}
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 18c6159..422ae90 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -36,8 +36,10 @@ import android.telephony.TelephonyManager;
import android.text.TextUtils;
import java.net.InetAddress;
+import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
+import java.util.Locale;
public class Utils {
@@ -308,4 +310,24 @@ public class Utils {
}
return addresses;
}
+
+ public static Locale createLocaleFromString(String localeStr) {
+ // TODO: is there a better way to actually construct a locale that will match?
+ // The main problem is, on top of Java specs, locale.toString() and
+ // new Locale(locale.toString()).toString() do not return equal() strings in
+ // many cases, because the constructor takes the only string as the language
+ // code. So : new Locale("en", "US").toString() => "en_US"
+ // And : new Locale("en_US").toString() => "en_us"
+ if (null == localeStr)
+ return Locale.getDefault();
+ String[] brokenDownLocale = localeStr.split("_", 3);
+ // split may not return a 0-length array.
+ if (1 == brokenDownLocale.length) {
+ return new Locale(brokenDownLocale[0]);
+ } else if (2 == brokenDownLocale.length) {
+ return new Locale(brokenDownLocale[0], brokenDownLocale[1]);
+ } else {
+ return new Locale(brokenDownLocale[0], brokenDownLocale[1], brokenDownLocale[2]);
+ }
+ }
}
diff --git a/src/com/android/settings/WallpaperTypeSettings.java b/src/com/android/settings/WallpaperTypeSettings.java
new file mode 100644
index 0000000..fa0b4e4
--- /dev/null
+++ b/src/com/android/settings/WallpaperTypeSettings.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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 com.android.settings;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.preference.Preference;
+
+import java.util.List;
+
+public class WallpaperTypeSettings extends SettingsPreferenceFragment {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ addPreferencesFromResource(R.xml.wallpaper_settings);
+
+ populateWallpaperTypes();
+ }
+
+ private void populateWallpaperTypes() {
+ // Search for activities that satisfy the ACTION_SET_WALLPAPER action
+ Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER);
+ final PackageManager pm = getPackageManager();
+ List<ResolveInfo> rList = pm.queryIntentActivities(intent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+
+ // Add Preference items for each of the matching activities
+ for (ResolveInfo info : rList) {
+ Preference pref = new Preference(getActivity());
+ Intent prefIntent = new Intent(intent);
+ prefIntent.setComponent(new ComponentName(
+ info.activityInfo.packageName, info.activityInfo.name));
+ pref.setIntent(prefIntent);
+ CharSequence label = info.loadLabel(pm);
+ if (label == null) label = info.activityInfo.packageName;
+ pref.setTitle(label);
+ getPreferenceScreen().addPreference(pref);
+ }
+ }
+}
diff --git a/src/com/android/settings/WirelessSettings.java b/src/com/android/settings/WirelessSettings.java
index 2844f3b..d09fcc5 100644
--- a/src/com/android/settings/WirelessSettings.java
+++ b/src/com/android/settings/WirelessSettings.java
@@ -16,35 +16,27 @@
package com.android.settings;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.TelephonyProperties;
-import com.android.settings.bluetooth.BluetoothEnabler;
-import com.android.settings.wifi.WifiEnabler;
-import com.android.settings.nfc.NfcEnabler;
-
import android.app.Activity;
import android.app.admin.DevicePolicyManager;
-import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.nfc.NfcAdapter;
import android.os.Bundle;
-import android.os.ServiceManager;
import android.os.SystemProperties;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.PreferenceScreen;
import android.provider.Settings;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.TelephonyProperties;
+import com.android.settings.nfc.NfcEnabler;
+
public class WirelessSettings extends SettingsPreferenceFragment {
private static final String KEY_TOGGLE_AIRPLANE = "toggle_airplane";
- private static final String KEY_TOGGLE_BLUETOOTH = "toggle_bluetooth";
- private static final String KEY_TOGGLE_WIFI = "toggle_wifi";
private static final String KEY_TOGGLE_NFC = "toggle_nfc";
- private static final String KEY_WIFI_SETTINGS = "wifi_settings";
- private static final String KEY_BT_SETTINGS = "bt_settings";
private static final String KEY_VPN_SETTINGS = "vpn_settings";
private static final String KEY_TETHER_SETTINGS = "tether_settings";
private static final String KEY_PROXY_SETTINGS = "proxy_settings";
@@ -55,9 +47,7 @@ public class WirelessSettings extends SettingsPreferenceFragment {
private AirplaneModeEnabler mAirplaneModeEnabler;
private CheckBoxPreference mAirplaneModePreference;
- private WifiEnabler mWifiEnabler;
private NfcEnabler mNfcEnabler;
- private BluetoothEnabler mBtEnabler;
/**
* Invoked on each preference click in this hierarchy, overrides
@@ -95,15 +85,10 @@ public class WirelessSettings extends SettingsPreferenceFragment {
addPreferencesFromResource(R.xml.wireless_settings);
final Activity activity = getActivity();
- CheckBoxPreference airplane = (CheckBoxPreference) findPreference(KEY_TOGGLE_AIRPLANE);
- CheckBoxPreference wifi = (CheckBoxPreference) findPreference(KEY_TOGGLE_WIFI);
- CheckBoxPreference bt = (CheckBoxPreference) findPreference(KEY_TOGGLE_BLUETOOTH);
+ mAirplaneModePreference = (CheckBoxPreference) findPreference(KEY_TOGGLE_AIRPLANE);
CheckBoxPreference nfc = (CheckBoxPreference) findPreference(KEY_TOGGLE_NFC);
- mAirplaneModeEnabler = new AirplaneModeEnabler(activity, airplane);
- mAirplaneModePreference = (CheckBoxPreference) findPreference(KEY_TOGGLE_AIRPLANE);
- mWifiEnabler = new WifiEnabler(activity, wifi);
- mBtEnabler = new BluetoothEnabler(activity, bt);
+ mAirplaneModeEnabler = new AirplaneModeEnabler(activity, mAirplaneModePreference);
mNfcEnabler = new NfcEnabler(activity, nfc);
String toggleable = Settings.System.getString(activity.getContentResolver(),
@@ -111,21 +96,12 @@ public class WirelessSettings extends SettingsPreferenceFragment {
// Manually set dependencies for Wifi when not toggleable.
if (toggleable == null || !toggleable.contains(Settings.System.RADIO_WIFI)) {
- wifi.setDependency(KEY_TOGGLE_AIRPLANE);
- findPreference(KEY_WIFI_SETTINGS).setDependency(KEY_TOGGLE_AIRPLANE);
findPreference(KEY_VPN_SETTINGS).setDependency(KEY_TOGGLE_AIRPLANE);
}
// Manually set dependencies for Bluetooth when not toggleable.
if (toggleable == null || !toggleable.contains(Settings.System.RADIO_BLUETOOTH)) {
- bt.setDependency(KEY_TOGGLE_AIRPLANE);
- findPreference(KEY_BT_SETTINGS).setDependency(KEY_TOGGLE_AIRPLANE);
- }
-
- // Remove Bluetooth Settings if Bluetooth service is not available.
- if (ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE) == null) {
- getPreferenceScreen().removePreference(bt);
- getPreferenceScreen().removePreference(findPreference(KEY_BT_SETTINGS));
+ // No bluetooth-dependent items in the list. Code kept in case one is added later.
}
// Remove NFC if its not available
@@ -191,8 +167,6 @@ public class WirelessSettings extends SettingsPreferenceFragment {
super.onResume();
mAirplaneModeEnabler.resume();
- mWifiEnabler.resume();
- mBtEnabler.resume();
mNfcEnabler.resume();
}
@@ -201,8 +175,6 @@ public class WirelessSettings extends SettingsPreferenceFragment {
super.onPause();
mAirplaneModeEnabler.pause();
- mWifiEnabler.pause();
- mBtEnabler.pause();
mNfcEnabler.pause();
}
diff --git a/src/com/android/settings/accounts/AccountPreferenceBase.java b/src/com/android/settings/accounts/AccountPreferenceBase.java
index a84bece..a0d6a7f 100644
--- a/src/com/android/settings/accounts/AccountPreferenceBase.java
+++ b/src/com/android/settings/accounts/AccountPreferenceBase.java
@@ -32,6 +32,7 @@ import android.content.Context;
import android.content.SyncAdapterType;
import android.content.SyncStatusObserver;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
@@ -113,7 +114,7 @@ class AccountPreferenceBase extends SettingsPreferenceFragment
mAccountTypeToAuthorities.put(sa.accountType, authorities);
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.d(TAG, "added authority " + sa.authority + " to accountType "
+ Log.d(TAG, "added authority " + sa.authority + " to accountType "
+ sa.accountType);
}
authorities.add(sa.authority);
@@ -136,7 +137,10 @@ class AccountPreferenceBase extends SettingsPreferenceFragment
icon = authContext.getResources().getDrawable(desc.iconId);
} catch (PackageManager.NameNotFoundException e) {
// TODO: place holder icon for missing account icons?
- Log.w(TAG, "No icon for account type " + accountType);
+ Log.w(TAG, "No icon name for account type " + accountType);
+ } catch (Resources.NotFoundException e) {
+ // TODO: place holder icon for missing account icons?
+ Log.w(TAG, "No icon resource for account type " + accountType);
}
}
return icon;
@@ -155,7 +159,9 @@ class AccountPreferenceBase extends SettingsPreferenceFragment
Context authContext = getActivity().createPackageContext(desc.packageName, 0);
label = authContext.getResources().getText(desc.labelId);
} catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "No label for account type " + ", type " + accountType);
+ Log.w(TAG, "No label name for account type " + accountType);
+ } catch (Resources.NotFoundException e) {
+ Log.w(TAG, "No label icon for account type " + accountType);
}
}
return label;
@@ -179,6 +185,8 @@ class AccountPreferenceBase extends SettingsPreferenceFragment
}
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Couldn't load preferences.xml file from " + desc.packageName);
+ } catch (Resources.NotFoundException e) {
+ Log.w(TAG, "Couldn't load preferences.xml file from " + desc.packageName);
}
}
return prefs;
diff --git a/src/com/android/settings/accounts/ChooseAccountActivity.java b/src/com/android/settings/accounts/ChooseAccountActivity.java
index 9576dee..631fe47 100644
--- a/src/com/android/settings/accounts/ChooseAccountActivity.java
+++ b/src/com/android/settings/accounts/ChooseAccountActivity.java
@@ -23,6 +23,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.SyncAdapterType;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.preference.Preference;
@@ -199,7 +200,10 @@ public class ChooseAccountActivity extends PreferenceActivity {
icon = authContext.getResources().getDrawable(desc.iconId);
} catch (PackageManager.NameNotFoundException e) {
// TODO: place holder icon for missing account icons?
- Log.w(TAG, "No icon for account type " + accountType);
+ Log.w(TAG, "No icon name for account type " + accountType);
+ } catch (Resources.NotFoundException e) {
+ // TODO: place holder icon for missing account icons?
+ Log.w(TAG, "No icon resource for account type " + accountType);
}
}
return icon;
@@ -218,7 +222,9 @@ public class ChooseAccountActivity extends PreferenceActivity {
Context authContext = createPackageContext(desc.packageName, 0);
label = authContext.getResources().getText(desc.labelId);
} catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "No label for account type " + ", type " + accountType);
+ Log.w(TAG, "No label name for account type " + accountType);
+ } catch (Resources.NotFoundException e) {
+ Log.w(TAG, "No label resource for account type " + accountType);
}
}
return label;
diff --git a/src/com/android/settings/applications/ApplicationsState.java b/src/com/android/settings/applications/ApplicationsState.java
index 11e4aae..31fd078 100644
--- a/src/com/android/settings/applications/ApplicationsState.java
+++ b/src/com/android/settings/applications/ApplicationsState.java
@@ -392,6 +392,14 @@ public class ApplicationsState {
for (int i=0; i<mApplications.size(); i++) {
final ApplicationInfo info = mApplications.get(i);
+ // Need to trim out any applications that are disabled by
+ // something different than the user.
+ if (!info.enabled && info.enabledSetting
+ != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ mApplications.remove(i);
+ i--;
+ continue;
+ }
final AppEntry entry = mEntriesMap.get(info.packageName);
if (entry != null) {
entry.info = info;
diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java
index a0a568b..caf7aef 100644
--- a/src/com/android/settings/applications/InstalledAppDetails.java
+++ b/src/com/android/settings/applications/InstalledAppDetails.java
@@ -79,7 +79,7 @@ public class InstalledAppDetails extends Fragment
implements View.OnClickListener, CompoundButton.OnCheckedChangeListener,
ApplicationsState.Callbacks {
private static final String TAG="InstalledAppDetails";
- static final boolean SUPPORT_DISABLE_APPS = false;
+ static final boolean SUPPORT_DISABLE_APPS = true;
private static final boolean localLOGV = false;
public static final String ARG_PACKAGE_NAME = "package";
@@ -829,7 +829,7 @@ public class InstalledAppDetails extends Fragment
} else {
if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
new DisableChanger(this, mAppEntry.info, mAppEntry.info.enabled ?
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER
: PackageManager.COMPONENT_ENABLED_STATE_DEFAULT).execute((Object)null);
} else {
uninstallPkg(packageName);
diff --git a/src/com/android/settings/bluetooth/AdvancedBluetoothSettings.java b/src/com/android/settings/bluetooth/AdvancedBluetoothSettings.java
new file mode 100644
index 0000000..83371cd
--- /dev/null
+++ b/src/com/android/settings/bluetooth/AdvancedBluetoothSettings.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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 com.android.settings.bluetooth;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+
+public class AdvancedBluetoothSettings extends SettingsPreferenceFragment
+ implements Preference.OnPreferenceChangeListener {
+
+ private static final String KEY_BT_DISCOVERABLE = "bt_discoverable";
+ private static final String KEY_BT_DISCOVERABLE_TIMEOUT = "bt_discoverable_timeout";
+ private static final String KEY_BT_NAME = "bt_name";
+ private static final String KEY_BT_SHOW_RECEIVED = "bt_show_received_files";
+
+ /* Private intent to show the list of received files */
+ private static final String BTOPP_ACTION_OPEN_RECEIVED_FILES =
+ "android.btopp.intent.action.OPEN_RECEIVED_FILES";
+
+ private BluetoothDiscoverableEnabler mDiscoverableEnabler;
+ private BluetoothNamePreference mNamePreference;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.bluetooth_advanced_settings);
+
+ LocalBluetoothManager localManager = LocalBluetoothManager.getInstance(getActivity());
+ if (localManager != null) {
+ LocalBluetoothAdapter localAdapter = localManager.getBluetoothAdapter();
+ mDiscoverableEnabler = new BluetoothDiscoverableEnabler(getActivity(),
+ localAdapter,
+ (CheckBoxPreference) findPreference(KEY_BT_DISCOVERABLE),
+ (ListPreference) findPreference(KEY_BT_DISCOVERABLE_TIMEOUT));
+ }
+
+ mNamePreference = (BluetoothNamePreference) findPreference(KEY_BT_NAME);
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mDiscoverableEnabler.resume();
+ mNamePreference.resume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ mNamePreference.pause();
+ mDiscoverableEnabler.pause();
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
+ if (KEY_BT_SHOW_RECEIVED.equals(preference.getKey())) {
+ Intent intent = new Intent(BTOPP_ACTION_OPEN_RECEIVED_FILES);
+ getActivity().sendBroadcast(intent);
+ return true;
+ }
+
+ return super.onPreferenceTreeClick(preferenceScreen, preference);
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ return true;
+ }
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothEnabler.java b/src/com/android/settings/bluetooth/BluetoothEnabler.java
index 79f23bb..f08e083 100644
--- a/src/com/android/settings/bluetooth/BluetoothEnabler.java
+++ b/src/com/android/settings/bluetooth/BluetoothEnabler.java
@@ -16,28 +16,27 @@
package com.android.settings.bluetooth;
-import com.android.settings.R;
-import com.android.settings.WirelessSettings;
-
import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.preference.Preference;
-import android.preference.CheckBoxPreference;
import android.provider.Settings;
+import android.widget.CompoundButton;
+import android.widget.Switch;
import android.widget.Toast;
+import com.android.settings.R;
+import com.android.settings.WirelessSettings;
+
/**
* BluetoothEnabler is a helper to manage the Bluetooth on/off checkbox
* preference. It turns on/off Bluetooth and ensures the summary of the
* preference reflects the current state.
*/
-public final class BluetoothEnabler implements Preference.OnPreferenceChangeListener {
+public final class BluetoothEnabler implements CompoundButton.OnCheckedChangeListener {
private final Context mContext;
- private final CheckBoxPreference mCheckBox;
- private final CharSequence mOriginalSummary;
+ private Switch mSwitch;
private final LocalBluetoothAdapter mLocalAdapter;
private final IntentFilter mIntentFilter;
@@ -50,17 +49,15 @@ public final class BluetoothEnabler implements Preference.OnPreferenceChangeList
}
};
- public BluetoothEnabler(Context context, CheckBoxPreference checkBox) {
+ public BluetoothEnabler(Context context, Switch switch_) {
mContext = context;
- mCheckBox = checkBox;
- mOriginalSummary = checkBox.getSummary();
- checkBox.setPersistent(false);
+ mSwitch = switch_;
LocalBluetoothManager manager = LocalBluetoothManager.getInstance(context);
if (manager == null) {
// Bluetooth is not supported
mLocalAdapter = null;
- checkBox.setEnabled(false);
+ mSwitch.setEnabled(false);
} else {
mLocalAdapter = manager.getBluetoothAdapter();
}
@@ -69,6 +66,7 @@ public final class BluetoothEnabler implements Preference.OnPreferenceChangeList
public void resume() {
if (mLocalAdapter == null) {
+ mSwitch.setEnabled(false);
return;
}
@@ -76,7 +74,7 @@ public final class BluetoothEnabler implements Preference.OnPreferenceChangeList
handleStateChanged(mLocalAdapter.getBluetoothState());
mContext.registerReceiver(mReceiver, mIntentFilter);
- mCheckBox.setOnPreferenceChangeListener(this);
+ mSwitch.setOnCheckedChangeListener(this);
}
public void pause() {
@@ -85,51 +83,57 @@ public final class BluetoothEnabler implements Preference.OnPreferenceChangeList
}
mContext.unregisterReceiver(mReceiver);
- mCheckBox.setOnPreferenceChangeListener(null);
+ mSwitch.setOnCheckedChangeListener(null);
}
- public boolean onPreferenceChange(Preference preference, Object value) {
- boolean enable = (Boolean) value;
+ public void setSwitch(Switch switch_) {
+ if (mSwitch == switch_) return;
+ mSwitch.setOnCheckedChangeListener(null);
+ mSwitch = switch_;
+ mSwitch.setOnCheckedChangeListener(this);
+
+ int bluetoothState = BluetoothAdapter.STATE_OFF;
+ if (mLocalAdapter != null) bluetoothState = mLocalAdapter.getBluetoothState();
+ boolean isOn = bluetoothState == BluetoothAdapter.STATE_ON;
+ boolean isOff = bluetoothState == BluetoothAdapter.STATE_OFF;
+ mSwitch.setChecked(isOn);
+ mSwitch.setEnabled(isOn || isOff);
+ }
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// Show toast message if Bluetooth is not allowed in airplane mode
- if (enable && !WirelessSettings
- .isRadioAllowed(mContext, Settings.System.RADIO_BLUETOOTH)) {
- Toast.makeText(mContext, R.string.wifi_in_airplane_mode,
- Toast.LENGTH_SHORT).show();
- return false;
+ if (isChecked &&
+ !WirelessSettings.isRadioAllowed(mContext, Settings.System.RADIO_BLUETOOTH)) {
+ Toast.makeText(mContext, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show();
+ // Reset switch to off
+ buttonView.setChecked(false);
}
- mLocalAdapter.setBluetoothEnabled(enable);
- mCheckBox.setEnabled(false);
-
- // Don't update UI to opposite state until we're sure
- return false;
+ if (mLocalAdapter != null) {
+ mLocalAdapter.setBluetoothEnabled(isChecked);
+ }
+ mSwitch.setEnabled(false);
}
void handleStateChanged(int state) {
switch (state) {
case BluetoothAdapter.STATE_TURNING_ON:
- mCheckBox.setSummary(R.string.wifi_starting);
- mCheckBox.setEnabled(false);
+ mSwitch.setEnabled(false);
break;
case BluetoothAdapter.STATE_ON:
- mCheckBox.setChecked(true);
- mCheckBox.setSummary(null);
- mCheckBox.setEnabled(true);
+ mSwitch.setChecked(true);
+ mSwitch.setEnabled(true);
break;
case BluetoothAdapter.STATE_TURNING_OFF:
- mCheckBox.setSummary(R.string.wifi_stopping);
- mCheckBox.setEnabled(false);
+ mSwitch.setEnabled(false);
break;
case BluetoothAdapter.STATE_OFF:
- mCheckBox.setChecked(false);
- mCheckBox.setSummary(mOriginalSummary);
- mCheckBox.setEnabled(true);
+ mSwitch.setChecked(false);
+ mSwitch.setEnabled(true);
break;
default:
- mCheckBox.setChecked(false);
- mCheckBox.setSummary(R.string.wifi_error);
- mCheckBox.setEnabled(true);
+ mSwitch.setChecked(false);
+ mSwitch.setEnabled(true);
}
}
}
diff --git a/src/com/android/settings/bluetooth/BluetoothSettings.java b/src/com/android/settings/bluetooth/BluetoothSettings.java
index 5e4e130..9c90b20 100644
--- a/src/com/android/settings/bluetooth/BluetoothSettings.java
+++ b/src/com/android/settings/bluetooth/BluetoothSettings.java
@@ -16,15 +16,20 @@
package com.android.settings.bluetooth;
+import android.app.ActionBar;
+import android.app.Activity;
+import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
-import android.content.Intent;
-import android.preference.CheckBoxPreference;
-import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
import android.util.Log;
+import android.view.Gravity;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
import android.view.View;
+import android.widget.Switch;
import com.android.settings.R;
@@ -35,77 +40,120 @@ import com.android.settings.R;
public final class BluetoothSettings extends DeviceListPreferenceFragment {
private static final String TAG = "BluetoothSettings";
- private static final String KEY_BT_CHECKBOX = "bt_checkbox";
- private static final String KEY_BT_DISCOVERABLE = "bt_discoverable";
- private static final String KEY_BT_DISCOVERABLE_TIMEOUT = "bt_discoverable_timeout";
- private static final String KEY_BT_NAME = "bt_name";
- private static final String KEY_BT_SHOW_RECEIVED = "bt_show_received_files";
+ private static final int MENU_ID_MAKE_DISCOVERABLE = Menu.FIRST;
+ private static final int MENU_ID_SCAN = Menu.FIRST + 1;
+ private static final int MENU_ID_ADVANCED = Menu.FIRST + 2;
- private BluetoothEnabler mEnabler;
- private BluetoothDiscoverableEnabler mDiscoverableEnabler;
- private BluetoothNamePreference mNamePreference;
-
- /* Private intent to show the list of received files */
- private static final String BTOPP_ACTION_OPEN_RECEIVED_FILES =
- "android.btopp.intent.action.OPEN_RECEIVED_FILES";
+ private BluetoothEnabler mBluetoothEnabler;
/** Initialize the filter to show bonded devices only. */
- public BluetoothSettings() {
- super(BluetoothDeviceFilter.BONDED_DEVICE_FILTER);
- }
+ //public BluetoothSettings() {
+ // super(BluetoothDeviceFilter.BONDED_DEVICE_FILTER);
+ //}
@Override
void addPreferencesForActivity() {
addPreferencesFromResource(R.xml.bluetooth_settings);
- mEnabler = new BluetoothEnabler(getActivity(),
- (CheckBoxPreference) findPreference(KEY_BT_CHECKBOX));
+ Activity activity = getActivity();
+
+ Switch actionBarSwitch = new Switch(activity);
+
+ if (activity instanceof PreferenceActivity) {
+ PreferenceActivity preferenceActivity = (PreferenceActivity) activity;
+ if (preferenceActivity.onIsHidingHeaders() || !preferenceActivity.onIsMultiPane()) {
+ final int padding = activity.getResources().getDimensionPixelSize(
+ R.dimen.action_bar_switch_padding);
+ actionBarSwitch.setPadding(0, 0, padding, 0);
+ activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
+ ActionBar.DISPLAY_SHOW_CUSTOM);
+ activity.getActionBar().setCustomView(actionBarSwitch, new ActionBar.LayoutParams(
+ ActionBar.LayoutParams.WRAP_CONTENT,
+ ActionBar.LayoutParams.WRAP_CONTENT,
+ Gravity.CENTER_VERTICAL | Gravity.RIGHT));
+ }
+ }
+
+ mBluetoothEnabler = new BluetoothEnabler(activity, actionBarSwitch);
- mDiscoverableEnabler = new BluetoothDiscoverableEnabler(getActivity(),
- mLocalAdapter,
- (CheckBoxPreference) findPreference(KEY_BT_DISCOVERABLE),
- (ListPreference) findPreference(KEY_BT_DISCOVERABLE_TIMEOUT));
+ if (mLocalAdapter != null && mLocalAdapter.isEnabled()) {
+ activity.getActionBar().setSubtitle(mLocalAdapter.getName());
+ }
+
+ // TODO activity.setTheme(android.R.style.Theme_Holo_SplitActionBarWhenNarrow);
- mNamePreference = (BluetoothNamePreference) findPreference(KEY_BT_NAME);
+ setHasOptionsMenu(true);
}
@Override
public void onResume() {
super.onResume();
- // Repopulate (which isn't too bad since it's cached in the settings
- // bluetooth manager)
- addDevices();
+ mBluetoothEnabler.resume();
- mEnabler.resume();
- mDiscoverableEnabler.resume();
- mNamePreference.resume();
+ updateContent(mLocalAdapter.getBluetoothState());
}
@Override
public void onPause() {
super.onPause();
- mNamePreference.pause();
- mDiscoverableEnabler.pause();
- mEnabler.pause();
+ mBluetoothEnabler.pause();
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ boolean bluetoothIsEnabled = mLocalAdapter.getBluetoothState() == BluetoothAdapter.STATE_ON;
+ menu.add(Menu.NONE, MENU_ID_MAKE_DISCOVERABLE, 0, R.string.bluetooth_visibility)
+ .setEnabled(bluetoothIsEnabled);
+ menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.bluetooth_preference_find_nearby_title)
+ .setIcon(R.drawable.ic_menu_scan_network).setEnabled(bluetoothIsEnabled);
+ menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.bluetooth_menu_advanced)
+ .setIcon(android.R.drawable.ic_menu_manage);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case MENU_ID_MAKE_DISCOVERABLE:
+ // TODO
+// if (mLocalAdapter.getBluetoothState() == BluetoothAdapter.STATE_ON) {
+// onAddNetworkPressed();
+// }
+ return true;
+ case MENU_ID_SCAN:
+ if (mLocalAdapter.getBluetoothState() == BluetoothAdapter.STATE_ON) {
+ mLocalAdapter.startScanning(true);
+ }
+ return true;
+ case MENU_ID_ADVANCED:
+ if (getActivity() instanceof PreferenceActivity) {
+ ((PreferenceActivity) getActivity()).startPreferencePanel(
+ AdvancedBluetoothSettings.class.getCanonicalName(),
+ null,
+ R.string.bluetooth_advanced_titlebar, null,
+ this, 0);
+ } else {
+ startFragment(this, AdvancedBluetoothSettings.class.getCanonicalName(), -1, null);
+ }
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
}
private final View.OnClickListener mListener = new View.OnClickListener() {
public void onClick(View v) {
// User clicked on advanced options icon for a device in the list
if (v.getTag() instanceof CachedBluetoothDevice) {
- CachedBluetoothDevice
- device = (CachedBluetoothDevice) v.getTag();
+ CachedBluetoothDevice device = (CachedBluetoothDevice) v.getTag();
Preference pref = new Preference(getActivity());
pref.setTitle(device.getName());
pref.setFragment(DeviceProfilesSettings.class.getName());
pref.getExtras().putParcelable(DeviceProfilesSettings.EXTRA_DEVICE,
device.getDevice());
- ((PreferenceActivity) getActivity())
- .onPreferenceStartFragment(BluetoothSettings.this,
- pref);
+ ((PreferenceActivity) getActivity()).onPreferenceStartFragment(
+ BluetoothSettings.this, pref);
} else {
Log.w(TAG, "onClick() called for other View: " + v);
}
@@ -113,19 +161,54 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment {
};
@Override
- public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
- Preference preference) {
- if (KEY_BT_SHOW_RECEIVED.equals(preference.getKey())) {
- Intent intent = new Intent(BTOPP_ACTION_OPEN_RECEIVED_FILES);
- getActivity().sendBroadcast(intent);
- return true;
+ void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
+ mLocalAdapter.stopScanning();
+ super.onDevicePreferenceClick(btPreference);
+ }
+
+ @Override
+ public void onBluetoothStateChanged(int bluetoothState) {
+ super.onBluetoothStateChanged(bluetoothState);
+ updateContent(bluetoothState);
+ }
+
+ private void updateContent(int bluetoothState) {
+ final PreferenceScreen preferenceScreen = getPreferenceScreen();
+ getActivity().invalidateOptionsMenu();
+ int messageId = 0;
+
+ switch (bluetoothState) {
+ case BluetoothAdapter.STATE_ON:
+ preferenceScreen.removeAll();
+ // Repopulate (which isn't too bad since it's cached in the settings bluetooth manager)
+ addDevices();
+ mLocalAdapter.startScanning(false);
+ return;
+
+ case BluetoothAdapter.STATE_TURNING_OFF:
+ int preferenceCount = preferenceScreen.getPreferenceCount();
+ for (int i = 0; i < preferenceCount; i++) {
+ preferenceScreen.getPreference(i).setEnabled(false);
+ }
+ return;
+
+ case BluetoothAdapter.STATE_OFF:
+ messageId = R.string.bluetooth_empty_list_bluetooth_off;
+ break;
+
+ case BluetoothAdapter.STATE_TURNING_ON:
+ messageId = R.string.bluetooth_turning_on;
+ break;
}
- return super.onPreferenceTreeClick(preferenceScreen, preference);
+ removeAllDevices();
+ // TODO: from xml, add top padding. Same as in wifi
+ Preference emptyListPreference = new Preference(getActivity());
+ emptyListPreference.setTitle(messageId);
+ preferenceScreen.addPreference(emptyListPreference);
}
- public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice,
- int bondState) {
+ public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
if (bondState == BluetoothDevice.BOND_BONDED) {
// add to "Paired devices" list after remote-initiated pairing
if (mDevicePreferenceMap.get(cachedDevice) == null) {
diff --git a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java
index a978e23..409edb9 100644
--- a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java
+++ b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java
@@ -21,6 +21,7 @@ import android.bluetooth.BluetoothDevice;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceCategory;
+import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.util.Log;
@@ -36,7 +37,6 @@ import java.util.WeakHashMap;
*
* @see BluetoothSettings
* @see DevicePickerFragment
- * @see BluetoothFindNearby
*/
public abstract class DeviceListPreferenceFragment extends
SettingsPreferenceFragment implements BluetoothCallback {
@@ -53,7 +53,7 @@ public abstract class DeviceListPreferenceFragment extends
LocalBluetoothAdapter mLocalAdapter;
LocalBluetoothManager mLocalManager;
- private PreferenceCategory mDeviceList;
+ private PreferenceGroup mDeviceListGroup;
final WeakHashMap<CachedBluetoothDevice, BluetoothDevicePreference> mDevicePreferenceMap =
new WeakHashMap<CachedBluetoothDevice, BluetoothDevicePreference>();
@@ -83,8 +83,13 @@ public abstract class DeviceListPreferenceFragment extends
addPreferencesForActivity();
- mDeviceList = (PreferenceCategory) findPreference(KEY_BT_DEVICE_LIST);
- if (mDeviceList == null) {
+ mDeviceListGroup = (PreferenceCategory) findPreference(KEY_BT_DEVICE_LIST);
+ if (mDeviceListGroup == null) {
+ // If null, device preferences are added directly to the root of the preference screen
+ mDeviceListGroup = getPreferenceScreen();
+ mDeviceListGroup.setOrderingAsAdded(false);
+ }
+ if (mDeviceListGroup == null) {
Log.e(TAG, "Could not find device list preference object!");
}
}
@@ -105,13 +110,15 @@ public abstract class DeviceListPreferenceFragment extends
@Override
public void onPause() {
super.onPause();
-
- mLocalAdapter.stopScanning();
+ removeAllDevices();
mLocalManager.setForegroundActivity(null);
mLocalManager.getEventManager().unregisterCallback(this);
+ }
+ void removeAllDevices() {
+ mLocalAdapter.stopScanning();
mDevicePreferenceMap.clear();
- mDeviceList.removeAll();
+ mDeviceListGroup.removeAll();
}
void addDevices() {
@@ -132,7 +139,7 @@ public abstract class DeviceListPreferenceFragment extends
}
if (preference instanceof BluetoothDevicePreference) {
- BluetoothDevicePreference btPreference = (BluetoothDevicePreference)preference;
+ BluetoothDevicePreference btPreference = (BluetoothDevicePreference) preference;
CachedBluetoothDevice device = btPreference.getCachedDevice();
mSelectedDevice = device.getDevice();
onDevicePreferenceClick(btPreference);
@@ -152,6 +159,9 @@ public abstract class DeviceListPreferenceFragment extends
return;
}
+ // No update while list shows state message
+ if (mLocalAdapter.getBluetoothState() != BluetoothAdapter.STATE_ON) return;
+
if (mFilter.matches(cachedDevice.getDevice())) {
createDevicePreference(cachedDevice);
}
@@ -162,7 +172,7 @@ public abstract class DeviceListPreferenceFragment extends
getActivity(), cachedDevice);
initDevicePreference(preference);
- mDeviceList.addPreference(preference);
+ mDeviceListGroup.addPreference(preference);
mDevicePreferenceMap.put(cachedDevice, preference);
}
@@ -170,13 +180,14 @@ public abstract class DeviceListPreferenceFragment extends
* Overridden in {@link BluetoothSettings} to add a listener.
* @param preference the newly added preference
*/
- void initDevicePreference(BluetoothDevicePreference preference) { }
+ void initDevicePreference(BluetoothDevicePreference preference) {
+ // Does nothing by default
+ }
public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
- BluetoothDevicePreference preference = mDevicePreferenceMap.remove(
- cachedDevice);
+ BluetoothDevicePreference preference = mDevicePreferenceMap.remove(cachedDevice);
if (preference != null) {
- mDeviceList.removePreference(preference);
+ mDeviceListGroup.removePreference(preference);
}
}
@@ -185,9 +196,10 @@ public abstract class DeviceListPreferenceFragment extends
}
private void updateProgressUi(boolean start) {
- if (mDeviceList instanceof ProgressCategory) {
- ((ProgressCategory) mDeviceList).setProgress(start);
+ if (mDeviceListGroup instanceof ProgressCategory) {
+ ((ProgressCategory) mDeviceListGroup).setProgress(start);
}
+ // else TODO Add a spinner at the end of the list to show in progress state
}
public void onBluetoothStateChanged(int bluetoothState) {
diff --git a/src/com/android/settings/bluetooth/DevicePickerFragment.java b/src/com/android/settings/bluetooth/DevicePickerFragment.java
index 126df02..3aeb7e2 100644
--- a/src/com/android/settings/bluetooth/DevicePickerFragment.java
+++ b/src/com/android/settings/bluetooth/DevicePickerFragment.java
@@ -89,7 +89,7 @@ public final class DevicePickerFragment extends DeviceListPreferenceFragment {
super.onBluetoothStateChanged(bluetoothState);
if (bluetoothState == BluetoothAdapter.STATE_ON) {
- mLocalAdapter.startScanning(false);
+ mLocalAdapter.startScanning(false);
}
}
diff --git a/src/com/android/settings/bluetooth/DeviceProfilesSettings.java b/src/com/android/settings/bluetooth/DeviceProfilesSettings.java
index 9db4baf..ecb7112 100644
--- a/src/com/android/settings/bluetooth/DeviceProfilesSettings.java
+++ b/src/com/android/settings/bluetooth/DeviceProfilesSettings.java
@@ -50,7 +50,7 @@ public final class DeviceProfilesSettings extends SettingsPreferenceFragment
private static final String KEY_RENAME_DEVICE = "rename_device";
private static final String KEY_PROFILE_CONTAINER = "profile_container";
private static final String KEY_UNPAIR = "unpair";
- private static final String KEY_ALLOW_INCOMING = "allow_incoming";
+ //private static final String KEY_ALLOW_INCOMING = "allow_incoming";
public static final String EXTRA_DEVICE = "device";
@@ -355,6 +355,7 @@ public final class DeviceProfilesSettings extends SettingsPreferenceFragment
mCachedDevice.unpair();
}
+ /*
private void setIncomingFileTransfersAllowed(boolean allow) {
// TODO: make an IPC call into BluetoothOpp to update
Log.d(TAG, "Set allow incoming = " + allow);
@@ -364,6 +365,7 @@ public final class DeviceProfilesSettings extends SettingsPreferenceFragment
// TODO: get this value from BluetoothOpp ???
return true;
}
+ */
private boolean getAutoConnect(LocalBluetoothProfile prof) {
return prof.isPreferred(mCachedDevice.getDevice());
diff --git a/src/com/android/settings/deviceinfo/PercentageBarChart.java b/src/com/android/settings/deviceinfo/PercentageBarChart.java
index 0c71c12..95973c4 100644
--- a/src/com/android/settings/deviceinfo/PercentageBarChart.java
+++ b/src/com/android/settings/deviceinfo/PercentageBarChart.java
@@ -71,20 +71,21 @@ public class PercentageBarChart extends View {
final int width = right - left;
- int lastX = left;
+ float lastX = left;
if (mEntries != null) {
for (final Entry e : mEntries) {
- final int entryWidth;
- if (e.percentage == 0f) {
- entryWidth = 0;
+ final float entryWidth;
+ if (e.percentage == 0.0f) {
+ entryWidth = 0.0f;
} else {
- entryWidth = Math.max(mMinTickWidth, (int) (width * e.percentage));
+ entryWidth = Math.max(mMinTickWidth, width * e.percentage);
}
- final int nextX = lastX + entryWidth;
- if (nextX >= right) {
- break;
+ final float nextX = lastX + entryWidth;
+ if (nextX > right) {
+ canvas.drawRect(lastX, top, right, bottom, e.paint);
+ return;
}
canvas.drawRect(lastX, top, nextX, bottom, e.paint);
@@ -92,7 +93,7 @@ public class PercentageBarChart extends View {
}
}
- canvas.drawRect(lastX, top, lastX + width, bottom, mEmptyPaint);
+ canvas.drawRect(lastX, top, right, bottom, mEmptyPaint);
}
/**
diff --git a/src/com/android/settings/deviceinfo/UsageBarPreference.java b/src/com/android/settings/deviceinfo/UsageBarPreference.java
index e9909f1..5aeaef5 100644
--- a/src/com/android/settings/deviceinfo/UsageBarPreference.java
+++ b/src/com/android/settings/deviceinfo/UsageBarPreference.java
@@ -36,17 +36,17 @@ public class UsageBarPreference extends Preference {
public UsageBarPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- setWidgetLayoutResource(R.layout.preference_memoryusage);
+ setLayoutResource(R.layout.preference_memoryusage);
}
public UsageBarPreference(Context context) {
super(context);
- setWidgetLayoutResource(R.layout.preference_memoryusage);
+ setLayoutResource(R.layout.preference_memoryusage);
}
public UsageBarPreference(Context context, AttributeSet attrs) {
super(context, attrs);
- setWidgetLayoutResource(R.layout.preference_memoryusage);
+ setLayoutResource(R.layout.preference_memoryusage);
}
public void addEntry(float percentage, int color) {
diff --git a/src/com/android/settings/fuelgauge/BatteryHistoryChart.java b/src/com/android/settings/fuelgauge/BatteryHistoryChart.java
index 97ebf43..13a962d 100644
--- a/src/com/android/settings/fuelgauge/BatteryHistoryChart.java
+++ b/src/com/android/settings/fuelgauge/BatteryHistoryChart.java
@@ -368,9 +368,9 @@ public class BatteryHistoryChart extends View {
}
if (rec.batteryLevel != lastLevel || pos == 1) {
lastLevel = rec.batteryLevel;
- lastInteresting = pos;
- mHistEnd = rec.time;
}
+ lastInteresting = pos;
+ mHistEnd = rec.time;
aggrStates |= rec.states;
}
}
@@ -438,7 +438,13 @@ public class BatteryHistoryChart extends View {
2, getResources().getDisplayMetrics());
if (h > (textHeight*6)) {
mLargeMode = true;
- mLineWidth = textHeight/2;
+ if (h > (textHeight*15)) {
+ // Plenty of room for the chart.
+ mLineWidth = textHeight/2;
+ } else {
+ // Compress lines to make more room for chart.
+ mLineWidth = textHeight/3;
+ }
mLevelTop = textHeight + mLineWidth;
mScreenOnPaint.setARGB(255, 32, 64, 255);
mGpsOnPaint.setARGB(255, 32, 64, 255);
@@ -472,7 +478,8 @@ public class BatteryHistoryChart extends View {
mWifiRunningOffset = mWakeLockOffset + barOffset;
mGpsOnOffset = mWifiRunningOffset + (mHaveWifi ? barOffset : 0);
mPhoneSignalOffset = mGpsOnOffset + (mHaveGps ? barOffset : 0);
- mLevelOffset = mPhoneSignalOffset + (mHavePhoneSignal ? barOffset : 0) + mLineWidth;
+ mLevelOffset = mPhoneSignalOffset + (mHavePhoneSignal ? barOffset : 0)
+ + ((mLineWidth*3)/2);
if (mHavePhoneSignal) {
mPhoneSignalChart.init(w);
}
@@ -670,8 +677,8 @@ public class BatteryHistoryChart extends View {
if (!mBatCriticalPath.isEmpty()) {
canvas.drawPath(mBatCriticalPath, mBatteryCriticalPaint);
}
- int top = height - (mHavePhoneSignal ? mPhoneSignalOffset - (mLineWidth/2) : 0);
if (mHavePhoneSignal) {
+ int top = height-mPhoneSignalOffset - (mLineWidth/2);
mPhoneSignalChart.draw(canvas, top, mLineWidth);
}
if (!mScreenOnPath.isEmpty()) {
diff --git a/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java b/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java
index a4808b0..c72f0ba 100644
--- a/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java
+++ b/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java
@@ -18,9 +18,11 @@ package com.android.settings.inputmethod;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.UserDictionarySettings;
import com.android.settings.Utils;
import com.android.settings.VoiceInputOutputSettings;
+import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Bundle;
@@ -30,12 +32,17 @@ import android.preference.PreferenceScreen;
import android.provider.Settings;
import android.view.inputmethod.InputMethodManager;
+import java.util.Set;
+
public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
implements Preference.OnPreferenceChangeListener{
private static final String KEY_PHONE_LANGUAGE = "phone_language";
private static final String KEY_CURRENT_INPUT_METHOD = "current_input_method";
private static final String KEY_INPUT_METHOD_SELECTOR = "input_method_selector";
+ private static final String KEY_LANGUAGE_SETTINGS_CATEGORY = "language_settings_category";
+ private static final String KEY_USER_DICTIONARY_SETTINGS = "key_user_dictionary_settings";
+
private int mDefaultInputMethodSelectorVisibility = 0;
private ListPreference mShowInputMethodSelectorPref;
@@ -77,6 +84,28 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
}
}
+ private void updateUserDictionaryPreference(Preference userDictionaryPreference) {
+ final Activity activity = getActivity();
+ final Set<String> localeList = UserDictionaryList.getUserDictionaryLocalesList(activity);
+ if (localeList.size() <= 1) {
+ userDictionaryPreference.setTitle(R.string.user_dict_single_settings_title);
+ userDictionaryPreference.setFragment(UserDictionarySettings.class.getName());
+ // If the size of localeList is 0, we don't set the locale parameter in the
+ // extras. This will be interpreted by the UserDictionarySettings class as
+ // meaning "the current locale".
+ // Note that with the current code for UserDictionaryList#getUserDictionaryLocalesList()
+ // the locale list always has at least one element, since it always includes the current
+ // locale explicitly. @see UserDictionaryList.getUserDictionaryLocalesList().
+ if (localeList.size() == 1) {
+ final String locale = (String)localeList.toArray()[0];
+ userDictionaryPreference.getExtras().putString("locale", locale);
+ }
+ } else {
+ userDictionaryPreference.setTitle(R.string.user_dict_multiple_settings_title);
+ userDictionaryPreference.setFragment(UserDictionaryList.class.getName());
+ }
+ }
+
@Override
public void onResume() {
super.onResume();
@@ -89,6 +118,7 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
}
}
+ updateUserDictionaryPreference(findPreference(KEY_USER_DICTIONARY_SETTINGS));
mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this);
}
diff --git a/src/com/android/settings/inputmethod/InputMethodAndSubtypeEnabler.java b/src/com/android/settings/inputmethod/InputMethodAndSubtypeEnabler.java
index 6e1d4d1..43d54a2 100644
--- a/src/com/android/settings/inputmethod/InputMethodAndSubtypeEnabler.java
+++ b/src/com/android/settings/inputmethod/InputMethodAndSubtypeEnabler.java
@@ -188,7 +188,8 @@ public class InputMethodAndSubtypeEnabler extends SettingsPreferenceFragment {
private PreferenceScreen createPreferenceHierarchy() {
// Root
- PreferenceScreen root = getPreferenceManager().createPreferenceScreen(getActivity());
+ final PreferenceScreen root = getPreferenceManager().createPreferenceScreen(getActivity());
+ final Context context = getActivity();
int N = (mInputMethodProperties == null ? 0 : mInputMethodProperties.size());
@@ -202,7 +203,7 @@ public class InputMethodAndSubtypeEnabler extends SettingsPreferenceFragment {
if (!TextUtils.isEmpty(mInputMethodId) && !mInputMethodId.equals(imiId)) {
continue;
}
- PreferenceCategory keyboardSettingsCategory = new PreferenceCategory(getActivity());
+ PreferenceCategory keyboardSettingsCategory = new PreferenceCategory(context);
root.addPreference(keyboardSettingsCategory);
PackageManager pm = getPackageManager();
CharSequence label = imi.loadLabel(pm);
@@ -210,31 +211,22 @@ public class InputMethodAndSubtypeEnabler extends SettingsPreferenceFragment {
keyboardSettingsCategory.setTitle(label);
keyboardSettingsCategory.setKey(imiId);
// TODO: Use toggle Preference if images are ready.
- CheckBoxPreference autoCB = new CheckBoxPreference(getActivity());
+ CheckBoxPreference autoCB = new CheckBoxPreference(context);
autoCB.setTitle(R.string.use_system_language_to_select_input_method_subtypes);
mSubtypeAutoSelectionCBMap.put(imiId, autoCB);
keyboardSettingsCategory.addPreference(autoCB);
- PreferenceCategory activeInputMethodsCategory = new PreferenceCategory(getActivity());
+ PreferenceCategory activeInputMethodsCategory = new PreferenceCategory(context);
activeInputMethodsCategory.setTitle(R.string.active_input_method_subtypes);
root.addPreference(activeInputMethodsCategory);
ArrayList<Preference> subtypePreferences = new ArrayList<Preference>();
if (subtypeCount > 0) {
for (int j = 0; j < subtypeCount; ++j) {
- InputMethodSubtype subtype = imi.getSubtypeAt(j);
- CharSequence subtypeLabel;
- int nameResId = subtype.getNameResId();
- if (nameResId != 0) {
- subtypeLabel = pm.getText(imi.getPackageName(), nameResId,
- imi.getServiceInfo().applicationInfo);
- } else {
- String mode = subtype.getMode();
- CharSequence language = subtype.getLocale();
- subtypeLabel = (mode == null ? "" : mode) + ","
- + (language == null ? "" : language);
- }
- CheckBoxPreference chkbxPref = new CheckBoxPreference(getActivity());
+ final InputMethodSubtype subtype = imi.getSubtypeAt(j);
+ final CharSequence subtypeLabel = subtype.getDisplayName(context,
+ imi.getPackageName(), imi.getServiceInfo().applicationInfo);
+ final CheckBoxPreference chkbxPref = new CheckBoxPreference(context);
chkbxPref.setKey(imiId + subtype.hashCode());
chkbxPref.setTitle(subtypeLabel);
activeInputMethodsCategory.addPreference(chkbxPref);
diff --git a/src/com/android/settings/inputmethod/InputMethodConfig.java b/src/com/android/settings/inputmethod/InputMethodConfig.java
index 2cfe35d..393292e 100644
--- a/src/com/android/settings/inputmethod/InputMethodConfig.java
+++ b/src/com/android/settings/inputmethod/InputMethodConfig.java
@@ -266,7 +266,6 @@ public class InputMethodConfig extends SettingsPreferenceFragment {
private void updateActiveInputMethodsSummary() {
final InputMethodManager imm =
(InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- final PackageManager pm = getPackageManager();
for (InputMethodInfo imi: mActiveInputMethodsPrefMap.keySet()) {
Preference pref = mActiveInputMethodsPrefMap.get(imi);
List<InputMethodSubtype> subtypes = imm.getEnabledInputMethodSubtypeList(imi, true);
@@ -276,8 +275,9 @@ public class InputMethodConfig extends SettingsPreferenceFragment {
if (subtypeAdded) {
summary.append(", ");
}
- summary.append(pm.getText(imi.getPackageName(), subtype.getNameResId(),
- imi.getServiceInfo().applicationInfo));
+ final CharSequence subtypeLabel = subtype.getDisplayName(getActivity(),
+ imi.getPackageName(), imi.getServiceInfo().applicationInfo);
+ summary.append(subtypeLabel);
subtypeAdded = true;
}
pref.setSummary(summary.toString());
diff --git a/src/com/android/settings/inputmethod/InputMethodDialogActivity.java b/src/com/android/settings/inputmethod/InputMethodDialogActivity.java
new file mode 100644
index 0000000..4efbf05
--- /dev/null
+++ b/src/com/android/settings/inputmethod/InputMethodDialogActivity.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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 com.android.settings.inputmethod;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.inputmethod.InputMethodManager;
+
+public class InputMethodDialogActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).
+ showInputMethodPicker();
+ finish();
+ }
+}
diff --git a/src/com/android/settings/inputmethod/UserDictionaryList.java b/src/com/android/settings/inputmethod/UserDictionaryList.java
new file mode 100644
index 0000000..5db2841
--- /dev/null
+++ b/src/com/android/settings/inputmethod/UserDictionaryList.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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 com.android.settings.inputmethod;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.UserDictionarySettings;
+import com.android.settings.Utils;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceGroup;
+import android.provider.UserDictionary;
+
+import java.util.Locale;
+import java.util.Set;
+import java.util.TreeSet;
+
+public class UserDictionaryList extends SettingsPreferenceFragment {
+
+ private static final String USER_DICTIONARY_SETTINGS_INTENT_ACTION =
+ "android.settings.USER_DICTIONARY_SETTINGS";
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getActivity()));
+ }
+
+ static Set<String> getUserDictionaryLocalesList(Activity activity) {
+ final Cursor cursor = activity.managedQuery(UserDictionary.Words.CONTENT_URI,
+ new String[] { UserDictionary.Words.LOCALE },
+ null, null, null);
+ final Set<String> localeList = new TreeSet<String>();
+ if (cursor.moveToFirst()) {
+ final int columnIndex = cursor.getColumnIndex(UserDictionary.Words.LOCALE);
+ do {
+ String locale = cursor.getString(columnIndex);
+ localeList.add(null != locale ? locale : "");
+ } while (cursor.moveToNext());
+ }
+ localeList.add(Locale.getDefault().toString());
+ return localeList;
+ }
+
+ /**
+ * Creates the entries that allow the user to go into the user dictionary for each locale.
+ * @param userDictGroup The group to put the settings in.
+ */
+ protected void createUserDictSettings(PreferenceGroup userDictGroup) {
+ final Activity activity = getActivity();
+ userDictGroup.removeAll();
+ final Set<String> localeList = UserDictionaryList.getUserDictionaryLocalesList(activity);
+
+ if (localeList.isEmpty()) {
+ userDictGroup.addPreference(createUserDictionaryPreference(null, activity));
+ } else {
+ for (String locale : localeList) {
+ userDictGroup.addPreference(createUserDictionaryPreference(locale, activity));
+ }
+ }
+ }
+
+ /**
+ * Create a single User Dictionary Preference object, with its parameters set.
+ * @param locale The locale for which this user dictionary is for.
+ * @return The corresponding preference.
+ */
+ protected Preference createUserDictionaryPreference(String locale, Activity activity) {
+ final Preference newPref = new Preference(getActivity());
+ final Intent intent = new Intent(USER_DICTIONARY_SETTINGS_INTENT_ACTION);
+ if (null == locale) {
+ newPref.setTitle(Locale.getDefault().getDisplayName());
+ } else {
+ if ("".equals(locale))
+ newPref.setTitle(getString(R.string.user_dict_settings_all_languages));
+ else
+ newPref.setTitle(Utils.createLocaleFromString(locale).getDisplayName());
+ intent.putExtra("locale", locale);
+ newPref.getExtras().putString("locale", locale);
+ }
+ newPref.setIntent(intent);
+ newPref.setFragment(UserDictionarySettings.class.getName());
+ return newPref;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ createUserDictSettings(getPreferenceScreen());
+ }
+}
diff --git a/src/com/android/settings/net/NetworkPolicyEditor.java b/src/com/android/settings/net/NetworkPolicyEditor.java
new file mode 100644
index 0000000..c50a490
--- /dev/null
+++ b/src/com/android/settings/net/NetworkPolicyEditor.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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 com.android.settings.net;
+
+import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER;
+import static android.net.NetworkTemplate.MATCH_MOBILE_4G;
+import static android.net.NetworkTemplate.MATCH_MOBILE_ALL;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.net.INetworkPolicyManager;
+import android.net.NetworkPolicy;
+import android.net.NetworkTemplate;
+import android.os.AsyncTask;
+import android.os.RemoteException;
+
+import com.android.internal.util.Objects;
+import com.google.android.collect.Lists;
+
+import java.util.ArrayList;
+
+/**
+ * Utility class to modify list of {@link NetworkPolicy}. Specifically knows
+ * about which policies can coexist.
+ */
+public class NetworkPolicyEditor {
+ // TODO: be more robust when missing policies from service
+
+ private INetworkPolicyManager mPolicyService;
+ private ArrayList<NetworkPolicy> mPolicies = Lists.newArrayList();
+
+ public NetworkPolicyEditor(INetworkPolicyManager policyService) {
+ mPolicyService = checkNotNull(policyService);
+ }
+
+ public void read() {
+ try {
+ final NetworkPolicy[] policies = mPolicyService.getNetworkPolicies();
+ mPolicies.clear();
+ for (NetworkPolicy policy : policies) {
+ mPolicies.add(policy);
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException("problem reading policies", e);
+ }
+ }
+
+ public void writeAsync() {
+ // TODO: consider making more robust by passing through service
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ write();
+ return null;
+ }
+ }.execute();
+ }
+
+ public void write() {
+ try {
+ final NetworkPolicy[] policies = mPolicies.toArray(new NetworkPolicy[mPolicies.size()]);
+ mPolicyService.setNetworkPolicies(policies);
+ } catch (RemoteException e) {
+ throw new RuntimeException("problem reading policies", e);
+ }
+ }
+
+ public NetworkPolicy getPolicy(NetworkTemplate template) {
+ for (NetworkPolicy policy : mPolicies) {
+ if (policy.template.equals(template)) {
+ return policy;
+ }
+ }
+ return null;
+ }
+
+ public void setPolicyCycleDay(NetworkTemplate template, int cycleDay) {
+ getPolicy(template).cycleDay = cycleDay;
+ writeAsync();
+ }
+
+ public void setPolicyWarningBytes(NetworkTemplate template, long warningBytes) {
+ getPolicy(template).warningBytes = warningBytes;
+ writeAsync();
+ }
+
+ public void setPolicyLimitBytes(NetworkTemplate template, long limitBytes) {
+ getPolicy(template).limitBytes = limitBytes;
+ writeAsync();
+ }
+
+ public boolean isMobilePolicySplit(String subscriberId) {
+ boolean has3g = false;
+ boolean has4g = false;
+ for (NetworkPolicy policy : mPolicies) {
+ final NetworkTemplate template = policy.template;
+ if (Objects.equal(subscriberId, template.getSubscriberId())) {
+ switch (template.getMatchRule()) {
+ case MATCH_MOBILE_3G_LOWER:
+ has3g = true;
+ break;
+ case MATCH_MOBILE_4G:
+ has4g = true;
+ break;
+ }
+ }
+ }
+ return has3g && has4g;
+ }
+
+ public void setMobilePolicySplit(String subscriberId, boolean split) {
+ final boolean beforeSplit = isMobilePolicySplit(subscriberId);
+
+ final NetworkTemplate template3g = new NetworkTemplate(MATCH_MOBILE_3G_LOWER, subscriberId);
+ final NetworkTemplate template4g = new NetworkTemplate(MATCH_MOBILE_4G, subscriberId);
+ final NetworkTemplate templateAll = new NetworkTemplate(MATCH_MOBILE_ALL, subscriberId);
+
+ if (split == beforeSplit) {
+ // already in requested state; skip
+ return;
+
+ } else if (beforeSplit && !split) {
+ // combine, picking most restrictive policy
+ final NetworkPolicy policy3g = getPolicy(template3g);
+ final NetworkPolicy policy4g = getPolicy(template4g);
+
+ final NetworkPolicy restrictive = policy3g.compareTo(policy4g) < 0 ? policy3g
+ : policy4g;
+ mPolicies.remove(policy3g);
+ mPolicies.remove(policy4g);
+ mPolicies.add(
+ new NetworkPolicy(templateAll, restrictive.cycleDay, restrictive.warningBytes,
+ restrictive.limitBytes));
+ writeAsync();
+
+ } else if (!beforeSplit && split) {
+ // duplicate existing policy into two rules
+ final NetworkPolicy policyAll = getPolicy(templateAll);
+ mPolicies.remove(policyAll);
+ mPolicies.add(new NetworkPolicy(
+ template3g, policyAll.cycleDay, policyAll.warningBytes, policyAll.limitBytes));
+ mPolicies.add(new NetworkPolicy(
+ template4g, policyAll.cycleDay, policyAll.warningBytes, policyAll.limitBytes));
+ writeAsync();
+
+ }
+ }
+
+}
diff --git a/src/com/android/settings/vpn/VpnSettings.java b/src/com/android/settings/vpn/VpnSettings.java
index 5d75b6a..9b96761 100644
--- a/src/com/android/settings/vpn/VpnSettings.java
+++ b/src/com/android/settings/vpn/VpnSettings.java
@@ -55,6 +55,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
+import java.nio.charset.Charsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -698,7 +699,7 @@ public class VpnSettings extends SettingsPreferenceFragment
}
private boolean isKeyStoreUnlocked() {
- return mKeyStore.test() == KeyStore.NO_ERROR;
+ return mKeyStore.state() == KeyStore.State.UNLOCKED;
}
// Returns true if the profile needs to access keystore
@@ -1034,7 +1035,7 @@ public class VpnSettings extends SettingsPreferenceFragment
String presharedKey = pskProfile.getPresharedKey();
String key = KEY_PREFIX_IPSEC_PSK + p.getId();
if (!TextUtils.isEmpty(presharedKey) &&
- !mKeyStore.put(key, presharedKey)) {
+ !mKeyStore.put(key, presharedKey.getBytes(Charsets.UTF_8))) {
Log.e(TAG, "keystore write failed: key=" + key);
}
pskProfile.setPresharedKey(key);
@@ -1046,7 +1047,7 @@ public class VpnSettings extends SettingsPreferenceFragment
if (l2tpProfile.isSecretEnabled()) {
String secret = l2tpProfile.getSecretString();
if (!TextUtils.isEmpty(secret) &&
- !mKeyStore.put(key, secret)) {
+ !mKeyStore.put(key, secret.getBytes(Charsets.UTF_8))) {
Log.e(TAG, "keystore write failed: key=" + key);
}
l2tpProfile.setSecretString(key);
diff --git a/src/com/android/settings/widget/ChartAxis.java b/src/com/android/settings/widget/ChartAxis.java
new file mode 100644
index 0000000..2b21d28
--- /dev/null
+++ b/src/com/android/settings/widget/ChartAxis.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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 com.android.settings.widget;
+
+/**
+ * Axis along a {@link ChartView} that knows how to convert between raw point
+ * and screen coordinate systems.
+ */
+public interface ChartAxis {
+
+ public void setBounds(long min, long max);
+ public void setSize(float size);
+
+ public float convertToPoint(long value);
+ public long convertToValue(float point);
+
+ public CharSequence getLabel(long value);
+
+ public float[] getTickPoints();
+
+}
diff --git a/src/com/android/settings/widget/ChartGridView.java b/src/com/android/settings/widget/ChartGridView.java
new file mode 100644
index 0000000..be71890
--- /dev/null
+++ b/src/com/android/settings/widget/ChartGridView.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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 com.android.settings.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.view.View;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Background of {@link ChartView} that renders grid lines as requested by
+ * {@link ChartAxis#getTickPoints()}.
+ */
+public class ChartGridView extends View {
+
+ private final ChartAxis mHoriz;
+ private final ChartAxis mVert;
+
+ private final Paint mPaintHoriz;
+ private final Paint mPaintVert;
+
+ public ChartGridView(Context context, ChartAxis horiz, ChartAxis vert) {
+ super(context);
+
+ mHoriz = Preconditions.checkNotNull(horiz, "missing horiz");
+ mVert = Preconditions.checkNotNull(vert, "missing vert");
+
+ setWillNotDraw(false);
+
+ // TODO: convert these colors to resources
+ mPaintHoriz = new Paint();
+ mPaintHoriz.setColor(Color.parseColor("#667bb5"));
+ mPaintHoriz.setStrokeWidth(2.0f);
+ mPaintHoriz.setStyle(Style.STROKE);
+ mPaintHoriz.setAntiAlias(true);
+
+ mPaintVert = new Paint();
+ mPaintVert.setColor(Color.parseColor("#28262c"));
+ mPaintVert.setStrokeWidth(1.0f);
+ mPaintVert.setStyle(Style.STROKE);
+ mPaintVert.setAntiAlias(true);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ final int width = getWidth();
+ final int height = getHeight();
+
+ final float[] vertTicks = mVert.getTickPoints();
+ for (float y : vertTicks) {
+ canvas.drawLine(0, y, width, y, mPaintVert);
+ }
+
+ final float[] horizTicks = mHoriz.getTickPoints();
+ for (float x : horizTicks) {
+ canvas.drawLine(x, 0, x, height, mPaintHoriz);
+ }
+
+ canvas.drawRect(0, 0, width, height, mPaintHoriz);
+ }
+}
diff --git a/src/com/android/settings/widget/ChartNetworkSeriesView.java b/src/com/android/settings/widget/ChartNetworkSeriesView.java
new file mode 100644
index 0000000..780ca46
--- /dev/null
+++ b/src/com/android/settings/widget/ChartNetworkSeriesView.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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 com.android.settings.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.net.NetworkStatsHistory;
+import android.util.Log;
+import android.view.View;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * {@link NetworkStatsHistory} series to render inside a {@link ChartView},
+ * using {@link ChartAxis} to map into screen coordinates.
+ */
+public class ChartNetworkSeriesView extends View {
+ private static final String TAG = "ChartNetworkSeriesView";
+ private static final boolean LOGD = true;
+
+ private final ChartAxis mHoriz;
+ private final ChartAxis mVert;
+
+ private Paint mPaintStroke;
+ private Paint mPaintFill;
+ private Paint mPaintFillDisabled;
+
+ private NetworkStatsHistory mStats;
+
+ private Path mPathStroke;
+ private Path mPathFill;
+
+ private ChartSweepView mSweep1;
+ private ChartSweepView mSweep2;
+
+ public ChartNetworkSeriesView(Context context, ChartAxis horiz, ChartAxis vert) {
+ super(context);
+
+ mHoriz = Preconditions.checkNotNull(horiz, "missing horiz");
+ mVert = Preconditions.checkNotNull(vert, "missing vert");
+
+ setChartColor(Color.parseColor("#24aae1"), Color.parseColor("#c050ade5"),
+ Color.parseColor("#88566abc"));
+
+ mPathStroke = new Path();
+ mPathFill = new Path();
+ }
+
+ public void setChartColor(int stroke, int fill, int disabled) {
+ mPaintStroke = new Paint();
+ mPaintStroke.setStrokeWidth(6.0f);
+ mPaintStroke.setColor(stroke);
+ mPaintStroke.setStyle(Style.STROKE);
+ mPaintStroke.setAntiAlias(true);
+
+ mPaintFill = new Paint();
+ mPaintFill.setColor(fill);
+ mPaintFill.setStyle(Style.FILL);
+ mPaintFill.setAntiAlias(true);
+
+ mPaintFillDisabled = new Paint();
+ mPaintFillDisabled.setColor(disabled);
+ mPaintFillDisabled.setStyle(Style.FILL);
+ mPaintFillDisabled.setAntiAlias(true);
+ }
+
+ public void bindNetworkStats(NetworkStatsHistory stats) {
+ mStats = stats;
+
+ mPathStroke.reset();
+ mPathFill.reset();
+ }
+
+ public void bindSweepRange(ChartSweepView sweep1, ChartSweepView sweep2) {
+ // TODO: generalize to support vertical sweeps
+ // TODO: enforce that both sweeps are along same dimension
+
+ mSweep1 = Preconditions.checkNotNull(sweep1, "missing sweep1");
+ mSweep2 = Preconditions.checkNotNull(sweep2, "missing sweep2");
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ generatePath();
+ }
+
+ /**
+ * Erase any existing {@link Path} and generate series outline based on
+ * currently bound {@link NetworkStatsHistory} data.
+ */
+ public void generatePath() {
+ if (LOGD) Log.d(TAG, "generatePath()");
+
+ mPathStroke.reset();
+ mPathFill.reset();
+
+ // bail when not enough stats to render
+ if (mStats == null || mStats.bucketCount < 2) return;
+
+ final int width = getWidth();
+ final int height = getHeight();
+
+ boolean started = false;
+ float firstX = 0;
+ float lastX = 0;
+ float lastY = 0;
+
+ // TODO: count fractional data from first bucket crossing start;
+ // currently it only accepts first full bucket.
+
+ long totalData = 0;
+
+ for (int i = 0; i < mStats.bucketCount; i++) {
+ final float x = mHoriz.convertToPoint(mStats.bucketStart[i]);
+ final float y = mVert.convertToPoint(totalData);
+
+ // skip until we find first stats on screen
+ if (i > 0 && !started && x > 0) {
+ mPathStroke.moveTo(lastX, lastY);
+ mPathFill.moveTo(lastX, lastY);
+ started = true;
+ firstX = x;
+ }
+
+ if (started) {
+ mPathStroke.lineTo(x, y);
+ mPathFill.lineTo(x, y);
+ totalData += mStats.rx[i] + mStats.tx[i];
+ }
+
+ // skip if beyond view
+ if (x > width) break;
+
+ lastX = x;
+ lastY = y;
+ }
+
+ if (LOGD) {
+ final RectF bounds = new RectF();
+ mPathFill.computeBounds(bounds, true);
+ Log.d(TAG, "onLayout() rendered with bounds=" + bounds.toString() + " and totalData="
+ + totalData);
+ }
+
+ // drop to bottom of graph from current location
+ mPathFill.lineTo(lastX, height);
+ mPathFill.lineTo(firstX, height);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+
+ // clip to sweep area
+ final float sweep1 = mSweep1.getPoint();
+ final float sweep2 = mSweep2.getPoint();
+ final float sweepLeft = Math.min(sweep1, sweep2);
+ final float sweepRight = Math.max(sweep1, sweep2);
+
+ int save;
+
+ save = canvas.save();
+ canvas.clipRect(0, 0, sweepLeft, getHeight());
+ canvas.drawPath(mPathFill, mPaintFillDisabled);
+ canvas.restoreToCount(save);
+
+ save = canvas.save();
+ canvas.clipRect(sweepRight, 0, getWidth(), getHeight());
+ canvas.drawPath(mPathFill, mPaintFillDisabled);
+ canvas.restoreToCount(save);
+
+ save = canvas.save();
+ canvas.clipRect(sweepLeft, 0, sweepRight, getHeight());
+ canvas.drawPath(mPathFill, mPaintFill);
+ canvas.drawPath(mPathStroke, mPaintStroke);
+ canvas.restoreToCount(save);
+
+ }
+}
diff --git a/src/com/android/settings/widget/ChartSweepView.java b/src/com/android/settings/widget/ChartSweepView.java
new file mode 100644
index 0000000..788caad
--- /dev/null
+++ b/src/com/android/settings/widget/ChartSweepView.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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 com.android.settings.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.DashPathEffect;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Sweep across a {@link ChartView} at a specific {@link ChartAxis} value, which
+ * a user can drag.
+ */
+public class ChartSweepView extends View {
+
+ private final Paint mPaintSweep;
+ private final Paint mPaintSweepDisabled;
+ private final Paint mPaintShadow;
+
+ private final ChartAxis mAxis;
+ private long mValue;
+
+ public interface OnSweepListener {
+ public void onSweep(ChartSweepView sweep, boolean sweepDone);
+ }
+
+ private OnSweepListener mListener;
+
+ private boolean mHorizontal;
+ private MotionEvent mTracking;
+
+ public ChartSweepView(Context context, ChartAxis axis, long value, int color) {
+ super(context);
+
+ mAxis = Preconditions.checkNotNull(axis, "missing axis");
+ mValue = value;
+
+ mPaintSweep = new Paint();
+ mPaintSweep.setColor(color);
+ mPaintSweep.setStrokeWidth(3.0f);
+ mPaintSweep.setStyle(Style.FILL_AND_STROKE);
+ mPaintSweep.setAntiAlias(true);
+
+ mPaintSweepDisabled = new Paint();
+ mPaintSweepDisabled.setColor(color);
+ mPaintSweepDisabled.setStrokeWidth(1.5f);
+ mPaintSweepDisabled.setStyle(Style.FILL_AND_STROKE);
+ mPaintSweepDisabled.setPathEffect(new DashPathEffect(new float[] { 5, 5 }, 0));
+ mPaintSweepDisabled.setAntiAlias(true);
+
+ mPaintShadow = new Paint();
+ mPaintShadow.setColor(Color.BLACK);
+ mPaintShadow.setStrokeWidth(6.0f);
+ mPaintShadow.setStyle(Style.FILL_AND_STROKE);
+ mPaintShadow.setAntiAlias(true);
+
+ }
+
+ public void addOnSweepListener(OnSweepListener listener) {
+ mListener = listener;
+ }
+
+ private void dispatchOnSweep(boolean sweepDone) {
+ if (mListener != null) {
+ mListener.onSweep(this, sweepDone);
+ }
+ }
+
+ public ChartAxis getAxis() {
+ return mAxis;
+ }
+
+ public void setValue(long value) {
+ mValue = value;
+ }
+
+ public long getValue() {
+ return mValue;
+ }
+
+ public float getPoint() {
+ return mAxis.convertToPoint(mValue);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (!isEnabled()) return false;
+
+ final View parent = (View) getParent();
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN: {
+ mTracking = event.copy();
+ return true;
+ }
+ case MotionEvent.ACTION_MOVE: {
+ getParent().requestDisallowInterceptTouchEvent(true);
+
+ if (mHorizontal) {
+ setTranslationY(event.getRawY() - mTracking.getRawY());
+ final float point = (getTop() + getTranslationY() + (getHeight() / 2))
+ - parent.getPaddingTop();
+ mValue = mAxis.convertToValue(point);
+ dispatchOnSweep(false);
+ } else {
+ setTranslationX(event.getRawX() - mTracking.getRawX());
+ final float point = (getLeft() + getTranslationX() + (getWidth() / 2))
+ - parent.getPaddingLeft();
+ mValue = mAxis.convertToValue(point);
+ dispatchOnSweep(false);
+ }
+ return true;
+ }
+ case MotionEvent.ACTION_UP: {
+ mTracking = null;
+ setTranslationX(0);
+ setTranslationY(0);
+ requestLayout();
+ dispatchOnSweep(true);
+ return true;
+ }
+ default: {
+ return false;
+ }
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // need at least 50px in each direction for grippies
+ // TODO: provide this value through params
+ setMeasuredDimension(50, 50);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+
+ // draw line across larger dimension
+ final int width = getWidth();
+ final int height = getHeight();
+
+ mHorizontal = width > height;
+
+ final Paint linePaint = isEnabled() ? mPaintSweep : mPaintSweepDisabled;
+
+ if (mHorizontal) {
+ final int centerY = height / 2;
+ final int endX = width - height;
+
+ canvas.drawLine(0, centerY, endX, centerY, mPaintShadow);
+ canvas.drawLine(0, centerY, endX, centerY, linePaint);
+ canvas.drawCircle(endX, centerY, 4.0f, mPaintShadow);
+ canvas.drawCircle(endX, centerY, 4.0f, mPaintSweep);
+ } else {
+ final int centerX = width / 2;
+ final int endY = height - width;
+
+ canvas.drawLine(centerX, 0, centerX, endY, mPaintShadow);
+ canvas.drawLine(centerX, 0, centerX, endY, linePaint);
+ canvas.drawCircle(centerX, endY, 4.0f, mPaintShadow);
+ canvas.drawCircle(centerX, endY, 4.0f, mPaintSweep);
+ }
+ }
+
+}
diff --git a/src/com/android/settings/widget/ChartView.java b/src/com/android/settings/widget/ChartView.java
new file mode 100644
index 0000000..3e5fc50
--- /dev/null
+++ b/src/com/android/settings/widget/ChartView.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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 com.android.settings.widget;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.FrameLayout;
+
+/**
+ * Container for two-dimensional chart, drawn with a combination of
+ * {@link ChartGridView}, {@link ChartNetworkSeriesView} and {@link ChartSweepView}
+ * children. The entire chart uses {@link ChartAxis} to map between raw values
+ * and screen coordinates.
+ */
+public class ChartView extends FrameLayout {
+ private static final String TAG = "ChartView";
+
+ // TODO: extend something that supports two-dimensional scrolling
+
+ final ChartAxis mHoriz;
+ final ChartAxis mVert;
+
+ private Rect mContent = new Rect();
+
+ public ChartView(Context context, ChartAxis horiz, ChartAxis vert) {
+ super(context);
+
+ mHoriz = checkNotNull(horiz, "missing horiz");
+ mVert = checkNotNull(vert, "missing vert");
+
+ setClipToPadding(false);
+ setClipChildren(false);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ mContent.set(getPaddingLeft(), getPaddingTop(), r - l - getPaddingRight(),
+ b - t - getPaddingBottom());
+ final int width = mContent.width();
+ final int height = mContent.height();
+
+ // no scrolling yet, so tell dimensions to fill exactly
+ mHoriz.setSize(width);
+ mVert.setSize(height);
+
+ final Rect parentRect = new Rect();
+ final Rect childRect = new Rect();
+
+ for (int i = 0; i < getChildCount(); i++) {
+ final View child = getChildAt(i);
+ final LayoutParams params = (LayoutParams) child.getLayoutParams();
+
+ parentRect.set(mContent);
+
+ if (child instanceof ChartNetworkSeriesView || child instanceof ChartGridView) {
+ // series are always laid out to fill entire graph area
+ // TODO: handle scrolling for series larger than content area
+ Gravity.apply(params.gravity, width, height, parentRect, childRect);
+ child.layout(childRect.left, childRect.top, childRect.right, childRect.bottom);
+
+ } else if (child instanceof ChartSweepView) {
+ // sweep is always placed along specific dimension
+ final ChartSweepView sweep = (ChartSweepView) child;
+ final ChartAxis axis = sweep.getAxis();
+ final float point = sweep.getPoint();
+
+ if (axis == mHoriz) {
+ parentRect.left = parentRect.right = (int) point + getPaddingLeft();
+ parentRect.bottom += child.getMeasuredWidth();
+ Gravity.apply(params.gravity, child.getMeasuredWidth(), parentRect.height(),
+ parentRect, childRect);
+
+ } else if (axis == mVert) {
+ parentRect.top = parentRect.bottom = (int) point + getPaddingTop();
+ parentRect.right += child.getMeasuredHeight();
+ Gravity.apply(params.gravity, parentRect.width(), child.getMeasuredHeight(),
+ parentRect, childRect);
+
+ } else {
+ throw new IllegalStateException("unexpected axis");
+ }
+ }
+
+ child.layout(childRect.left, childRect.top, childRect.right, childRect.bottom);
+ }
+ }
+
+ public static LayoutParams buildChartParams() {
+ final LayoutParams params = new LayoutParams(MATCH_PARENT, MATCH_PARENT);
+ params.gravity = Gravity.LEFT | Gravity.BOTTOM;
+ return params;
+ }
+
+ public static LayoutParams buildSweepParams() {
+ final LayoutParams params = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+ params.gravity = Gravity.CENTER;
+ return params;
+ }
+
+}
diff --git a/src/com/android/settings/widget/DataUsageChartView.java b/src/com/android/settings/widget/DataUsageChartView.java
new file mode 100644
index 0000000..6c62fa8
--- /dev/null
+++ b/src/com/android/settings/widget/DataUsageChartView.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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 com.android.settings.widget;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.net.NetworkPolicy;
+import android.net.NetworkStatsHistory;
+import android.text.format.DateUtils;
+import android.view.View;
+
+import com.android.settings.widget.ChartSweepView.OnSweepListener;
+
+/**
+ * Specific {@link ChartView} that displays {@link ChartNetworkSeriesView} along
+ * with {@link ChartSweepView} for inspection ranges and warning/limits.
+ */
+public class DataUsageChartView extends ChartView {
+
+ private static final long KB_IN_BYTES = 1024;
+ private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
+ private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
+
+ private ChartNetworkSeriesView mSeries;
+
+ // TODO: limit sweeps at graph boundaries
+ private ChartSweepView mSweepTime1;
+ private ChartSweepView mSweepTime2;
+ private ChartSweepView mSweepDataWarn;
+ private ChartSweepView mSweepDataLimit;
+
+ public interface DataUsageChartListener {
+ public void onInspectRangeChanged();
+ public void onWarningChanged();
+ public void onLimitChanged();
+ }
+
+ private DataUsageChartListener mListener;
+
+ private static ChartAxis buildTimeAxis() {
+ return new TimeAxis();
+ }
+
+ private static ChartAxis buildDataAxis() {
+ return new InvertedChartAxis(new DataAxis());
+ }
+
+ public DataUsageChartView(Context context) {
+ super(context, buildTimeAxis(), buildDataAxis());
+ setPadding(20, 20, 20, 20);
+
+ addView(new ChartGridView(context, mHoriz, mVert), buildChartParams());
+
+ mSeries = new ChartNetworkSeriesView(context, mHoriz, mVert);
+ addView(mSeries, buildChartParams());
+
+ mSweepTime1 = new ChartSweepView(context, mHoriz, 0L, Color.parseColor("#ffffff"));
+ mSweepTime2 = new ChartSweepView(context, mHoriz, 0L, Color.parseColor("#ffffff"));
+ mSweepDataWarn = new ChartSweepView(context, mVert, 0L, Color.parseColor("#f7931d"));
+ mSweepDataLimit = new ChartSweepView(context, mVert, 0L, Color.parseColor("#be1d2c"));
+
+ addView(mSweepTime1, buildSweepParams());
+ addView(mSweepTime2, buildSweepParams());
+ addView(mSweepDataWarn, buildSweepParams());
+ addView(mSweepDataLimit, buildSweepParams());
+
+ mSeries.bindSweepRange(mSweepTime1, mSweepTime2);
+
+ mSweepDataWarn.addOnSweepListener(mWarningListener);
+ mSweepDataLimit.addOnSweepListener(mLimitListener);
+
+ mSweepTime1.addOnSweepListener(mSweepListener);
+ mSweepTime2.addOnSweepListener(mSweepListener);
+
+ mSweepDataWarn.setVisibility(View.INVISIBLE);
+ mSweepDataLimit.setVisibility(View.INVISIBLE);
+
+ }
+
+ public void setChartColor(int stroke, int fill, int disabled) {
+ mSeries.setChartColor(stroke, fill, disabled);
+ }
+
+ public void setListener(DataUsageChartListener listener) {
+ mListener = listener;
+ }
+
+ public void bindNetworkStats(NetworkStatsHistory stats) {
+ mSeries.bindNetworkStats(stats);
+ }
+
+ public void bindNetworkPolicy(NetworkPolicy policy) {
+ if (policy == null) {
+ mSweepDataLimit.setVisibility(View.INVISIBLE);
+ mSweepDataWarn.setVisibility(View.INVISIBLE);
+ return;
+ }
+
+ if (policy.limitBytes != NetworkPolicy.LIMIT_DISABLED) {
+ mSweepDataLimit.setVisibility(View.VISIBLE);
+ mSweepDataLimit.setValue(policy.limitBytes);
+ mSweepDataLimit.setEnabled(true);
+ } else {
+ // TODO: set limit default based on axis maximum
+ mSweepDataLimit.setVisibility(View.VISIBLE);
+ mSweepDataLimit.setValue(5 * GB_IN_BYTES);
+ mSweepDataLimit.setEnabled(false);
+ }
+
+ if (policy.warningBytes != NetworkPolicy.WARNING_DISABLED) {
+ mSweepDataWarn.setVisibility(View.VISIBLE);
+ mSweepDataWarn.setValue(policy.warningBytes);
+ } else {
+ mSweepDataWarn.setVisibility(View.INVISIBLE);
+ }
+
+ requestLayout();
+ }
+
+ private OnSweepListener mSweepListener = new OnSweepListener() {
+ public void onSweep(ChartSweepView sweep, boolean sweepDone) {
+ // always update graph clip region
+ mSeries.invalidate();
+
+ // update detail list only when done sweeping
+ if (sweepDone && mListener != null) {
+ mListener.onInspectRangeChanged();
+ }
+ }
+ };
+
+ private OnSweepListener mWarningListener = new OnSweepListener() {
+ public void onSweep(ChartSweepView sweep, boolean sweepDone) {
+ if (sweepDone && mListener != null) {
+ mListener.onWarningChanged();
+ }
+ }
+ };
+
+ private OnSweepListener mLimitListener = new OnSweepListener() {
+ public void onSweep(ChartSweepView sweep, boolean sweepDone) {
+ if (sweepDone && mListener != null) {
+ mListener.onLimitChanged();
+ }
+ }
+ };
+
+ /**
+ * Return current inspection range (start and end time) based on internal
+ * {@link ChartSweepView} positions.
+ */
+ public long[] getInspectRange() {
+ final long sweep1 = mSweepTime1.getValue();
+ final long sweep2 = mSweepTime2.getValue();
+ final long start = Math.min(sweep1, sweep2);
+ final long end = Math.max(sweep1, sweep2);
+ return new long[] { start, end };
+ }
+
+ public long getWarningBytes() {
+ return mSweepDataWarn.getValue();
+ }
+
+ public long getLimitBytes() {
+ return mSweepDataLimit.getValue();
+ }
+
+ /**
+ * Set the exact time range that should be displayed, updating how
+ * {@link ChartNetworkSeriesView} paints. Moves inspection ranges to be the
+ * last "week" of available data, without triggering listener events.
+ */
+ public void setVisibleRange(long start, long end, long dataBoundary) {
+ mHoriz.setBounds(start, end);
+
+ // default sweeps to last week of data
+ final long halfRange = (end + start) / 2;
+ final long sweepMax = Math.min(end, dataBoundary);
+ final long sweepMin = Math.max(start, (sweepMax - DateUtils.WEEK_IN_MILLIS));
+
+ mSweepTime1.setValue(sweepMin);
+ mSweepTime2.setValue(sweepMax);
+
+ requestLayout();
+ mSeries.generatePath();
+ mSeries.invalidate();
+ }
+
+ public static class TimeAxis implements ChartAxis {
+ private static final long TICK_INTERVAL = DateUtils.DAY_IN_MILLIS * 7;
+
+ private long mMin;
+ private long mMax;
+ private float mSize;
+
+ public TimeAxis() {
+ final long currentTime = System.currentTimeMillis();
+ setBounds(currentTime - DateUtils.DAY_IN_MILLIS * 30, currentTime);
+ }
+
+ /** {@inheritDoc} */
+ public void setBounds(long min, long max) {
+ mMin = min;
+ mMax = max;
+ }
+
+ /** {@inheritDoc} */
+ public void setSize(float size) {
+ this.mSize = size;
+ }
+
+ /** {@inheritDoc} */
+ public float convertToPoint(long value) {
+ return (mSize * (value - mMin)) / (mMax - mMin);
+ }
+
+ /** {@inheritDoc} */
+ public long convertToValue(float point) {
+ return (long) (mMin + ((point * (mMax - mMin)) / mSize));
+ }
+
+ /** {@inheritDoc} */
+ public CharSequence getLabel(long value) {
+ // TODO: convert to string
+ return Long.toString(value);
+ }
+
+ /** {@inheritDoc} */
+ public float[] getTickPoints() {
+ // tick mark for every week
+ final int tickCount = (int) ((mMax - mMin) / TICK_INTERVAL);
+ final float[] tickPoints = new float[tickCount];
+ for (int i = 0; i < tickCount; i++) {
+ tickPoints[i] = convertToPoint(mMax - (TICK_INTERVAL * i));
+ }
+ return tickPoints;
+ }
+ }
+
+ public static class DataAxis implements ChartAxis {
+ private long mMin;
+ private long mMax;
+ private long mMinLog;
+ private long mMaxLog;
+ private float mSize;
+
+ public DataAxis() {
+ // TODO: adapt ranges to show when history >5GB, and handle 4G
+ // interfaces with higher limits.
+ setBounds(1 * MB_IN_BYTES, 5 * GB_IN_BYTES);
+ }
+
+ /** {@inheritDoc} */
+ public void setBounds(long min, long max) {
+ mMin = min;
+ mMax = max;
+ mMinLog = (long) Math.log(mMin);
+ mMaxLog = (long) Math.log(mMax);
+ }
+
+ /** {@inheritDoc} */
+ public void setSize(float size) {
+ this.mSize = size;
+ }
+
+ /** {@inheritDoc} */
+ public float convertToPoint(long value) {
+ return (mSize * (value - mMin)) / (mMax - mMin);
+
+ // TODO: finish tweaking log scale
+// if (value > mMin) {
+// return (float) ((mSize * (Math.log(value) - mMinLog)) / (mMaxLog - mMinLog));
+// } else {
+// return 0;
+// }
+ }
+
+ /** {@inheritDoc} */
+ public long convertToValue(float point) {
+ return (long) (mMin + ((point * (mMax - mMin)) / mSize));
+
+ // TODO: finish tweaking log scale
+// return (long) Math.pow(Math.E, (mMinLog + ((point * (mMaxLog - mMinLog)) / mSize)));
+ }
+
+ /** {@inheritDoc} */
+ public CharSequence getLabel(long value) {
+ // TODO: convert to string
+ return Long.toString(value);
+ }
+
+ /** {@inheritDoc} */
+ public float[] getTickPoints() {
+ final float[] tickPoints = new float[16];
+
+ long value = mMax;
+ float mult = 0.8f;
+ for (int i = 0; i < tickPoints.length; i++) {
+ tickPoints[i] = convertToPoint(value);
+ value = (long) (value * mult);
+ mult *= 0.9;
+ }
+ return tickPoints;
+ }
+ }
+
+}
diff --git a/src/com/android/settings/widget/InvertedChartAxis.java b/src/com/android/settings/widget/InvertedChartAxis.java
new file mode 100644
index 0000000..e7e7893
--- /dev/null
+++ b/src/com/android/settings/widget/InvertedChartAxis.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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 com.android.settings.widget;
+
+/**
+ * Utility to invert another {@link ChartAxis}.
+ */
+public class InvertedChartAxis implements ChartAxis {
+ private final ChartAxis mWrapped;
+ private float mSize;
+
+ public InvertedChartAxis(ChartAxis wrapped) {
+ mWrapped = wrapped;
+ }
+
+ /** {@inheritDoc} */
+ public void setBounds(long min, long max) {
+ mWrapped.setBounds(min, max);
+ }
+
+ /** {@inheritDoc} */
+ public void setSize(float size) {
+ mSize = size;
+ mWrapped.setSize(size);
+ }
+
+ /** {@inheritDoc} */
+ public float convertToPoint(long value) {
+ return mSize - mWrapped.convertToPoint(value);
+ }
+
+ /** {@inheritDoc} */
+ public long convertToValue(float point) {
+ return mWrapped.convertToValue(mSize - point);
+ }
+
+ /** {@inheritDoc} */
+ public CharSequence getLabel(long value) {
+ return mWrapped.getLabel(value);
+ }
+
+ /** {@inheritDoc} */
+ public float[] getTickPoints() {
+ final float[] points = mWrapped.getTickPoints();
+ for (int i = 0; i < points.length; i++) {
+ points[i] = mSize - points[i];
+ }
+ return points;
+ }
+}
diff --git a/src/com/android/settings/wifi/AdvancedSettings.java b/src/com/android/settings/wifi/AdvancedSettings.java
index faea9f2..4855c99 100644
--- a/src/com/android/settings/wifi/AdvancedSettings.java
+++ b/src/com/android/settings/wifi/AdvancedSettings.java
@@ -16,21 +16,23 @@
package com.android.settings.wifi;
-import com.android.settings.R;
-import com.android.settings.SettingsPreferenceFragment;
-import com.android.settings.Utils;
-
-import android.app.Activity;
import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
-import android.os.SystemProperties;
+import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
import android.preference.Preference;
+import android.preference.PreferenceScreen;
+import android.provider.Settings;
+import android.provider.Settings.Secure;
import android.text.TextUtils;
-import android.widget.Toast;
import android.util.Log;
+import android.widget.Toast;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.Utils;
public class AdvancedSettings extends SettingsPreferenceFragment
implements Preference.OnPreferenceChangeListener {
@@ -39,6 +41,8 @@ public class AdvancedSettings extends SettingsPreferenceFragment
private static final String KEY_MAC_ADDRESS = "mac_address";
private static final String KEY_CURRENT_IP_ADDRESS = "current_ip_address";
private static final String KEY_FREQUENCY_BAND = "frequency_band";
+ private static final String KEY_NOTIFY_OPEN_NETWORKS = "notify_open_networks";
+ private static final String KEY_SLEEP_POLICY = "sleep_policy";
private WifiManager mWifiManager;
@@ -62,27 +66,61 @@ public class AdvancedSettings extends SettingsPreferenceFragment
}
private void initPreferences() {
+ CheckBoxPreference notifyOpenNetworks =
+ (CheckBoxPreference) findPreference(KEY_NOTIFY_OPEN_NETWORKS);
+ notifyOpenNetworks.setChecked(Secure.getInt(getContentResolver(),
+ Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0) == 1);
+ notifyOpenNetworks.setEnabled(mWifiManager.isWifiEnabled());
- ListPreference pref = (ListPreference) findPreference(KEY_FREQUENCY_BAND);
+ ListPreference frequencyPref = (ListPreference) findPreference(KEY_FREQUENCY_BAND);
if (mWifiManager.isDualBandSupported()) {
- pref.setOnPreferenceChangeListener(this);
+ frequencyPref.setOnPreferenceChangeListener(this);
int value = mWifiManager.getFrequencyBand();
if (value != -1) {
- pref.setValue(String.valueOf(value));
+ frequencyPref.setValue(String.valueOf(value));
} else {
Log.e(TAG, "Failed to fetch frequency band");
}
} else {
- getPreferenceScreen().removePreference(pref);
+ if (frequencyPref != null) {
+ // null if it has already been removed before resume
+ getPreferenceScreen().removePreference(frequencyPref);
+ }
+ }
+
+ ListPreference sleepPolicyPref = (ListPreference) findPreference(KEY_SLEEP_POLICY);
+ if (sleepPolicyPref != null) {
+ if (Utils.isWifiOnly()) {
+ sleepPolicyPref.setEntries(R.array.wifi_sleep_policy_entries_wifi_only);
+ sleepPolicyPref.setSummary(R.string.wifi_setting_sleep_policy_summary_wifi_only);
+ }
+ sleepPolicyPref.setOnPreferenceChangeListener(this);
+ int value = Settings.System.getInt(getContentResolver(),
+ Settings.System.WIFI_SLEEP_POLICY,
+ Settings.System.WIFI_SLEEP_POLICY_NEVER);
+ sleepPolicyPref.setValue(String.valueOf(value));
}
}
+ @Override
+ public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
+ String key = preference.getKey();
+
+ if (KEY_NOTIFY_OPEN_NETWORKS.equals(key)) {
+ Secure.putInt(getContentResolver(),
+ Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
+ ((CheckBoxPreference) preference).isChecked() ? 1 : 0);
+ } else {
+ return super.onPreferenceTreeClick(screen, preference);
+ }
+ return true;
+ }
+
public boolean onPreferenceChange(Preference preference, Object newValue) {
String key = preference.getKey();
- if (key == null) return true;
- if (key.equals(KEY_FREQUENCY_BAND)) {
+ if (KEY_FREQUENCY_BAND.equals(key)) {
try {
mWifiManager.setFrequencyBand(Integer.parseInt(((String) newValue)), true);
} catch (NumberFormatException e) {
@@ -92,6 +130,17 @@ public class AdvancedSettings extends SettingsPreferenceFragment
}
}
+ if (KEY_SLEEP_POLICY.equals(key)) {
+ try {
+ Settings.System.putInt(getContentResolver(),
+ Settings.System.WIFI_SLEEP_POLICY, Integer.parseInt(((String) newValue)));
+ } catch (NumberFormatException e) {
+ Toast.makeText(getActivity(), R.string.wifi_setting_sleep_policy_error,
+ Toast.LENGTH_SHORT).show();
+ return false;
+ }
+ }
+
return true;
}
diff --git a/src/com/android/settings/wifi/AdvancedWifiSettings.java b/src/com/android/settings/wifi/AdvancedWifiSettings.java
new file mode 100644
index 0000000..1da8b68
--- /dev/null
+++ b/src/com/android/settings/wifi/AdvancedWifiSettings.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * 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 com.android.settings.wifi;
+
+import android.content.Context;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.text.TextUtils;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.Utils;
+
+public class AdvancedWifiSettings extends SettingsPreferenceFragment
+ implements Preference.OnPreferenceChangeListener {
+
+ private static final String TAG = "AdvancedWifiSettings";
+ private static final String KEY_MAC_ADDRESS = "mac_address";
+ private static final String KEY_CURRENT_IP_ADDRESS = "current_ip_address";
+ private static final String KEY_FREQUENCY_BAND = "frequency_band";
+
+ private WifiManager mWifiManager;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.wifi_advanced_settings);
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ initPreferences();
+ refreshWifiInfo();
+ }
+
+ private void initPreferences() {
+
+ ListPreference pref = (ListPreference) findPreference(KEY_FREQUENCY_BAND);
+
+ if (mWifiManager.isDualBandSupported()) {
+ pref.setOnPreferenceChangeListener(this);
+ int value = mWifiManager.getFrequencyBand();
+ if (value != -1) {
+ pref.setValue(String.valueOf(value));
+ } else {
+ Log.e(TAG, "Failed to fetch frequency band");
+ }
+ } else {
+ getPreferenceScreen().removePreference(pref);
+ }
+ }
+
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ String key = preference.getKey();
+ if (key == null) return true;
+
+ if (key.equals(KEY_FREQUENCY_BAND)) {
+ try {
+ mWifiManager.setFrequencyBand(Integer.parseInt(((String) newValue)), true);
+ } catch (NumberFormatException e) {
+ Toast.makeText(getActivity(), R.string.wifi_setting_frequency_band_error,
+ Toast.LENGTH_SHORT).show();
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private void refreshWifiInfo() {
+ WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
+
+ Preference wifiMacAddressPref = findPreference(KEY_MAC_ADDRESS);
+ String macAddress = wifiInfo == null ? null : wifiInfo.getMacAddress();
+ wifiMacAddressPref.setSummary(!TextUtils.isEmpty(macAddress) ? macAddress
+ : getActivity().getString(R.string.status_unavailable));
+
+ Preference wifiIpAddressPref = findPreference(KEY_CURRENT_IP_ADDRESS);
+ String ipAddress = Utils.getWifiIpAddresses(getActivity());
+ wifiIpAddressPref.setSummary(ipAddress == null ?
+ getActivity().getString(R.string.status_unavailable) : ipAddress);
+ }
+
+}
diff --git a/src/com/android/settings/wifi/WifiConfigController.java b/src/com/android/settings/wifi/WifiConfigController.java
index 91f4110..a8ce94f 100644
--- a/src/com/android/settings/wifi/WifiConfigController.java
+++ b/src/com/android/settings/wifi/WifiConfigController.java
@@ -16,33 +16,29 @@
package com.android.settings.wifi;
+import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
+
import android.content.Context;
-import android.content.DialogInterface;
import android.content.res.Resources;
-import android.net.DhcpInfo;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkUtils;
-import android.net.Proxy;
import android.net.ProxyProperties;
import android.net.RouteInfo;
import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiConfiguration.IpAssignment;
import android.net.wifi.WifiConfiguration.AuthAlgorithm;
+import android.net.wifi.WifiConfiguration.IpAssignment;
import android.net.wifi.WifiConfiguration.KeyMgmt;
-import android.net.wifi.WpsConfiguration;
-import android.net.wifi.WpsConfiguration.Setup;
-
-import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
import android.net.wifi.WifiConfiguration.ProxySettings;
import android.net.wifi.WifiInfo;
+import android.net.wifi.WpsConfiguration;
+import android.net.wifi.WpsConfiguration.Setup;
import android.security.Credentials;
import android.security.KeyStore;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
-import android.text.format.Formatter;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -57,12 +53,10 @@ import com.android.settings.ProxySelector;
import com.android.settings.R;
import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.UnknownHostException;
import java.util.Iterator;
/**
- * The class for allowing UIs like {@link WifiDialog} and {@link WifiConfigPreference} to
+ * The class for allowing UIs like {@link WifiDialog} and {@link WifiConfigUiBase} to
* share the logic for controlling buttons, text fields, etc.
*/
public class WifiConfigController implements TextWatcher,
@@ -429,7 +423,9 @@ public class WifiConfigController implements TextWatcher,
int networkPrefixLength = -1;
try {
networkPrefixLength = Integer.parseInt(mNetworkPrefixLengthView.getText().toString());
- } catch (NumberFormatException e) { }
+ } catch (NumberFormatException e) {
+ // Use -1
+ }
if (networkPrefixLength < 0 || networkPrefixLength > 32) {
return R.string.wifi_ip_settings_invalid_network_prefix_length;
}
@@ -698,6 +694,7 @@ public class WifiConfigController implements TextWatcher,
private void setSelection(Spinner spinner, String value) {
if (value != null) {
+ @SuppressWarnings("unchecked")
ArrayAdapter<String> adapter = (ArrayAdapter<String>) spinner.getAdapter();
for (int i = adapter.getCount() - 1; i >= 0; --i) {
if (value.equals(adapter.getItem(i))) {
@@ -719,10 +716,12 @@ public class WifiConfigController implements TextWatcher,
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ // work done in afterTextChanged
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
+ // work done in afterTextChanged
}
@Override
@@ -750,5 +749,6 @@ public class WifiConfigController implements TextWatcher,
@Override
public void onNothingSelected(AdapterView<?> parent) {
+ //
}
}
diff --git a/src/com/android/settings/wifi/WifiEnabler.java b/src/com/android/settings/wifi/WifiEnabler.java
index 7f1221e..223022d 100644
--- a/src/com/android/settings/wifi/WifiEnabler.java
+++ b/src/com/android/settings/wifi/WifiEnabler.java
@@ -16,9 +16,6 @@
package com.android.settings.wifi;
-import com.android.settings.R;
-import com.android.settings.WirelessSettings;
-
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -27,19 +24,19 @@ import android.net.NetworkInfo;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.preference.Preference;
-import android.preference.CheckBoxPreference;
import android.provider.Settings;
-import android.text.TextUtils;
+import android.widget.CompoundButton;
+import android.widget.Switch;
import android.widget.Toast;
+import com.android.settings.R;
+import com.android.settings.WirelessSettings;
+
import java.util.concurrent.atomic.AtomicBoolean;
-public class WifiEnabler implements Preference.OnPreferenceChangeListener {
+public class WifiEnabler implements CompoundButton.OnCheckedChangeListener {
private final Context mContext;
- private final CheckBoxPreference mCheckBox;
- private final CharSequence mOriginalSummary;
-
+ private Switch mSwitch;
private AtomicBoolean mConnected = new AtomicBoolean(false);
private final WifiManager mWifiManager;
@@ -65,11 +62,9 @@ public class WifiEnabler implements Preference.OnPreferenceChangeListener {
}
};
- public WifiEnabler(Context context, CheckBoxPreference checkBox) {
+ public WifiEnabler(Context context, Switch switch_) {
mContext = context;
- mCheckBox = checkBox;
- mOriginalSummary = checkBox.getSummary();
- checkBox.setPersistent(false);
+ mSwitch = switch_;
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
mIntentFilter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
@@ -81,78 +76,86 @@ public class WifiEnabler implements Preference.OnPreferenceChangeListener {
public void resume() {
// Wi-Fi state is sticky, so just let the receiver update UI
mContext.registerReceiver(mReceiver, mIntentFilter);
- mCheckBox.setOnPreferenceChangeListener(this);
+ mSwitch.setOnCheckedChangeListener(this);
}
public void pause() {
mContext.unregisterReceiver(mReceiver);
- mCheckBox.setOnPreferenceChangeListener(null);
+ mSwitch.setOnCheckedChangeListener(null);
}
- public boolean onPreferenceChange(Preference preference, Object value) {
- boolean enable = (Boolean) value;
+ public void setSwitch(Switch switch_) {
+ if (mSwitch == switch_) return;
+ mSwitch.setOnCheckedChangeListener(null);
+ mSwitch = switch_;
+ mSwitch.setOnCheckedChangeListener(this);
+
+ final int wifiState = mWifiManager.getWifiState();
+ boolean isEnabled = wifiState == WifiManager.WIFI_STATE_ENABLED;
+ boolean isDisabled = wifiState == WifiManager.WIFI_STATE_DISABLED;
+ mSwitch.setChecked(isEnabled);
+ mSwitch.setEnabled(isEnabled || isDisabled);
+ }
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// Show toast message if Wi-Fi is not allowed in airplane mode
- if (enable && !WirelessSettings
- .isRadioAllowed(mContext, Settings.System.RADIO_WIFI)) {
- Toast.makeText(mContext, R.string.wifi_in_airplane_mode,
- Toast.LENGTH_SHORT).show();
- return false;
+ if (isChecked && !WirelessSettings.isRadioAllowed(mContext, Settings.System.RADIO_WIFI)) {
+ Toast.makeText(mContext, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show();
+ // Reset switch to off. No infinite check/listenenr loop.
+ buttonView.setChecked(false);
}
- /**
- * Disable tethering if enabling Wifi
- */
+ // Disable tethering if enabling Wifi
int wifiApState = mWifiManager.getWifiApState();
- if (enable && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) ||
+ if (isChecked && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) ||
(wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) {
mWifiManager.setWifiApEnabled(null, false);
}
- if (mWifiManager.setWifiEnabled(enable)) {
- mCheckBox.setEnabled(false);
+
+ if (mWifiManager.setWifiEnabled(isChecked)) {
+ // Intent has been taken into account, disable until new state is active
+ mSwitch.setEnabled(false);
} else {
- mCheckBox.setSummary(R.string.wifi_error);
+ // Error
+ Toast.makeText(mContext, R.string.wifi_error, Toast.LENGTH_SHORT).show();
}
-
- // Don't update UI to opposite state until we're sure
- return false;
}
private void handleWifiStateChanged(int state) {
switch (state) {
case WifiManager.WIFI_STATE_ENABLING:
- mCheckBox.setSummary(R.string.wifi_starting);
- mCheckBox.setEnabled(false);
+ mSwitch.setEnabled(false);
break;
case WifiManager.WIFI_STATE_ENABLED:
- mCheckBox.setChecked(true);
- mCheckBox.setSummary(null);
- mCheckBox.setEnabled(true);
+ mSwitch.setChecked(true);
+ mSwitch.setEnabled(true);
break;
case WifiManager.WIFI_STATE_DISABLING:
- mCheckBox.setSummary(R.string.wifi_stopping);
- mCheckBox.setEnabled(false);
+ mSwitch.setEnabled(false);
break;
case WifiManager.WIFI_STATE_DISABLED:
- mCheckBox.setChecked(false);
- mCheckBox.setSummary(mOriginalSummary);
- mCheckBox.setEnabled(true);
+ mSwitch.setChecked(false);
+ mSwitch.setEnabled(true);
break;
default:
- mCheckBox.setChecked(false);
- mCheckBox.setSummary(R.string.wifi_error);
- mCheckBox.setEnabled(true);
+ mSwitch.setChecked(false);
+ mSwitch.setEnabled(true);
}
}
- private void handleStateChanged(NetworkInfo.DetailedState state) {
+ private void handleStateChanged(@SuppressWarnings("unused") NetworkInfo.DetailedState state) {
+ // After the refactoring from a CheckBoxPreference to a Switch, this method is useless since
+ // there is nowhere to display a summary.
+ // This code is kept in case a future change re-introduces an associated text.
+ /*
// WifiInfo is valid if and only if Wi-Fi is enabled.
- // Here we use the state of the check box as an optimization.
- if (state != null && mCheckBox.isChecked()) {
+ // Here we use the state of the switch as an optimization.
+ if (state != null && mSwitch.isChecked()) {
WifiInfo info = mWifiManager.getConnectionInfo();
if (info != null) {
- mCheckBox.setSummary(Summary.get(mContext, info.getSSID(), state));
+ //setSummary(Summary.get(mContext, info.getSSID(), state));
}
}
+ */
}
}
diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java
index ab5e686..a829602 100644
--- a/src/com/android/settings/wifi/WifiSettings.java
+++ b/src/com/android/settings/wifi/WifiSettings.java
@@ -18,6 +18,7 @@ package com.android.settings.wifi;
import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
+import android.app.ActionBar;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
@@ -31,40 +32,35 @@ import android.net.NetworkInfo.DetailedState;
import android.net.wifi.ScanResult;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.net.wifi.WpsResult;
-import android.net.wifi.WifiConfiguration.KeyMgmt;
-import android.net.wifi.WpsConfiguration;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
-import android.preference.CheckBoxPreference;
import android.preference.Preference;
-import android.preference.ListPreference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
-import android.provider.Settings.Secure;
-import android.provider.Settings;
import android.security.Credentials;
import android.security.KeyStore;
import android.util.Log;
import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.widget.Toast;
import android.widget.AdapterView.AdapterContextMenuInfo;
+import android.widget.Switch;
+import android.widget.Toast;
import com.android.internal.util.AsyncChannel;
-import com.android.settings.ProgressCategoryBase;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
-import com.android.settings.Utils;
import java.util.ArrayList;
import java.util.Collection;
@@ -83,14 +79,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
* other decorations specific to that screen.
*/
public class WifiSettings extends SettingsPreferenceFragment
- implements DialogInterface.OnClickListener, Preference.OnPreferenceChangeListener {
+ implements DialogInterface.OnClickListener {
private static final String TAG = "WifiSettings";
private static final int MENU_ID_SCAN = Menu.FIRST;
- private static final int MENU_ID_ADVANCED = Menu.FIRST + 1;
- private static final int MENU_ID_CONNECT = Menu.FIRST + 2;
- private static final int MENU_ID_FORGET = Menu.FIRST + 3;
- private static final int MENU_ID_MODIFY = Menu.FIRST + 4;
- private static final String KEY_SLEEP_POLICY = "sleep_policy";
+ private static final int MENU_ID_ADD_NETWORK = Menu.FIRST + 1;
+ private static final int MENU_ID_ADVANCED = Menu.FIRST + 2;
+ private static final int MENU_ID_CONNECT = Menu.FIRST + 3;
+ private static final int MENU_ID_FORGET = Menu.FIRST + 4;
+ private static final int MENU_ID_MODIFY = Menu.FIRST + 5;
private final IntentFilter mFilter;
private final BroadcastReceiver mReceiver;
@@ -98,12 +94,8 @@ public class WifiSettings extends SettingsPreferenceFragment
private WifiManager mWifiManager;
private WifiEnabler mWifiEnabler;
- private CheckBoxPreference mNotifyOpenNetworks;
- private ProgressCategoryBase mAccessPoints;
- private Preference mAddNetwork;
// An access point being editted is stored here.
private AccessPoint mSelectedAccessPoint;
- private boolean mEdit;
private DetailedState mLastState;
private WifiInfo mLastInfo;
@@ -180,60 +172,48 @@ public class WifiSettings extends SettingsPreferenceFragment
// state, start it off in the right state
mEnableNextOnConnection = intent.getBooleanExtra(EXTRA_ENABLE_NEXT_ON_CONNECT, false);
- // Avoid re-adding on returning from an overlapping activity/fragment.
- if (getPreferenceScreen() == null || getPreferenceScreen().getPreferenceCount() < 2) {
- if (mEnableNextOnConnection) {
- if (hasNextButton()) {
- final ConnectivityManager connectivity = (ConnectivityManager)
- getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
- if (connectivity != null) {
- NetworkInfo info = connectivity.getNetworkInfo(
- ConnectivityManager.TYPE_WIFI);
- changeNextButtonState(info.isConnected());
- }
+ if (mEnableNextOnConnection) {
+ if (hasNextButton()) {
+ final ConnectivityManager connectivity = (ConnectivityManager)
+ getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
+ if (connectivity != null) {
+ NetworkInfo info = connectivity.getNetworkInfo(
+ ConnectivityManager.TYPE_WIFI);
+ changeNextButtonState(info.isConnected());
}
}
+ }
- if (mInXlSetupWizard) {
- addPreferencesFromResource(R.xml.wifi_access_points_for_wifi_setup_xl);
- } else if (intent.getBooleanExtra("only_access_points", false)) {
- addPreferencesFromResource(R.xml.wifi_access_points);
- } else {
- addPreferencesFromResource(R.xml.wifi_settings);
- mWifiEnabler = new WifiEnabler(activity,
- (CheckBoxPreference) findPreference("enable_wifi"));
- mNotifyOpenNetworks =
- (CheckBoxPreference) findPreference("notify_open_networks");
- mNotifyOpenNetworks.setChecked(Secure.getInt(getContentResolver(),
- Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0) == 1);
- }
- // This may be either ProgressCategory or AccessPointCategoryForXL.
- final ProgressCategoryBase preference =
- (ProgressCategoryBase) findPreference("access_points");
- mAccessPoints = preference;
- mAccessPoints.setOrderingAsAdded(false);
- mAddNetwork = findPreference("add_network");
-
- ListPreference pref = (ListPreference) findPreference(KEY_SLEEP_POLICY);
- if (pref != null) {
- if (Utils.isWifiOnly()) {
- pref.setEntries(R.array.wifi_sleep_policy_entries_wifi_only);
- pref.setSummary(R.string.wifi_setting_sleep_policy_summary_wifi_only);
+ if (mInXlSetupWizard) {
+ addPreferencesFromResource(R.xml.wifi_access_points_for_wifi_setup_xl);
+ } else {
+ addPreferencesFromResource(R.xml.wifi_settings);
+
+ Switch actionBarSwitch = new Switch(activity);
+
+ if (activity instanceof PreferenceActivity) {
+ PreferenceActivity preferenceActivity = (PreferenceActivity) activity;
+ if (preferenceActivity.onIsHidingHeaders() || !preferenceActivity.onIsMultiPane()) {
+ final int padding = activity.getResources().getDimensionPixelSize(
+ R.dimen.action_bar_switch_padding);
+ actionBarSwitch.setPadding(0, 0, padding, 0);
+ activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
+ ActionBar.DISPLAY_SHOW_CUSTOM);
+ activity.getActionBar().setCustomView(actionBarSwitch, new ActionBar.LayoutParams(
+ ActionBar.LayoutParams.WRAP_CONTENT,
+ ActionBar.LayoutParams.WRAP_CONTENT,
+ Gravity.CENTER_VERTICAL | Gravity.RIGHT));
}
- pref.setOnPreferenceChangeListener(this);
- int value = Settings.System.getInt(getContentResolver(),
- Settings.System.WIFI_SLEEP_POLICY,
- Settings.System.WIFI_SLEEP_POLICY_NEVER);
- pref.setValue(String.valueOf(value));
}
- registerForContextMenu(getListView());
- setHasOptionsMenu(true);
+ mWifiEnabler = new WifiEnabler(activity, actionBarSwitch);
}
+ registerForContextMenu(getListView());
+ setHasOptionsMenu(true);
+
// After confirming PreferenceScreen is available, we call super.
super.onActivityCreated(savedInstanceState);
-
}
@Override
@@ -245,10 +225,11 @@ public class WifiSettings extends SettingsPreferenceFragment
getActivity().registerReceiver(mReceiver, mFilter);
if (mKeyStoreNetworkId != INVALID_NETWORK_ID &&
- KeyStore.getInstance().test() == KeyStore.NO_ERROR) {
+ KeyStore.getInstance().state() == KeyStore.State.UNLOCKED) {
mWifiManager.connectNetwork(mKeyStoreNetworkId);
}
mKeyStoreNetworkId = INVALID_NETWORK_ID;
+
updateAccessPoints();
}
@@ -270,8 +251,11 @@ public class WifiSettings extends SettingsPreferenceFragment
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// We don't want menus in Setup Wizard XL.
if (!mInXlSetupWizard) {
+ final boolean wifiIsEnabled = mWifiManager.isWifiEnabled();
menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.wifi_menu_scan)
- .setIcon(R.drawable.ic_menu_scan_network);
+ .setIcon(R.drawable.ic_menu_scan_network).setEnabled(wifiIsEnabled);
+ menu.add(Menu.NONE, MENU_ID_ADD_NETWORK, 0, R.string.wifi_add_network)
+ .setIcon(android.R.drawable.ic_menu_add).setEnabled(wifiIsEnabled);
menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced)
.setIcon(android.R.drawable.ic_menu_manage);
}
@@ -286,6 +270,11 @@ public class WifiSettings extends SettingsPreferenceFragment
mScanner.forceScan();
}
return true;
+ case MENU_ID_ADD_NETWORK:
+ if (mWifiManager.isWifiEnabled()) {
+ onAddNetworkPressed();
+ }
+ return true;
case MENU_ID_ADVANCED:
if (getActivity() instanceof PreferenceActivity) {
((PreferenceActivity) getActivity()).startPreferencePanel(
@@ -363,43 +352,17 @@ public class WifiSettings extends SettingsPreferenceFragment
if (preference instanceof AccessPoint) {
mSelectedAccessPoint = (AccessPoint) preference;
showConfigUi(mSelectedAccessPoint, false);
- } else if (preference == mAddNetwork) {
- onAddNetworkPressed();
- } else if (preference == mNotifyOpenNetworks) {
- Secure.putInt(getContentResolver(),
- Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
- mNotifyOpenNetworks.isChecked() ? 1 : 0);
} else {
return super.onPreferenceTreeClick(screen, preference);
}
return true;
}
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- String key = preference.getKey();
- if (key == null) return true;
-
- if (key.equals(KEY_SLEEP_POLICY)) {
- try {
- Settings.System.putInt(getContentResolver(),
- Settings.System.WIFI_SLEEP_POLICY, Integer.parseInt(((String) newValue)));
- } catch (NumberFormatException e) {
- Toast.makeText(getActivity(), R.string.wifi_setting_sleep_policy_error,
- Toast.LENGTH_SHORT).show();
- return false;
- }
- }
-
- return true;
- }
-
-
/**
* Shows an appropriate Wifi configuration component.
* Called when a user clicks "Add network" preference or one of available networks is selected.
*/
private void showConfigUi(AccessPoint accessPoint, boolean edit) {
- mEdit = edit;
if (mInXlSetupWizard) {
((WifiSettingsForSetupWizardXL)getActivity()).showConfigUi(accessPoint, edit);
} else {
@@ -417,7 +380,7 @@ public class WifiSettings extends SettingsPreferenceFragment
private boolean requireKeyStore(WifiConfiguration config) {
if (WifiConfigController.requireKeyStore(config) &&
- KeyStore.getInstance().test() != KeyStore.NO_ERROR) {
+ KeyStore.getInstance().state() != KeyStore.State.UNLOCKED) {
mKeyStoreNetworkId = config.networkId;
Credentials.getInstance().unlock(getActivity());
return true;
@@ -430,20 +393,40 @@ public class WifiSettings extends SettingsPreferenceFragment
* the strength of network and the security for it.
*/
private void updateAccessPoints() {
- mAccessPoints.removeAll();
+ final PreferenceScreen preferenceScreen = getPreferenceScreen();
+ preferenceScreen.removeAll();
+ final int wifiState = mWifiManager.getWifiState();
+
+ switch (wifiState) {
+ case WifiManager.WIFI_STATE_ENABLED:
+ // AccessPoints are automatically sorted with TreeSet.
+ final Collection<AccessPoint> accessPoints = constructAccessPoints();
+ if (mInXlSetupWizard) {
+ ((WifiSettingsForSetupWizardXL)getActivity()).onAccessPointsUpdated(
+ getPreferenceScreen(), accessPoints);
+ } else {
+ for (AccessPoint accessPoint : accessPoints) {
+ getPreferenceScreen().addPreference(accessPoint);
+ }
+ }
+ break;
- // AccessPoints are automatically sorted with TreeSet.
- final Collection<AccessPoint> accessPoints = constructAccessPoints();
- if (mInXlSetupWizard) {
- ((WifiSettingsForSetupWizardXL)getActivity()).onAccessPointsUpdated(
- mAccessPoints, accessPoints);
- } else {
- for (AccessPoint accessPoint : accessPoints) {
- mAccessPoints.addPreference(accessPoint);
- }
+ case WifiManager.WIFI_STATE_DISABLING:
+ addMessagePreference(R.string.wifi_stopping);
+ break;
+
+ case WifiManager.WIFI_STATE_DISABLED:
+ addMessagePreference(R.string.wifi_empty_list_wifi_off);
+ break;
}
}
+ private void addMessagePreference(int messageId) {
+ Preference emptyListPreference = new Preference(getActivity());
+ emptyListPreference.setTitle(messageId);
+ getPreferenceScreen().addPreference(emptyListPreference);
+ }
+
private Collection<AccessPoint> constructAccessPoints() {
Collection<AccessPoint> accessPoints = new ArrayList<AccessPoint>();
@@ -542,9 +525,9 @@ public class WifiSettings extends SettingsPreferenceFragment
mLastState = state;
}
- for (int i = mAccessPoints.getPreferenceCount() - 1; i >= 0; --i) {
+ for (int i = getPreferenceScreen().getPreferenceCount() - 1; i >= 0; --i) {
// Maybe there's a WifiConfigPreference
- Preference preference = mAccessPoints.getPreference(i);
+ Preference preference = getPreferenceScreen().getPreference(i);
if (preference instanceof AccessPoint) {
final AccessPoint accessPoint = (AccessPoint) preference;
accessPoint.update(mLastInfo, mLastState);
@@ -557,12 +540,25 @@ public class WifiSettings extends SettingsPreferenceFragment
}
private void updateWifiState(int state) {
- if (state == WifiManager.WIFI_STATE_ENABLED) {
- mScanner.resume();
- } else {
- mScanner.pause();
- mAccessPoints.removeAll();
+ getActivity().invalidateOptionsMenu();
+
+ switch (state) {
+ case WifiManager.WIFI_STATE_ENABLED:
+ mScanner.resume();
+ return; // not break, to avoid pause
+
+ case WifiManager.WIFI_STATE_ENABLING:
+ getPreferenceScreen().removeAll();
+ addMessagePreference(R.string.wifi_starting);
+ break;
+
+ case WifiManager.WIFI_STATE_DISABLED:
+ getPreferenceScreen().removeAll();
+ addMessagePreference(R.string.wifi_empty_list_wifi_off);
+ break;
}
+
+ mScanner.pause();
}
private class Scanner extends Handler {
@@ -580,7 +576,6 @@ public class WifiSettings extends SettingsPreferenceFragment
void pause() {
mRetry = 0;
- mAccessPoints.setProgress(false);
removeMessages(0);
}
@@ -594,7 +589,6 @@ public class WifiSettings extends SettingsPreferenceFragment
Toast.LENGTH_LONG).show();
return;
}
- mAccessPoints.setProgress(mRetry != 0);
// Combo scans can take 5-6s to complete. Increase interval to 10s.
sendEmptyMessageDelayed(0, 10000);
}
@@ -636,6 +630,7 @@ public class WifiSettings extends SettingsPreferenceFragment
}
break;
}
+ break;
//TODO: more connectivity feedback
default:
//Ignore
@@ -740,7 +735,7 @@ public class WifiSettings extends SettingsPreferenceFragment
mScanner.resume();
}
- mAccessPoints.removeAll();
+ getPreferenceScreen().removeAll();
}
/**
@@ -753,8 +748,9 @@ public class WifiSettings extends SettingsPreferenceFragment
}
/* package */ int getAccessPointsCount() {
- if (mAccessPoints != null) {
- return mAccessPoints.getPreferenceCount();
+ final boolean wifiIsEnabled = mWifiManager.isWifiEnabled();
+ if (wifiIsEnabled) {
+ return getPreferenceScreen().getPreferenceCount();
} else {
return 0;
}
diff --git a/src/com/android/settings/wifi/WifiSettingsForSetupWizardXL.java b/src/com/android/settings/wifi/WifiSettingsForSetupWizardXL.java
index a73f96c..a3f1764 100644
--- a/src/com/android/settings/wifi/WifiSettingsForSetupWizardXL.java
+++ b/src/com/android/settings/wifi/WifiSettingsForSetupWizardXL.java
@@ -16,8 +16,6 @@
package com.android.settings.wifi;
-import com.android.settings.R;
-
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
@@ -28,11 +26,9 @@ import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
-import android.preference.PreferenceCategory;
+import android.preference.PreferenceScreen;
import android.text.TextUtils;
import android.util.Log;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
@@ -43,6 +39,7 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import com.android.internal.util.AsyncChannel;
+import com.android.settings.R;
import java.util.Collection;
import java.util.EnumMap;
@@ -75,12 +72,6 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
sNetworkStateMap.put(DetailedState.FAILED, DetailedState.FAILED);
}
- /**
- * Used with {@link Button#setTag(Object)} to remember "Connect" button is pressed in
- * with "add network" flow.
- */
- private static final int CONNECT_BUTTON_TAG_ADD_NETWORK = 1;
-
private WifiSettings mWifiSettings;
private WifiManager mWifiManager;
@@ -166,7 +157,7 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
// At first, Wifi module doesn't return SCANNING state (it's too early), so we manually
// show it.
- showScanningProgressBar();
+ showScanningState();
}
private void initViews() {
@@ -291,17 +282,16 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
switch (state) {
case SCANNING: {
- // Let users know the device is working correctly though currently there's
- // no visible network on the list.
- if (mWifiSettings.getAccessPointsCount() == 0) {
- showScanningState();
- } else {
- // Users already see available networks.
- showDisconnectedProgressBar();
- if (mScreenState == SCREEN_STATE_DISCONNECTED) {
+ if (mScreenState == SCREEN_STATE_DISCONNECTED) {
+ if (mWifiSettings.getAccessPointsCount() == 0) {
+ showScanningState();
+ } else {
+ showDisconnectedProgressBar();
mWifiSettingsFragmentLayout.setVisibility(View.VISIBLE);
mBottomPadding.setVisibility(View.GONE);
}
+ } else {
+ showDisconnectedProgressBar();
}
break;
}
@@ -316,7 +306,8 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
break;
}
default: // DISCONNECTED, FAILED
- if (mScreenState != SCREEN_STATE_CONNECTED) {
+ if (mScreenState != SCREEN_STATE_CONNECTED &&
+ mWifiSettings.getAccessPointsCount() > 0) {
showDisconnectedState(Summary.get(this, state));
}
break;
@@ -326,7 +317,8 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
private void showDisconnectedState(String stateString) {
showDisconnectedProgressBar();
- if (mScreenState == SCREEN_STATE_DISCONNECTED) {
+ if (mScreenState == SCREEN_STATE_DISCONNECTED &&
+ mWifiSettings.getAccessPointsCount() > 0) {
mWifiSettingsFragmentLayout.setVisibility(View.VISIBLE);
mBottomPadding.setVisibility(View.GONE);
}
@@ -474,13 +466,11 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
parent.removeAllViews();
mWifiConfig = new WifiConfigUiForSetupWizardXL(this, parent, selectedAccessPoint, edit);
- // Tag will be updated in this method when needed.
- mConnectButton.setTag(null);
if (selectedAccessPoint == null) { // "Add network" flow
showAddNetworkTitle();
mConnectButton.setVisibility(View.VISIBLE);
- mConnectButton.setTag(CONNECT_BUTTON_TAG_ADD_NETWORK);
+ showDisconnectedProgressBar();
showEditingButtonState();
} else if (selectedAccessPoint.security == AccessPoint.SECURITY_NONE) {
mNetworkName = selectedAccessPoint.getTitle().toString();
@@ -490,6 +480,7 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
} else {
mNetworkName = selectedAccessPoint.getTitle().toString();
showEditingTitle();
+ showDisconnectedProgressBar();
showEditingButtonState();
if (selectedAccessPoint.security == AccessPoint.SECURITY_EAP) {
onEapNetworkSelected();
@@ -645,8 +636,9 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
mAddNetworkButton.setEnabled(true);
mRefreshButton.setEnabled(true);
mSkipOrNextButton.setEnabled(true);
- mWifiSettingsFragmentLayout.setVisibility(View.VISIBLE);
showDisconnectedProgressBar();
+ mWifiSettingsFragmentLayout.setVisibility(View.VISIBLE);
+ mBottomPadding.setVisibility(View.GONE);
}
setPaddingVisibility(View.VISIBLE);
@@ -671,9 +663,10 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
/**
* Called when the list of AccessPoints are modified and this Activity needs to refresh
* the list.
+ * @param preferenceScreen
*/
/* package */ void onAccessPointsUpdated(
- PreferenceCategory holder, Collection<AccessPoint> accessPoints) {
+ PreferenceScreen preferenceScreen, Collection<AccessPoint> accessPoints) {
// If we already show some of access points but the bar still shows "scanning" state, it
// should be stopped.
if (mProgressBar.isIndeterminate() && accessPoints.size() > 0) {
@@ -688,20 +681,12 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
for (AccessPoint accessPoint : accessPoints) {
accessPoint.setLayoutResource(R.layout.custom_preference);
- holder.addPreference(accessPoint);
+ preferenceScreen.addPreference(accessPoint);
}
}
private void refreshAccessPoints(boolean disconnectNetwork) {
- final Object tag = mConnectButton.getTag();
- if (tag != null && (tag instanceof Integer) &&
- ((Integer)tag == CONNECT_BUTTON_TAG_ADD_NETWORK)) {
- // In "Add network" flow, we won't get DetaledState available for changing ProgressBar
- // state. Instead we manually show previous status here.
- showDisconnectedState(Summary.get(this, mPreviousNetworkState));
- } else {
- showScanningState();
- }
+ showScanningState();
if (disconnectNetwork) {
mWifiManager.disconnect();
@@ -801,11 +786,6 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
mWifiManager.connectNetwork(config);
}
- @Override
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
- }
-
/**
* Replace the current background with a new background whose id is resId if needed.
*/