diff options
Diffstat (limited to 'src/com/android/settings')
63 files changed, 4646 insertions, 2014 deletions
diff --git a/src/com/android/settings/AccessibilitySettings.java b/src/com/android/settings/AccessibilitySettings.java index d78d2d8ef..104ee9e6b 100644 --- a/src/com/android/settings/AccessibilitySettings.java +++ b/src/com/android/settings/AccessibilitySettings.java @@ -22,14 +22,13 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ServiceInfo; +import android.content.pm.PackageManager.NameNotFoundException; import android.net.Uri; import android.os.Bundle; import android.os.SystemProperties; import android.preference.CheckBoxPreference; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceCategory; import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; @@ -47,7 +46,7 @@ import java.util.Map; /** * Activity with the accessibility settings. */ -public class AccessibilitySettings extends PreferenceActivity { +public class AccessibilitySettings extends SettingsPreferenceFragment { private static final String DEFAULT_SCREENREADER_MARKET_LINK = "market://search?q=pname:com.google.android.marvin.talkback"; @@ -57,6 +56,9 @@ public class AccessibilitySettings extends PreferenceActivity { private static final String ACCESSIBILITY_SERVICES_CATEGORY = "accessibility_services_category"; + private static final String TOGGLE_ACCESSIBILITY_SCRIPT_INJECTION_CHECKBOX = + "toggle_accessibility_script_injection_checkbox"; + private static final String POWER_BUTTON_CATEGORY = "power_button_category"; @@ -65,6 +67,8 @@ public class AccessibilitySettings extends PreferenceActivity { private CheckBoxPreference mToggleCheckBox; + private CheckBoxPreference mToggleScriptInjectionCheckBox; + private PreferenceCategory mPowerButtonCategory; private CheckBoxPreference mPowerButtonEndsCallCheckBox; @@ -77,23 +81,21 @@ public class AccessibilitySettings extends PreferenceActivity { private PreferenceGroup mAccessibilityServicesCategory; @Override - protected void onCreate(Bundle icicle) { + public void onCreate(Bundle icicle) { super.onCreate(icicle); addPreferencesFromResource(R.xml.accessibility_settings); mToggleCheckBox = (CheckBoxPreference) findPreference( - TOGGLE_ACCESSIBILITY_SERVICE_CHECKBOX); + TOGGLE_ACCESSIBILITY_SERVICE_CHECKBOX); + + mToggleScriptInjectionCheckBox = (CheckBoxPreference) findPreference( + TOGGLE_ACCESSIBILITY_SCRIPT_INJECTION_CHECKBOX); mPowerButtonCategory = (PreferenceCategory) findPreference(POWER_BUTTON_CATEGORY); mPowerButtonEndsCallCheckBox = (CheckBoxPreference) findPreference( - POWER_BUTTON_ENDS_CALL_CHECKBOX); + POWER_BUTTON_ENDS_CALL_CHECKBOX); addAccessibilitServicePreferences(); - } - - @Override - protected void onResume() { - super.onResume(); final HashSet<String> enabled = new HashSet<String>(); String settingValue = Settings.Secure.getString(getContentResolver(), @@ -137,7 +139,14 @@ public class AccessibilitySettings extends PreferenceActivity { displayNoAppsAlert(); } - if (KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_POWER)) { + // set the accessibility script injection category + boolean scriptInjectionEnabled = (Settings.Secure.getInt(getContentResolver(), + Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, 0) == 1); + mToggleScriptInjectionCheckBox.setChecked(scriptInjectionEnabled); + mToggleScriptInjectionCheckBox.setEnabled(true); + + if (KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_POWER) + && Utils.isVoiceCapable(getActivity())) { int incallPowerBehavior = Settings.Secure.getInt(getContentResolver(), Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR, Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_DEFAULT); @@ -149,13 +158,14 @@ public class AccessibilitySettings extends PreferenceActivity { mPowerButtonEndsCallCheckBox.setChecked(powerButtonCheckboxEnabled); mPowerButtonEndsCallCheckBox.setEnabled(true); } else { - // No POWER key on the current device; this entire category is irrelevant. + // No POWER key on the current device or no voice capability; + // this entire category is irrelevant. getPreferenceScreen().removePreference(mPowerButtonCategory); } } @Override - protected void onPause() { + public void onPause() { super.onPause(); persistEnabledAccessibilityServices(); @@ -184,7 +194,6 @@ public class AccessibilitySettings extends PreferenceActivity { final String key = preference.getKey(); if (TOGGLE_ACCESSIBILITY_SERVICE_CHECKBOX.equals(key)) { - boolean isChecked = ((CheckBoxPreference) preference).isChecked(); handleEnableAccessibilityStateChange((CheckBoxPreference) preference); } else if (POWER_BUTTON_ENDS_CALL_CHECKBOX.equals(key)) { boolean isChecked = ((CheckBoxPreference) preference).isChecked(); @@ -195,6 +204,8 @@ public class AccessibilitySettings extends PreferenceActivity { Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR, (isChecked ? Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP : Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_SCREEN_OFF)); + } else if (TOGGLE_ACCESSIBILITY_SCRIPT_INJECTION_CHECKBOX.equals(key)) { + handleToggleAccessibilityScriptInjection((CheckBoxPreference) preference); } else if (preference instanceof CheckBoxPreference) { handleEnableAccessibilityServiceStateChange((CheckBoxPreference) preference); } @@ -214,10 +225,12 @@ public class AccessibilitySettings extends PreferenceActivity { setAccessibilityServicePreferencesState(true); } else { final CheckBoxPreference checkBoxPreference = preference; - AlertDialog dialog = (new AlertDialog.Builder(this)) + // TODO: DialogFragment? + AlertDialog dialog = (new AlertDialog.Builder(getActivity())) .setTitle(android.R.string.dialog_alert_title) .setIcon(android.R.drawable.ic_dialog_alert) - .setMessage(getString(R.string.accessibility_service_disable_warning)) + .setMessage(getResources(). + getString(R.string.accessibility_service_disable_warning)) .setCancelable(true) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @@ -239,6 +252,42 @@ public class AccessibilitySettings extends PreferenceActivity { } /** + * Handles the change of the accessibility script injection setting state. + * + * @param preference The preference for enabling/disabling accessibility script injection. + */ + private void handleToggleAccessibilityScriptInjection(CheckBoxPreference preference) { + if (preference.isChecked()) { + final CheckBoxPreference checkBoxPreference = preference; + // TODO: DialogFragment? + AlertDialog dialog = (new AlertDialog.Builder(getActivity())) + .setTitle(android.R.string.dialog_alert_title) + .setIcon(android.R.drawable.ic_dialog_alert) + .setMessage(getActivity().getString( + R.string.accessibility_script_injection_security_warning)) + .setCancelable(true) + .setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + Settings.Secure.putInt(getContentResolver(), + Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, 1); + } + }) + .setNegativeButton(android.R.string.cancel, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + checkBoxPreference.setChecked(false); + } + }) + .create(); + dialog.show(); + } else { + Settings.Secure.putInt(getContentResolver(), + Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, 0); + } + } + + /** * Handles the change of the preference for enabling/disabling an AccessibilityService. * * @param preference The preference. @@ -246,12 +295,14 @@ public class AccessibilitySettings extends PreferenceActivity { private void handleEnableAccessibilityServiceStateChange(CheckBoxPreference preference) { if (preference.isChecked()) { final CheckBoxPreference checkBoxPreference = preference; - AlertDialog dialog = (new AlertDialog.Builder(this)) + // TODO: DialogFragment? + AlertDialog dialog = (new AlertDialog.Builder(getActivity())) .setTitle(android.R.string.dialog_alert_title) .setIcon(android.R.drawable.ic_dialog_alert) - .setMessage(getString(R.string.accessibility_service_security_warning, - mAccessibilityServices.get(preference.getKey()) - .applicationInfo.loadLabel(getPackageManager()))) + .setMessage(getResources(). + getString(R.string.accessibility_service_security_warning, + mAccessibilityServices.get(preference.getKey()) + .applicationInfo.loadLabel(getActivity().getPackageManager()))) .setCancelable(true) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @@ -318,9 +369,9 @@ public class AccessibilitySettings extends PreferenceActivity { mAccessibilityServices.put(key, serviceInfo); - CheckBoxPreference preference = new CheckBoxPreference(this); + CheckBoxPreference preference = new CheckBoxPreference(getActivity()); preference.setKey(key); - preference.setTitle(serviceInfo.loadLabel(getPackageManager())); + preference.setTitle(serviceInfo.loadLabel(getActivity().getPackageManager())); mAccessibilityServicesCategory.addPreference(preference); } } @@ -332,13 +383,14 @@ public class AccessibilitySettings extends PreferenceActivity { */ private void displayNoAppsAlert() { try { - PackageManager pm = getPackageManager(); + PackageManager pm = getActivity().getPackageManager(); ApplicationInfo info = pm.getApplicationInfo("com.android.vending", 0); } catch (NameNotFoundException e) { // This is a no-op if the user does not have Android Market return; } - AlertDialog.Builder noAppsAlert = new AlertDialog.Builder(this); + // TODO: DialogFragment? + AlertDialog.Builder noAppsAlert = new AlertDialog.Builder(getActivity()); noAppsAlert.setTitle(R.string.accessibility_service_no_apps_title); noAppsAlert.setMessage(R.string.accessibility_service_no_apps_message); @@ -351,7 +403,7 @@ public class AccessibilitySettings extends PreferenceActivity { Uri marketUri = Uri.parse(screenreaderMarketLink); Intent marketIntent = new Intent(Intent.ACTION_VIEW, marketUri); startActivity(marketIntent); - finish(); + getFragmentManager().popBackStack(); } }); diff --git a/src/com/android/settings/AccountPreference.java b/src/com/android/settings/AccountPreference.java new file mode 100644 index 000000000..a860d1a5d --- /dev/null +++ b/src/com/android/settings/AccountPreference.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2008 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 java.util.ArrayList; + +import android.accounts.Account; +import android.content.Context; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.preference.Preference; +import android.util.Log; +import android.view.View; +import android.widget.ImageView; + +/** + * AccountPreference is used to display a username, status and provider icon for an account on + * the device. + */ +public class AccountPreference extends Preference { + private static final String TAG = "AccountPreference"; + public static final int SYNC_ENABLED = 0; // all know sync adapters are enabled and OK + public static final int SYNC_DISABLED = 1; // no sync adapters are enabled + public static final int SYNC_ERROR = 2; // one or more sync adapters have a problem + private int mStatus; + private Account mAccount; + private ArrayList<String> mAuthorities; + private Drawable mProviderIcon; + private ImageView mSyncStatusIcon; + private ImageView mProviderIconView; + + public AccountPreference(Context context, Account account, Drawable icon, + ArrayList<String> authorities) { + super(context); + mAccount = account; + mAuthorities = authorities; + mProviderIcon = icon; + setLayoutResource(R.layout.account_preference); + setTitle(mAccount.name); + setSummary(""); + // Add account info to the intent for AccountSyncSettings + Intent intent = new Intent("android.settings.ACCOUNT_SYNC_SETTINGS"); + intent.putExtra("account", mAccount); + setIntent(intent); + setPersistent(false); + setSyncStatus(SYNC_DISABLED); + } + + public Account getAccount() { + return mAccount; + } + + public ArrayList<String> getAuthorities() { + return mAuthorities; + } + + @Override + protected void onBindView(View view) { + super.onBindView(view); + setSummary(getSyncStatusMessage(mStatus)); + mProviderIconView = (ImageView) view.findViewById(R.id.providerIcon); + mProviderIconView.setImageDrawable(mProviderIcon); + mSyncStatusIcon = (ImageView) view.findViewById(R.id.syncStatusIcon); + mSyncStatusIcon.setImageResource(getSyncStatusIcon(mStatus)); + } + + public void setProviderIcon(Drawable icon) { + mProviderIcon = icon; + if (mProviderIconView != null) { + mProviderIconView.setImageDrawable(icon); + } + } + + public void setSyncStatus(int status) { + mStatus = status; + if (mSyncStatusIcon != null) { + mSyncStatusIcon.setImageResource(getSyncStatusIcon(status)); + } + setSummary(getSyncStatusMessage(status)); + } + + private int getSyncStatusMessage(int status) { + int res; + switch (status) { + case SYNC_ENABLED: + res = R.string.sync_enabled; + break; + case SYNC_DISABLED: + res = R.string.sync_disabled; + break; + case SYNC_ERROR: + res = R.string.sync_error; + break; + default: + res = R.string.sync_error; + Log.e(TAG, "Unknown sync status: " + status); + } + return res; + } + + private int getSyncStatusIcon(int status) { + int res; + switch (status) { + case SYNC_ENABLED: + res = R.drawable.ic_sync_green; + break; + case SYNC_DISABLED: + res = R.drawable.ic_sync_grey; + break; + case SYNC_ERROR: + res = R.drawable.ic_sync_red; + break; + default: + res = R.drawable.ic_sync_red; + Log.e(TAG, "Unknown sync status: " + status); + } + return res; + } + + @Override + public int compareTo(Preference other) { + if (!(other instanceof AccountPreference)) { + // Put other preference types above us + return 1; + } + return mAccount.name.compareTo(((AccountPreference) other).mAccount.name); + } +} diff --git a/src/com/android/settings/AirplaneModeEnabler.java b/src/com/android/settings/AirplaneModeEnabler.java index ff4b27dcc..ccfe541c5 100644 --- a/src/com/android/settings/AirplaneModeEnabler.java +++ b/src/com/android/settings/AirplaneModeEnabler.java @@ -64,8 +64,6 @@ public class AirplaneModeEnabler implements Preference.OnPreferenceChangeListene public void resume() { - // This is the widget enabled state, not the preference toggled state - mCheckBoxPref.setEnabled(true); mCheckBoxPref.setChecked(isAirplaneModeOn(mContext)); mPhoneStateReceiver.registerIntent(); @@ -84,13 +82,14 @@ public class AirplaneModeEnabler implements Preference.OnPreferenceChangeListene private void setAirplaneModeOn(boolean enabling) { - mCheckBoxPref.setEnabled(false); mCheckBoxPref.setSummary(enabling ? R.string.airplane_mode_turning_on : R.string.airplane_mode_turning_off); // Change the system setting Settings.System.putInt(mContext.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, enabling ? 1 : 0); + // Update the UI to reflect system setting + mCheckBoxPref.setChecked(enabling); // Post the intent Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); @@ -100,14 +99,17 @@ public class AirplaneModeEnabler implements Preference.OnPreferenceChangeListene /** * Called when we've received confirmation that the airplane mode was set. + * TODO: We update the checkbox summary when we get notified + * that mobile radio is powered up/down. We should not have dependency + * on one radio alone. We need to do the following: + * - handle the case of wifi/bluetooth failures + * - mobile does not send failure notification, fail on timeout. */ private void onAirplaneModeChanged() { ServiceState serviceState = mPhoneStateReceiver.getServiceState(); boolean airplaneModeEnabled = serviceState.getState() == ServiceState.STATE_POWER_OFF; - mCheckBoxPref.setChecked(airplaneModeEnabled); mCheckBoxPref.setSummary(airplaneModeEnabled ? null : mContext.getString(R.string.airplane_mode_summary)); - mCheckBoxPref.setEnabled(true); } /** @@ -128,7 +130,7 @@ public class AirplaneModeEnabler implements Preference.OnPreferenceChangeListene // update database based on the current checkbox state setAirplaneModeOn(isAirplaneModeOn); } else { - // update checkbox state based on database value + // update summary onAirplaneModeChanged(); } } diff --git a/src/com/android/settings/ApplicationSettings.java b/src/com/android/settings/ApplicationSettings.java index a919ae849..ed7c7a085 100644 --- a/src/com/android/settings/ApplicationSettings.java +++ b/src/com/android/settings/ApplicationSettings.java @@ -23,12 +23,11 @@ import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.ListPreference; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceScreen; import android.preference.Preference.OnPreferenceChangeListener; import android.provider.Settings; -public class ApplicationSettings extends PreferenceActivity implements +public class ApplicationSettings extends SettingsPreferenceFragment implements DialogInterface.OnClickListener { private static final String KEY_TOGGLE_INSTALL_APPLICATIONS = "toggle_install_applications"; @@ -51,7 +50,7 @@ public class ApplicationSettings extends PreferenceActivity implements private DialogInterface mWarnInstallApps; @Override - protected void onCreate(Bundle icicle) { + public void onCreate(Bundle icicle) { super.onCreate(icicle); addPreferencesFromResource(R.xml.application_settings); @@ -102,7 +101,7 @@ public class ApplicationSettings extends PreferenceActivity implements } @Override - protected void onDestroy() { + public void onDestroy() { super.onDestroy(); if (mWarnInstallApps != null) { mWarnInstallApps.dismiss(); @@ -157,8 +156,9 @@ public class ApplicationSettings extends PreferenceActivity implements } private void warnAppInstallation() { - mWarnInstallApps = new AlertDialog.Builder(this) - .setTitle(getString(R.string.error_title)) + // TODO: DialogFragment? + mWarnInstallApps = new AlertDialog.Builder(getActivity()).setTitle( + getResources().getString(R.string.error_title)) .setIcon(com.android.internal.R.drawable.ic_dialog_alert) .setMessage(getResources().getString(R.string.install_all_warning)) .setPositiveButton(android.R.string.yes, this) diff --git a/src/com/android/settings/ChooseLockGeneric.java b/src/com/android/settings/ChooseLockGeneric.java index 0672ad922..5247e791e 100644 --- a/src/com/android/settings/ChooseLockGeneric.java +++ b/src/com/android/settings/ChooseLockGeneric.java @@ -138,7 +138,7 @@ public class ChooseLockGeneric extends PreferenceActivity { } else if (KEY_UNLOCK_SET_PIN.equals(key)) { enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; } else if (KEY_UNLOCK_SET_PASSWORD.equals(key)) { - enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; + enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; } if (!enabled) { pref.setSummary(R.string.unlock_set_unlock_disabled_summary); diff --git a/src/com/android/settings/ChooseLockPassword.java b/src/com/android/settings/ChooseLockPassword.java index b5e72d7f7..4d447f433 100644 --- a/src/com/android/settings/ChooseLockPassword.java +++ b/src/com/android/settings/ChooseLockPassword.java @@ -48,6 +48,12 @@ public class ChooseLockPassword extends Activity implements OnClickListener, OnE private TextView mPasswordEntry; private int mPasswordMinLength = 4; private int mPasswordMaxLength = 16; + private int mPasswordMinLetters = 0; + private int mPasswordMinUpperCase = 0; + private int mPasswordMinLowerCase = 0; + private int mPasswordMinSymbols = 0; + private int mPasswordMinNumeric = 0; + private int mPasswordMinNonLetter = 0; private LockPatternUtils mLockPatternUtils; private int mRequestedQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; private ChooseLockSettingsHelper mChooseLockSettingsHelper; @@ -61,6 +67,12 @@ public class ChooseLockPassword extends Activity implements OnClickListener, OnE private Button mNextButton; public static final String PASSWORD_MIN_KEY = "lockscreen.password_min"; public static final String PASSWORD_MAX_KEY = "lockscreen.password_max"; + public static final String PASSWORD_MIN_LETTERS_KEY = "lockscreen.password_min_letters"; + public static final String PASSWORD_MIN_LOWERCASE_KEY = "lockscreen.password_min_lowercase"; + public static final String PASSWORD_MIN_UPPERCASE_KEY = "lockscreen.password_min_uppercase"; + public static final String PASSWORD_MIN_NUMERIC_KEY = "lockscreen.password_min_numeric"; + public static final String PASSWORD_MIN_SYMBOLS_KEY = "lockscreen.password_min_symbols"; + public static final String PASSWORD_MIN_NONLETTER_KEY = "lockscreen.password_min_nonletter"; private static Handler mHandler = new Handler(); private static final int CONFIRM_EXISTING_REQUEST = 58; static final int RESULT_FINISHED = RESULT_FIRST_USER; @@ -101,19 +113,27 @@ public class ChooseLockPassword extends Activity implements OnClickListener, OnE protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mLockPatternUtils = new LockPatternUtils(this); - mRequestedQuality = getIntent().getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, mRequestedQuality); - mPasswordMinLength = getIntent().getIntExtra(PASSWORD_MIN_KEY, mPasswordMinLength); + mRequestedQuality = Math.max(getIntent().getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, + mRequestedQuality), mLockPatternUtils.getRequestedPasswordQuality()); + mPasswordMinLength = Math.max( + getIntent().getIntExtra(PASSWORD_MIN_KEY, mPasswordMinLength), mLockPatternUtils + .getRequestedMinimumPasswordLength()); mPasswordMaxLength = getIntent().getIntExtra(PASSWORD_MAX_KEY, mPasswordMaxLength); - + mPasswordMinLetters = Math.max(getIntent().getIntExtra(PASSWORD_MIN_LETTERS_KEY, + mPasswordMinLetters), mLockPatternUtils.getRequestedPasswordMinimumLetters()); + mPasswordMinUpperCase = Math.max(getIntent().getIntExtra(PASSWORD_MIN_UPPERCASE_KEY, + mPasswordMinUpperCase), mLockPatternUtils.getRequestedPasswordMinimumUpperCase()); + mPasswordMinLowerCase = Math.max(getIntent().getIntExtra(PASSWORD_MIN_LOWERCASE_KEY, + mPasswordMinLowerCase), mLockPatternUtils.getRequestedPasswordMinimumLowerCase()); + mPasswordMinNumeric = Math.max(getIntent().getIntExtra(PASSWORD_MIN_NUMERIC_KEY, + mPasswordMinNumeric), mLockPatternUtils.getRequestedPasswordMinimumNumeric()); + mPasswordMinSymbols = Math.max(getIntent().getIntExtra(PASSWORD_MIN_SYMBOLS_KEY, + mPasswordMinSymbols), mLockPatternUtils.getRequestedPasswordMinimumSymbols()); + mPasswordMinNonLetter = Math.max(getIntent().getIntExtra(PASSWORD_MIN_NONLETTER_KEY, + mPasswordMinNonLetter), mLockPatternUtils.getRequestedPasswordMinimumNonLetter()); final boolean confirmCredentials = getIntent().getBooleanExtra("confirm_credentials", true); - int minMode = mLockPatternUtils.getRequestedPasswordQuality(); - if (mRequestedQuality < minMode) { - mRequestedQuality = minMode; - } - int minLength = mLockPatternUtils.getRequestedMinimumPasswordLength(); - if (mPasswordMinLength < minLength) { - mPasswordMinLength = minLength; - } + + initViews(); mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this); if (savedInstanceState == null) { @@ -142,7 +162,8 @@ public class ChooseLockPassword extends Activity implements OnClickListener, OnE mPasswordEntry.addTextChangedListener(this); mIsAlphaMode = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == mRequestedQuality - || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == mRequestedQuality; + || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == mRequestedQuality + || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mRequestedQuality; mKeyboardHelper = new PasswordEntryKeyboardHelper(this, mKeyboardView, mPasswordEntry); mKeyboardHelper.setKeyboardMode(mIsAlphaMode ? PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA @@ -212,9 +233,12 @@ public class ChooseLockPassword extends Activity implements OnClickListener, OnE R.string.lockpassword_password_too_long : R.string.lockpassword_pin_too_long, mPasswordMaxLength); } - boolean hasAlpha = false; - boolean hasDigit = false; - boolean hasSymbol = false; + int letters = 0; + int numbers = 0; + int lowercase = 0; + int symbols = 0; + int uppercase = 0; + int nonletter = 0; for (int i = 0; i < password.length(); i++) { char c = password.charAt(i); // allow non white space Latin-1 characters only @@ -222,32 +246,65 @@ public class ChooseLockPassword extends Activity implements OnClickListener, OnE return getString(R.string.lockpassword_illegal_character); } if (c >= '0' && c <= '9') { - hasDigit = true; - } else if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) { - hasAlpha = true; + numbers++; + nonletter++; + } else if (c >= 'A' && c <= 'Z') { + letters++; + uppercase++; + } else if (c >= 'a' && c <= 'z') { + letters++; + lowercase++; } else { - hasSymbol = true; + symbols++; + nonletter++; } } if (DevicePolicyManager.PASSWORD_QUALITY_NUMERIC == mRequestedQuality - && (hasAlpha | hasSymbol)) { - // This shouldn't be possible unless user finds some way to bring up soft keyboard + && (letters > 0 || symbols > 0)) { + // This shouldn't be possible unless user finds some way to bring up + // soft keyboard return getString(R.string.lockpassword_pin_contains_non_digits); + } else if (DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mRequestedQuality) { + if (letters < mPasswordMinLetters) { + return String.format(getResources().getQuantityString( + R.plurals.lockpassword_password_requires_letters, mPasswordMinLetters), + mPasswordMinLetters); + } else if (numbers < mPasswordMinNumeric) { + return String.format(getResources().getQuantityString( + R.plurals.lockpassword_password_requires_numeric, mPasswordMinNumeric), + mPasswordMinNumeric); + } else if (lowercase < mPasswordMinLowerCase) { + return String.format(getResources().getQuantityString( + R.plurals.lockpassword_password_requires_lowercase, mPasswordMinLowerCase), + mPasswordMinLowerCase); + } else if (uppercase < mPasswordMinUpperCase) { + return String.format(getResources().getQuantityString( + R.plurals.lockpassword_password_requires_uppercase, mPasswordMinUpperCase), + mPasswordMinUpperCase); + } else if (symbols < mPasswordMinSymbols) { + return String.format(getResources().getQuantityString( + R.plurals.lockpassword_password_requires_symbols, mPasswordMinSymbols), + mPasswordMinSymbols); + } else if (nonletter < mPasswordMinNonLetter) { + return String.format(getResources().getQuantityString( + R.plurals.lockpassword_password_requires_nonletter, mPasswordMinNonLetter), + mPasswordMinNonLetter); + } } else { final boolean alphabetic = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == mRequestedQuality; final boolean alphanumeric = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == mRequestedQuality; - final boolean symbolic = false; // not yet - if ((alphabetic || alphanumeric) && !hasAlpha) { + if ((alphabetic || alphanumeric) && letters == 0) { return getString(R.string.lockpassword_password_requires_alpha); } - if (alphanumeric && !hasDigit) { + if (alphanumeric && numbers == 0) { return getString(R.string.lockpassword_password_requires_digit); } - if (symbolic && !hasSymbol) { - return getString(R.string.lockpassword_password_requires_symbol); - } + } + if(mLockPatternUtils.checkPasswordHistory(password)) { + return getString(mIsAlphaMode ? R.string.lockpassword_password_recently_used + : R.string.lockpassword_pin_recently_used); } return null; } @@ -306,7 +363,7 @@ public class ChooseLockPassword extends Activity implements OnClickListener, OnE 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) { + if (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_DOWN) { handleNext(); return true; } diff --git a/src/com/android/settings/ChooseLockPatternExample.java b/src/com/android/settings/ChooseLockPatternExample.java index cba88b01c..3c96d534f 100644 --- a/src/com/android/settings/ChooseLockPatternExample.java +++ b/src/com/android/settings/ChooseLockPatternExample.java @@ -66,6 +66,7 @@ public class ChooseLockPatternExample extends Activity implements View.OnClickLi stopAnimation(mAnimation); Intent intent = new Intent(this, ChooseLockPattern.class); intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); + intent.putExtra("confirm_credentials", false); startActivity(intent); finish(); } diff --git a/src/com/android/settings/ChooseLockPatternTutorial.java b/src/com/android/settings/ChooseLockPatternTutorial.java index ee0019f70..e699abff8 100644 --- a/src/com/android/settings/ChooseLockPatternTutorial.java +++ b/src/com/android/settings/ChooseLockPatternTutorial.java @@ -35,6 +35,7 @@ public class ChooseLockPatternTutorial extends Activity implements View.OnClickL if (savedInstanceState == null && lockPatternUtils.isPatternEverChosen()) { Intent intent = new Intent(this, ChooseLockPattern.class); intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); + intent.putExtra("confirm_credentials", false); startActivity(intent); finish(); } else { diff --git a/src/com/android/settings/ChooseLockSettingsHelper.java b/src/com/android/settings/ChooseLockSettingsHelper.java index ba83f8e58..abcfc050f 100644 --- a/src/com/android/settings/ChooseLockSettingsHelper.java +++ b/src/com/android/settings/ChooseLockSettingsHelper.java @@ -52,6 +52,7 @@ public class ChooseLockSettingsHelper { case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: + case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: // TODO: update UI layout for ConfirmPassword to show message and details launched = confirmPassword(request); break; diff --git a/src/com/android/settings/ConfirmLockPassword.java b/src/com/android/settings/ConfirmLockPassword.java index 6bc135b01..0e893faa0 100644 --- a/src/com/android/settings/ConfirmLockPassword.java +++ b/src/com/android/settings/ConfirmLockPassword.java @@ -65,7 +65,8 @@ public class ConfirmLockPassword extends Activity implements OnClickListener, mKeyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard); mHeaderText = (TextView) findViewById(R.id.headerText); final boolean isAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == storedQuality - || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == storedQuality; + || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == storedQuality + || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == storedQuality; mHeaderText.setText(isAlpha ? R.string.lockpassword_confirm_your_password_header : R.string.lockpassword_confirm_your_pin_header); mKeyboardHelper = new PasswordEntryKeyboardHelper(this, mKeyboardView, mPasswordEntry); diff --git a/src/com/android/settings/ConfirmLockPattern.java b/src/com/android/settings/ConfirmLockPattern.java index eb9a4d8e5..8d44875b4 100644 --- a/src/com/android/settings/ConfirmLockPattern.java +++ b/src/com/android/settings/ConfirmLockPattern.java @@ -144,6 +144,11 @@ public class ConfirmLockPattern extends Activity { long deadline = mLockPatternUtils.getLockoutAttemptDeadline(); if (deadline != 0) { handleAttemptLockout(deadline); + } else if (!mLockPatternView.isEnabled()) { + // The deadline has passed, but the timer was cancelled... + // Need to clean up. + mNumWrongConfirmAttempts = 0; + updateStage(Stage.NeedToUnlock); } } diff --git a/src/com/android/settings/DateTimeSettings.java b/src/com/android/settings/DateTimeSettings.java index 5b38651ce..4d199b8d4 100644 --- a/src/com/android/settings/DateTimeSettings.java +++ b/src/com/android/settings/DateTimeSettings.java @@ -16,8 +16,9 @@ package com.android.settings; -import android.app.Dialog; +import android.app.Activity; import android.app.DatePickerDialog; +import android.app.Dialog; import android.app.TimePickerDialog; import android.content.BroadcastReceiver; import android.content.Context; @@ -30,7 +31,6 @@ import android.os.SystemClock; import android.preference.CheckBoxPreference; import android.preference.ListPreference; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceScreen; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; @@ -42,51 +42,56 @@ import java.util.Calendar; import java.util.Date; import java.util.TimeZone; -public class DateTimeSettings - extends PreferenceActivity +public class DateTimeSettings extends SettingsPreferenceFragment implements OnSharedPreferenceChangeListener, - TimePickerDialog.OnTimeSetListener , DatePickerDialog.OnDateSetListener { + TimePickerDialog.OnTimeSetListener, DatePickerDialog.OnDateSetListener { private static final String HOURS_12 = "12"; private static final String HOURS_24 = "24"; - + + // Used for showing the current date format, which looks like "12/31/2010", "2010/12/13", etc. + // The date value is dummy (independent of actual date). private Calendar mDummyDate; + private static final String KEY_DATE_FORMAT = "date_format"; private static final String KEY_AUTO_TIME = "auto_time"; + private static final String KEY_AUTO_TIME_ZONE = "auto_zone"; private static final int DIALOG_DATEPICKER = 0; private static final int DIALOG_TIMEPICKER = 1; - - private CheckBoxPreference mAutoPref; + + private CheckBoxPreference mAutoTimePref; private Preference mTimePref; private Preference mTime24Pref; + private CheckBoxPreference mAutoTimeZonePref; private Preference mTimeZone; private Preference mDatePref; private ListPreference mDateFormat; - + @Override - protected void onCreate(Bundle icicle) { + public void onCreate(Bundle icicle) { super.onCreate(icicle); - + addPreferencesFromResource(R.xml.date_time_prefs); - - initUI(); + + initUI(); } - + private void initUI() { - boolean autoEnabled = getAutoState(); + boolean autoTimeEnabled = getAutoState(Settings.System.AUTO_TIME); + boolean autoTimeZoneEnabled = getAutoState(Settings.System.AUTO_TIME_ZONE); mDummyDate = Calendar.getInstance(); mDummyDate.set(mDummyDate.get(Calendar.YEAR), 11, 31, 13, 0, 0); - - mAutoPref = (CheckBoxPreference) findPreference(KEY_AUTO_TIME); - mAutoPref.setChecked(autoEnabled); + + mAutoTimePref = (CheckBoxPreference) findPreference(KEY_AUTO_TIME); + mAutoTimePref.setChecked(autoTimeEnabled); mTimePref = findPreference("time"); mTime24Pref = findPreference("24 hour"); mTimeZone = findPreference("timezone"); mDatePref = findPreference("date"); mDateFormat = (ListPreference) findPreference(KEY_DATE_FORMAT); - + String [] dateFormats = getResources().getStringArray(R.array.date_format_values); String [] formattedDates = new String[dateFormats.length]; String currentFormat = getDateFormat(); @@ -97,8 +102,8 @@ public class DateTimeSettings } for (int i = 0; i < formattedDates.length; i++) { String formatted = - DateFormat.getDateFormatForSetting(this, dateFormats[i]). - format(mDummyDate.getTime()); + DateFormat.getDateFormatForSetting(getActivity(), dateFormats[i]) + .format(mDummyDate.getTime()); if (dateFormats[i].length() == 0) { formattedDates[i] = getResources(). @@ -107,22 +112,23 @@ public class DateTimeSettings formattedDates[i] = formatted; } } - + mDateFormat.setEntries(formattedDates); mDateFormat.setEntryValues(R.array.date_format_values); mDateFormat.setValue(currentFormat); - - mTimePref.setEnabled(!autoEnabled); - mDatePref.setEnabled(!autoEnabled); - mTimeZone.setEnabled(!autoEnabled); + + mTimePref.setEnabled(!autoTimeEnabled); + mDatePref.setEnabled(!autoTimeEnabled); + mTimeZone.setEnabled(!autoTimeZoneEnabled); } - + @Override - protected void onResume() { + public void onResume() { super.onResume(); - - getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this); + + getPreferenceScreen().getSharedPreferences() + .registerOnSharedPreferenceChangeListener(this); ((CheckBoxPreference)mTime24Pref).setChecked(is24Hour()); @@ -131,74 +137,70 @@ public class DateTimeSettings filter.addAction(Intent.ACTION_TIME_TICK); filter.addAction(Intent.ACTION_TIME_CHANGED); filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); - registerReceiver(mIntentReceiver, filter, null, null); - - updateTimeAndDateDisplay(); + getActivity().registerReceiver(mIntentReceiver, filter, null, null); + + updateTimeAndDateDisplay(getActivity()); } - @Override - protected void onPause() { + @Override + public void onPause() { super.onPause(); - unregisterReceiver(mIntentReceiver); - getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this); + getActivity().unregisterReceiver(mIntentReceiver); + getPreferenceScreen().getSharedPreferences() + .unregisterOnSharedPreferenceChangeListener(this); } - - private void updateTimeAndDateDisplay() { - java.text.DateFormat shortDateFormat = DateFormat.getDateFormat(this); - Date now = Calendar.getInstance().getTime(); + + private void updateTimeAndDateDisplay(Context context) { + java.text.DateFormat shortDateFormat = DateFormat.getDateFormat(context); + final Calendar now = Calendar.getInstance(); Date dummyDate = mDummyDate.getTime(); - mTimePref.setSummary(DateFormat.getTimeFormat(this).format(now)); - mTimeZone.setSummary(getTimeZoneText()); - mDatePref.setSummary(shortDateFormat.format(now)); + mTimePref.setSummary(DateFormat.getTimeFormat(getActivity()).format(now.getTime())); + mTimeZone.setSummary(getTimeZoneText(now.getTimeZone())); + mDatePref.setSummary(shortDateFormat.format(now.getTime())); mDateFormat.setSummary(shortDateFormat.format(dummyDate)); } + @Override public void onDateSet(DatePicker view, int year, int month, int day) { - Calendar c = Calendar.getInstance(); - - c.set(Calendar.YEAR, year); - c.set(Calendar.MONTH, month); - c.set(Calendar.DAY_OF_MONTH, day); - long when = c.getTimeInMillis(); - - if (when / 1000 < Integer.MAX_VALUE) { - SystemClock.setCurrentTimeMillis(when); + setDate(year, month, day); + final Activity activity = getActivity(); + if (activity != null) { + updateTimeAndDateDisplay(activity); } - updateTimeAndDateDisplay(); } + @Override public void onTimeSet(TimePicker view, int hourOfDay, int minute) { - Calendar c = Calendar.getInstance(); - - c.set(Calendar.HOUR_OF_DAY, hourOfDay); - c.set(Calendar.MINUTE, minute); - long when = c.getTimeInMillis(); - - if (when / 1000 < Integer.MAX_VALUE) { - SystemClock.setCurrentTimeMillis(when); + setTime(hourOfDay, minute); + final Activity activity = getActivity(); + if (activity != null) { + updateTimeAndDateDisplay(activity); } - updateTimeAndDateDisplay(); - + // We don't need to call timeUpdated() here because the TIME_CHANGED // broadcast is sent by the AlarmManager as a side effect of setting the // SystemClock time. } + @Override public void onSharedPreferenceChanged(SharedPreferences preferences, String key) { if (key.equals(KEY_DATE_FORMAT)) { - String format = preferences.getString(key, + String format = preferences.getString(key, getResources().getString(R.string.default_date_format)); - Settings.System.putString(getContentResolver(), + Settings.System.putString(getContentResolver(), Settings.System.DATE_FORMAT, format); - updateTimeAndDateDisplay(); + updateTimeAndDateDisplay(getActivity()); } else if (key.equals(KEY_AUTO_TIME)) { boolean autoEnabled = preferences.getBoolean(key, true); - Settings.System.putInt(getContentResolver(), - Settings.System.AUTO_TIME, + Settings.System.putInt(getContentResolver(), Settings.System.AUTO_TIME, autoEnabled ? 1 : 0); mTimePref.setEnabled(!autoEnabled); mDatePref.setEnabled(!autoEnabled); - mTimeZone.setEnabled(!autoEnabled); + } else if (key.equals(KEY_AUTO_TIME_ZONE)) { + boolean autoZoneEnabled = preferences.getBoolean(key, true); + Settings.System.putInt( + getContentResolver(), Settings.System.AUTO_TIME_ZONE, autoZoneEnabled ? 1 : 0); + mTimeZone.setEnabled(!autoZoneEnabled); } } @@ -210,7 +212,7 @@ public class DateTimeSettings case DIALOG_DATEPICKER: { final Calendar calendar = Calendar.getInstance(); d = new DatePickerDialog( - this, + getActivity(), this, calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), @@ -221,11 +223,11 @@ public class DateTimeSettings case DIALOG_TIMEPICKER: { final Calendar calendar = Calendar.getInstance(); d = new TimePickerDialog( - this, + getActivity(), this, calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), - DateFormat.is24HourFormat(this)); + DateFormat.is24HourFormat(getActivity())); d.setTitle(getResources().getString(R.string.date_time_changeTime_text)); break; } @@ -237,6 +239,7 @@ public class DateTimeSettings return d; } + /* @Override public void onPrepareDialog(int id, Dialog d) { switch (id) { @@ -261,7 +264,7 @@ public class DateTimeSettings break; } } - + */ @Override public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { if (preference == mDatePref) { @@ -272,65 +275,76 @@ public class DateTimeSettings showDialog(DIALOG_TIMEPICKER); } else if (preference == mTime24Pref) { set24Hour(((CheckBoxPreference)mTime24Pref).isChecked()); - updateTimeAndDateDisplay(); + updateTimeAndDateDisplay(getActivity()); timeUpdated(); - } else if (preference == mTimeZone) { - Intent intent = new Intent(); - intent.setClass(this, ZoneList.class); - startActivityForResult(intent, 0); } - return false; + return super.onPreferenceTreeClick(preferenceScreen, preference); } - + @Override - protected void onActivityResult(int requestCode, int resultCode, + public void onActivityResult(int requestCode, int resultCode, Intent data) { - updateTimeAndDateDisplay(); + updateTimeAndDateDisplay(getActivity()); } - + private void timeUpdated() { Intent timeChanged = new Intent(Intent.ACTION_TIME_CHANGED); - sendBroadcast(timeChanged); + getActivity().sendBroadcast(timeChanged); } - + /* Get & Set values from the system settings */ - + private boolean is24Hour() { - return DateFormat.is24HourFormat(this); + return DateFormat.is24HourFormat(getActivity()); } - + private void set24Hour(boolean is24Hour) { Settings.System.putString(getContentResolver(), Settings.System.TIME_12_24, is24Hour? HOURS_24 : HOURS_12); } - + private String getDateFormat() { - return Settings.System.getString(getContentResolver(), + return Settings.System.getString(getContentResolver(), Settings.System.DATE_FORMAT); } - - private boolean getAutoState() { + + private boolean getAutoState(String name) { try { - return Settings.System.getInt(getContentResolver(), - Settings.System.AUTO_TIME) > 0; + return Settings.System.getInt(getContentResolver(), name) > 0; } catch (SettingNotFoundException snfe) { return true; } } - private void setDateFormat(String format) { - if (format.length() == 0) { - format = null; + /* Helper routines to format timezone */ + + /* package */ static void setDate(int year, int month, int day) { + Calendar c = Calendar.getInstance(); + + c.set(Calendar.YEAR, year); + c.set(Calendar.MONTH, month); + c.set(Calendar.DAY_OF_MONTH, day); + long when = c.getTimeInMillis(); + + if (when / 1000 < Integer.MAX_VALUE) { + SystemClock.setCurrentTimeMillis(when); } + } + + /* package */ static void setTime(int hourOfDay, int minute) { + Calendar c = Calendar.getInstance(); + + c.set(Calendar.HOUR_OF_DAY, hourOfDay); + c.set(Calendar.MINUTE, minute); + long when = c.getTimeInMillis(); - Settings.System.putString(getContentResolver(), Settings.System.DATE_FORMAT, format); + if (when / 1000 < Integer.MAX_VALUE) { + SystemClock.setCurrentTimeMillis(when); + } } - - /* Helper routines to format timezone */ - - private String getTimeZoneText() { - TimeZone tz = java.util.Calendar.getInstance().getTimeZone(); + + /* package */ static String getTimeZoneText(TimeZone tz) { boolean daylight = tz.inDaylightTime(new Date()); StringBuilder sb = new StringBuilder(); @@ -339,10 +353,10 @@ public class DateTimeSettings append(", "). append(tz.getDisplayName(daylight, TimeZone.LONG)); - return sb.toString(); + return sb.toString(); } - private char[] formatOffset(int off) { + private static char[] formatOffset(int off) { off = off / 1000 / 60; char[] buf = new char[9]; @@ -357,7 +371,7 @@ public class DateTimeSettings buf[3] = '+'; } - int hours = off / 60; + int hours = off / 60; int minutes = off % 60; buf[4] = (char) ('0' + hours / 10); @@ -370,11 +384,14 @@ public class DateTimeSettings return buf; } - + private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - updateTimeAndDateDisplay(); + final Activity activity = getActivity(); + if (activity != null) { + updateTimeAndDateDisplay(activity); + } } }; } diff --git a/src/com/android/settings/DateTimeSettingsSetupWizard.java b/src/com/android/settings/DateTimeSettingsSetupWizard.java index a6a60c1b2..61e5255a7 100644 --- a/src/com/android/settings/DateTimeSettingsSetupWizard.java +++ b/src/com/android/settings/DateTimeSettingsSetupWizard.java @@ -16,25 +16,205 @@ package com.android.settings; +import com.android.settings.ZonePicker.ZoneSelectionListener; + +import android.app.Activity; +import android.app.StatusBarManager; +import android.content.Context; +import android.content.res.Configuration; import android.os.Bundle; +import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; +import android.util.Log; import android.view.View; -import android.view.Window; import android.view.View.OnClickListener; +import android.view.Window; +import android.view.inputmethod.InputMethodManager; +import android.widget.Button; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.DatePicker; +import android.widget.TimePicker; + +import java.util.Calendar; +import java.util.TimeZone; + +public class DateTimeSettingsSetupWizard extends Activity + implements OnClickListener, ZoneSelectionListener, OnCheckedChangeListener{ + private static final String TAG = DateTimeSettingsSetupWizard.class.getSimpleName(); + + private boolean mXLargeScreenSize; + + /* Available only in XL */ + private CompoundButton mAutoDateTimeButton; + private CompoundButton mAutoTimeZoneButton; + private Button mTimeZone; + private TimePicker mTimePicker; + private DatePicker mDatePicker; + private InputMethodManager mInputMethodManager; + + private StatusBarManager mStatusBarManager; -public class DateTimeSettingsSetupWizard extends DateTimeSettings implements OnClickListener { - private View mNextButton; - @Override - protected void onCreate(Bundle icicle) { + protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE); - super.onCreate(icicle); + super.onCreate(savedInstanceState); setContentView(R.layout.date_time_settings_setupwizard); - mNextButton = findViewById(R.id.next_button); - mNextButton.setOnClickListener(this); + mStatusBarManager = (StatusBarManager)getSystemService(Context.STATUS_BAR_SERVICE); + + mXLargeScreenSize = (getResources().getConfiguration().screenLayout + & Configuration.SCREENLAYOUT_SIZE_MASK) + == Configuration.SCREENLAYOUT_SIZE_XLARGE; + if (mXLargeScreenSize) { + initUiForXl(); + } else { + findViewById(R.id.next_button).setOnClickListener(this); + } + } + + public void initUiForXl() { + final boolean autoTimeZoneEnabled = isAutoTimeZoneEnabled(); + mAutoTimeZoneButton = (CompoundButton)findViewById(R.id.time_zone_auto); + mAutoTimeZoneButton.setChecked(autoTimeZoneEnabled); + mAutoTimeZoneButton.setOnCheckedChangeListener(this); + mAutoTimeZoneButton.setText(autoTimeZoneEnabled ? R.string.zone_auto_summaryOn : + R.string.zone_auto_summaryOff); + + final TimeZone tz = TimeZone.getDefault(); + mTimeZone = (Button)findViewById(R.id.current_time_zone); + mTimeZone.setText(DateTimeSettings.getTimeZoneText(tz)); + mTimeZone.setOnClickListener(this); + mTimeZone.setEnabled(!autoTimeZoneEnabled); + + final boolean autoDateTimeEnabled = isAutoDateTimeEnabled(); + mAutoDateTimeButton = (CompoundButton)findViewById(R.id.date_time_auto); + 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); + mTimePicker.setEnabled(!autoDateTimeEnabled); + mDatePicker = (DatePicker)findViewById(R.id.date_picker); + mDatePicker.setEnabled(!autoDateTimeEnabled); + + mInputMethodManager = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); + + ((ZonePicker)getFragmentManager().findFragmentById(R.id.zone_picker_fragment)) + .setZoneSelectionListener(this); + + ((Button)findViewById(R.id.next_button)).setOnClickListener(this); + ((Button)findViewById(R.id.skip_button)).setOnClickListener(this); + + if (mStatusBarManager != null) { + mStatusBarManager.disable(StatusBarManager.DISABLE_EXPAND + | StatusBarManager.DISABLE_NOTIFICATION_ICONS + | StatusBarManager.DISABLE_NOTIFICATION_ALERTS + | StatusBarManager.DISABLE_SYSTEM_INFO + | StatusBarManager.DISABLE_NAVIGATION); + } else { + Log.e(TAG, "StatusBarManager isn't available."); + } + } + + @Override + public void onDestroy() { + if (mStatusBarManager != null) { + mStatusBarManager.disable(StatusBarManager.DISABLE_NONE); + } + super.onDestroy(); + } + + @Override + public void onClick(View view) { + switch (view.getId()) { + case R.id.current_time_zone: { + findViewById(R.id.current_time_zone).setVisibility(View.GONE); + findViewById(R.id.zone_picker).setVisibility(View.VISIBLE); + break; + } + case R.id.next_button: { + if (mXLargeScreenSize) { + Settings.System.putInt(getContentResolver(), Settings.System.AUTO_TIME_ZONE, + mAutoTimeZoneButton.isChecked() ? 1 : 0); + Settings.System.putInt(getContentResolver(), Settings.System.AUTO_TIME, + mAutoDateTimeButton.isChecked() ? 1 : 0); + // Note: in non-XL, Date & Time is stored by DatePickerDialog/TimePickerDialog, + // so we don't need to save those values there, while in XL, we need to as + // we don't use those Dialogs. + DateTimeSettings.setDate(mDatePicker.getYear(), mDatePicker.getMonth(), + mDatePicker.getDayOfMonth()); + DateTimeSettings.setTime( + mTimePicker.getCurrentHour(), mTimePicker.getCurrentMinute()); + } + } // $FALL-THROUGH$ + case R.id.skip_button: { + setResult(RESULT_OK); + finish(); + break; + } + } + } + + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + final boolean autoEnabled = isChecked; // just for readibility. + if (buttonView == mAutoTimeZoneButton) { + // In XL screen, we save all the state only when the next button is pressed. + if (!mXLargeScreenSize) { + Settings.System.putInt(getContentResolver(), + Settings.System.AUTO_TIME_ZONE, + isChecked ? 1 : 0); + } + mTimeZone.setEnabled(!autoEnabled); + if (isChecked) { + findViewById(R.id.current_time_zone).setVisibility(View.VISIBLE); + findViewById(R.id.zone_picker).setVisibility(View.GONE); + } + } else if (buttonView == mAutoDateTimeButton) { + if (!mXLargeScreenSize) { + Settings.System.putInt(getContentResolver(), + Settings.System.AUTO_TIME, + isChecked ? 1 : 0); + } + mTimePicker.setEnabled(!autoEnabled); + mDatePicker.setEnabled(!autoEnabled); + } + if (autoEnabled) { + final View focusedView = getCurrentFocus(); + if (focusedView != null) { + mInputMethodManager.hideSoftInputFromWindow(focusedView.getWindowToken(), 0); + focusedView.clearFocus(); + } + } + } + + @Override + public void onZoneSelected(TimeZone tz) { + findViewById(R.id.current_time_zone).setVisibility(View.VISIBLE); + findViewById(R.id.zone_picker).setVisibility(View.GONE); + final Calendar now = Calendar.getInstance(tz); + mTimeZone.setText(DateTimeSettings.getTimeZoneText(tz)); + mDatePicker.updateDate(now.get(Calendar.YEAR), now.get(Calendar.MONTH), + now.get(Calendar.DAY_OF_MONTH)); + mTimePicker.setCurrentHour(now.get(Calendar.HOUR)); + mTimePicker.setCurrentMinute(now.get(Calendar.MINUTE)); + } + + private boolean isAutoDateTimeEnabled() { + try { + return Settings.System.getInt(getContentResolver(), Settings.System.AUTO_TIME) > 0; + } catch (SettingNotFoundException e) { + return true; + } } - public void onClick(View v) { - setResult(RESULT_OK); - finish(); + private boolean isAutoTimeZoneEnabled() { + try { + return Settings.System.getInt(getContentResolver(), + Settings.System.AUTO_TIME_ZONE) > 0; + } catch (SettingNotFoundException e) { + return true; + } } } diff --git a/src/com/android/settings/DeviceInfoSettings.java b/src/com/android/settings/DeviceInfoSettings.java index 048d10aa6..0d6549aa0 100644 --- a/src/com/android/settings/DeviceInfoSettings.java +++ b/src/com/android/settings/DeviceInfoSettings.java @@ -16,17 +16,16 @@ package com.android.settings; +import android.app.Activity; import android.content.Intent; import android.os.Build; import android.os.Bundle; import android.os.SystemClock; import android.os.SystemProperties; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; import android.util.Log; -import android.view.MotionEvent; import java.io.BufferedReader; import java.io.FileReader; @@ -34,7 +33,7 @@ import java.io.IOException; import java.util.regex.Matcher; import java.util.regex.Pattern; -public class DeviceInfoSettings extends PreferenceActivity { +public class DeviceInfoSettings extends SettingsPreferenceFragment { private static final String TAG = "DeviceInfoSettings"; private static final String KEY_CONTAINER = "container"; @@ -49,7 +48,7 @@ public class DeviceInfoSettings extends PreferenceActivity { long[] mHits = new long[3]; @Override - protected void onCreate(Bundle icicle) { + public void onCreate(Bundle icicle) { super.onCreate(icicle); addPreferencesFromResource(R.xml.device_info_settings); @@ -69,24 +68,24 @@ public class DeviceInfoSettings extends PreferenceActivity { * Settings is a generic app and should not contain any device-specific * info. */ - + final Activity act = getActivity(); // These are contained in the "container" preference group PreferenceGroup parentPreference = (PreferenceGroup) findPreference(KEY_CONTAINER); - Utils.updatePreferenceToSpecificActivityOrRemove(this, parentPreference, KEY_TERMS, + Utils.updatePreferenceToSpecificActivityOrRemove(act, parentPreference, KEY_TERMS, Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY); - Utils.updatePreferenceToSpecificActivityOrRemove(this, parentPreference, KEY_LICENSE, + Utils.updatePreferenceToSpecificActivityOrRemove(act, parentPreference, KEY_LICENSE, Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY); - Utils.updatePreferenceToSpecificActivityOrRemove(this, parentPreference, KEY_COPYRIGHT, + Utils.updatePreferenceToSpecificActivityOrRemove(act, parentPreference, KEY_COPYRIGHT, Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY); - Utils.updatePreferenceToSpecificActivityOrRemove(this, parentPreference, KEY_TEAM, + Utils.updatePreferenceToSpecificActivityOrRemove(act, parentPreference, KEY_TEAM, Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY); // These are contained by the root preference screen parentPreference = getPreferenceScreen(); - Utils.updatePreferenceToSpecificActivityOrRemove(this, parentPreference, + Utils.updatePreferenceToSpecificActivityOrRemove(act, parentPreference, KEY_SYSTEM_UPDATE_SETTINGS, Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY); - Utils.updatePreferenceToSpecificActivityOrRemove(this, parentPreference, KEY_CONTRIBUTORS, + Utils.updatePreferenceToSpecificActivityOrRemove(act, parentPreference, KEY_CONTRIBUTORS, Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY); } diff --git a/src/com/android/settings/DisplaySettings.java b/src/com/android/settings/DisplaySettings.java index fbb07c13c..5945884af 100644 --- a/src/com/android/settings/DisplaySettings.java +++ b/src/com/android/settings/DisplaySettings.java @@ -18,8 +18,6 @@ package com.android.settings; import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT; -import java.util.ArrayList; - import android.app.admin.DevicePolicyManager; import android.content.ContentResolver; import android.content.Context; @@ -29,13 +27,14 @@ import android.os.ServiceManager; import android.preference.CheckBoxPreference; import android.preference.ListPreference; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceScreen; import android.provider.Settings; import android.util.Log; import android.view.IWindowManager; -public class DisplaySettings extends PreferenceActivity implements +import java.util.ArrayList; + +public class DisplaySettings extends SettingsPreferenceFragment implements Preference.OnPreferenceChangeListener { private static final String TAG = "DisplaySettings"; @@ -53,9 +52,9 @@ public class DisplaySettings extends PreferenceActivity implements private IWindowManager mWindowManager; @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - ContentResolver resolver = getContentResolver(); + ContentResolver resolver = getActivity().getContentResolver(); mWindowManager = IWindowManager.Stub.asInterface(ServiceManager.getService("window")); addPreferencesFromResource(R.xml.display_settings); @@ -75,7 +74,8 @@ public class DisplaySettings extends PreferenceActivity implements private void disableUnusableTimeouts(ListPreference screenTimeoutPreference) { final DevicePolicyManager dpm = - (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); + (DevicePolicyManager) getActivity().getSystemService( + Context.DEVICE_POLICY_SERVICE); final long maxTimeout = dpm != null ? dpm.getMaximumTimeToLock(null) : 0; if (maxTimeout == 0) { return; // policy not enforced @@ -109,7 +109,7 @@ public class DisplaySettings extends PreferenceActivity implements } @Override - protected void onResume() { + public void onResume() { super.onResume(); updateState(true); diff --git a/src/com/android/settings/DockSettings.java b/src/com/android/settings/DockSettings.java index 0d46ce9a9..b36864021 100644 --- a/src/com/android/settings/DockSettings.java +++ b/src/com/android/settings/DockSettings.java @@ -16,6 +16,8 @@ package com.android.settings; +import com.android.settings.bluetooth.DockEventReceiver; + import android.app.AlertDialog; import android.app.Dialog; import android.bluetooth.BluetoothDevice; @@ -27,13 +29,10 @@ import android.content.IntentFilter; import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceScreen; import android.provider.Settings; -import com.android.settings.bluetooth.DockEventReceiver; - -public class DockSettings extends PreferenceActivity { +public class DockSettings extends SettingsPreferenceFragment { private static final int DIALOG_NOT_DOCKED = 1; private static final String KEY_AUDIO_SETTINGS = "dock_audio"; @@ -52,7 +51,7 @@ public class DockSettings extends PreferenceActivity { }; @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ContentResolver resolver = getContentResolver(); addPreferencesFromResource(R.xml.dock_settings); @@ -61,18 +60,18 @@ public class DockSettings extends PreferenceActivity { } @Override - protected void onResume() { + public void onResume() { super.onResume(); IntentFilter filter = new IntentFilter(Intent.ACTION_DOCK_EVENT); - registerReceiver(mReceiver, filter); + getActivity().registerReceiver(mReceiver, filter); } @Override - protected void onPause() { + public void onPause() { super.onPause(); - unregisterReceiver(mReceiver); + getActivity().unregisterReceiver(mReceiver); } private void initDockSettings() { @@ -120,7 +119,7 @@ public class DockSettings extends PreferenceActivity { if (dockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) { // remove undocked dialog if currently showing. try { - dismissDialog(DIALOG_NOT_DOCKED); + removeDialog(DIALOG_NOT_DOCKED); } catch (IllegalArgumentException iae) { // Maybe it was already dismissed } @@ -139,8 +138,8 @@ public class DockSettings extends PreferenceActivity { } else { Intent i = new Intent(mDockIntent); i.setAction(DockEventReceiver.ACTION_DOCK_SHOW_UI); - i.setClass(this, DockEventReceiver.class); - sendBroadcast(i); + i.setClass(getActivity(), DockEventReceiver.class); + getActivity().sendBroadcast(i); } } else if (preference == mDockSounds) { Settings.System.putInt(getContentResolver(), Settings.System.DOCK_SOUNDS_ENABLED, @@ -159,7 +158,7 @@ public class DockSettings extends PreferenceActivity { } private Dialog createUndockedMessage() { - final AlertDialog.Builder ab = new AlertDialog.Builder(this); + final AlertDialog.Builder ab = new AlertDialog.Builder(getActivity()); ab.setTitle(R.string.dock_not_found_title); ab.setMessage(R.string.dock_not_found_text); ab.setPositiveButton(android.R.string.ok, null); diff --git a/src/com/android/settings/IconPreferenceScreen.java b/src/com/android/settings/IconPreferenceScreen.java index 31abf0a86..64fce29de 100644 --- a/src/com/android/settings/IconPreferenceScreen.java +++ b/src/com/android/settings/IconPreferenceScreen.java @@ -22,13 +22,16 @@ import android.graphics.drawable.Drawable; import android.preference.Preference; import android.util.AttributeSet; import android.view.View; -import android.view.ViewGroup; import android.widget.ImageView; +import android.widget.TextView; public class IconPreferenceScreen extends Preference { private Drawable mIcon; + // Whether or not the text and icon should be highlighted (as selected) + private boolean mHighlight; + public IconPreferenceScreen(Context context, AttributeSet attrs) { this(context, attrs, 0); } @@ -48,6 +51,7 @@ public class IconPreferenceScreen extends Preference { if (imageView != null && mIcon != null) { imageView.setImageDrawable(mIcon); } + TextView textView = (TextView) view.findViewById(android.R.id.title); } /** @@ -71,4 +75,9 @@ public class IconPreferenceScreen extends Preference { public Drawable getIcon() { return mIcon; } + + public void setHighlighted(boolean highlight) { + mHighlight = highlight; + notifyChanged(); + } } diff --git a/src/com/android/settings/LanguageSettings.java b/src/com/android/settings/LanguageSettings.java index 1252eec26..2dab6d9f6 100644 --- a/src/com/android/settings/LanguageSettings.java +++ b/src/com/android/settings/LanguageSettings.java @@ -16,24 +16,21 @@ package com.android.settings; +import android.app.Activity; import android.app.AlertDialog; -import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.res.Configuration; import android.os.Bundle; -import android.os.Environment; -import android.os.SystemProperties; import android.preference.CheckBoxPreference; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; import android.provider.Settings; import android.text.TextUtils; -import android.view.View.OnClickListener; +import android.util.Log; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; @@ -41,9 +38,12 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; -public class LanguageSettings extends PreferenceActivity { - +public class LanguageSettings extends SettingsPreferenceFragment { + private static final String TAG = LanguageSettings.class.getSimpleName(); + private static final String KEY_PHONE_LANGUAGE = "phone_language"; + private static final String KEY_INPUT_METHOD = "input_method"; + private boolean mHaveHardKeyboard; private List<InputMethodInfo> mInputMethodProperties; @@ -63,12 +63,12 @@ public class LanguageSettings extends PreferenceActivity { } @Override - protected void onCreate(Bundle icicle) { + public void onCreate(Bundle icicle) { super.onCreate(icicle); addPreferencesFromResource(R.xml.language_settings); - if (getAssets().getLocales().length == 1) { + if (getActivity().getAssets().getLocales().length == 1) { getPreferenceScreen(). removePreference(findPreference("language_category")); } else { @@ -107,12 +107,12 @@ public class LanguageSettings extends PreferenceActivity { InputMethodInfo property = mInputMethodProperties.get(i); String prefKey = property.getId(); - CharSequence label = property.loadLabel(getPackageManager()); + CharSequence label = property.loadLabel(getActivity().getPackageManager()); boolean systemIME = isSystemIme(property); // Add a check box. // Don't show the toggle if it's the only keyboard in the system, or it's a system IME. if (mHaveHardKeyboard || (N > 1 && !systemIME)) { - CheckBoxPreference chkbxPref = new CheckBoxPreference(this); + CheckBoxPreference chkbxPref = new CheckBoxPreference(getActivity()); chkbxPref.setKey(prefKey); chkbxPref.setTitle(label); textCategory.addPreference(chkbxPref); @@ -121,7 +121,7 @@ public class LanguageSettings extends PreferenceActivity { // If setting activity is available, add a setting screen entry. if (null != property.getSettingsActivity()) { - PreferenceScreen prefScreen = new PreferenceScreen(this, null); + PreferenceScreen prefScreen = new PreferenceScreen(getActivity(), null); String settingsActivity = property.getSettingsActivity(); if (settingsActivity.lastIndexOf("/") < 0) { settingsActivity = property.getPackageName() + "/" + settingsActivity; @@ -129,7 +129,8 @@ public class LanguageSettings extends PreferenceActivity { prefScreen.setKey(settingsActivity); prefScreen.setTitle(label); if (N == 1) { - prefScreen.setSummary(getString(R.string.onscreen_keyboard_settings_summary)); + prefScreen.setSummary(getResources().getString( + R.string.onscreen_keyboard_settings_summary)); } else { CharSequence settingsLabel = getResources().getString( R.string.input_methods_settings_label_format, label); @@ -141,7 +142,7 @@ public class LanguageSettings extends PreferenceActivity { } @Override - protected void onResume() { + public void onResume() { super.onResume(); final HashSet<String> enabled = new HashSet<String>(); @@ -178,7 +179,7 @@ public class LanguageSettings extends PreferenceActivity { } @Override - protected void onPause() { + public void onPause() { super.onPause(); StringBuilder builder = new StringBuilder(256); @@ -258,7 +259,8 @@ public class LanguageSettings extends PreferenceActivity { return super.onPreferenceTreeClick(preferenceScreen, preference); } if (mDialog == null) { - mDialog = (new AlertDialog.Builder(this)) + // TODO: DialogFragment? + mDialog = (new AlertDialog.Builder(getActivity())) .setTitle(android.R.string.dialog_alert_title) .setIcon(android.R.drawable.ic_dialog_alert) .setCancelable(true) @@ -282,15 +284,21 @@ public class LanguageSettings extends PreferenceActivity { mDialog.dismiss(); } } - mDialog.setMessage(getString(R.string.ime_security_warning, - selImi.getServiceInfo().applicationInfo.loadLabel( - getPackageManager()))); + mDialog.setMessage(getResources().getString( + R.string.ime_security_warning, + selImi.getServiceInfo().applicationInfo.loadLabel(getPackageManager()))); mDialog.show(); } else if (id.equals(mLastTickedInputMethodId)) { mLastTickedInputMethodId = null; } } else if (preference instanceof PreferenceScreen) { - if (preference.getIntent() == null) { + if (preference.getFragment() != null) { + // Fragment will be handled correctly by the super class. + } else if (KEY_INPUT_METHOD.equals(preference.getKey())) { + final InputMethodManager imm = (InputMethodManager) + getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showInputMethodPicker(); + } else if (preference.getIntent() == null) { PreferenceScreen pref = (PreferenceScreen) preference; String activityName = pref.getKey(); String packageName = activityName.substring(0, activityName @@ -311,7 +319,7 @@ public class LanguageSettings extends PreferenceActivity { } @Override - protected void onDestroy() { + public void onDestroy() { super.onDestroy(); if (mDialog != null) { mDialog.dismiss(); diff --git a/src/com/android/settings/LocalePicker.java b/src/com/android/settings/LocalePicker.java index dcd61414b..8b94ccb00 100644 --- a/src/com/android/settings/LocalePicker.java +++ b/src/com/android/settings/LocalePicker.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * 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. @@ -16,176 +16,18 @@ package com.android.settings; -import android.app.ActivityManagerNative; -import android.app.IActivityManager; -import android.app.ListActivity; -import android.app.backup.BackupManager; -import android.content.res.Configuration; -import android.os.Bundle; -import android.os.RemoteException; -import android.os.SystemProperties; -import android.util.Log; -import android.view.View; -import android.widget.ArrayAdapter; -import android.widget.ListView; - -import java.io.BufferedWriter; -import java.io.FileOutputStream; -import java.text.Collator; -import java.util.Arrays; import java.util.Locale; -public class LocalePicker extends ListActivity { - private static final String TAG = "LocalePicker"; - private static final boolean DEBUG = false; - - Loc[] mLocales; - String[] mSpecialLocaleCodes; - String[] mSpecialLocaleNames; - - private static class Loc implements Comparable { - static Collator sCollator = Collator.getInstance(); - - String label; - Locale locale; - - public Loc(String label, Locale locale) { - this.label = label; - this.locale = locale; - } - - @Override - public String toString() { - return this.label; - } - - public int compareTo(Object o) { - return sCollator.compare(this.label, ((Loc) o).label); - } - } - - int getContentView() { - return R.layout.locale_picker; - } - - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - setContentView(getContentView()); - - mSpecialLocaleCodes = getResources().getStringArray(R.array.special_locale_codes); - mSpecialLocaleNames = getResources().getStringArray(R.array.special_locale_names); - - String[] locales = getAssets().getLocales(); - Arrays.sort(locales); - - final int origSize = locales.length; - Loc[] preprocess = new Loc[origSize]; - int finalSize = 0; - for (int i = 0 ; i < origSize; i++ ) { - String s = locales[i]; - int len = s.length(); - if (len == 5) { - String language = s.substring(0, 2); - String country = s.substring(3, 5); - Locale l = new Locale(language, country); - - if (finalSize == 0) { - if (DEBUG) { - Log.v(TAG, "adding initial "+ toTitleCase(l.getDisplayLanguage(l))); - } - preprocess[finalSize++] = - new Loc(toTitleCase(l.getDisplayLanguage(l)), l); - } else { - // check previous entry: - // same lang and a country -> upgrade to full name and - // insert ours with full name - // diff lang -> insert ours with lang-only name - if (preprocess[finalSize-1].locale.getLanguage().equals( - language)) { - if (DEBUG) { - Log.v(TAG, "backing up and fixing "+ - preprocess[finalSize-1].label+" to "+ - getDisplayName(preprocess[finalSize-1].locale)); - } - preprocess[finalSize-1].label = toTitleCase( - getDisplayName(preprocess[finalSize-1].locale)); - if (DEBUG) { - Log.v(TAG, " and adding "+ toTitleCase(getDisplayName(l))); - } - preprocess[finalSize++] = - new Loc(toTitleCase(getDisplayName(l)), l); - } else { - String displayName; - if (s.equals("zz_ZZ")) { - displayName = "Pseudo..."; - } else { - displayName = toTitleCase(l.getDisplayLanguage(l)); - } - if (DEBUG) { - Log.v(TAG, "adding "+displayName); - } - preprocess[finalSize++] = new Loc(displayName, l); - } - } - } - } - mLocales = new Loc[finalSize]; - for (int i = 0; i < finalSize ; i++) { - mLocales[i] = preprocess[i]; - } - Arrays.sort(mLocales); - int layoutId = R.layout.locale_picker_item; - int fieldId = R.id.locale; - ArrayAdapter<Loc> adapter = - new ArrayAdapter<Loc>(this, layoutId, fieldId, mLocales); - getListView().setAdapter(adapter); - } - - private static String toTitleCase(String s) { - if (s.length() == 0) { - return s; - } - - return Character.toUpperCase(s.charAt(0)) + s.substring(1); - } - - private String getDisplayName(Locale l) { - String code = l.toString(); - - for (int i = 0; i < mSpecialLocaleCodes.length; i++) { - if (mSpecialLocaleCodes[i].equals(code)) { - return mSpecialLocaleNames[i]; - } - } - - return l.getDisplayName(l); - } - - @Override - public void onResume() { - super.onResume(); - getListView().requestFocus(); +public class LocalePicker extends com.android.internal.app.LocalePicker + implements com.android.internal.app.LocalePicker.LocaleSelectionListener { + public LocalePicker() { + super(); + setLocaleSelectionListener(this); } @Override - protected void onListItemClick(ListView l, View v, int position, long id) { - try { - IActivityManager am = ActivityManagerNative.getDefault(); - Configuration config = am.getConfiguration(); - - Loc loc = mLocales[position]; - config.locale = loc.locale; - - // indicate this isn't some passing default - the user wants this remembered - config.userSetLocale = true; - - am.updateConfiguration(config); - // Trigger the dirty bit for the Settings Provider. - BackupManager.dataChanged("com.android.providers.settings"); - } catch (RemoteException e) { - // Intentionally left blank - } - finish(); + public void onLocaleSelected(Locale locale) { + getActivity().onBackPressed(); + LocalePicker.updateLocale(locale); } } diff --git a/src/com/android/settings/LocalePickerInSetupWizard.java b/src/com/android/settings/LocalePickerInSetupWizard.java deleted file mode 100644 index b160e899b..000000000 --- a/src/com/android/settings/LocalePickerInSetupWizard.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2007 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.app.ActivityManagerNative; -import android.app.IActivityManager; -import android.app.ListActivity; -import android.content.res.Configuration; -import android.os.Bundle; -import android.os.RemoteException; -import android.os.SystemProperties; -import android.util.Log; -import android.view.View; -import android.widget.ArrayAdapter; -import android.widget.ListView; - -import java.io.BufferedWriter; -import java.io.FileOutputStream; -import java.util.Arrays; -import java.util.Locale; - -public class LocalePickerInSetupWizard extends LocalePicker { - - @Override - int getContentView() { - return R.layout.locale_picker_in_setupwizard; - } - -} diff --git a/src/com/android/settings/ManageAccountsSettings.java b/src/com/android/settings/ManageAccountsSettings.java new file mode 100644 index 000000000..f138674bf --- /dev/null +++ b/src/com/android/settings/ManageAccountsSettings.java @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2008 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 com.google.android.collect.Maps; + +import com.android.settings.SettingsPreferenceFragment.SettingsDialogFragment; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AuthenticatorDescription; +import android.accounts.OnAccountsUpdateListener; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.ContentResolver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SyncAdapterType; +import android.content.SyncInfo; +import android.content.SyncStatusInfo; +import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; +import android.net.ConnectivityManager; +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.Preference; +import android.preference.PreferenceCategory; +import android.preference.PreferenceFragment; +import android.preference.PreferenceScreen; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +public class ManageAccountsSettings extends PreferenceFragment + implements View.OnClickListener, OnAccountsUpdateListener, DialogCreatable { + private static final String TAG = ManageAccountsSettings.class.getSimpleName(); + + private static final String AUTHORITIES_FILTER_KEY = "authorities"; + private static final boolean LDEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private static final String AUTO_SYNC_CHECKBOX_KEY = "syncAutomaticallyCheckBox"; + private static final String MANAGE_ACCOUNTS_CATEGORY_KEY = "manageAccountsCategory"; + private static final String BACKGROUND_DATA_CHECKBOX_KEY = "backgroundDataCheckBox"; + private static final int DIALOG_DISABLE_BACKGROUND_DATA = 1; + + private CheckBoxPreference mBackgroundDataCheckBox; + private PreferenceCategory mManageAccountsCategory; + private String[] mAuthorities; + private TextView mErrorInfoView; + private Button mAddAccountButton; + private CheckBoxPreference mAutoSyncCheckbox; + + private SettingsDialogFragment mDialogFragment; + + private AuthenticatorDescription[] mAuthDescs; + private Map<String, AuthenticatorDescription> mTypeToAuthDescription + = new HashMap<String, AuthenticatorDescription>(); + private HashMap<String, ArrayList<String>> mAccountTypeToAuthorities = null; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.manage_accounts_screen, container, false); + return view; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + final Activity activity = getActivity(); + addPreferencesFromResource(R.xml.manage_accounts_settings); + final View view = getView(); + + mErrorInfoView = (TextView)view.findViewById(R.id.sync_settings_error_info); + mErrorInfoView.setVisibility(View.GONE); + mErrorInfoView.setCompoundDrawablesWithIntrinsicBounds( + activity.getResources().getDrawable(R.drawable.ic_list_syncerror), + null, null, null); + + mBackgroundDataCheckBox = (CheckBoxPreference) findPreference(BACKGROUND_DATA_CHECKBOX_KEY); + mAutoSyncCheckbox = (CheckBoxPreference) findPreference(AUTO_SYNC_CHECKBOX_KEY); + + mManageAccountsCategory = (PreferenceCategory)findPreference(MANAGE_ACCOUNTS_CATEGORY_KEY); + mAuthorities = activity.getIntent().getStringArrayExtra(AUTHORITIES_FILTER_KEY); + mAddAccountButton = (Button)view.findViewById(R.id.add_account_button); + mAddAccountButton.setOnClickListener(this); + + AccountManager.get(activity).addOnAccountsUpdatedListener(this, null, true); + updateAuthDescriptions(activity); + } + + @Override + public void onDestroy() { + AccountManager.get(getActivity()).removeOnAccountsUpdatedListener(this); + super.onDestroy(); + } + + @Override + public boolean onPreferenceTreeClick(PreferenceScreen preferences, Preference preference) { + if (preference == mBackgroundDataCheckBox) { + final ConnectivityManager connManager = (ConnectivityManager) + getActivity().getSystemService(Context.CONNECTIVITY_SERVICE); + final boolean oldBackgroundDataSetting = connManager.getBackgroundDataSetting(); + final boolean backgroundDataSetting = mBackgroundDataCheckBox.isChecked(); + if (oldBackgroundDataSetting != backgroundDataSetting) { + if (backgroundDataSetting) { + setBackgroundDataInt(true); + onSyncStateUpdated(); + } else { + // This will get unchecked only if the user hits "Ok" + mBackgroundDataCheckBox.setChecked(true); + showDialog(DIALOG_DISABLE_BACKGROUND_DATA); + } + } + } else if (preference == mAutoSyncCheckbox) { + ContentResolver.setMasterSyncAutomatically(mAutoSyncCheckbox.isChecked()); + onSyncStateUpdated(); + } else { + return false; + } + return true; + } + + @Override + public Dialog onCreateDialog(int id) { + switch (id) { + case DIALOG_DISABLE_BACKGROUND_DATA: + final CheckBoxPreference pref = + (CheckBoxPreference) findPreference(BACKGROUND_DATA_CHECKBOX_KEY); + return new AlertDialog.Builder(getActivity()) + .setTitle(R.string.background_data_dialog_title) + .setIcon(android.R.drawable.ic_dialog_alert) + .setMessage(R.string.background_data_dialog_message) + .setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + setBackgroundDataInt(false); + pref.setChecked(false); + onSyncStateUpdated(); + } + }) + .setNegativeButton(android.R.string.cancel, null) + .create(); + } + + return null; + } + + void showDialog(int dialogId) { + if (mDialogFragment != null) { + Log.e(TAG, "Old dialog fragment not null!"); + } + mDialogFragment = new SettingsDialogFragment(this, dialogId); + mDialogFragment.show(getActivity().getFragmentManager(), Integer.toString(dialogId)); + } + + private void setBackgroundDataInt(boolean enabled) { + final ConnectivityManager connManager = (ConnectivityManager) + getActivity().getSystemService(Context.CONNECTIVITY_SERVICE); + connManager.setBackgroundDataSetting(enabled); + } + + private void onSyncStateUpdated() { + // Set background connection state + final ConnectivityManager connManager = (ConnectivityManager) + getActivity().getSystemService(Context.CONNECTIVITY_SERVICE); + final boolean backgroundDataSetting = connManager.getBackgroundDataSetting(); + mBackgroundDataCheckBox.setChecked(backgroundDataSetting); + boolean masterSyncAutomatically = ContentResolver.getMasterSyncAutomatically(); + mAutoSyncCheckbox.setChecked(masterSyncAutomatically); + + // iterate over all the preferences, setting the state properly for each + SyncInfo currentSync = ContentResolver.getCurrentSync(); + + boolean anySyncFailed = false; // true if sync on any account failed + + // only track userfacing sync adapters when deciding if account is synced or not + final SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypes(); + HashSet<String> userFacing = new HashSet<String>(); + for (int k = 0, n = syncAdapters.length; k < n; k++) { + final SyncAdapterType sa = syncAdapters[k]; + if (sa.isUserVisible()) { + userFacing.add(sa.authority); + } + } + for (int i = 0, count = mManageAccountsCategory.getPreferenceCount(); i < count; i++) { + Preference pref = mManageAccountsCategory.getPreference(i); + if (! (pref instanceof AccountPreference)) { + continue; + } + + AccountPreference accountPref = (AccountPreference) pref; + Account account = accountPref.getAccount(); + int syncCount = 0; + boolean syncIsFailing = false; + final ArrayList<String> authorities = accountPref.getAuthorities(); + if (authorities != null) { + for (String authority : authorities) { + SyncStatusInfo status = ContentResolver.getSyncStatus(account, authority); + boolean syncEnabled = ContentResolver.getSyncAutomatically(account, authority) + && masterSyncAutomatically + && backgroundDataSetting + && (ContentResolver.getIsSyncable(account, authority) > 0); + boolean authorityIsPending = ContentResolver.isSyncPending(account, authority); + boolean activelySyncing = currentSync != null + && currentSync.authority.equals(authority) + && new Account(currentSync.account.name, currentSync.account.type) + .equals(account); + boolean lastSyncFailed = status != null + && syncEnabled + && status.lastFailureTime != 0 + && status.getLastFailureMesgAsInt(0) + != ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS; + if (lastSyncFailed && !activelySyncing && !authorityIsPending) { + syncIsFailing = true; + anySyncFailed = true; + } + syncCount += syncEnabled && userFacing.contains(authority) ? 1 : 0; + } + } else { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "no syncadapters found for " + account); + } + } + int syncStatus = AccountPreference.SYNC_DISABLED; + if (syncIsFailing) { + syncStatus = AccountPreference.SYNC_ERROR; + } else if (syncCount == 0) { + syncStatus = AccountPreference.SYNC_DISABLED; + } else if (syncCount > 0) { + syncStatus = AccountPreference.SYNC_ENABLED; + } + accountPref.setSyncStatus(syncStatus); + } + + mErrorInfoView.setVisibility(anySyncFailed ? View.VISIBLE : View.GONE); + } + + @Override + public void onAccountsUpdated(Account[] accounts) { + mManageAccountsCategory.removeAll(); + for (int i = 0, n = accounts.length; i < n; i++) { + final Account account = accounts[i]; + final ArrayList<String> auths = getAuthoritiesForAccountType(account.type); + + boolean showAccount = true; + if (mAuthorities != null && auths != null) { + showAccount = false; + for (String requestedAuthority : mAuthorities) { + if (auths.contains(requestedAuthority)) { + showAccount = true; + break; + } + } + } + + if (showAccount) { + final Drawable icon = getDrawableForType(account.type); + final AccountPreference preference = + new AccountPreference(getActivity(), account, icon, auths); + mManageAccountsCategory.addPreference(preference); + } + } + onSyncStateUpdated(); + } + + private void onAuthDescriptionsUpdated() { + // Update account icons for all account preference items + for (int i = 0; i < mManageAccountsCategory.getPreferenceCount(); i++) { + AccountPreference pref = (AccountPreference) mManageAccountsCategory.getPreference(i); + pref.setProviderIcon(getDrawableForType(pref.getAccount().type)); + pref.setSummary(getLabelForType(pref.getAccount().type)); + } + } + + public void onClick(View v) { + if (v == mAddAccountButton) { + Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS"); + intent.putExtra(AUTHORITIES_FILTER_KEY, mAuthorities); + startActivity(intent); + } + } + + /* The logic below is copied from AcountPrefernceBase */ + + private Drawable getDrawableForType(final String accountType) { + Drawable icon = null; + if (mTypeToAuthDescription.containsKey(accountType)) { + try { + AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType); + Context authContext = getActivity().createPackageContext(desc.packageName, 0); + 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); + } + } + return icon; + } + + private CharSequence getLabelForType(final String accountType) { + CharSequence label = null; + if (mTypeToAuthDescription.containsKey(accountType)) { + try { + AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType); + 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); + } + } + return label; + } + + private ArrayList<String> getAuthoritiesForAccountType(String type) { + if (mAccountTypeToAuthorities == null) { + mAccountTypeToAuthorities = Maps.newHashMap(); + SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypes(); + for (int i = 0, n = syncAdapters.length; i < n; i++) { + final SyncAdapterType sa = syncAdapters[i]; + ArrayList<String> authorities = mAccountTypeToAuthorities.get(sa.accountType); + if (authorities == null) { + authorities = new ArrayList<String>(); + mAccountTypeToAuthorities.put(sa.accountType, authorities); + } + if (LDEBUG) { + Log.d(TAG, "added authority " + sa.authority + " to accountType " + + sa.accountType); + } + authorities.add(sa.authority); + } + } + return mAccountTypeToAuthorities.get(type); + } + + private void updateAuthDescriptions(Context context) { + mAuthDescs = AccountManager.get(context).getAuthenticatorTypes(); + for (int i = 0; i < mAuthDescs.length; i++) { + mTypeToAuthDescription.put(mAuthDescs[i].type, mAuthDescs[i]); + } + onAuthDescriptionsUpdated(); + } +} diff --git a/src/com/android/settings/PhysicalKeyboardSettings.java b/src/com/android/settings/PhysicalKeyboardSettings.java index acd0bd677..b9591836c 100644 --- a/src/com/android/settings/PhysicalKeyboardSettings.java +++ b/src/com/android/settings/PhysicalKeyboardSettings.java @@ -20,12 +20,11 @@ import android.content.ContentResolver; import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceScreen; import android.provider.Settings.System; -public class PhysicalKeyboardSettings extends PreferenceActivity { - +public class PhysicalKeyboardSettings extends SettingsPreferenceFragment { + private final String[] mSettingsUiKey = { "auto_caps", "auto_replace", @@ -47,14 +46,14 @@ public class PhysicalKeyboardSettings extends PreferenceActivity { }; @Override - protected void onCreate(Bundle icicle) { + public void onCreate(Bundle icicle) { super.onCreate(icicle); addPreferencesFromResource(R.xml.keyboard_settings); } @Override - protected void onResume() { + public void onResume() { super.onResume(); ContentResolver resolver = getContentResolver(); for (int i = 0; i < mSettingsUiKey.length; i++) { @@ -66,7 +65,8 @@ public class PhysicalKeyboardSettings extends PreferenceActivity { @Override - public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { + public boolean onPreferenceTreeClick( + PreferenceScreen preferenceScreen, Preference preference) { // Physical keyboard stuff for (int i = 0; i < mSettingsUiKey.length; i++) { diff --git a/src/com/android/settings/PrivacySettings.java b/src/com/android/settings/PrivacySettings.java index 29deacf29..826c9cfe1 100644 --- a/src/com/android/settings/PrivacySettings.java +++ b/src/com/android/settings/PrivacySettings.java @@ -22,23 +22,18 @@ import android.app.backup.IBackupManager; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; -import android.content.SharedPreferences; -import android.content.pm.PackageManager.NameNotFoundException; import android.os.Bundle; import android.os.RemoteException; import android.os.ServiceManager; import android.preference.CheckBoxPreference; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceScreen; import android.provider.Settings; -import android.text.method.LinkMovementMethod; -import android.widget.TextView; /** * Gesture lock pattern settings. */ -public class PrivacySettings extends PreferenceActivity implements +public class PrivacySettings extends SettingsPreferenceFragment implements DialogInterface.OnClickListener { // Vendor specific @@ -54,7 +49,7 @@ public class PrivacySettings extends PreferenceActivity implements private int mDialogType; @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.privacy_settings); final PreferenceScreen screen = getPreferenceScreen(); @@ -63,7 +58,8 @@ public class PrivacySettings extends PreferenceActivity implements mAutoRestore = (CheckBoxPreference) screen.findPreference(AUTO_RESTORE); // Vendor specific - if (getPackageManager().resolveContentProvider(GSETTINGS_PROVIDER, 0) == null) { + if (getActivity().getPackageManager(). + resolveContentProvider(GSETTINGS_PROVIDER, 0) == null) { screen.removePreference(findPreference(BACKUP_CATEGORY)); } updateToggles(); @@ -110,7 +106,8 @@ public class PrivacySettings extends PreferenceActivity implements mDialogType = DIALOG_ERASE_BACKUP; CharSequence msg = getResources().getText(R.string.backup_erase_dialog_message); - mConfirmDialog = new AlertDialog.Builder(this).setMessage(msg) + // TODO: DialogFragment? + mConfirmDialog = new AlertDialog.Builder(getActivity()).setMessage(msg) .setTitle(R.string.backup_erase_dialog_title) .setIcon(android.R.drawable.ic_dialog_alert) .setPositiveButton(android.R.string.ok, this) diff --git a/src/com/android/settings/ProgressCategory.java b/src/com/android/settings/ProgressCategory.java index f611137a5..c5b68b6dd 100644 --- a/src/com/android/settings/ProgressCategory.java +++ b/src/com/android/settings/ProgressCategory.java @@ -17,13 +17,10 @@ package com.android.settings; import android.content.Context; -import android.preference.PreferenceCategory; import android.util.AttributeSet; import android.view.View; -import java.util.Map; - -public class ProgressCategory extends PreferenceCategory { +public class ProgressCategory extends ProgressCategoryBase { private boolean mProgress = false; private View oldView = null; @@ -36,10 +33,10 @@ public class ProgressCategory extends PreferenceCategory { @Override public void onBindView(View view) { super.onBindView(view); - View textView = view.findViewById(R.id.scanning_text); - View progressBar = view.findViewById(R.id.scanning_progress); + final View textView = view.findViewById(R.id.scanning_text); + final View progressBar = view.findViewById(R.id.scanning_progress); - int visibility = mProgress ? View.VISIBLE : View.INVISIBLE; + final int visibility = mProgress ? View.VISIBLE : View.INVISIBLE; textView.setVisibility(visibility); progressBar.setVisibility(visibility); @@ -50,11 +47,8 @@ public class ProgressCategory extends PreferenceCategory { } oldView = view; } - - /** - * Turn on/off the progress indicator and text on the right. - * @param progressOn whether or not the progress should be displayed - */ + + @Override public void setProgress(boolean progressOn) { mProgress = progressOn; notifyChanged(); diff --git a/src/com/android/settings/ProgressCategoryBase.java b/src/com/android/settings/ProgressCategoryBase.java new file mode 100644 index 000000000..d120b9495 --- /dev/null +++ b/src/com/android/settings/ProgressCategoryBase.java @@ -0,0 +1,33 @@ +/* + * 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 android.content.Context; +import android.preference.PreferenceCategory; +import android.util.AttributeSet; + +public abstract class ProgressCategoryBase extends PreferenceCategory { + public ProgressCategoryBase(Context context, AttributeSet attrs) { + super(context, attrs); + } + + /** + * Turn on/off the progress indicator and text on the right. + * @param progressOn whether or not the progress should be displayed + */ + public abstract void setProgress(boolean progressOn); +}
\ No newline at end of file diff --git a/src/com/android/settings/ProxySelector.java b/src/com/android/settings/ProxySelector.java index 66c81c622..aea09e76a 100644 --- a/src/com/android/settings/ProxySelector.java +++ b/src/com/android/settings/ProxySelector.java @@ -16,10 +16,15 @@ package com.android.settings; +import com.android.settings.SettingsPreferenceFragment.SettingsDialogFragment; + import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; +import android.app.Fragment; +import android.app.admin.DevicePolicyManager; import android.content.ContentResolver; +import android.content.Context; import android.content.Intent; import android.net.Proxy; import android.os.Bundle; @@ -28,9 +33,11 @@ import android.text.Selection; import android.text.Spannable; import android.text.TextUtils; import android.util.Log; +import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnFocusChangeListener; +import android.view.ViewGroup; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; @@ -38,112 +45,128 @@ import android.widget.TextView; import java.util.regex.Matcher; import java.util.regex.Pattern; -/** - * To start the Proxy Selector activity, create the following intent. - * - * <code> - * Intent intent = new Intent(); - * intent.setClassName("com.android.browser.ProxySelector"); - * startActivity(intent); - * </code> - * - * you can add extra options to the intent by using - * - * <code> - * intent.putExtra(key, value); - * </code> - * - * the extra options are: - * - * button-label: a string label to display for the okay button - * title: the title of the window - * error-text: If not null, will be used as the label of the error message. - */ -public class ProxySelector extends Activity -{ - private final static String LOGTAG = "Settings"; +public class ProxySelector extends Fragment implements DialogCreatable { + private static final String TAG = "ProxySelector"; EditText mHostnameField; EditText mPortField; + EditText mExclusionListField; Button mOKButton; + Button mClearButton; + Button mDefaultButton; // Matches blank input, ips, and domain names - private static final String HOSTNAME_REGEXP = "^$|^[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*(\\.[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*)*$"; + private static final String HOSTNAME_REGEXP = + "^$|^[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*(\\.[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*)*$"; private static final Pattern HOSTNAME_PATTERN; + private static final String EXCLLIST_REGEXP = + "$|^(.?[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*(\\.[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*)*)+" + + "(,(.?[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*(\\.[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*)*))*$"; + private static final Pattern EXCLLIST_PATTERN; static { HOSTNAME_PATTERN = Pattern.compile(HOSTNAME_REGEXP); + EXCLLIST_PATTERN = Pattern.compile(EXCLLIST_REGEXP); } private static final int ERROR_DIALOG_ID = 0; + private SettingsDialogFragment mDialogFragment; + private View mView; + + @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); + } - if (android.util.Config.LOGV) Log.v(LOGTAG, "[ProxySelector] onStart"); - - setContentView(R.layout.proxy); - initView(); + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + mView = inflater.inflate(R.layout.proxy, container, false); + initView(mView); + // TODO: Populate based on connection status populateFields(false); + return mView; } @Override - protected Dialog onCreateDialog(int id) { + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + final DevicePolicyManager dpm = + (DevicePolicyManager)getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE); + + final boolean userSetGlobalProxy = (dpm.getGlobalProxyAdmin() == null); + // Disable UI if the Global Proxy is being controlled by a Device Admin + mHostnameField.setEnabled(userSetGlobalProxy); + mPortField.setEnabled(userSetGlobalProxy); + mExclusionListField.setEnabled(userSetGlobalProxy); + mOKButton.setEnabled(userSetGlobalProxy); + mClearButton.setEnabled(userSetGlobalProxy); + mDefaultButton.setEnabled(userSetGlobalProxy); + } + + // Dialog management + + @Override + public Dialog onCreateDialog(int id) { if (id == ERROR_DIALOG_ID) { String hostname = mHostnameField.getText().toString().trim(); String portStr = mPortField.getText().toString().trim(); - String msg = getString(validate(hostname, portStr)); + String exclList = mExclusionListField.getText().toString().trim(); + String msg = getActivity().getString(validate(hostname, portStr, exclList)); - return new AlertDialog.Builder(this) + return new AlertDialog.Builder(getActivity()) .setTitle(R.string.proxy_error) .setPositiveButton(R.string.proxy_error_dismiss, null) .setMessage(msg) .create(); } - return super.onCreateDialog(id); + return null; } - @Override - protected void onPrepareDialog(int id, Dialog dialog) { - super.onPrepareDialog(id, dialog); - - if (id == ERROR_DIALOG_ID) { - String hostname = mHostnameField.getText().toString().trim(); - String portStr = mPortField.getText().toString().trim(); - String msg = getString(validate(hostname, portStr)); - ((AlertDialog)dialog).setMessage(msg); + private void showDialog(int dialogId) { + if (mDialogFragment != null) { + Log.e(TAG, "Old dialog fragment not null!"); } + mDialogFragment = new SettingsDialogFragment(this, dialogId); + mDialogFragment.show(getActivity().getFragmentManager(), Integer.toString(dialogId)); } - void initView() { - - mHostnameField = (EditText)findViewById(R.id.hostname); + private void initView(View view) { + mHostnameField = (EditText)view.findViewById(R.id.hostname); mHostnameField.setOnFocusChangeListener(mOnFocusChangeHandler); - mPortField = (EditText)findViewById(R.id.port); + mPortField = (EditText)view.findViewById(R.id.port); mPortField.setOnClickListener(mOKHandler); mPortField.setOnFocusChangeListener(mOnFocusChangeHandler); - mOKButton = (Button)findViewById(R.id.action); + mExclusionListField = (EditText)view.findViewById(R.id.exclusionlist); + mExclusionListField.setOnFocusChangeListener(mOnFocusChangeHandler); + + mOKButton = (Button)view.findViewById(R.id.action); mOKButton.setOnClickListener(mOKHandler); - Button b = (Button)findViewById(R.id.clear); - b.setOnClickListener(mClearHandler); + mClearButton = (Button)view.findViewById(R.id.clear); + mClearButton.setOnClickListener(mClearHandler); - b = (Button)findViewById(R.id.defaultView); - b.setOnClickListener(mDefaultHandler); + mDefaultButton = (Button)view.findViewById(R.id.defaultView); + mDefaultButton.setOnClickListener(mDefaultHandler); } void populateFields(boolean useDefault) { + final Activity activity = getActivity(); String hostname = null; int port = -1; + String exclList = null; if (useDefault) { // Use the default proxy settings provided by the carrier hostname = Proxy.getDefaultHost(); port = Proxy.getDefaultPort(); } else { // Use the last setting given by the user - hostname = Proxy.getHost(this); - port = Proxy.getPort(this); + ContentResolver res = getActivity().getContentResolver(); + hostname = Proxy.getHost(activity); + port = Proxy.getPort(activity); + exclList = Settings.Secure.getString(res, Settings.Secure.HTTP_PROXY_EXCLUSION_LIST); } if (hostname == null) { @@ -155,7 +178,9 @@ public class ProxySelector extends Activity String portStr = port == -1 ? "" : Integer.toString(port); mPortField.setText(portStr); - Intent intent = getIntent(); + mExclusionListField.setText(exclList); + + final Intent intent = activity.getIntent(); String buttonLabel = intent.getStringExtra("button-label"); if (!TextUtils.isEmpty(buttonLabel)) { @@ -164,7 +189,7 @@ public class ProxySelector extends Activity String title = intent.getStringExtra("title"); if (!TextUtils.isEmpty(title)) { - setTitle(title); + activity.setTitle(title); } } @@ -172,11 +197,14 @@ public class ProxySelector extends Activity * validate syntax of hostname and port entries * @return 0 on success, string resource ID on failure */ - int validate(String hostname, String port) { + int validate(String hostname, String port, String exclList) { Matcher match = HOSTNAME_PATTERN.matcher(hostname); + Matcher listMatch = EXCLLIST_PATTERN.matcher(exclList); if (!match.matches()) return R.string.proxy_error_invalid_host; + if (!listMatch.matches()) return R.string.proxy_error_invalid_exclusion_list; + if (hostname.length() > 0 && port.length() == 0) { return R.string.proxy_error_empty_port; } @@ -205,9 +233,10 @@ public class ProxySelector extends Activity String hostname = mHostnameField.getText().toString().trim(); String portStr = mPortField.getText().toString().trim(); + String exclList = mExclusionListField.getText().toString().trim(); int port = -1; - int result = validate(hostname, portStr); + int result = validate(hostname, portStr, exclList); if (result > 0) { showDialog(ERROR_DIALOG_ID); return false; @@ -231,7 +260,7 @@ public class ProxySelector extends Activity // controls. // FIXME: If the user types in a proxy that matches the default, should // we keep that setting? Can be fixed with a new UI. - ContentResolver res = getContentResolver(); + ContentResolver res = getActivity().getContentResolver(); if (hostname.equals(Proxy.getDefaultHost()) && port == Proxy.getDefaultPort()) { // If the user hit the default button and didn't change any of @@ -244,7 +273,8 @@ public class ProxySelector extends Activity hostname += ':' + portStr; } Settings.Secure.putString(res, Settings.Secure.HTTP_PROXY, hostname); - sendBroadcast(new Intent(Proxy.PROXY_CHANGE_ACTION)); + Settings.Secure.putString(res, Settings.Secure.HTTP_PROXY_EXCLUSION_LIST, exclList); + getActivity().sendBroadcast(new Intent(Proxy.PROXY_CHANGE_ACTION)); return true; } @@ -252,7 +282,7 @@ public class ProxySelector extends Activity OnClickListener mOKHandler = new OnClickListener() { public void onClick(View v) { if (saveToDb()) { - finish(); + getActivity().onBackPressed(); } } }; @@ -261,12 +291,14 @@ public class ProxySelector extends Activity public void onClick(View v) { mHostnameField.setText(""); mPortField.setText(""); + mExclusionListField.setText(""); } }; OnClickListener mDefaultHandler = new OnClickListener() { public void onClick(View v) { - populateFields(true); + // TODO: populate based on connection status + populateFields(false); } }; diff --git a/src/com/android/settings/RadioInfo.java b/src/com/android/settings/RadioInfo.java index f0fcdd7f2..fd643cd69 100644 --- a/src/com/android/settings/RadioInfo.java +++ b/src/com/android/settings/RadioInfo.java @@ -17,13 +17,11 @@ package com.android.settings; import android.app.Activity; -import android.app.AlertDialog; -import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.content.SharedPreferences; import android.content.res.Resources; +import android.net.LinkProperties; import android.net.Uri; import android.os.AsyncResult; import android.os.Bundle; @@ -33,7 +31,6 @@ import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; -import android.preference.PreferenceManager; import android.telephony.CellLocation; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; @@ -66,9 +63,8 @@ import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; import java.io.IOException; +import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; @@ -760,7 +756,7 @@ public class RadioInfo extends Activity { List<DataConnection> dcs = phone.getCurrentDataConnectionList(); for (DataConnection dc : dcs) { - sb.append(" State: ").append(dc.getStateAsString()).append("\n"); + sb.append(" State=").append(dc.getStateAsString()).append("\n"); if (dc.isActive()) { long timeElapsed = (System.currentTimeMillis() - dc.getConnectionTime())/1000; @@ -774,16 +770,8 @@ public class RadioInfo extends Activity { sb.append("\n to ") .append(pdp.getApn().toString()); } - sb.append("\ninterface: ") - .append(phone.getInterfaceName(phone.getActiveApnTypes()[0])) - .append("\naddress: ") - .append(phone.getIpAddress(phone.getActiveApnTypes()[0])) - .append("\ngateway: ") - .append(phone.getGateway(phone.getActiveApnTypes()[0])); - String[] dns = phone.getDnsServers(phone.getActiveApnTypes()[0]); - if (dns != null) { - sb.append("\ndns: ").append(dns[0]).append(", ").append(dns[1]); - } + sb.append("\nLinkProperties: "); + sb.append(phone.getLinkProperties(phone.getActiveApnTypes()[0]).toString()); } else if (dc.isInactive()) { sb.append(" disconnected with last try at ") .append(DateUtils.timeString(dc.getLastFailTime())) @@ -801,7 +789,6 @@ public class RadioInfo extends Activity { sb.append("\n==================="); } - disconnects.setText(sb.toString()); } diff --git a/src/com/android/settings/RingerVolumePreference.java b/src/com/android/settings/RingerVolumePreference.java index 3ecd81969..4479cb0c4 100644 --- a/src/com/android/settings/RingerVolumePreference.java +++ b/src/com/android/settings/RingerVolumePreference.java @@ -45,6 +45,12 @@ public class RingerVolumePreference extends VolumePreference implements R.id.media_volume_seekbar, R.id.alarm_volume_seekbar }; + + private static final int[] NEED_VOICE_CAPABILITY_ID = new int[] { + com.android.internal.R.id.seekbar, R.id.notification_volume_title, + R.id.notification_volume_seekbar + }; + private static final int[] SEEKBAR_TYPE = new int[] { AudioManager.STREAM_NOTIFICATION, AudioManager.STREAM_MUSIC, @@ -83,17 +89,28 @@ public class RingerVolumePreference extends VolumePreference implements getContext().getContentResolver(), Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1) == 1); setNotificationVolumeVisibility(!mNotificationsUseRingVolumeCheckbox.isChecked()); + disableSettingsThatNeedVoice(view); + } + + private void disableSettingsThatNeedVoice(View parent) { + final boolean voiceCapable = getContext().getResources() + .getBoolean(com.android.internal.R.bool.config_voice_capable); + if (!voiceCapable) { + for (int id : NEED_VOICE_CAPABILITY_ID) { + parent.findViewById(id).setVisibility(View.GONE); + } + } } @Override protected void onDialogClosed(boolean positiveResult) { super.onDialogClosed(positiveResult); - + if (!positiveResult) { for (SeekBarVolumizer vol : mSeekBarVolumizer) { if (vol != null) vol.revertVolume(); } - } + } cleanup(); } @@ -102,13 +119,13 @@ public class RingerVolumePreference extends VolumePreference implements super.onActivityStop(); cleanup(); } - + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { setNotificationVolumeVisibility(!isChecked); - + Settings.System.putInt(getContext().getContentResolver(), Settings.System.NOTIFICATIONS_USE_RING_VOLUME, isChecked ? 1 : 0); - + if (isChecked) { // The user wants the notification to be same as ring, so do a // one-time sync right now diff --git a/src/com/android/settings/SecuritySettings.java b/src/com/android/settings/SecuritySettings.java index 454ea6023..dacc19f43 100644 --- a/src/com/android/settings/SecuritySettings.java +++ b/src/com/android/settings/SecuritySettings.java @@ -17,8 +17,9 @@ package com.android.settings; -import java.util.Observable; -import java.util.Observer; +import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT; + +import com.android.internal.widget.LockPatternUtils; import android.app.AlertDialog; import android.app.Dialog; @@ -33,25 +34,30 @@ import android.location.LocationManager; import android.os.Bundle; import android.os.SystemProperties; import android.preference.CheckBoxPreference; +import android.preference.ListPreference; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceCategory; import android.preference.PreferenceManager; import android.preference.PreferenceScreen; +import android.preference.Preference.OnPreferenceChangeListener; import android.provider.Settings; import android.security.Credentials; import android.security.KeyStore; import android.telephony.TelephonyManager; +import android.util.Log; import android.view.View; import android.widget.TextView; import android.widget.Toast; -import com.android.internal.widget.LockPatternUtils; +import java.util.ArrayList; +import java.util.Observable; +import java.util.Observer; /** * Gesture lock pattern settings. */ -public class SecuritySettings extends PreferenceActivity { +public class SecuritySettings extends SettingsPreferenceFragment + implements OnPreferenceChangeListener { private static final String KEY_UNLOCK_SET_OR_CHANGE = "unlock_set_or_change"; // Lock Settings @@ -75,7 +81,9 @@ public class SecuritySettings extends PreferenceActivity { private static final String LOCATION_NETWORK = "location_network"; private static final String LOCATION_GPS = "location_gps"; private static final String ASSISTED_GPS = "assisted_gps"; + private static final String LOCK_AFTER_TIMEOUT_KEY = "lock_after_timeout"; private static final int SET_OR_CHANGE_LOCK_METHOD_REQUEST = 123; + private static final int FALLBACK_LOCK_AFTER_TIMEOUT_VALUE = 5000; // compatible with pre-Froyo // Credential storage private CredentialStorage mCredentialStorage = new CredentialStorage(); @@ -93,8 +101,13 @@ public class SecuritySettings extends PreferenceActivity { // 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 SettingsObserver mSettingsObserver; + private final class SettingsObserver implements Observer { public void update(Observable o, Object arg) { updateToggles(); @@ -102,26 +115,36 @@ public class SecuritySettings extends PreferenceActivity { } @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mLockPatternUtils = new LockPatternUtils(this); + mLockPatternUtils = new LockPatternUtils(getActivity()); mDPM = (DevicePolicyManager)getSystemService(Context.DEVICE_POLICY_SERVICE); - mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this); + mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity()); createPreferenceHierarchy(); updateToggles(); + } + @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); - mContentQueryMap.addObserver(new SettingsObserver()); + mContentQueryMap.addObserver(mSettingsObserver = new SettingsObserver()); + } + + @Override + public void onStop() { + super.onStop(); + mContentQueryMap.deleteObserver(mSettingsObserver); } private PreferenceScreen createPreferenceHierarchy() { @@ -151,13 +174,14 @@ public class SecuritySettings extends PreferenceActivity { break; case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: + case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: addPreferencesFromResource(R.xml.security_settings_password); break; } } - // set or change current. Should be common to all unlock preference screens - // mSetOrChange = (PreferenceScreen) pm.findPreference(KEY_UNLOCK_SET_OR_CHANGE); + // lock after preference + mLockAfter = setupLockAfterPreference(pm); // visible pattern mVisiblePattern = (CheckBoxPreference) pm.findPreference(KEY_VISIBLE_PATTERN); @@ -171,22 +195,22 @@ public class SecuritySettings extends PreferenceActivity { if (TelephonyManager.PHONE_TYPE_CDMA != activePhoneType) { PreferenceScreen simLockPreferences = getPreferenceManager() - .createPreferenceScreen(this); + .createPreferenceScreen(getActivity()); simLockPreferences.setTitle(R.string.sim_lock_settings_category); // Intent to launch SIM lock settings simLockPreferences.setIntent(new Intent().setClassName(PACKAGE, ICC_LOCK_SETTINGS)); - PreferenceCategory simLockCat = new PreferenceCategory(this); + PreferenceCategory simLockCat = new PreferenceCategory(getActivity()); simLockCat.setTitle(R.string.sim_lock_settings_title); root.addPreference(simLockCat); simLockCat.addPreference(simLockPreferences); } // Passwords - PreferenceCategory passwordsCat = new PreferenceCategory(this); + PreferenceCategory passwordsCat = new PreferenceCategory(getActivity()); passwordsCat.setTitle(R.string.security_passwords_title); root.addPreference(passwordsCat); - CheckBoxPreference showPassword = mShowPassword = new CheckBoxPreference(this); + CheckBoxPreference showPassword = mShowPassword = new CheckBoxPreference(getActivity()); showPassword.setKey("show_password"); showPassword.setTitle(R.string.show_password); showPassword.setSummary(R.string.show_password_summary); @@ -194,36 +218,91 @@ public class SecuritySettings extends PreferenceActivity { passwordsCat.addPreference(showPassword); // Device policies - PreferenceCategory devicePoliciesCat = new PreferenceCategory(this); + PreferenceCategory devicePoliciesCat = new PreferenceCategory(getActivity()); devicePoliciesCat.setTitle(R.string.device_admin_title); root.addPreference(devicePoliciesCat); - Preference deviceAdminButton = new Preference(this); + Preference deviceAdminButton = new Preference(getActivity()); deviceAdminButton.setTitle(R.string.manage_device_admin); deviceAdminButton.setSummary(R.string.manage_device_admin_summary); Intent deviceAdminIntent = new Intent(); - deviceAdminIntent.setClass(this, DeviceAdminSettings.class); + deviceAdminIntent.setClass(getActivity(), DeviceAdminSettings.class); deviceAdminButton.setIntent(deviceAdminIntent); devicePoliciesCat.addPreference(deviceAdminButton); // Credential storage - PreferenceCategory credentialsCat = new PreferenceCategory(this); + PreferenceCategory credentialsCat = new PreferenceCategory(getActivity()); credentialsCat.setTitle(R.string.credentials_category); root.addPreference(credentialsCat); mCredentialStorage.createPreferences(credentialsCat, CredentialStorage.TYPE_KEYSTORE); // File System Encryption - PreferenceCategory encryptedfsCat = new PreferenceCategory(this); + PreferenceCategory encryptedfsCat = new PreferenceCategory(getActivity()); encryptedfsCat.setTitle(R.string.encrypted_fs_category); //root.addPreference(encryptedfsCat); mCredentialStorage.createPreferences(encryptedfsCat, CredentialStorage.TYPE_ENCRYPTEDFS); return root; } + private ListPreference setupLockAfterPreference(PreferenceManager pm) { + ListPreference result = (ListPreference) pm.findPreference(LOCK_AFTER_TIMEOUT_KEY); + if (result != null) { + int lockAfterValue = Settings.Secure.getInt(getContentResolver(), + Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, + FALLBACK_LOCK_AFTER_TIMEOUT_VALUE); + result.setValue(String.valueOf(lockAfterValue)); + result.setOnPreferenceChangeListener(this); + final long adminTimeout = mDPM != null ? mDPM.getMaximumTimeToLock(null) : 0; + final ContentResolver cr = getContentResolver(); + final long displayTimeout = Math.max(0, + Settings.System.getInt(cr, SCREEN_OFF_TIMEOUT, 0)); + if (adminTimeout > 0) { + // This setting is a slave to display timeout when a device policy is enforced. + // As such, maxLockTimeout = adminTimeout - displayTimeout. + // If there isn't enough time, shows "immediately" setting. + disableUnusableTimeouts(result, Math.max(0, adminTimeout - displayTimeout)); + } + } + return result; + } + + private static void disableUnusableTimeouts(ListPreference pref, long maxTimeout) { + final CharSequence[] entries = pref.getEntries(); + final CharSequence[] values = pref.getEntryValues(); + ArrayList<CharSequence> revisedEntries = new ArrayList<CharSequence>(); + ArrayList<CharSequence> revisedValues = new ArrayList<CharSequence>(); + for (int i = 0; i < values.length; i++) { + long timeout = Long.valueOf(values[i].toString()); + if (timeout <= maxTimeout) { + revisedEntries.add(entries[i]); + revisedValues.add(values[i]); + } + } + if (revisedEntries.size() != entries.length || revisedValues.size() != values.length) { + pref.setEntries( + revisedEntries.toArray(new CharSequence[revisedEntries.size()])); + pref.setEntryValues( + revisedValues.toArray(new CharSequence[revisedValues.size()])); + final int userPreference = Integer.valueOf(pref.getValue()); + if (userPreference <= maxTimeout) { + pref.setValue(String.valueOf(userPreference)); + } else { + // There will be no highlighted selection since nothing in the list matches + // maxTimeout. The user can still select anything less than maxTimeout. + // TODO: maybe append maxTimeout to the list and mark selected. + } + } + pref.setEnabled(revisedEntries.size() > 0); + } + @Override - protected void onResume() { + public void onResume() { super.onResume(); + // Make sure we reload the preference hierarchy since some of these settings + // depend on others... + createPreferenceHierarchy(); + final LockPatternUtils lockPatternUtils = mChooseLockSettingsHelper.utils(); if (mVisiblePattern != null) { mVisiblePattern.setChecked(lockPatternUtils.isVisiblePatternEnabled()); @@ -245,7 +324,7 @@ public class SecuritySettings extends PreferenceActivity { final LockPatternUtils lockPatternUtils = mChooseLockSettingsHelper.utils(); if (KEY_UNLOCK_SET_OR_CHANGE.equals(key)) { - Intent intent = new Intent(this, ChooseLockGeneric.class); + Intent intent = new Intent(getActivity(), ChooseLockGeneric.class); startActivityForResult(intent, SET_OR_CHANGE_LOCK_METHOD_REQUEST); } else if (KEY_LOCK_ENABLED.equals(key)) { lockPatternUtils.setLockPatternEnabled(isToggled(preference)); @@ -299,7 +378,7 @@ public class SecuritySettings extends PreferenceActivity { * @see #confirmPatternThenDisableAndClear */ @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { + public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); createPreferenceHierarchy(); } @@ -341,7 +420,7 @@ public class SecuritySettings extends PreferenceActivity { mState = mKeyStore.test(); updatePreferences(mState); - Intent intent = getIntent(); + Intent intent = getActivity().getIntent(); if (!mExternal && intent != null && Credentials.UNLOCK_ACTION.equals(intent.getAction())) { mExternal = true; @@ -350,7 +429,8 @@ public class SecuritySettings extends PreferenceActivity { } else if (mState == KeyStore.LOCKED) { showUnlockDialog(); } else { - finish(); + // TODO: Verify if this is the right way + SecuritySettings.this.getFragmentManager().popBackStack(); } } } @@ -392,13 +472,13 @@ public class SecuritySettings extends PreferenceActivity { Boolean bval = (Boolean)value; mWillEnableEncryptedFS = bval.booleanValue(); showSwitchEncryptedFSDialog(); - } + } return true; } public boolean onPreferenceClick(Preference preference) { if (preference == mInstallButton) { - Credentials.getInstance().installFromSdCard(SecuritySettings.this); + Credentials.getInstance().installFromSdCard(SecuritySettings.this.getActivity()); } else if (preference == mPasswordButton) { showPasswordDialog(); } else if (preference == mResetButton) { @@ -419,11 +499,12 @@ public class SecuritySettings extends PreferenceActivity { if (button == DialogInterface.BUTTON_POSITIVE) { Intent intent = new Intent("android.intent.action.MASTER_CLEAR"); intent.putExtra("enableEFS", mWillEnableEncryptedFS); - sendBroadcast(intent); + getActivity().sendBroadcast(intent); updatePreferences(mState); } else if (button == DialogInterface.BUTTON_NEGATIVE) { // Cancel action - Toast.makeText(SecuritySettings.this, R.string.encrypted_fs_cancel_confirm, + Toast.makeText(SecuritySettings.this.getActivity(), + R.string.encrypted_fs_cancel_confirm, Toast.LENGTH_SHORT).show(); updatePreferences(mState); } else { @@ -434,7 +515,10 @@ public class SecuritySettings extends PreferenceActivity { } public void onDismiss(DialogInterface dialog) { - if (mSubmit && !isFinishing()) { + // TODO: + //if (mSubmit && !isFinishing()) { + + if (mSubmit) { mSubmit = false; if (!checkPassword((Dialog) dialog)) { ((Dialog) dialog).show(); @@ -443,7 +527,8 @@ public class SecuritySettings extends PreferenceActivity { } updatePreferences(mState); if (mExternal) { - finish(); + // TODO: + // finish(); } } @@ -518,25 +603,25 @@ public class SecuritySettings extends PreferenceActivity { private void createPreferences(PreferenceCategory category, int type) { switch(type) { case TYPE_KEYSTORE: - mAccessCheckBox = new CheckBoxPreference(SecuritySettings.this); + mAccessCheckBox = new CheckBoxPreference(SecuritySettings.this.getActivity()); mAccessCheckBox.setTitle(R.string.credentials_access); mAccessCheckBox.setSummary(R.string.credentials_access_summary); mAccessCheckBox.setOnPreferenceChangeListener(this); category.addPreference(mAccessCheckBox); - mInstallButton = new Preference(SecuritySettings.this); + mInstallButton = new Preference(SecuritySettings.this.getActivity()); mInstallButton.setTitle(R.string.credentials_install_certificates); mInstallButton.setSummary(R.string.credentials_install_certificates_summary); mInstallButton.setOnPreferenceClickListener(this); category.addPreference(mInstallButton); - mPasswordButton = new Preference(SecuritySettings.this); + mPasswordButton = new Preference(SecuritySettings.this.getActivity()); mPasswordButton.setTitle(R.string.credentials_set_password); mPasswordButton.setSummary(R.string.credentials_set_password_summary); mPasswordButton.setOnPreferenceClickListener(this); category.addPreference(mPasswordButton); - mResetButton = new Preference(SecuritySettings.this); + mResetButton = new Preference(SecuritySettings.this.getActivity()); mResetButton.setTitle(R.string.credentials_reset); mResetButton.setSummary(R.string.credentials_reset_summary); mResetButton.setOnPreferenceClickListener(this); @@ -544,7 +629,8 @@ public class SecuritySettings extends PreferenceActivity { break; case TYPE_ENCRYPTEDFS: - mEncryptedFSEnabled = new CheckBoxPreference(SecuritySettings.this); + mEncryptedFSEnabled = new CheckBoxPreference(SecuritySettings.this + .getActivity()); mEncryptedFSEnabled.setTitle(R.string.encrypted_fs_enable); mEncryptedFSEnabled.setSummary(R.string.encrypted_fs_enable_summary); mEncryptedFSEnabled.setOnPreferenceChangeListener(this); @@ -567,20 +653,20 @@ public class SecuritySettings extends PreferenceActivity { if (mState == state) { return; } else if (state == KeyStore.NO_ERROR) { - Toast.makeText(SecuritySettings.this, R.string.credentials_enabled, + Toast.makeText(SecuritySettings.this.getActivity(), R.string.credentials_enabled, Toast.LENGTH_SHORT).show(); } else if (state == KeyStore.UNINITIALIZED) { - Toast.makeText(SecuritySettings.this, R.string.credentials_erased, + Toast.makeText(SecuritySettings.this.getActivity(), R.string.credentials_erased, Toast.LENGTH_SHORT).show(); } else if (state == KeyStore.LOCKED) { - Toast.makeText(SecuritySettings.this, R.string.credentials_disabled, + Toast.makeText(SecuritySettings.this.getActivity(), R.string.credentials_disabled, Toast.LENGTH_SHORT).show(); } mState = state; } private void showUnlockDialog() { - View view = View.inflate(SecuritySettings.this, + View view = View.inflate(SecuritySettings.this.getActivity(), R.layout.credentials_unlock_dialog, null); // Show extra hint only when the action comes from outside. @@ -588,7 +674,7 @@ public class SecuritySettings extends PreferenceActivity { view.findViewById(R.id.hint).setVisibility(View.VISIBLE); } - Dialog dialog = new AlertDialog.Builder(SecuritySettings.this) + Dialog dialog = new AlertDialog.Builder(SecuritySettings.this.getActivity()) .setView(view) .setTitle(R.string.credentials_unlock) .setPositiveButton(android.R.string.ok, this) @@ -600,7 +686,7 @@ public class SecuritySettings extends PreferenceActivity { } private void showPasswordDialog() { - View view = View.inflate(SecuritySettings.this, + View view = View.inflate(SecuritySettings.this.getActivity(), R.layout.credentials_password_dialog, null); if (mState == KeyStore.UNINITIALIZED) { @@ -610,7 +696,7 @@ public class SecuritySettings extends PreferenceActivity { view.findViewById(R.id.old_password).setVisibility(View.VISIBLE); } - Dialog dialog = new AlertDialog.Builder(SecuritySettings.this) + Dialog dialog = new AlertDialog.Builder(SecuritySettings.this.getActivity()) .setView(view) .setTitle(R.string.credentials_set_password) .setPositiveButton(android.R.string.ok, this) @@ -623,17 +709,18 @@ public class SecuritySettings extends PreferenceActivity { private void showResetDialog() { mShowingDialog = DLG_RESET; - new AlertDialog.Builder(SecuritySettings.this) + new AlertDialog.Builder(SecuritySettings.this.getActivity()) .setTitle(android.R.string.dialog_alert_title) .setIcon(android.R.drawable.ic_dialog_alert) .setMessage(R.string.credentials_reset_hint) - .setNeutralButton(getString(android.R.string.ok), this) - .setNegativeButton(getString(android.R.string.cancel), this) + .setNeutralButton(getResources().getString(android.R.string.ok), this) + .setNegativeButton(getResources().getString(android.R.string.cancel), this) .create().show(); } private void showSwitchEncryptedFSDialog() { - AlertDialog.Builder builder = new AlertDialog.Builder(SecuritySettings.this) + AlertDialog.Builder builder = new AlertDialog.Builder(SecuritySettings.this + .getActivity()) .setCancelable(false) .setTitle(R.string.encrypted_fs_alert_dialog_title); @@ -651,4 +738,17 @@ public class SecuritySettings extends PreferenceActivity { } } } + + public boolean onPreferenceChange(Preference preference, Object value) { + if (preference == mLockAfter) { + int lockAfter = Integer.parseInt((String) value); + try { + Settings.Secure.putInt(getContentResolver(), + Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, lockAfter); + } catch (NumberFormatException e) { + Log.e("SecuritySettings", "could not persist lockAfter timeout setting", e); + } + } + return true; + } } diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java index d92483407..738acc3e2 100644 --- a/src/com/android/settings/Settings.java +++ b/src/com/android/settings/Settings.java @@ -16,48 +16,61 @@ package com.android.settings; -import android.net.sip.SipManager; +import android.app.Fragment; import android.os.Bundle; -import android.preference.Preference; import android.preference.PreferenceActivity; -import android.preference.PreferenceGroup; -public class Settings extends PreferenceActivity { +import java.util.List; - private static final String KEY_PARENT = "parent"; - private static final String KEY_CALL_SETTINGS = "call_settings"; - private static final String KEY_SYNC_SETTINGS = "sync_settings"; - private static final String KEY_DOCK_SETTINGS = "dock_settings"; - - private static final String KEY_OPERATOR_SETTINGS = "operator_settings"; - private static final String KEY_MANUFACTURER_SETTINGS = "manufacturer_settings"; +/** + * Top-level settings activity to handle single pane and double pane UI layout. + */ +public class Settings extends PreferenceActivity implements + SettingsPreferenceFragment.FragmentStarter { + + // TODO: Update Call Settings based on airplane mode state. + /** + * Populate the activity with the top-level headers. + */ @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - addPreferencesFromResource(R.xml.settings); + public void onBuildHeaders(List<Header> target) { + loadHeadersFromResource(R.xml.settings_headers, target); - PreferenceGroup parent = (PreferenceGroup) findPreference(KEY_PARENT); - Utils.updatePreferenceToSpecificActivityOrRemove(this, parent, KEY_SYNC_SETTINGS, 0); + updateHeaderList(target); + } - Preference dockSettings = parent.findPreference(KEY_DOCK_SETTINGS); - if (getResources().getBoolean(R.bool.has_dock_settings) == false && dockSettings != null) { - parent.removePreference(dockSettings); + private void updateHeaderList(List<Header> target) { + int i = 0; + while (i < target.size()) { + Header header = target.get(i); + long id = header.id; + if (id == R.id.dock_settings) { + if (!needsDockSettings()) + target.remove(header); + } else if (id == R.id.operator_settings || id == R.id.manufacturer_settings) { + Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header); + } else if (id == R.id.call_settings) { + if (!Utils.isVoiceCapable(this)) + target.remove(header); + } + if (target.get(i) == header) + i++; } - - Utils.updatePreferenceToSpecificActivityFromMetaDataOrRemove(this, parent, - KEY_OPERATOR_SETTINGS); - Utils.updatePreferenceToSpecificActivityFromMetaDataOrRemove(this, parent, - KEY_MANUFACTURER_SETTINGS); } - - @Override - protected void onResume() { - super.onResume(); - findPreference(KEY_CALL_SETTINGS).setEnabled( - !AirplaneModeEnabler.isAirplaneModeOn(this) - || SipManager.isVoipSupported(this)); + + private boolean needsDockSettings() { + return getResources().getBoolean(R.bool.has_dock_settings); } + public boolean startFragment(Fragment caller, String fragmentClass, int requestCode, + Bundle extras) { + Fragment f = Fragment.instantiate(this, fragmentClass, extras); + caller.setTargetFragment(f, requestCode); + if (f instanceof SettingsPreferenceFragment) { + SettingsPreferenceFragment spf = (SettingsPreferenceFragment) f; + spf.setFragmentStarter(this); + } + return true; + } } diff --git a/src/com/android/settings/SettingsPreferenceFragment.java b/src/com/android/settings/SettingsPreferenceFragment.java new file mode 100644 index 000000000..f41561e86 --- /dev/null +++ b/src/com/android/settings/SettingsPreferenceFragment.java @@ -0,0 +1,348 @@ +/* + * 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 android.app.Activity; +import android.app.Dialog; +import android.app.DialogFragment; +import android.app.Fragment; +import android.content.ContentResolver; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.os.Bundle; +import android.preference.PreferenceActivity; +import android.preference.PreferenceFragment; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; + +/** + * Letting the class, assumed to be Fragment, create a Dialog on it. Should be useful + * you want to utilize some capability in {@link SettingsPreferenceFragment} but don't want + * the class inherit the class itself (See {@link ProxySelector} for example). + */ +interface DialogCreatable { + public Dialog onCreateDialog(int dialogId); +} + +/** + * Base class for Settings fragments, with some helper functions and dialog management. + */ +public class SettingsPreferenceFragment extends PreferenceFragment + implements DialogCreatable { + + private static final String TAG = "SettingsPreferenceFragment"; + + // Originally from PreferenceActivity. + private static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar"; + private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip"; + private static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text"; + private static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text"; + + private SettingsDialogFragment mDialogFragment; + + private OnStateListener mOnStateListener; + private FragmentStarter mFragmentStarter; + + private int mResultCode = Activity.RESULT_CANCELED; + private Intent mResultData; + + private Button mNextButton; + + private boolean mReportedCreation; + + interface OnStateListener { + + void onCreated(SettingsPreferenceFragment fragment); + + void onDestroyed(SettingsPreferenceFragment fragment); + } + + public void setOnStateListener(OnStateListener listener) { + mOnStateListener = listener; + } + + /** + * Letting the class, assumed to be Fragment, start another Fragment object. + * The target Fragment object is stored in the caller Fragment using + * {@link Fragment#setTargetFragment(Fragment, int)}. The caller + * is able to obtain result code and result data via + * {@link SettingsPreferenceFragment#getResultCode()} and + * {@link SettingsPreferenceFragment#getResultData()} accordingly. + */ + interface FragmentStarter { + public boolean startFragment( + Fragment caller, String fragmentClass, int requestCode, Bundle extras); + } + + public void setFragmentStarter(FragmentStarter starter) { + mFragmentStarter = starter; + } + + @Override + public void onResume() { + super.onResume(); + + final Fragment f = getTargetFragment(); + final int requestCode = getTargetRequestCode(); + + // TargetFragment becomes invalid when this object is resumed. Notify it to + // FragmentManager. Without this code, FragmentManager wrongly take the TargetFragment + // as live, and throws IllegalStateException. + setTargetFragment(null, -1); + + if (f != null && (f instanceof SettingsPreferenceFragment)) { + final SettingsPreferenceFragment spf = (SettingsPreferenceFragment)f; + final int resultCode = spf.getResultCode(); + final Intent resultData = spf.getResultData(); + onActivityResult(requestCode, resultCode, resultData); + } + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + if (mOnStateListener != null && !mReportedCreation) { + mOnStateListener.onCreated(this); + // So that we don't report it on the way back to this fragment + mReportedCreation = true; + } + + setupButtonBar(); + } + + public final void setResult(int resultCode) { + mResultCode = resultCode; + mResultData = null; + } + + public final void setResult(int resultCode, Intent data) { + mResultCode = resultCode; + mResultData = data; + } + + public final int getResultCode() { + return mResultCode; + } + + public final Intent getResultData() { + return mResultData; + } + + /* + * The name is intentionally made different from Activity#finish(), so that + * users won't misunderstand its meaning. + */ + public final void finishFragment() { + getActivity().onBackPressed(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (mOnStateListener != null) { + mOnStateListener.onDestroyed(this); + } + } + + // Some helpers for functions used by the settings fragments when they were activities + + /** + * Returns the ContentResolver from the owning Activity. + */ + protected ContentResolver getContentResolver() { + return getActivity().getContentResolver(); + } + + /** + * Returns the specified system service from the owning Activity. + */ + protected Object getSystemService(final String name) { + return getActivity().getSystemService(name); + } + + /** + * Returns the Resources from the owning Activity. + */ + protected Resources getResources() { + return getActivity().getResources(); + } + + /** + * Returns the PackageManager from the owning Activity. + */ + protected PackageManager getPackageManager() { + return getActivity().getPackageManager(); + } + + // Dialog management + + protected void showDialog(int dialogId) { + if (mDialogFragment != null) { + Log.e(TAG, "Old dialog fragment not null!"); + } + mDialogFragment = new SettingsDialogFragment(this, dialogId); + mDialogFragment.show(getActivity().getFragmentManager(), Integer.toString(dialogId)); + } + + @Override + public Dialog onCreateDialog(int dialogId) { + return null; + } + + protected void removeDialog(int dialogId) { + if (mDialogFragment != null && mDialogFragment.getDialogId() == dialogId + && mDialogFragment.isVisible()) { + mDialogFragment.dismiss(); + } + mDialogFragment = null; + } + + static class SettingsDialogFragment extends DialogFragment { + private int mDialogId; + + private DialogCreatable mFragment; + + SettingsDialogFragment(DialogCreatable fragment, int dialogId) { + mDialogId = dialogId; + mFragment = fragment; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + return mFragment.onCreateDialog(mDialogId); + } + + public int getDialogId() { + return mDialogId; + } + } + + protected boolean hasNextButton() { + return mNextButton != null; + } + + protected Button getNextButton() { + return mNextButton; + } + + public void finish() { + getActivity().onBackPressed(); + } + + public boolean startFragment( + Fragment caller, String fragmentClass, int requestCode, Bundle extras) { + if (mFragmentStarter != null) { + return mFragmentStarter.startFragment(caller, fragmentClass, requestCode, extras); + } else { + Log.w(TAG, "FragmentStarter is not set."); + return false; + } + } + + /** + * Sets up Button Bar possibly required in the Fragment. Probably available only in + * phones. + * + * Previously {@link PreferenceActivity} had the capability as hidden functionality. + */ + private void setupButtonBar() { + // Originally from PreferenceActivity, which has had button bar inside its layout. + final Activity activity = getActivity(); + final Intent intent = activity.getIntent(); + final View buttonBar = activity.findViewById(com.android.internal.R.id.button_bar); + if (!intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false) || buttonBar == null) { + return; + } + + buttonBar.setVisibility(View.VISIBLE); + View tmpView = activity.findViewById(com.android.internal.R.id.back_button); + if (tmpView != null) { + // TODO: Assume this is pressed only in single pane, finishing current Activity. + try { + final Button backButton = (Button)tmpView; + backButton.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + activity.setResult(Activity.RESULT_CANCELED); + activity.finish(); + } + }); + if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) { + String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT); + if (TextUtils.isEmpty(buttonText)) { + backButton.setVisibility(View.GONE); + } + else { + backButton.setText(buttonText); + } + } + } catch (ClassCastException e) { + Log.w(TAG, "The view originally for back_button is used not as Button. " + + "Ignored."); + } + } + + tmpView = activity.findViewById(com.android.internal.R.id.skip_button); + if (tmpView != null) { + try { + final Button skipButton = (Button)tmpView; + skipButton.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + activity.setResult(Activity.RESULT_OK); + activity.finish(); + } + }); + if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) { + skipButton.setVisibility(View.VISIBLE); + } + } catch (ClassCastException e) { + Log.w(TAG, "The view originally for skip_button is used not as Button. " + + "Ignored."); + } + } + + tmpView = activity.findViewById(com.android.internal.R.id.next_button); + if (tmpView != null) { + try { + mNextButton = (Button)tmpView; + mNextButton.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + activity.setResult(Activity.RESULT_OK); + activity.finish(); + } + }); + // set our various button parameters + if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) { + String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT); + if (TextUtils.isEmpty(buttonText)) { + mNextButton.setVisibility(View.GONE); + } + else { + mNextButton.setText(buttonText); + } + } + } catch (ClassCastException e) { + Log.w(TAG, "The view originally for next_button is used not as Button. " + + "Ignored."); + mNextButton = null; + } + } + } +} diff --git a/src/com/android/settings/SoundSettings.java b/src/com/android/settings/SoundSettings.java index a735268b1..8582f171f 100644 --- a/src/com/android/settings/SoundSettings.java +++ b/src/com/android/settings/SoundSettings.java @@ -23,26 +23,21 @@ import android.content.Intent; import android.content.IntentFilter; import android.media.AudioManager; import android.os.Bundle; -import android.os.RemoteException; -import android.os.ServiceManager; import android.preference.CheckBoxPreference; import android.preference.ListPreference; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.telephony.TelephonyManager; import android.util.Log; -import android.view.IWindowManager; -public class SoundSettings extends PreferenceActivity implements +public class SoundSettings extends SettingsPreferenceFragment implements Preference.OnPreferenceChangeListener { private static final String TAG = "SoundAndDisplaysSettings"; /** If there is no setting in the provider, use this. */ - private static final int FALLBACK_SCREEN_TIMEOUT_VALUE = 30000; private static final int FALLBACK_EMERGENCY_TONE_VALUE = 0; private static final String KEY_SILENT = "silent"; @@ -54,12 +49,21 @@ public class SoundSettings extends PreferenceActivity implements private static final String KEY_SOUND_SETTINGS = "sound_settings"; private static final String KEY_NOTIFICATION_PULSE = "notification_pulse"; private static final String KEY_LOCK_SOUNDS = "lock_sounds"; + private static final String KEY_RINGTONE = "ringtone"; + private static final String KEY_NOTIFICATION_SOUND = "notification_sound"; + private static final String KEY_CATEGORY_CALLS = "category_calls"; + private static final String KEY_CATEGORY_NOTIFICATION = "category_notification"; private static final String VALUE_VIBRATE_NEVER = "never"; private static final String VALUE_VIBRATE_ALWAYS = "always"; private static final String VALUE_VIBRATE_ONLY_SILENT = "silent"; private static final String VALUE_VIBRATE_UNLESS_SILENT = "notsilent"; + private static final String[] NEED_VOICE_CAPABILITY = { + KEY_RINGTONE, KEY_DTMF_TONE, KEY_CATEGORY_CALLS, + KEY_EMERGENCY_TONE + }; + private CheckBoxPreference mSilent; /* @@ -90,7 +94,7 @@ public class SoundSettings extends PreferenceActivity implements private PreferenceGroup mSoundSettings; @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ContentResolver resolver = getContentResolver(); int activePhoneType = TelephonyManager.getDefault().getPhoneType(); @@ -137,8 +141,8 @@ public class SoundSettings extends PreferenceActivity implements mSoundSettings = (PreferenceGroup) findPreference(KEY_SOUND_SETTINGS); mNotificationPulse = (CheckBoxPreference) mSoundSettings.findPreference(KEY_NOTIFICATION_PULSE); - if (mNotificationPulse != null && - getResources().getBoolean(R.bool.has_intrusive_led) == false) { + if (mNotificationPulse != null + && getResources().getBoolean(R.bool.has_intrusive_led) == false) { mSoundSettings.removePreference(mNotificationPulse); } else { try { @@ -150,23 +154,31 @@ public class SoundSettings extends PreferenceActivity implements } } + if (!Utils.isVoiceCapable(getActivity())) { + for (String prefKey : NEED_VOICE_CAPABILITY) { + Preference pref = findPreference(prefKey); + if (pref != null) { + getPreferenceScreen().removePreference(pref); + } + } + } } @Override - protected void onResume() { + public void onResume() { super.onResume(); updateState(true); IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION); - registerReceiver(mReceiver, filter); + getActivity().registerReceiver(mReceiver, filter); } @Override - protected void onPause() { + public void onPause() { super.onPause(); - unregisterReceiver(mReceiver); + getActivity().unregisterReceiver(mReceiver); } private String getPhoneVibrateSettingValue() { diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java index b92457113..f632a02ce 100644 --- a/src/com/android/settings/TetherSettings.java +++ b/src/com/android/settings/TetherSettings.java @@ -16,21 +16,24 @@ package com.android.settings; +import com.android.settings.bluetooth.BluetoothSettings; import com.android.settings.wifi.WifiApEnabler; +import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; -import android.os.Bundle; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothPan; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.AssetManager; import android.net.ConnectivityManager; +import android.os.Bundle; import android.os.Environment; import android.preference.CheckBoxPreference; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceScreen; import android.webkit.WebView; @@ -41,10 +44,14 @@ import java.util.Locale; /* * Displays preferences for Tethering. */ -public class TetherSettings extends PreferenceActivity { +public class TetherSettings extends SettingsPreferenceFragment { + private static final String TAG = "TetheringSettings"; + private static final String USB_TETHER_SETTINGS = "usb_tether_settings"; private static final String ENABLE_WIFI_AP = "enable_wifi_ap"; private static final String WIFI_AP_SETTINGS = "wifi_ap_settings"; + private static final String ENABLE_BLUETOOTH_TETHERING = "enable_bluetooth_tethering"; + private static final String BLUETOOTH_TETHER_SETTINGS = "bluetooth_tether_settings"; private static final String TETHERING_HELP = "tethering_help"; private static final String USB_HELP_MODIFIER = "usb_"; private static final String WIFI_HELP_MODIFIER = "wifi_"; @@ -59,6 +66,10 @@ public class TetherSettings extends PreferenceActivity { private CheckBoxPreference mEnableWifiAp; private PreferenceScreen mWifiApSettings; private WifiApEnabler mWifiApEnabler; + + private CheckBoxPreference mBluetoothTether; + private PreferenceScreen mBluetoothSettings; + private PreferenceScreen mTetherHelp; private BroadcastReceiver mTetherChangeReceiver; @@ -67,48 +78,85 @@ public class TetherSettings extends PreferenceActivity { private String[] mWifiRegexs; + private String[] mBluetoothRegexs; + private BluetoothPan mBluetoothPan; + @Override - protected void onCreate(Bundle icicle) { + public void onCreate(Bundle icicle) { super.onCreate(icicle); - addPreferencesFromResource(R.xml.tether_prefs); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + final Activity activity = getActivity(); + mBluetoothPan = new BluetoothPan(activity); mEnableWifiAp = (CheckBoxPreference) findPreference(ENABLE_WIFI_AP); mWifiApSettings = (PreferenceScreen) findPreference(WIFI_AP_SETTINGS); mUsbTether = (CheckBoxPreference) findPreference(USB_TETHER_SETTINGS); + mBluetoothTether = (CheckBoxPreference) findPreference(ENABLE_BLUETOOTH_TETHERING); + mBluetoothSettings = (PreferenceScreen) findPreference(BLUETOOTH_TETHER_SETTINGS); mTetherHelp = (PreferenceScreen) findPreference(TETHERING_HELP); ConnectivityManager cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE); mUsbRegexs = cm.getTetherableUsbRegexs(); - if (mUsbRegexs.length == 0) { - getPreferenceScreen().removePreference(mUsbTether); + mWifiRegexs = cm.getTetherableWifiRegexs(); + mBluetoothRegexs = cm.getTetherableBluetoothRegexs(); - setTitle(R.string.tether_settings_title_wifi); - } + boolean usbAvailable = mUsbRegexs.length != 0; + boolean wifiAvailable = mWifiRegexs.length != 0; + boolean bluetoothAvailable = mBluetoothRegexs.length != 0; - mWifiRegexs = cm.getTetherableWifiRegexs(); - if (mWifiRegexs.length == 0) { + + if (!usbAvailable || Utils.isMonkeyRunning()) { + getPreferenceScreen().removePreference(mUsbTether); + } + if (!wifiAvailable) { getPreferenceScreen().removePreference(mEnableWifiAp); getPreferenceScreen().removePreference(mWifiApSettings); - - setTitle(R.string.tether_settings_title_usb); - } else if (mUsbRegexs.length != 0) { - // have both - setTitle(R.string.tether_settings_title_both); } - mWifiApEnabler = new WifiApEnabler(this, mEnableWifiAp); - mView = new WebView(this); + if (!bluetoothAvailable) { + getPreferenceScreen().removePreference(mBluetoothTether); + getPreferenceScreen().removePreference(mBluetoothSettings); + } else { + if (mBluetoothPan.isTetheringOn()) { + mBluetoothTether.setChecked(true); + mBluetoothSettings.setEnabled(true); + } else { + mBluetoothTether.setChecked(false); + mBluetoothSettings.setEnabled(false); + } + } + if (wifiAvailable && usbAvailable && bluetoothAvailable){ + activity.setTitle(R.string.tether_settings_title_all); + } else if (wifiAvailable && usbAvailable){ + activity.setTitle(R.string.tether_settings_title_all); + } else if (wifiAvailable && bluetoothAvailable){ + activity.setTitle(R.string.tether_settings_title_all); + } else if (wifiAvailable) { + activity.setTitle(R.string.tether_settings_title_wifi); + } else if (usbAvailable && bluetoothAvailable) { + activity.setTitle(R.string.tether_settings_title_usb_bluetooth); + } else if (usbAvailable) { + activity.setTitle(R.string.tether_settings_title_usb); + } else { + activity.setTitle(R.string.tether_settings_title_bluetooth); + } + mWifiApEnabler = new WifiApEnabler(activity, mEnableWifiAp); + mView = new WebView(activity); } @Override - protected Dialog onCreateDialog(int id) { + public Dialog onCreateDialog(int id) { if (id == DIALOG_TETHER_HELP) { Locale locale = Locale.getDefault(); // check for the full language + country resource, if not there, try just language - AssetManager am = getAssets(); + final AssetManager am = getActivity().getAssets(); String path = HELP_PATH.replace("%y", locale.getLanguage().toLowerCase()); path = path.replace("%z", "_"+locale.getCountry().toLowerCase()); boolean useCountry = true; @@ -138,7 +186,7 @@ public class TetherSettings extends PreferenceActivity { mView.loadUrl(url); - return new AlertDialog.Builder(this) + return new AlertDialog.Builder(getActivity()) .setCancelable(true) .setTitle(R.string.tethering_help_button_text) .setView(mView) @@ -148,6 +196,7 @@ public class TetherSettings extends PreferenceActivity { } private class TetherChangeReceiver extends BroadcastReceiver { + @Override public void onReceive(Context content, Intent intent) { if (intent.getAction().equals(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)) { // TODO - this should understand the interface types @@ -157,37 +206,46 @@ public class TetherSettings extends PreferenceActivity { ConnectivityManager.EXTRA_ACTIVE_TETHER); ArrayList<String> errored = intent.getStringArrayListExtra( ConnectivityManager.EXTRA_ERRORED_TETHER); - updateState((String[]) available.toArray(), (String[]) active.toArray(), - (String[]) errored.toArray()); + updateState(available.toArray(new String[available.size()]), + active.toArray(new String[active.size()]), + errored.toArray(new String[errored.size()])); } else if (intent.getAction().equals(Intent.ACTION_MEDIA_SHARED) || intent.getAction().equals(Intent.ACTION_MEDIA_UNSHARED)) { updateState(); + } else if (intent.getAction().equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { + updateState(); } } } @Override - protected void onResume() { - super.onResume(); + public void onStart() { + super.onStart(); + + final Activity activity = getActivity(); - IntentFilter filter = new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED); mTetherChangeReceiver = new TetherChangeReceiver(); - Intent intent = registerReceiver(mTetherChangeReceiver, filter); + IntentFilter filter = new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED); + Intent intent = activity.registerReceiver(mTetherChangeReceiver, filter); filter = new IntentFilter(); filter.addAction(Intent.ACTION_MEDIA_SHARED); filter.addAction(Intent.ACTION_MEDIA_UNSHARED); filter.addDataScheme("file"); - registerReceiver(mTetherChangeReceiver, filter); + activity.registerReceiver(mTetherChangeReceiver, filter); + + filter = new IntentFilter(); + filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); + activity.registerReceiver(mTetherChangeReceiver, filter); - if (intent != null) mTetherChangeReceiver.onReceive(this, intent); + if (intent != null) mTetherChangeReceiver.onReceive(activity, intent); mWifiApEnabler.resume(); } @Override - protected void onPause() { - super.onPause(); - unregisterReceiver(mTetherChangeReceiver); + public void onStop() { + super.onStop(); + getActivity().unregisterReceiver(mTetherChangeReceiver); mTetherChangeReceiver = null; mWifiApEnabler.pause(); } @@ -204,6 +262,13 @@ public class TetherSettings extends PreferenceActivity { private void updateState(String[] available, String[] tethered, String[] errored) { + updateUsbState(available, tethered, errored); + updateBluetoothState(available, tethered, errored); + } + + + private void updateUsbState(String[] available, String[] tethered, + String[] errored) { ConnectivityManager cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE); boolean usbTethered = false; @@ -260,8 +325,68 @@ public class TetherSettings extends PreferenceActivity { } } + private void updateBluetoothState(String[] available, String[] tethered, + String[] errored) { + ConnectivityManager cm = + (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE); + boolean bluetoothTethered = false; + boolean bluetoothAvailable = false; + int bluetoothError = ConnectivityManager.TETHER_ERROR_NO_ERROR; + boolean bluetoothErrored = false; + for (String s : available) { + for (String regex : mBluetoothRegexs) { + if (s.matches(regex)) { + bluetoothAvailable = true; + if (bluetoothError == ConnectivityManager.TETHER_ERROR_NO_ERROR) { + bluetoothError = cm.getLastTetherError(s); + } + } + } + } + for (String s : tethered) { + for (String regex : mBluetoothRegexs) { + if (s.matches(regex)) bluetoothTethered = true; + } + } + for (String s: errored) { + for (String regex : mBluetoothRegexs) { + if (s.matches(regex)) bluetoothErrored = true; + } + } + + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + int btState = adapter.getState(); + if (btState == BluetoothAdapter.STATE_TURNING_OFF) { + mBluetoothTether.setEnabled(false); + mBluetoothSettings.setEnabled(false); + mBluetoothTether.setSummary(R.string.wifi_stopping); + } else if (btState == BluetoothAdapter.STATE_TURNING_ON) { + mBluetoothTether.setEnabled(false); + mBluetoothSettings.setEnabled(false); + mBluetoothTether.setSummary(R.string.bluetooth_turning_on); + } else if (mBluetoothPan.isTetheringOn()) { + mBluetoothTether.setChecked(true); + if (btState == BluetoothAdapter.STATE_ON) { + mBluetoothTether.setEnabled(true); + mBluetoothSettings.setEnabled(true); + if (bluetoothTethered) { + mBluetoothTether.setSummary(R.string.bluetooth_tethering_connected_subtext); + } else if (bluetoothErrored) { + mBluetoothTether.setSummary(R.string.bluetooth_tethering_errored_subtext); + } else { + mBluetoothTether.setSummary(R.string.bluetooth_tethering_available_subtext); + } + } + } else { + mBluetoothTether.setEnabled(true); + mBluetoothTether.setChecked(false); + mBluetoothSettings.setEnabled(false); + mBluetoothTether.setSummary(R.string.bluetooth_tethering_off_subtext); + } + } + @Override - public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { + public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) { if (preference == mUsbTether) { boolean newState = mUsbTether.isChecked(); @@ -296,11 +421,52 @@ public class TetherSettings extends PreferenceActivity { } mUsbTether.setSummary(""); } - } else if (preference == mTetherHelp) { + } else if(preference == mBluetoothTether) { + boolean bluetoothTetherState = mBluetoothTether.isChecked(); + + if (bluetoothTetherState) { + // turn on Bluetooth first + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + if (adapter.getState() == BluetoothAdapter.STATE_OFF) { + adapter.enable(); + mBluetoothTether.setSummary(R.string.bluetooth_turning_on); + mBluetoothTether.setEnabled(false); + mBluetoothSettings.setEnabled(false); + } else { + mBluetoothSettings.setEnabled(true); + } + + mBluetoothPan.setBluetoothTethering(true); + mBluetoothTether.setSummary(R.string.bluetooth_tethering_available_subtext); + } else { + boolean errored = false; + ConnectivityManager cm = + (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE); + String [] tethered = cm.getTetheredIfaces(); + String bluetoothIface = findIface(tethered, mBluetoothRegexs); + if (bluetoothIface != null && + cm.untether(bluetoothIface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) { + errored = true; + } + + mBluetoothPan.setBluetoothTethering(false); + mBluetoothSettings.setEnabled(false); + if (errored) { + mBluetoothTether.setSummary(R.string.bluetooth_tethering_errored_subtext); + } else { + mBluetoothTether.setSummary(R.string.bluetooth_tethering_off_subtext); + } + } + } else if (preference == mBluetoothSettings) { + preference.getExtras().putString(BluetoothSettings.ACTION, + BluetoothSettings.ACTION_LAUNCH_TETHER_PICKER); + } else if (preference == mTetherHelp) { showDialog(DIALOG_TETHER_HELP); + return true; } - return false; + + return super.onPreferenceTreeClick(screen, preference); } private String findIface(String[] ifaces, String[] regexes) { diff --git a/src/com/android/settings/TextToSpeechSettings.java b/src/com/android/settings/TextToSpeechSettings.java index 89a464198..dc3a96b6b 100644 --- a/src/com/android/settings/TextToSpeechSettings.java +++ b/src/com/android/settings/TextToSpeechSettings.java @@ -16,29 +16,30 @@ package com.android.settings; -import static android.provider.Settings.Secure.TTS_USE_DEFAULTS; -import static android.provider.Settings.Secure.TTS_DEFAULT_RATE; -import static android.provider.Settings.Secure.TTS_DEFAULT_LANG; import static android.provider.Settings.Secure.TTS_DEFAULT_COUNTRY; -import static android.provider.Settings.Secure.TTS_DEFAULT_VARIANT; +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.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.Preference.OnPreferenceClickListener; -import android.preference.PreferenceActivity; import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; -import android.preference.CheckBoxPreference; +import android.preference.Preference.OnPreferenceClickListener; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.speech.tts.TextToSpeech; @@ -49,7 +50,7 @@ import java.util.List; import java.util.Locale; import java.util.StringTokenizer; -public class TextToSpeechSettings extends PreferenceActivity implements +public class TextToSpeechSettings extends SettingsPreferenceFragment implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener, TextToSpeech.OnInitListener { @@ -90,8 +91,6 @@ public class TextToSpeechSettings extends PreferenceActivity implements private String mDefaultEng = ""; private int mDefaultRate = TextToSpeech.Engine.DEFAULT_RATE; - // Array of strings used to demonstrate TTS in the different languages. - private String[] mDemoStrings; // Index of the current string to use for the demo. private int mDemoStringIndex = 0; @@ -109,16 +108,14 @@ public class TextToSpeechSettings extends PreferenceActivity implements private static final int GET_SAMPLE_TEXT = 1983; @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.tts_settings); - addEngineSpecificSettings(); - - mDemoStrings = getResources().getStringArray(R.array.tts_demo_strings); + final Activity activity = getActivity(); + addEngineSpecificSettings(activity); - setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM); + activity.setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM); mEnableDemo = false; mTtsStarted = false; @@ -128,18 +125,18 @@ public class TextToSpeechSettings extends PreferenceActivity implements mDefaultCountry = currentLocale.getISO3Country(); mDefaultLocVariant = currentLocale.getVariant(); - mTts = new TextToSpeech(this, this); + mTts = new TextToSpeech(activity, this); + initClickers(); } @Override - protected void onStart() { + public void onStart() { super.onStart(); if (mTtsStarted){ // whenever we return to this screen, we don't know the state of the // system, so we have to recheck that we can play the demo, or it must be disabled. // TODO make the TTS service listen to "changes in the system", i.e. sd card un/mount - initClickers(); updateWidgetState(); checkVoiceData(); } @@ -147,7 +144,7 @@ public class TextToSpeechSettings extends PreferenceActivity implements @Override - protected void onDestroy() { + public void onDestroy() { super.onDestroy(); if (mTts != null) { mTts.shutdown(); @@ -155,7 +152,7 @@ public class TextToSpeechSettings extends PreferenceActivity implements } @Override - protected void onPause() { + public void onPause() { super.onPause(); if ((mDefaultRatePref != null) && (mDefaultRatePref.getDialog() != null)) { mDefaultRatePref.getDialog().dismiss(); @@ -168,9 +165,7 @@ public class TextToSpeechSettings extends PreferenceActivity implements } } - - - private void addEngineSpecificSettings() { + 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]; @@ -180,14 +175,14 @@ public class TextToSpeechSettings extends PreferenceActivity implements String prefKey = ""; final String pluginPackageName = enginesArray[i].activityInfo.packageName; if (!enginesArray[i].activityInfo.packageName.equals(SYSTEM_TTS)) { - CheckBoxPreference chkbxPref = new CheckBoxPreference(this); + 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(this); + Preference pref = new Preference(context); prefKey = KEY_PLUGIN_SETTINGS_PREFIX + pluginPackageName; pref.setKey(prefKey); pref.setTitle(enginesArray[i].loadLabel(pm)); @@ -360,9 +355,8 @@ public class TextToSpeechSettings extends PreferenceActivity implements mDefaultLocVariant = new String(); } mTts.setLanguage(new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant)); - mTts.setSpeechRate((float)(mDefaultRate/100.0f)); + mTts.setSpeechRate(mDefaultRate/100.0f); initDefaultSettings(); - initClickers(); updateWidgetState(); checkVoiceData(); mTtsStarted = true; @@ -378,7 +372,8 @@ public class TextToSpeechSettings extends PreferenceActivity implements /** * Called when voice data integrity check returns */ - protected void onActivityResult(int requestCode, int resultCode, Intent data) { + @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; @@ -402,7 +397,7 @@ public class TextToSpeechSettings extends PreferenceActivity implements } if (available.size() > 0){ if (mTts == null) { - mTts = new TextToSpeech(this, this); + mTts = new TextToSpeech(getActivity(), this); } ListPreference ttsLanguagePref = (ListPreference) findPreference("tts_default_lang"); @@ -478,7 +473,7 @@ public class TextToSpeechSettings extends PreferenceActivity implements updateWidgetState(); } else if (requestCode == GET_SAMPLE_TEXT) { if (resultCode == TextToSpeech.LANG_AVAILABLE) { - String sample = getString(R.string.tts_demo); + String sample = getActivity().getString(R.string.tts_demo); if ((data != null) && (data.getStringExtra("sampleText") != null)) { sample = data.getStringExtra("sampleText"); } @@ -492,7 +487,6 @@ public class TextToSpeechSettings extends PreferenceActivity implements } } - public boolean onPreferenceChange(Preference preference, Object objValue) { if (KEY_TTS_USE_DEFAULT.equals(preference.getKey())) { // "Use Defaults" @@ -507,7 +501,7 @@ public class TextToSpeechSettings extends PreferenceActivity implements Settings.Secure.putInt(getContentResolver(), TTS_DEFAULT_RATE, mDefaultRate); if (mTts != null) { - mTts.setSpeechRate((float)(mDefaultRate/100.0f)); + mTts.setSpeechRate(mDefaultRate/100.0f); } Log.i(TAG, "TTS default rate is " + mDefaultRate); } catch (NumberFormatException e) { @@ -575,10 +569,11 @@ public class TextToSpeechSettings extends PreferenceActivity implements if (!chkPref.getKey().equals(KEY_TTS_USE_DEFAULT)){ if (chkPref.isChecked()) { chkPref.setChecked(false); - AlertDialog d = (new AlertDialog.Builder(this)) + AlertDialog d = (new AlertDialog.Builder(getActivity())) .setTitle(android.R.string.dialog_alert_title) .setIcon(android.R.drawable.ic_dialog_alert) - .setMessage(getString(R.string.tts_engine_security_warning, + .setMessage( + getActivity().getString(R.string.tts_engine_security_warning, chkPref.getTitle())) .setCancelable(true) .setPositiveButton(android.R.string.ok, diff --git a/src/com/android/settings/UserDictionarySettings.java b/src/com/android/settings/UserDictionarySettings.java index 6ffcb3d6b..7bd5d56ac 100644 --- a/src/com/android/settings/UserDictionarySettings.java +++ b/src/com/android/settings/UserDictionarySettings.java @@ -16,23 +16,32 @@ package com.android.settings; +import com.android.settings.SettingsPreferenceFragment.SettingsDialogFragment; + +import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; -import android.app.ListActivity; +import android.app.ListFragment; import android.content.Context; import android.content.DialogInterface; +import android.content.Intent; import android.database.Cursor; import android.os.Bundle; import android.provider.UserDictionary; import android.text.InputType; +import android.util.Log; import android.view.ContextMenu; +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.WindowManager; import android.view.ContextMenu.ContextMenuInfo; import android.widget.AlphabetIndexer; import android.widget.EditText; +import android.widget.ImageView; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.SectionIndexer; @@ -42,7 +51,8 @@ import android.widget.AdapterView.AdapterContextMenuInfo; import java.util.Locale; -public class UserDictionarySettings extends ListActivity { +public class UserDictionarySettings extends ListFragment implements DialogCreatable { + private static final String TAG = "UserDictionarySettings"; private static final String INSTANCE_KEY_DIALOG_EDITING_WORD = "DIALOG_EDITING_WORD"; private static final String INSTANCE_KEY_ADDED_WORD = "DIALOG_ADDED_WORD"; @@ -50,7 +60,10 @@ public class UserDictionarySettings extends ListActivity { private static final String[] QUERY_PROJECTION = { UserDictionary.Words._ID, UserDictionary.Words.WORD }; - + + private static final int INDEX_ID = 0; + private static final int INDEX_WORD = 1; + // 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 " @@ -69,52 +82,65 @@ public class UserDictionarySettings extends ListActivity { /** The word being edited in the dialog (null means the user is adding a word). */ private String mDialogEditingWord; - + + private View mView; private Cursor mCursor; private boolean mAddedWordAlready; private boolean mAutoReturn; - + + private SettingsDialogFragment mDialogFragment; + @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + mView = inflater.inflate(R.layout.list_content_with_empty_view, container, false); + return mView; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); - setContentView(R.layout.list_content_with_empty_view); - mCursor = createCursor(); - setListAdapter(createAdapter()); - - TextView emptyView = (TextView) findViewById(R.id.empty); + TextView emptyView = (TextView)mView.findViewById(R.id.empty); emptyView.setText(R.string.user_dict_settings_empty_text); - - ListView listView = getListView(); + + final ListView listView = getListView(); + listView.setAdapter(createAdapter()); listView.setFastScrollEnabled(true); listView.setEmptyView(emptyView); registerForContextMenu(listView); + setHasOptionsMenu(true); + + if (savedInstanceState != null) { + mDialogEditingWord = savedInstanceState.getString(INSTANCE_KEY_DIALOG_EDITING_WORD); + mAddedWordAlready = savedInstanceState.getBoolean(INSTANCE_KEY_ADDED_WORD, false); + } } @Override - protected void onResume() { + public void onResume() { super.onResume(); + final Intent intent = getActivity().getIntent(); if (!mAddedWordAlready - && getIntent().getAction().equals("com.android.settings.USER_DICTIONARY_INSERT")) { - String word = getIntent().getStringExtra(EXTRA_WORD); + && intent.getAction().equals("com.android.settings.USER_DICTIONARY_INSERT")) { + final String word = intent.getStringExtra(EXTRA_WORD); mAutoReturn = true; if (word != null) { showAddOrEditDialog(word); } } } - @Override - protected void onRestoreInstanceState(Bundle state) { - super.onRestoreInstanceState(state); - mDialogEditingWord = state.getString(INSTANCE_KEY_DIALOG_EDITING_WORD); - mAddedWordAlready = state.getBoolean(INSTANCE_KEY_ADDED_WORD, false); - } @Override - protected void onSaveInstanceState(Bundle outState) { + public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString(INSTANCE_KEY_DIALOG_EDITING_WORD, mDialogEditingWord); outState.putBoolean(INSTANCE_KEY_ADDED_WORD, mAddedWordAlready); @@ -123,21 +149,21 @@ public class UserDictionarySettings extends ListActivity { private Cursor createCursor() { String currentLocale = Locale.getDefault().toString(); // Case-insensitive sort - return managedQuery(UserDictionary.Words.CONTENT_URI, QUERY_PROJECTION, + return getActivity().managedQuery(UserDictionary.Words.CONTENT_URI, QUERY_PROJECTION, QUERY_SELECTION, new String[] { currentLocale }, "UPPER(" + UserDictionary.Words.WORD + ")"); } private ListAdapter createAdapter() { - return new MyAdapter(this, - android.R.layout.simple_list_item_1, mCursor, - new String[] { UserDictionary.Words.WORD }, - new int[] { android.R.id.text1 }); + return new MyAdapter(getActivity(), + R.layout.user_dictionary_item, mCursor, + new String[] { UserDictionary.Words.WORD, UserDictionary.Words._ID }, + new int[] { android.R.id.text1, R.id.delete_button }, this); } @Override - protected void onListItemClick(ListView l, View v, int position, long id) { - openContextMenu(v); + public void onListItemClick(ListView l, View v, int position, long id) { + getActivity().openContextMenu(v); } @Override @@ -175,10 +201,11 @@ public class UserDictionarySettings extends ListActivity { } @Override - public boolean onCreateOptionsMenu(Menu menu) { - menu.add(0, OPTIONS_MENU_ADD, 0, R.string.user_dict_settings_add_menu_title) + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + MenuItem actionItem = + menu.add(0, OPTIONS_MENU_ADD, 0, R.string.user_dict_settings_add_menu_title) .setIcon(R.drawable.ic_menu_add); - return true; + actionItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); } @Override @@ -191,7 +218,7 @@ public class UserDictionarySettings extends ListActivity { mDialogEditingWord = editingWord; showDialog(DIALOG_ADD_OR_EDIT); } - + private String getWord(int position) { mCursor.moveToPosition(position); // Handle a possible race-condition @@ -202,14 +229,16 @@ public class UserDictionarySettings extends ListActivity { } @Override - protected Dialog onCreateDialog(int id) { - View content = getLayoutInflater().inflate(R.layout.dialog_edittext, null); + public Dialog onCreateDialog(int id) { + final Activity activity = getActivity(); + final View content = activity.getLayoutInflater().inflate(R.layout.dialog_edittext, null); final EditText editText = (EditText) content.findViewById(R.id.edittext); + editText.setText(mDialogEditingWord); // No prediction in soft keyboard mode. TODO: Create a better way to disable prediction editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE); - - AlertDialog dialog = new AlertDialog.Builder(this) + + AlertDialog dialog = new AlertDialog.Builder(activity) .setTitle(mDialogEditingWord != null ? R.string.user_dict_settings_edit_dialog_title : R.string.user_dict_settings_add_dialog_title) @@ -217,11 +246,11 @@ public class UserDictionarySettings extends ListActivity { .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { onAddOrEditFinished(editText.getText().toString()); - if (mAutoReturn) finish(); + if (mAutoReturn) activity.onBackPressed(); }}) .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { - if (mAutoReturn) finish(); + if (mAutoReturn) activity.onBackPressed(); }}) .create(); dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | @@ -229,14 +258,12 @@ public class UserDictionarySettings extends ListActivity { return dialog; } - @Override - protected void onPrepareDialog(int id, Dialog d) { - AlertDialog dialog = (AlertDialog) d; - d.setTitle(mDialogEditingWord != null - ? R.string.user_dict_settings_edit_dialog_title - : R.string.user_dict_settings_add_dialog_title); - EditText editText = (EditText) dialog.findViewById(R.id.edittext); - editText.setText(mDialogEditingWord); + private void showDialog(int dialogId) { + if (mDialogFragment != null) { + Log.e(TAG, "Old dialog fragment not null!"); + } + mDialogFragment = new SettingsDialogFragment(this, dialogId); + mDialogFragment.show(getActivity().getFragmentManager(), Integer.toString(dialogId)); } private void onAddOrEditFinished(String word) { @@ -249,26 +276,48 @@ public class UserDictionarySettings extends ListActivity { deleteWord(word); // TODO: present UI for picking whether to add word to all locales, or current. - UserDictionary.Words.addWord(this, word.toString(), + UserDictionary.Words.addWord(getActivity(), word.toString(), 250, UserDictionary.Words.LOCALE_TYPE_ALL); - mCursor.requery(); + if (!mCursor.requery()) { + throw new IllegalStateException("can't requery on already-closed cursor."); + } mAddedWordAlready = true; } private void deleteWord(String word) { - getContentResolver().delete(UserDictionary.Words.CONTENT_URI, DELETE_SELECTION, - new String[] { word }); + getActivity().getContentResolver().delete( + UserDictionary.Words.CONTENT_URI, DELETE_SELECTION, new String[] { word }); } - - private static class MyAdapter extends SimpleCursorAdapter implements SectionIndexer { - private AlphabetIndexer mIndexer; - - public MyAdapter(Context context, int layout, Cursor c, String[] from, int[] to) { + + private static class MyAdapter extends SimpleCursorAdapter implements SectionIndexer, + View.OnClickListener { + + private AlphabetIndexer mIndexer; + private UserDictionarySettings mSettings; + + private ViewBinder mViewBinder = new ViewBinder() { + + public boolean setViewValue(View v, Cursor c, int columnIndex) { + if (v instanceof ImageView && columnIndex == INDEX_ID) { + v.setOnClickListener(MyAdapter.this); + v.setTag(c.getString(INDEX_WORD)); + return true; + } + + return false; + } + }; + + public MyAdapter(Context context, int layout, Cursor c, String[] from, int[] to, + UserDictionarySettings settings) { super(context, layout, c, from, to); + mSettings = settings; int wordColIndex = c.getColumnIndexOrThrow(UserDictionary.Words.WORD); - String alphabet = context.getString(com.android.internal.R.string.fast_scroll_alphabet); - mIndexer = new AlphabetIndexer(c, wordColIndex, alphabet); + String alphabet = context.getString( + com.android.internal.R.string.fast_scroll_alphabet); + mIndexer = new AlphabetIndexer(c, wordColIndex, alphabet); + setViewBinder(mViewBinder); } public int getPositionForSection(int section) { @@ -282,5 +331,9 @@ public class UserDictionarySettings extends ListActivity { public Object[] getSections() { return mIndexer.getSections(); } + + public void onClick(View v) { + mSettings.deleteWord((String) v.getTag()); + } } } diff --git a/src/com/android/settings/UserLeaveHintListener.java b/src/com/android/settings/UserLeaveHintListener.java new file mode 100644 index 000000000..c5c2a7a47 --- /dev/null +++ b/src/com/android/settings/UserLeaveHintListener.java @@ -0,0 +1,24 @@ +/* + * 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; + +/** + * Interface enabling fragments to listen to Activity#onUserLeaveHint(). + */ +public interface UserLeaveHintListener { + public void onUserLeaveHint(); +}
\ No newline at end of file diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index b29ec06f5..d6354035d 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -21,14 +21,16 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.os.SystemProperties; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.os.SystemProperties; import android.preference.Preference; import android.preference.PreferenceGroup; +import android.preference.PreferenceActivity.Header; +import android.telephony.TelephonyManager; import android.text.TextUtils; import java.util.List; @@ -201,10 +203,79 @@ public class Utils { return false; } + public static boolean updateHeaderToSpecificActivityFromMetaDataOrRemove(Context context, + List<Header> target, Header header) { + + Intent intent = header.intent; + if (intent != null) { + // Find the activity that is in the system image + PackageManager pm = context.getPackageManager(); + List<ResolveInfo> list = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA); + int listSize = list.size(); + for (int i = 0; i < listSize; i++) { + ResolveInfo resolveInfo = list.get(i); + if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) + != 0) { + Drawable icon = null; + String title = null; + String summary = null; + + // Get the activity's meta-data + try { + Resources res = pm.getResourcesForApplication( + resolveInfo.activityInfo.packageName); + Bundle metaData = resolveInfo.activityInfo.metaData; + + if (res != null && metaData != null) { + icon = res.getDrawable(metaData.getInt(META_DATA_PREFERENCE_ICON)); + title = res.getString(metaData.getInt(META_DATA_PREFERENCE_TITLE)); + summary = res.getString(metaData.getInt(META_DATA_PREFERENCE_SUMMARY)); + } + } catch (NameNotFoundException e) { + // Ignore + } catch (NotFoundException e) { + // Ignore + } + + // Set the preference title to the activity's label if no + // meta-data is found + if (TextUtils.isEmpty(title)) { + title = resolveInfo.loadLabel(pm).toString(); + } + + // Set icon, title and summary for the preference + // TODO: + //header.icon = icon; + header.title = title; + header.summary = summary; + // Replace the intent with this specific activity + header.intent = new Intent().setClassName(resolveInfo.activityInfo.packageName, + resolveInfo.activityInfo.name); + + return true; + } + } + } + + // Did not find a matching activity, so remove the preference + if (target.remove(header)) System.err.println("Removed " + header.id); + + return false; + } + /** * Returns true if Monkey is running. */ public static boolean isMonkeyRunning() { return SystemProperties.getBoolean("ro.monkey", false); } + + /** + * Returns whether the device is voice-capable (meaning, it is also a phone). + */ + public static boolean isVoiceCapable(Context context) { + TelephonyManager telephony = + (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); + return telephony != null && telephony.isVoiceCapable(); + } } diff --git a/src/com/android/settings/VoiceInputOutputSettings.java b/src/com/android/settings/VoiceInputOutputSettings.java index 30beddad7..b07c69ee6 100644 --- a/src/com/android/settings/VoiceInputOutputSettings.java +++ b/src/com/android/settings/VoiceInputOutputSettings.java @@ -31,7 +31,6 @@ import android.content.res.XmlResourceParser; import android.os.Bundle; import android.preference.ListPreference; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceCategory; import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; @@ -49,7 +48,7 @@ import java.util.List; /** * Settings screen for voice input/output. */ -public class VoiceInputOutputSettings extends PreferenceActivity +public class VoiceInputOutputSettings extends SettingsPreferenceFragment implements OnPreferenceChangeListener { private static final String TAG = "VoiceInputOutputSettings"; @@ -67,7 +66,7 @@ public class VoiceInputOutputSettings extends PreferenceActivity private HashMap<String, ResolveInfo> mAvailableRecognizersMap; @Override - protected void onCreate(Bundle icicle) { + public void onCreate(Bundle icicle) { super.onCreate(icicle); addPreferencesFromResource(R.xml.voice_input_output_settings); diff --git a/src/com/android/settings/WirelessSettings.java b/src/com/android/settings/WirelessSettings.java index 78cf8cff6..6d1ce8d27 100644 --- a/src/com/android/settings/WirelessSettings.java +++ b/src/com/android/settings/WirelessSettings.java @@ -16,27 +16,26 @@ 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 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.net.wifi.WifiManager; import android.os.Bundle; import android.os.ServiceManager; import android.os.SystemProperties; import android.preference.CheckBoxPreference; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceScreen; import android.provider.Settings; -import android.util.Log; -import com.android.internal.telephony.TelephonyIntents; -import com.android.internal.telephony.TelephonyProperties; -import com.android.settings.bluetooth.BluetoothEnabler; -import com.android.settings.wifi.WifiEnabler; - -public class WirelessSettings extends PreferenceActivity { +public class WirelessSettings extends SettingsPreferenceFragment { private static final String KEY_TOGGLE_AIRPLANE = "toggle_airplane"; private static final String KEY_TOGGLE_BLUETOOTH = "toggle_bluetooth"; @@ -45,6 +44,7 @@ public class WirelessSettings extends PreferenceActivity { 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"; public static final String EXIT_ECM_RESULT = "exit_ecm_result"; public static final int REQUEST_CODE_EXIT_ECM = 1; @@ -69,7 +69,7 @@ public class WirelessSettings extends PreferenceActivity { return true; } // Let the intents be launched by the Preference manager - return false; + return super.onPreferenceTreeClick(preferenceScreen, preference); } public static boolean isRadioAllowed(Context context, String type) { @@ -83,21 +83,22 @@ public class WirelessSettings extends PreferenceActivity { } @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); 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); - mAirplaneModeEnabler = new AirplaneModeEnabler(this, airplane); + mAirplaneModeEnabler = new AirplaneModeEnabler(activity, airplane); mAirplaneModePreference = (CheckBoxPreference) findPreference(KEY_TOGGLE_AIRPLANE); - mWifiEnabler = new WifiEnabler(this, wifi); - mBtEnabler = new BluetoothEnabler(this, bt); + mWifiEnabler = new WifiEnabler(activity, wifi); + mBtEnabler = new BluetoothEnabler(activity, bt); - String toggleable = Settings.System.getString(getContentResolver(), + String toggleable = Settings.System.getString(activity.getContentResolver(), Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS); // Manually set dependencies for Wifi when not toggleable. @@ -118,50 +119,72 @@ public class WirelessSettings extends PreferenceActivity { findPreference(KEY_BT_SETTINGS).setEnabled(false); } + // Enable Proxy selector settings if allowed. + Preference mGlobalProxy = findPreference(KEY_PROXY_SETTINGS); + DevicePolicyManager mDPM = (DevicePolicyManager) + activity.getSystemService(Context.DEVICE_POLICY_SERVICE); + mGlobalProxy.setEnabled(mDPM.getGlobalProxyAdmin() == null); + // Disable Tethering if it's not allowed ConnectivityManager cm = - (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE); + (ConnectivityManager) activity.getSystemService(Context.CONNECTIVITY_SERVICE); if (!cm.isTetheringSupported()) { getPreferenceScreen().removePreference(findPreference(KEY_TETHER_SETTINGS)); } else { String[] usbRegexs = cm.getTetherableUsbRegexs(); String[] wifiRegexs = cm.getTetherableWifiRegexs(); + String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs(); + + boolean usbAvailable = usbRegexs.length != 0; + boolean wifiAvailable = wifiRegexs.length != 0; + boolean bluetoothAvailable = bluetoothRegexs.length != 0; + Preference p = findPreference(KEY_TETHER_SETTINGS); - if (wifiRegexs.length == 0) { + if (wifiAvailable && usbAvailable && bluetoothAvailable) { + p.setTitle(R.string.tether_settings_title_all); + p.setSummary(R.string.tether_settings_summary_all); + } else if (wifiAvailable && usbAvailable) { + p.setTitle(R.string.tether_settings_title_all); + p.setSummary(R.string.tether_settings_summary_usb_wifi); + } else if (wifiAvailable && bluetoothAvailable) { + p.setTitle(R.string.tether_settings_title_all); + p.setSummary(R.string.tether_settings_summary_wifi_bluetooth); + } else if (wifiAvailable) { + p.setTitle(R.string.tether_settings_title_wifi); + p.setSummary(R.string.tether_settings_summary_wifi); + } else if (usbAvailable && bluetoothAvailable) { + p.setTitle(R.string.tether_settings_title_usb_bluetooth); + p.setSummary(R.string.tether_settings_summary_usb_bluetooth); + } else if (usbAvailable) { p.setTitle(R.string.tether_settings_title_usb); p.setSummary(R.string.tether_settings_summary_usb); } else { - if (usbRegexs.length == 0) { - p.setTitle(R.string.tether_settings_title_wifi); - p.setSummary(R.string.tether_settings_summary_wifi); - } else { - p.setTitle(R.string.tether_settings_title_both); - p.setSummary(R.string.tether_settings_summary_both); - } + p.setTitle(R.string.tether_settings_title_bluetooth); + p.setSummary(R.string.tether_settings_summary_bluetooth); } } } @Override - protected void onResume() { + public void onResume() { super.onResume(); - + mAirplaneModeEnabler.resume(); mWifiEnabler.resume(); mBtEnabler.resume(); } - + @Override - protected void onPause() { + public void onPause() { super.onPause(); - + mAirplaneModeEnabler.pause(); mWifiEnabler.pause(); mBtEnabler.pause(); } - + @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { + public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_CODE_EXIT_ECM) { Boolean isChoiceYes = data.getBooleanExtra(EXIT_ECM_RESULT, false); // Set Airplane mode based on the return value and checkbox state diff --git a/src/com/android/settings/ZoneList.java b/src/com/android/settings/ZoneList.java deleted file mode 100644 index aaaf98949..000000000 --- a/src/com/android/settings/ZoneList.java +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright (C) 2006 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.app.AlarmManager; -import android.app.ListActivity; -import android.content.Context; -import android.content.res.XmlResourceParser; -import android.os.Bundle; -import android.text.TextUtils; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.ListAdapter; -import android.widget.ListView; -import android.widget.SimpleAdapter; - -import org.xmlpull.v1.XmlPullParserException; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.TimeZone; - -/** - * This activity displays a list of time zones that match a filter string - * such as "Africa", "Europe", etc. Choosing an item from the list will set - * the time zone. Pressing Back without choosing from the list will not - * result in a change in the time zone setting. - */ -public class ZoneList extends ListActivity { - - private static final String TAG = "ZoneList"; - private static final String KEY_ID = "id"; - private static final String KEY_DISPLAYNAME = "name"; - private static final String KEY_GMT = "gmt"; - private static final String KEY_OFFSET = "offset"; - private static final String XMLTAG_TIMEZONE = "timezone"; - - private static final int HOURS_1 = 60 * 60000; - private static final int HOURS_24 = 24 * HOURS_1; - private static final int HOURS_HALF = HOURS_1 / 2; - - private static final int MENU_TIMEZONE = Menu.FIRST+1; - private static final int MENU_ALPHABETICAL = Menu.FIRST; - - // Initial focus position - private int mDefault; - - private boolean mSortedByTimezone; - - private SimpleAdapter mTimezoneSortedAdapter; - private SimpleAdapter mAlphabeticalAdapter; - - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - - String[] from = new String[] {KEY_DISPLAYNAME, KEY_GMT}; - int[] to = new int[] {android.R.id.text1, android.R.id.text2}; - - MyComparator comparator = new MyComparator(KEY_OFFSET); - - List<HashMap> timezoneSortedList = getZones(); - Collections.sort(timezoneSortedList, comparator); - mTimezoneSortedAdapter = new SimpleAdapter(this, - (List) timezoneSortedList, - android.R.layout.simple_list_item_2, - from, - to); - - List<HashMap> alphabeticalList = new ArrayList<HashMap>(timezoneSortedList); - comparator.setSortingKey(KEY_DISPLAYNAME); - Collections.sort(alphabeticalList, comparator); - mAlphabeticalAdapter = new SimpleAdapter(this, - (List) alphabeticalList, - android.R.layout.simple_list_item_2, - from, - to); - - // Sets the adapter - setSorting(true); - - // If current timezone is in this list, move focus to it - setSelection(mDefault); - - // Assume user may press Back - setResult(RESULT_CANCELED); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - menu.add(0, MENU_ALPHABETICAL, 0, R.string.zone_list_menu_sort_alphabetically) - .setIcon(android.R.drawable.ic_menu_sort_alphabetically); - menu.add(0, MENU_TIMEZONE, 0, R.string.zone_list_menu_sort_by_timezone) - .setIcon(R.drawable.ic_menu_3d_globe); - - return true; - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - - if (mSortedByTimezone) { - menu.findItem(MENU_TIMEZONE).setVisible(false); - menu.findItem(MENU_ALPHABETICAL).setVisible(true); - } else { - menu.findItem(MENU_TIMEZONE).setVisible(true); - menu.findItem(MENU_ALPHABETICAL).setVisible(false); - } - - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - - case MENU_TIMEZONE: - setSorting(true); - return true; - - case MENU_ALPHABETICAL: - setSorting(false); - return true; - - default: - return false; - } - } - - private void setSorting(boolean timezone) { - setListAdapter(timezone ? mTimezoneSortedAdapter : mAlphabeticalAdapter); - mSortedByTimezone = timezone; - } - - private List<HashMap> getZones() { - List<HashMap> myData = new ArrayList<HashMap>(); - long date = Calendar.getInstance().getTimeInMillis(); - try { - XmlResourceParser xrp = getResources().getXml(R.xml.timezones); - while (xrp.next() != XmlResourceParser.START_TAG) - continue; - xrp.next(); - while (xrp.getEventType() != XmlResourceParser.END_TAG) { - while (xrp.getEventType() != XmlResourceParser.START_TAG) { - if (xrp.getEventType() == XmlResourceParser.END_DOCUMENT) { - return myData; - } - xrp.next(); - } - if (xrp.getName().equals(XMLTAG_TIMEZONE)) { - String id = xrp.getAttributeValue(0); - String displayName = xrp.nextText(); - addItem(myData, id, displayName, date); - } - while (xrp.getEventType() != XmlResourceParser.END_TAG) { - xrp.next(); - } - xrp.next(); - } - xrp.close(); - } catch (XmlPullParserException xppe) { - Log.e(TAG, "Ill-formatted timezones.xml file"); - } catch (java.io.IOException ioe) { - Log.e(TAG, "Unable to read timezones.xml file"); - } - - return myData; - } - - protected void addItem(List<HashMap> myData, String id, String displayName, - long date) { - HashMap map = new HashMap(); - map.put(KEY_ID, id); - map.put(KEY_DISPLAYNAME, displayName); - TimeZone tz = TimeZone.getTimeZone(id); - int offset = tz.getOffset(date); - int p = Math.abs(offset); - StringBuilder name = new StringBuilder(); - name.append("GMT"); - - if (offset < 0) { - name.append('-'); - } else { - name.append('+'); - } - - name.append(p / (HOURS_1)); - name.append(':'); - - int min = p / 60000; - min %= 60; - - if (min < 10) { - name.append('0'); - } - name.append(min); - - map.put(KEY_GMT, name.toString()); - map.put(KEY_OFFSET, offset); - - if (id.equals(TimeZone.getDefault().getID())) { - mDefault = myData.size(); - } - - myData.add(map); - } - - @Override - protected void onListItemClick(ListView l, View v, int position, long id) { - Map map = (Map) l.getItemAtPosition(position); - // Update the system timezone value - AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE); - alarm.setTimeZone((String) map.get(KEY_ID)); - setResult(RESULT_OK); - finish(); - } - - private static class MyComparator implements Comparator<HashMap> { - private String mSortingKey; - - public MyComparator(String sortingKey) { - mSortingKey = sortingKey; - } - - public void setSortingKey(String sortingKey) { - mSortingKey = sortingKey; - } - - public int compare(HashMap map1, HashMap map2) { - Object value1 = map1.get(mSortingKey); - Object value2 = map2.get(mSortingKey); - - /* - * This should never happen, but just in-case, put non-comparable - * items at the end. - */ - if (!isComparable(value1)) { - return isComparable(value2) ? 1 : 0; - } else if (!isComparable(value2)) { - return -1; - } - - return ((Comparable) value1).compareTo(value2); - } - - private boolean isComparable(Object value) { - return (value != null) && (value instanceof Comparable); - } - } - -} diff --git a/src/com/android/settings/ZonePicker.java b/src/com/android/settings/ZonePicker.java index def5036ae..f0c61edc8 100644 --- a/src/com/android/settings/ZonePicker.java +++ b/src/com/android/settings/ZonePicker.java @@ -16,55 +16,265 @@ package com.android.settings; -import android.app.ListActivity; -import android.content.Intent; +import android.app.Activity; +import android.app.AlarmManager; +import android.app.ListFragment; +import android.content.Context; +import android.content.res.XmlResourceParser; import android.os.Bundle; +import android.util.Log; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; -import android.widget.ArrayAdapter; import android.widget.ListView; +import android.widget.SimpleAdapter; +import org.xmlpull.v1.XmlPullParserException; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.TimeZone; + +/** + * The class displaying a list of time zones that match a filter string + * such as "Africa", "Europe", etc. Choosing an item from the list will set + * the time zone. Pressing Back without choosing from the list will not + * result in a change in the time zone setting. + */ +public class ZonePicker extends ListFragment { + private static final String TAG = "ZonePicker"; + + public static interface ZoneSelectionListener { + // You can add any argument if you really need it... + public void onZoneSelected(TimeZone tz); + } + + private static final String KEY_ID = "id"; + private static final String KEY_DISPLAYNAME = "name"; + private static final String KEY_GMT = "gmt"; + private static final String KEY_OFFSET = "offset"; + private static final String XMLTAG_TIMEZONE = "timezone"; + + private static final int HOURS_1 = 60 * 60000; + + private static final int MENU_TIMEZONE = Menu.FIRST+1; + private static final int MENU_ALPHABETICAL = Menu.FIRST; + + // Initial focus position + private int mDefault; + + private boolean mSortedByTimezone; + + private SimpleAdapter mTimezoneSortedAdapter; + private SimpleAdapter mAlphabeticalAdapter; + + private ZoneSelectionListener mListener; + + @Override + public void onActivityCreated(Bundle savedInstanseState) { + super.onActivityCreated(savedInstanseState); + + final String[] from = new String[] {KEY_DISPLAYNAME, KEY_GMT}; + final int[] to = new int[] {android.R.id.text1, android.R.id.text2}; -public class ZonePicker extends ListActivity { + MyComparator comparator = new MyComparator(KEY_OFFSET); + + Activity activity = getActivity(); + List<HashMap> timezoneSortedList = getZones(); + Collections.sort(timezoneSortedList, comparator); + mTimezoneSortedAdapter = new SimpleAdapter(activity, + (List) timezoneSortedList, + android.R.layout.simple_list_item_2, + from, + to); + + List<HashMap> alphabeticalList = new ArrayList<HashMap>(timezoneSortedList); + comparator.setSortingKey(KEY_DISPLAYNAME); + Collections.sort(alphabeticalList, comparator); + mAlphabeticalAdapter = new SimpleAdapter(getActivity(), + (List) alphabeticalList, + android.R.layout.simple_list_item_2, + from, + to); + + // Sets the adapter + setSorting(true); + + // If current timezone is in this list, move focus to it + setSelection(mDefault); + } - private ArrayAdapter<CharSequence> mFilterAdapter; - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - mFilterAdapter = ArrayAdapter.createFromResource(this, - R.array.timezone_filters, android.R.layout.simple_list_item_1); - setListAdapter(mFilterAdapter); + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + menu.add(0, MENU_ALPHABETICAL, 0, R.string.zone_list_menu_sort_alphabetically) + .setIcon(android.R.drawable.ic_menu_sort_alphabetically); + menu.add(0, MENU_TIMEZONE, 0, R.string.zone_list_menu_sort_by_timezone) + .setIcon(R.drawable.ic_menu_3d_globe); + super.onCreateOptionsMenu(menu, inflater); } - - protected void addItem(List<Map> data, String name, String zone) { - HashMap temp = new HashMap(); - temp.put("title", name); - temp.put("zone", zone); - data.add(temp); + + @Override + public void onPrepareOptionsMenu(Menu menu) { + if (mSortedByTimezone) { + menu.findItem(MENU_TIMEZONE).setVisible(false); + menu.findItem(MENU_ALPHABETICAL).setVisible(true); + } else { + menu.findItem(MENU_TIMEZONE).setVisible(true); + menu.findItem(MENU_ALPHABETICAL).setVisible(false); + } } @Override - protected void onListItemClick(ListView l, View v, int position, long id) { - String filter = (String) mFilterAdapter.getItem(position); - // If All is chosen, reset the filter - if (filter.equals("All")) { - filter = null; + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case MENU_TIMEZONE: + setSorting(true); + return true; + + case MENU_ALPHABETICAL: + setSorting(false); + return true; + + default: + return false; + } + } + + public void setZoneSelectionListener(ZoneSelectionListener listener) { + mListener = listener; + } + + private void setSorting(boolean timezone) { + setListAdapter(timezone ? mTimezoneSortedAdapter : mAlphabeticalAdapter); + mSortedByTimezone = timezone; + } + + private List<HashMap> getZones() { + List<HashMap> myData = new ArrayList<HashMap>(); + long date = Calendar.getInstance().getTimeInMillis(); + try { + XmlResourceParser xrp = getActivity().getResources().getXml(R.xml.timezones); + while (xrp.next() != XmlResourceParser.START_TAG) + continue; + xrp.next(); + while (xrp.getEventType() != XmlResourceParser.END_TAG) { + while (xrp.getEventType() != XmlResourceParser.START_TAG) { + if (xrp.getEventType() == XmlResourceParser.END_DOCUMENT) { + return myData; + } + xrp.next(); + } + if (xrp.getName().equals(XMLTAG_TIMEZONE)) { + String id = xrp.getAttributeValue(0); + String displayName = xrp.nextText(); + addItem(myData, id, displayName, date); + } + while (xrp.getEventType() != XmlResourceParser.END_TAG) { + xrp.next(); + } + xrp.next(); + } + xrp.close(); + } catch (XmlPullParserException xppe) { + Log.e(TAG, "Ill-formatted timezones.xml file"); + } catch (java.io.IOException ioe) { + Log.e(TAG, "Unable to read timezones.xml file"); + } + + return myData; + } + + protected void addItem(List<HashMap> myData, String id, String displayName, + long date) { + HashMap map = new HashMap(); + map.put(KEY_ID, id); + map.put(KEY_DISPLAYNAME, displayName); + TimeZone tz = TimeZone.getTimeZone(id); + int offset = tz.getOffset(date); + int p = Math.abs(offset); + StringBuilder name = new StringBuilder(); + name.append("GMT"); + + if (offset < 0) { + name.append('-'); + } else { + name.append('+'); + } + + name.append(p / (HOURS_1)); + name.append(':'); + + int min = p / 60000; + min %= 60; + + if (min < 10) { + name.append('0'); + } + name.append(min); + + map.put(KEY_GMT, name.toString()); + map.put(KEY_OFFSET, offset); + + if (id.equals(TimeZone.getDefault().getID())) { + mDefault = myData.size(); } - Intent zoneList = new Intent(); - zoneList.setClass(this, ZoneList.class); - zoneList.putExtra("filter", filter); - - startActivityForResult(zoneList, 0); + + myData.add(map); } - + @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - // If subactivity has resulted in a timezone selection, close this act. - if (resultCode == RESULT_OK) { - finish(); + public void onListItemClick(ListView l, View v, int position, long id) { + Map map = (Map) l.getItemAtPosition(position); + // Update the system timezone value + final Activity activity = getActivity(); + AlarmManager alarm = (AlarmManager) activity.getSystemService(Context.ALARM_SERVICE); + String tzId = (String) map.get(KEY_ID); + alarm.setTimeZone(tzId); + final TimeZone tz = TimeZone.getTimeZone(tzId); + if (mListener != null) { + mListener.onZoneSelected(tz); + } else { + getActivity().onBackPressed(); + } + } + + private static class MyComparator implements Comparator<HashMap> { + private String mSortingKey; + + public MyComparator(String sortingKey) { + mSortingKey = sortingKey; + } + + public void setSortingKey(String sortingKey) { + mSortingKey = sortingKey; } - } + + public int compare(HashMap map1, HashMap map2) { + Object value1 = map1.get(mSortingKey); + Object value2 = map2.get(mSortingKey); + + /* + * This should never happen, but just in-case, put non-comparable + * items at the end. + */ + if (!isComparable(value1)) { + return isComparable(value2) ? 1 : 0; + } else if (!isComparable(value2)) { + return -1; + } + + return ((Comparable) value1).compareTo(value2); + } + + private boolean isComparable(Object value) { + return (value != null) && (value instanceof Comparable); + } + } } diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java index 5b0218fd8..f0b1705b3 100644 --- a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java +++ b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java @@ -34,6 +34,7 @@ public class BluetoothDevicePreference extends Preference implements CachedBluet private static int sDimAlpha = Integer.MIN_VALUE; private CachedBluetoothDevice mCachedDevice; + private int mAccessibleProfile; /** * Cached local copy of whether the device is busy. This is only updated @@ -41,7 +42,8 @@ public class BluetoothDevicePreference extends Preference implements CachedBluet */ private boolean mIsBusy; - public BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice) { + public BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice, + int accessibleProfile) { super(context); if (sDimAlpha == Integer.MIN_VALUE) { @@ -51,6 +53,7 @@ public class BluetoothDevicePreference extends Preference implements CachedBluet } mCachedDevice = cachedDevice; + mAccessibleProfile = accessibleProfile; setLayoutResource(R.layout.preference_bluetooth); @@ -83,7 +86,7 @@ public class BluetoothDevicePreference extends Preference implements CachedBluet * related to BluetoothHeadset not bound to the actual * BluetoothHeadsetService when we got here. */ - setSummary(mCachedDevice.getSummary()); + setSummary(mCachedDevice.getSummary(mAccessibleProfile)); // Used to gray out the item mIsBusy = mCachedDevice.isBusy(); diff --git a/src/com/android/settings/bluetooth/BluetoothEventRedirector.java b/src/com/android/settings/bluetooth/BluetoothEventRedirector.java index 4d124b33e..08354310a 100644 --- a/src/com/android/settings/bluetooth/BluetoothEventRedirector.java +++ b/src/com/android/settings/bluetooth/BluetoothEventRedirector.java @@ -24,6 +24,8 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; +import android.bluetooth.BluetoothInputDevice; +import android.bluetooth.BluetoothPan; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -119,6 +121,31 @@ public class BluetoothEventRedirector { mManager.getCachedDeviceManager().onProfileStateChanged(device, Profile.A2DP, newState); + } else if (action.equals(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED)) { + final int newState = intent.getIntExtra( + BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, 0); + final int oldState = intent.getIntExtra( + BluetoothInputDevice.EXTRA_PREVIOUS_INPUT_DEVICE_STATE, 0); + if (newState == BluetoothInputDevice.STATE_DISCONNECTED && + oldState == BluetoothInputDevice.STATE_CONNECTING) { + Log.i(TAG, "Failed to connect BT HID"); + } + + mManager.getCachedDeviceManager().onProfileStateChanged(device, + Profile.HID, newState); + + } else if (action.equals(BluetoothPan.ACTION_PAN_STATE_CHANGED)) { + final int newState = intent.getIntExtra( + BluetoothPan.EXTRA_PAN_STATE, 0); + final int oldState = intent.getIntExtra( + BluetoothPan.EXTRA_PREVIOUS_PAN_STATE, 0); + if (newState == BluetoothPan.STATE_DISCONNECTED && + oldState == BluetoothPan.STATE_CONNECTING) { + Log.i(TAG, "Failed to connect BT PAN"); + } + mManager.getCachedDeviceManager().onProfileStateChanged(device, + Profile.PAN, newState); + } else if (action.equals(BluetoothDevice.ACTION_CLASS_CHANGED)) { mManager.getCachedDeviceManager().onBtClassChanged(device); @@ -166,6 +193,7 @@ public class BluetoothEventRedirector { // Fine-grained state broadcasts filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED); filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED); + filter.addAction(BluetoothPan.ACTION_PAN_STATE_CHANGED); filter.addAction(BluetoothDevice.ACTION_CLASS_CHANGED); filter.addAction(BluetoothDevice.ACTION_UUID); diff --git a/src/com/android/settings/bluetooth/BluetoothSettings.java b/src/com/android/settings/bluetooth/BluetoothSettings.java index 78c531ce3..88d1e83b4 100644 --- a/src/com/android/settings/bluetooth/BluetoothSettings.java +++ b/src/com/android/settings/bluetooth/BluetoothSettings.java @@ -18,26 +18,34 @@ package com.android.settings.bluetooth; import com.android.settings.ProgressCategory; import com.android.settings.R; +import com.android.settings.SettingsPreferenceFragment; +import com.android.settings.UserLeaveHintListener; +import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile; +import android.app.Activity; +import android.app.AlertDialog; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevicePicker; +import android.bluetooth.BluetoothPan; import android.bluetooth.BluetoothUuid; import android.content.BroadcastReceiver; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.os.ParcelUuid; import android.preference.CheckBoxPreference; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceScreen; +import android.text.TextUtils; +import android.util.Log; import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; import android.view.MenuItem; import android.view.View; -import android.view.ContextMenu.ContextMenuInfo; import android.widget.AdapterView.AdapterContextMenuInfo; import java.util.List; @@ -47,8 +55,8 @@ import java.util.WeakHashMap; * BluetoothSettings is the Settings screen for Bluetooth configuration and * connection management. */ -public class BluetoothSettings extends PreferenceActivity - implements LocalBluetoothManager.Callback { +public class BluetoothSettings extends SettingsPreferenceFragment + implements LocalBluetoothManager.Callback, UserLeaveHintListener { private static final String TAG = "BluetoothSettings"; @@ -60,6 +68,11 @@ public class BluetoothSettings extends PreferenceActivity private static final int SCREEN_TYPE_SETTINGS = 0; private static final int SCREEN_TYPE_DEVICEPICKER = 1; + private static final int SCREEN_TYPE_TETHERING = 2; + + public static final String ACTION = "bluetooth_action"; + public static final String ACTION_LAUNCH_TETHER_PICKER = + "com.android.settings.bluetooth.action.LAUNCH_TETHER_PICKER"; private int mScreenType; private int mFilterType; @@ -88,16 +101,19 @@ public class BluetoothSettings extends PreferenceActivity if (intent.getAction().equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { onBluetoothStateChanged(mLocalManager.getBluetoothState()); - } else if (intent.getAction().equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED) - && mScreenType == SCREEN_TYPE_DEVICEPICKER) { + } else if (intent.getAction().equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) { int bondState = intent .getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR); if (bondState == BluetoothDevice.BOND_BONDED) { BluetoothDevice device = intent .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (device.equals(mSelectedDevice)) { - sendDevicePickedIntent(device); - finish(); + if (mScreenType == SCREEN_TYPE_DEVICEPICKER) { + sendDevicePickedIntent(device); + finish(); + } else if (mScreenType == SCREEN_TYPE_TETHERING) { + onPanDevicePicked(); + } } } } @@ -105,11 +121,19 @@ public class BluetoothSettings extends PreferenceActivity }; @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + // We delay calling super.onActivityCreated(). See WifiSettings.java for more info. - mLocalManager = LocalBluetoothManager.getInstance(this); - if (mLocalManager == null) finish(); + final Activity activity = getActivity(); + mLocalManager = LocalBluetoothManager.getInstance(activity); + if (mLocalManager == null) { + finish(); + } // Note: // If an application wish to show the BT device list, it can send an @@ -121,8 +145,13 @@ public class BluetoothSettings extends PreferenceActivity // -DEVICE_PICKER_NEED_AUTH: to show if bonding procedure needed. mFilterType = BluetoothDevicePicker.FILTER_TYPE_ALL; - Intent intent = getIntent(); - String action = intent.getAction(); + final Intent intent = activity.getIntent(); + + // This additional argument comes from PreferenceScreen (See TetherSettings.java). + String action = getArguments().getString(ACTION); + if (TextUtils.isEmpty(action)) { + action = intent.getAction(); + } if (action.equals(BluetoothDevicePicker.ACTION_LAUNCH)) { mScreenType = SCREEN_TYPE_DEVICEPICKER; @@ -132,17 +161,23 @@ public class BluetoothSettings extends PreferenceActivity mLaunchPackage = intent.getStringExtra(BluetoothDevicePicker.EXTRA_LAUNCH_PACKAGE); mLaunchClass = intent.getStringExtra(BluetoothDevicePicker.EXTRA_LAUNCH_CLASS); - setTitle(getString(R.string.device_picker)); + activity.setTitle(activity.getString(R.string.device_picker)); + addPreferencesFromResource(R.xml.device_picker); + } else if (action.equals(ACTION_LAUNCH_TETHER_PICKER)){ + mScreenType = SCREEN_TYPE_TETHERING; + mFilterType = BluetoothDevicePicker.FILTER_TYPE_PANU; + + activity.setTitle(activity.getString(R.string.device_picker)); addPreferencesFromResource(R.xml.device_picker); } else { addPreferencesFromResource(R.xml.bluetooth_settings); mEnabler = new BluetoothEnabler( - this, + activity, (CheckBoxPreference) findPreference(KEY_BT_CHECKBOX)); mDiscoverableEnabler = new BluetoothDiscoverableEnabler( - this, + activity, (CheckBoxPreference) findPreference(KEY_BT_DISCOVERABLE)); mNamePreference = (BluetoothNamePreference) findPreference(KEY_BT_NAME); @@ -153,10 +188,12 @@ public class BluetoothSettings extends PreferenceActivity mDeviceList = (ProgressCategory) findPreference(KEY_BT_DEVICE_LIST); registerForContextMenu(getListView()); + + super.onActivityCreated(savedInstanceState); } @Override - protected void onResume() { + public void onResume() { super.onResume(); // Repopulate (which isn't too bad since it's cached in the settings @@ -179,18 +216,17 @@ public class BluetoothSettings extends PreferenceActivity IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); - registerReceiver(mReceiver, intentFilter); - mLocalManager.setForegroundActivity(this); + getActivity().registerReceiver(mReceiver, intentFilter); + mLocalManager.setForegroundActivity(getActivity()); } @Override - protected void onPause() { + public void onPause() { super.onPause(); - mLocalManager.setForegroundActivity(null); mDevicePreferenceMap.clear(); mDeviceList.removeAll(); - unregisterReceiver(mReceiver); + getActivity().unregisterReceiver(mReceiver); mLocalManager.unregisterCallback(this); if (mScreenType == SCREEN_TYPE_SETTINGS) { @@ -201,8 +237,7 @@ public class BluetoothSettings extends PreferenceActivity } @Override - protected void onUserLeaveHint() { - super.onUserLeaveHint(); + public void onUserLeaveHint() { mLocalManager.stopScanning(); } @@ -240,6 +275,18 @@ public class BluetoothSettings extends PreferenceActivity } else { btPreference.getCachedDevice().onClicked(); } + } else if (mScreenType == SCREEN_TYPE_TETHERING){ + CachedBluetoothDevice device = btPreference.getCachedDevice(); + + mSelectedDevice = device.getDevice(); + mLocalManager.stopScanning(); + mLocalManager.persistSelectedDeviceInPicker(mSelectedDevice.getAddress()); + if ((device.getBondState() == BluetoothDevice.BOND_BONDED)) { + onPanDevicePicked(); + // don't call finish so that users can see it connecting + } else { + btPreference.getCachedDevice().onClicked(); + } } return true; } @@ -301,13 +348,14 @@ public class BluetoothSettings extends PreferenceActivity switch(mFilterType) { case BluetoothDevicePicker.FILTER_TYPE_TRANSFER: - if (uuids != null) + if (uuids != null) { if (BluetoothUuid.containsAnyUuid(uuids, LocalBluetoothProfileManager.OPP_PROFILE_UUIDS)) return true; - if (bluetoothClass != null - && bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_OPP)) { - return true; - } + } + if (bluetoothClass != null + && bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_OPP)) { + return true; + } break; case BluetoothDevicePicker.FILTER_TYPE_AUDIO: if (uuids != null) { @@ -322,6 +370,27 @@ public class BluetoothSettings extends PreferenceActivity if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) return true; } break; + case BluetoothDevicePicker.FILTER_TYPE_PANU: + if (uuids != null) { + if (BluetoothUuid.containsAnyUuid(uuids, + LocalBluetoothProfileManager.PANU_PROFILE_UUIDS)) return true; + + } + if (bluetoothClass != null + && bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_PANU)) { + return true; + } + break; + case BluetoothDevicePicker.FILTER_TYPE_NAP: + if (uuids != null) { + if (BluetoothUuid.containsAnyUuid(uuids, + LocalBluetoothProfileManager.NAP_PROFILE_UUIDS)) return true; + } + if (bluetoothClass != null + && bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_NAP)) { + return true; + } + break; default: return true; } @@ -329,7 +398,14 @@ public class BluetoothSettings extends PreferenceActivity } private void createDevicePreference(CachedBluetoothDevice cachedDevice) { - BluetoothDevicePreference preference = new BluetoothDevicePreference(this, cachedDevice); + BluetoothDevicePreference preference; + if (mScreenType == SCREEN_TYPE_TETHERING) { + preference = new BluetoothDevicePreference( + getActivity(), cachedDevice, CachedBluetoothDevice.PAN_PROFILE); + } else { + preference = new BluetoothDevicePreference( + getActivity(), cachedDevice, CachedBluetoothDevice.OTHER_PROFILES); + } mDeviceList.addPreference(preference); mDevicePreferenceMap.put(cachedDevice, preference); } @@ -355,12 +431,53 @@ public class BluetoothSettings extends PreferenceActivity } } + private void onPanDevicePicked() { + final Activity activity = getActivity(); + final LocalBluetoothProfileManager profileManager = + LocalBluetoothProfileManager.getProfileManager(mLocalManager, Profile.PAN); + int status = profileManager.getConnectionStatus(mSelectedDevice); + if (SettingsBtStatus.isConnectionStatusConnected(status)) { + String name = mSelectedDevice.getName(); + if (TextUtils.isEmpty(name)) { + name = activity.getString(R.string.bluetooth_device); + } + String message = activity.getString(R.string.bluetooth_untether_blank, name); + DialogInterface.OnClickListener disconnectListener = + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + profileManager.disconnect(mSelectedDevice); + } + }; + new AlertDialog.Builder(activity) + .setTitle(name) + .setMessage(message) + .setPositiveButton(android.R.string.ok, disconnectListener) + .setNegativeButton(android.R.string.cancel, null) + .create() + .show(); + } else if (status == SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED) { + if (profileManager.getConnectedDevices().size() >= BluetoothPan.MAX_CONNECTIONS) { + new AlertDialog.Builder(activity) + .setIcon(android.R.drawable.ic_dialog_alert) + .setTitle(R.string.bluetooth_error_title) + .setMessage(activity.getString(R.string.bluetooth_tethering_overflow_error, + BluetoothPan.MAX_CONNECTIONS)) + .setNegativeButton(android.R.string.ok, null) + .create() + .show(); + return; + } + profileManager.connect(mSelectedDevice); + } + } + private void sendDevicePickedIntent(BluetoothDevice device) { Intent intent = new Intent(BluetoothDevicePicker.ACTION_DEVICE_SELECTED); - if (mLaunchPackage != null && mLaunchClass != null) { + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); + if (mScreenType == SCREEN_TYPE_DEVICEPICKER && + mLaunchPackage != null && mLaunchClass != null) { intent.setClassName(mLaunchPackage, mLaunchClass); } - intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); - sendBroadcast(intent); + getActivity().sendBroadcast(intent); } } diff --git a/src/com/android/settings/bluetooth/CachedBluetoothDevice.java b/src/com/android/settings/bluetooth/CachedBluetoothDevice.java index d778f1128..53e599d25 100644 --- a/src/com/android/settings/bluetooth/CachedBluetoothDevice.java +++ b/src/com/android/settings/bluetooth/CachedBluetoothDevice.java @@ -56,6 +56,9 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> private static final int CONTEXT_ITEM_UNPAIR = Menu.FIRST + 3; private static final int CONTEXT_ITEM_CONNECT_ADVANCED = Menu.FIRST + 4; + public static final int PAN_PROFILE = 1; + public static final int OTHER_PROFILES = 2; + private final BluetoothDevice mDevice; private String mName; private short mRssi; @@ -565,6 +568,12 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> Log.v(TAG, "opp classbits != uuid"); printUuids = true; } + + if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_HID) != + mProfiles.contains(Profile.HID)) { + Log.v(TAG, "hid classbits != uuid"); + printUuids = true; + } } if (printUuids) { @@ -635,9 +644,9 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } } - public int getSummary() { + public int getSummary(int accessibleProfile) { // TODO: clean up - int oneOffSummary = getOneOffSummary(); + int oneOffSummary = getOneOffSummary(accessibleProfile); if (oneOffSummary != 0) { return oneOffSummary; } @@ -663,23 +672,43 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> * * @return A one-off summary that is applicable for the current state, or 0. */ - private int getOneOffSummary() { - boolean isA2dpConnected = false, isHeadsetConnected = false, isConnecting = false; + private int getOneOffSummary(int accessibleProfile) { + boolean isA2dpConnected = false; + boolean isHeadsetConnected = false; + boolean isHidConnected = false; + boolean isPanConnected = false; + boolean isConnecting = false; + + if (accessibleProfile == OTHER_PROFILES) { + if (mProfiles.contains(Profile.A2DP)) { + LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager + .getProfileManager(mLocalManager, Profile.A2DP); + isConnecting = profileManager.getConnectionStatus(mDevice) == + SettingsBtStatus.CONNECTION_STATUS_CONNECTING; + isA2dpConnected = profileManager.isConnected(mDevice); + } - if (mProfiles.contains(Profile.A2DP)) { - LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager - .getProfileManager(mLocalManager, Profile.A2DP); - isConnecting = profileManager.getConnectionStatus(mDevice) == - SettingsBtStatus.CONNECTION_STATUS_CONNECTING; - isA2dpConnected = profileManager.isConnected(mDevice); - } + if (mProfiles.contains(Profile.HEADSET)) { + LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager + .getProfileManager(mLocalManager, Profile.HEADSET); + isConnecting |= profileManager.getConnectionStatus(mDevice) == + SettingsBtStatus.CONNECTION_STATUS_CONNECTING; + isHeadsetConnected = profileManager.isConnected(mDevice); + } - if (mProfiles.contains(Profile.HEADSET)) { + if (mProfiles.contains(Profile.HID)) { + LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager + .getProfileManager(mLocalManager, Profile.HID); + isConnecting |= profileManager.getConnectionStatus(mDevice) == + SettingsBtStatus.CONNECTION_STATUS_CONNECTING; + isHidConnected = profileManager.isConnected(mDevice); + } + } else if (accessibleProfile == PAN_PROFILE && mProfiles.contains(Profile.PAN)) { LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager - .getProfileManager(mLocalManager, Profile.HEADSET); + .getProfileManager(mLocalManager, Profile.PAN); isConnecting |= profileManager.getConnectionStatus(mDevice) == SettingsBtStatus.CONNECTION_STATUS_CONNECTING; - isHeadsetConnected = profileManager.isConnected(mDevice); + isPanConnected = profileManager.isConnected(mDevice); } if (isConnecting) { @@ -692,6 +721,10 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> return R.string.bluetooth_summary_connected_to_a2dp; } else if (isHeadsetConnected) { return R.string.bluetooth_summary_connected_to_headset; + } else if (isHidConnected) { + return R.string.bluetooth_summary_connected_to_hid; + } else if (isPanConnected) { + return R.string.bluetooth_summary_connected_to_pan; } else { return 0; } @@ -708,7 +741,8 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } private boolean isConnectableProfile(Profile profile) { - return profile.equals(Profile.HEADSET) || profile.equals(Profile.A2DP); + return profile.equals(Profile.HEADSET) || profile.equals(Profile.A2DP) || + profile.equals(Profile.HID); } public void onCreateContextMenu(ContextMenu menu) { diff --git a/src/com/android/settings/bluetooth/ConnectSpecificProfilesActivity.java b/src/com/android/settings/bluetooth/ConnectSpecificProfilesActivity.java index ed9a97411..ac2b4d81a 100644 --- a/src/com/android/settings/bluetooth/ConnectSpecificProfilesActivity.java +++ b/src/com/android/settings/bluetooth/ConnectSpecificProfilesActivity.java @@ -232,7 +232,8 @@ public class ConnectSpecificProfilesActivity extends PreferenceActivity * If the device is online, show status. Otherwise, show a summary that * describes what the checkbox does. */ - mOnlineModePreference.setSummary(mOnlineMode ? mCachedDevice.getSummary() + mOnlineModePreference.setSummary(mOnlineMode ? + mCachedDevice.getSummary(CachedBluetoothDevice.OTHER_PROFILES) : R.string.bluetooth_device_advanced_online_mode_summary); } @@ -299,6 +300,8 @@ public class ConnectSpecificProfilesActivity extends PreferenceActivity return R.string.bluetooth_a2dp_profile_summary_use_for; case HEADSET: return R.string.bluetooth_headset_profile_summary_use_for; + case HID: + return R.string.bluetooth_hid_profile_summary_use_for; default: return 0; } diff --git a/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java b/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java index 01714fe60..6193a4e87 100644 --- a/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java +++ b/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java @@ -21,6 +21,8 @@ import com.android.settings.R; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; +import android.bluetooth.BluetoothInputDevice; +import android.bluetooth.BluetoothPan; import android.bluetooth.BluetoothUuid; import android.os.Handler; import android.os.ParcelUuid; @@ -55,6 +57,18 @@ public abstract class LocalBluetoothProfileManager { BluetoothUuid.ObexObjectPush }; + /* package */ static final ParcelUuid[] HID_PROFILE_UUIDS = new ParcelUuid[] { + BluetoothUuid.Hid + }; + + /* package */ static final ParcelUuid[] PANU_PROFILE_UUIDS = new ParcelUuid[] { + BluetoothUuid.PANU + }; + + /* package */ static final ParcelUuid[] NAP_PROFILE_UUIDS = new ParcelUuid[] { + BluetoothUuid.NAP + }; + /** * An interface for notifying BluetoothHeadset IPC clients when they have * been connected to the BluetoothHeadset service. @@ -97,6 +111,12 @@ public abstract class LocalBluetoothProfileManager { profileManager = new OppProfileManager(localManager); sProfileMap.put(Profile.OPP, profileManager); + + profileManager = new HidProfileManager(localManager); + sProfileMap.put(Profile.HID, profileManager); + + profileManager = new PanProfileManager(localManager); + sProfileMap.put(Profile.PAN, profileManager); } } } @@ -161,6 +181,14 @@ public abstract class LocalBluetoothProfileManager { if (BluetoothUuid.containsAnyUuid(uuids, OPP_PROFILE_UUIDS)) { profiles.add(Profile.OPP); } + + if (BluetoothUuid.containsAnyUuid(uuids, HID_PROFILE_UUIDS)) { + profiles.add(Profile.HID); + } + + if (BluetoothUuid.containsAnyUuid(uuids, PANU_PROFILE_UUIDS)) { + profiles.add(Profile.PAN); + } } protected LocalBluetoothProfileManager(LocalBluetoothManager localManager) { @@ -195,7 +223,9 @@ public abstract class LocalBluetoothProfileManager { public enum Profile { HEADSET(R.string.bluetooth_profile_headset), A2DP(R.string.bluetooth_profile_a2dp), - OPP(R.string.bluetooth_profile_opp); + OPP(R.string.bluetooth_profile_opp), + HID(R.string.bluetooth_profile_hid), + PAN(R.string.bluetooth_profile_pan); public final int localizedString; @@ -518,4 +548,162 @@ public abstract class LocalBluetoothProfileManager { } } } + + private static class HidProfileManager extends LocalBluetoothProfileManager { + private BluetoothInputDevice mService; + + public HidProfileManager(LocalBluetoothManager localManager) { + super(localManager); + mService = new BluetoothInputDevice(localManager.getContext()); + } + + @Override + public boolean connect(BluetoothDevice device) { + return mService.connectInputDevice(device); + } + + @Override + public int convertState(int hidState) { + switch (hidState) { + case BluetoothInputDevice.STATE_CONNECTED: + return SettingsBtStatus.CONNECTION_STATUS_CONNECTED; + case BluetoothInputDevice.STATE_CONNECTING: + return SettingsBtStatus.CONNECTION_STATUS_CONNECTING; + case BluetoothInputDevice.STATE_DISCONNECTED: + return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED; + case BluetoothInputDevice.STATE_DISCONNECTING: + return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING; + default: + return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN; + } + } + + @Override + public boolean disconnect(BluetoothDevice device) { + return mService.disconnectInputDevice(device); + } + + @Override + public Set<BluetoothDevice> getConnectedDevices() { + return mService.getConnectedInputDevices(); + } + + @Override + public int getConnectionStatus(BluetoothDevice device) { + return convertState(mService.getInputDeviceState(device)); + } + + @Override + public int getPreferred(BluetoothDevice device) { + return mService.getInputDevicePriority(device); + } + + @Override + public int getSummary(BluetoothDevice device) { + final int connectionStatus = getConnectionStatus(device); + + if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) { + return R.string.bluetooth_hid_profile_summary_connected; + } else { + return SettingsBtStatus.getConnectionStatusSummary(connectionStatus); + } + } + + @Override + public boolean isPreferred(BluetoothDevice device) { + return mService.getInputDevicePriority(device) > BluetoothInputDevice.PRIORITY_OFF; + } + + @Override + public boolean isProfileReady() { + return true; + } + + @Override + public void setPreferred(BluetoothDevice device, boolean preferred) { + if (preferred) { + if (mService.getInputDevicePriority(device) < BluetoothInputDevice.PRIORITY_ON) { + mService.setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_ON); + } + } else { + mService.setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_OFF); + } + } + } + + private static class PanProfileManager extends LocalBluetoothProfileManager { + private BluetoothPan mService; + + public PanProfileManager(LocalBluetoothManager localManager) { + super(localManager); + mService = new BluetoothPan(localManager.getContext()); + } + + @Override + public boolean connect(BluetoothDevice device) { + return mService.connect(device); + } + + @Override + public int convertState(int panState) { + switch (panState) { + case BluetoothPan.STATE_CONNECTED: + return SettingsBtStatus.CONNECTION_STATUS_CONNECTED; + case BluetoothPan.STATE_CONNECTING: + return SettingsBtStatus.CONNECTION_STATUS_CONNECTING; + case BluetoothPan.STATE_DISCONNECTED: + return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED; + case BluetoothPan.STATE_DISCONNECTING: + return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING; + default: + return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN; + } + } + + @Override + public boolean disconnect(BluetoothDevice device) { + return mService.disconnect(device); + } + + @Override + public int getSummary(BluetoothDevice device) { + final int connectionStatus = getConnectionStatus(device); + + if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) { + return R.string.bluetooth_pan_profile_summary_connected; + } else { + return SettingsBtStatus.getConnectionStatusSummary(connectionStatus); + } + } + + @Override + public boolean isProfileReady() { + return true; + } + + @Override + public Set<BluetoothDevice> getConnectedDevices() { + return mService.getConnectedDevices(); + } + + @Override + public int getConnectionStatus(BluetoothDevice device) { + return convertState(mService.getPanDeviceState(device)); + } + + @Override + public int getPreferred(BluetoothDevice device) { + return -1; + } + + @Override + public boolean isPreferred(BluetoothDevice device) { + return false; + } + + @Override + public void setPreferred(BluetoothDevice device, boolean preferred) { + return; + } + } } diff --git a/src/com/android/settings/deviceinfo/Memory.java b/src/com/android/settings/deviceinfo/Memory.java index b57484908..e22c39d62 100644 --- a/src/com/android/settings/deviceinfo/Memory.java +++ b/src/com/android/settings/deviceinfo/Memory.java @@ -16,6 +16,9 @@ package com.android.settings.deviceinfo; +import com.android.settings.R; +import com.android.settings.SettingsPreferenceFragment; + import android.app.ActivityManager; import android.app.AlertDialog; import android.app.Dialog; @@ -26,35 +29,29 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.DialogInterface.OnCancelListener; import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.os.Bundle; -import android.os.Handler; +import android.hardware.Usb; +import android.os.Environment; import android.os.IBinder; -import android.os.Message; import android.os.RemoteException; -import android.os.Environment; -import android.os.storage.IMountService; import android.os.ServiceManager; import android.os.StatFs; -import android.os.storage.StorageManager; +import android.os.storage.IMountService; import android.os.storage.StorageEventListener; +import android.os.storage.StorageManager; +import android.preference.CheckBoxPreference; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceScreen; +import android.provider.Settings; import android.text.format.Formatter; import android.util.Log; import android.widget.Toast; -import com.android.settings.R; - import java.io.File; -import java.util.HashSet; import java.util.List; -import java.util.Set; -public class Memory extends PreferenceActivity implements OnCancelListener { +public class Memory extends SettingsPreferenceFragment implements OnCancelListener { private static final String TAG = "Memory"; private static final boolean localLOGV = false; @@ -66,6 +63,8 @@ public class Memory extends PreferenceActivity implements OnCancelListener { private static final String MEMORY_SD_FORMAT = "memory_sd_format"; + private static final String PTP_MODE_TOGGLE = "ptp_mode_toggle"; + private static final int DLG_CONFIRM_UNMOUNT = 1; private static final int DLG_ERROR_UNMOUNT = 2; @@ -75,6 +74,7 @@ public class Memory extends PreferenceActivity implements OnCancelListener { private Preference mSdAvail; private Preference mSdMountToggle; private Preference mSdFormat; + private CheckBoxPreference mPtpModeToggle; // Access using getMountService() private IMountService mMountService = null; @@ -82,7 +82,7 @@ public class Memory extends PreferenceActivity implements OnCancelListener { private StorageManager mStorageManager = null; @Override - protected void onCreate(Bundle icicle) { + public void onCreate(Bundle icicle) { super.onCreate(icicle); if (mStorageManager == null) { @@ -97,16 +97,26 @@ public class Memory extends PreferenceActivity implements OnCancelListener { mSdAvail = findPreference(MEMORY_SD_AVAIL); mSdMountToggle = findPreference(MEMORY_SD_MOUNT_TOGGLE); mSdFormat = findPreference(MEMORY_SD_FORMAT); + + mPtpModeToggle = (CheckBoxPreference)findPreference(PTP_MODE_TOGGLE); + if (Usb.isFunctionSupported(Usb.USB_FUNCTION_MTP)) { + mPtpModeToggle.setChecked(Settings.System.getInt( + getContentResolver(), + Settings.System.USE_PTP_INTERFACE, 0) != 0); + } else { + // hide the PTP mode toggle checkbox if MTP is not supported + getPreferenceScreen().removePreference(mPtpModeToggle); + } } @Override - protected void onResume() { + public void onResume() { super.onResume(); IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_SCANNER_STARTED); intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED); intentFilter.addDataScheme("file"); - registerReceiver(mReceiver, intentFilter); + getActivity().registerReceiver(mReceiver, intentFilter); updateMemoryStatus(); } @@ -123,13 +133,13 @@ public class Memory extends PreferenceActivity implements OnCancelListener { }; @Override - protected void onPause() { + public void onPause() { super.onPause(); - unregisterReceiver(mReceiver); + getActivity().unregisterReceiver(mReceiver); } @Override - protected void onDestroy() { + public void onDestroy() { if (mStorageManager != null && mStorageListener != null) { mStorageManager.unregisterListener(mStorageListener); } @@ -160,11 +170,16 @@ public class Memory extends PreferenceActivity implements OnCancelListener { return true; } else if (preference == mSdFormat) { Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setClass(this, com.android.settings.MediaFormat.class); + intent.setClass(getActivity(), com.android.settings.MediaFormat.class); startActivity(intent); return true; + } else if (preference == mPtpModeToggle) { + Settings.System.putInt(getContentResolver(), + Settings.System.USE_PTP_INTERFACE, + mPtpModeToggle.isChecked() ? 1 : 0); + return true; } - + return false; } @@ -176,10 +191,10 @@ public class Memory extends PreferenceActivity implements OnCancelListener { }; @Override - public Dialog onCreateDialog(int id, Bundle args) { + public Dialog onCreateDialog(int id) { switch (id) { case DLG_CONFIRM_UNMOUNT: - return new AlertDialog.Builder(this) + return new AlertDialog.Builder(getActivity()) .setTitle(R.string.dlg_confirm_unmount_title) .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { @@ -190,7 +205,7 @@ public class Memory extends PreferenceActivity implements OnCancelListener { .setOnCancelListener(this) .create(); case DLG_ERROR_UNMOUNT: - return new AlertDialog.Builder(this ) + return new AlertDialog.Builder(getActivity()) .setTitle(R.string.dlg_error_unmount_title) .setNeutralButton(R.string.dlg_ok, null) .setMessage(R.string.dlg_error_unmount_text) @@ -202,7 +217,7 @@ public class Memory extends PreferenceActivity implements OnCancelListener { private void doUnmount(boolean force) { // Present a toast here - Toast.makeText(this, R.string.unmount_inform_text, Toast.LENGTH_SHORT).show(); + Toast.makeText(getActivity(), R.string.unmount_inform_text, Toast.LENGTH_SHORT).show(); IMountService mountService = getMountService(); String extStoragePath = Environment.getExternalStorageDirectory().toString(); try { @@ -225,7 +240,6 @@ public class Memory extends PreferenceActivity implements OnCancelListener { private boolean hasAppsAccessingStorage() throws RemoteException { String extStoragePath = Environment.getExternalStorageDirectory().toString(); IMountService mountService = getMountService(); - boolean showPidDialog = false; int stUsers[] = mountService.getStorageUsers(extStoragePath); if (stUsers != null && stUsers.length > 0) { return true; @@ -325,11 +339,12 @@ public class Memory extends PreferenceActivity implements OnCancelListener { } private String formatSize(long size) { - return Formatter.formatFileSize(this, size); + return Formatter.formatFileSize(getActivity(), size); } public void onCancel(DialogInterface dialog) { - finish(); + // TODO: Is this really required? + // finish(); } } diff --git a/src/com/android/settings/vpn/VpnEditor.java b/src/com/android/settings/vpn/VpnEditor.java index 349befbf1..3ab0b909d 100644 --- a/src/com/android/settings/vpn/VpnEditor.java +++ b/src/com/android/settings/vpn/VpnEditor.java @@ -17,7 +17,9 @@ package com.android.settings.vpn; import com.android.settings.R; +import com.android.settings.SettingsPreferenceFragment; +import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; @@ -27,22 +29,19 @@ import android.net.vpn.L2tpIpsecPskProfile; import android.net.vpn.L2tpProfile; import android.net.vpn.PptpProfile; import android.net.vpn.VpnProfile; -import android.net.vpn.VpnType; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; -import android.preference.PreferenceActivity; -import android.preference.PreferenceGroup; import android.text.TextUtils; -import android.view.KeyEvent; +import android.util.Log; import android.view.Menu; +import android.view.MenuInflater; import android.view.MenuItem; -import android.view.View; /** * The activity class for editing a new or existing VPN profile. */ -public class VpnEditor extends PreferenceActivity { +public class VpnEditor extends SettingsPreferenceFragment { private static final int MENU_SAVE = Menu.FIRST; private static final int MENU_CANCEL = Menu.FIRST + 1; private static final int CONFIRM_DIALOG_ID = 0; @@ -56,59 +55,75 @@ public class VpnEditor extends PreferenceActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - VpnProfile p = (VpnProfile) ((savedInstanceState == null) - ? getIntent().getParcelableExtra(VpnSettings.KEY_VPN_PROFILE) - : savedInstanceState.getParcelable(KEY_PROFILE)); - mProfileEditor = getEditor(p); - mAddingProfile = TextUtils.isEmpty(p.getName()); // Loads the XML preferences file addPreferencesFromResource(R.xml.vpn_edit); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + VpnProfile p; + if (savedInstanceState != null) { + p = (VpnProfile)savedInstanceState.getParcelable(KEY_PROFILE); + } else { + p = (VpnProfile)getArguments().getParcelable(VpnSettings.KEY_VPN_PROFILE); + if (p == null) { + p = getActivity().getIntent().getParcelableExtra(VpnSettings.KEY_VPN_PROFILE); + } + } + + mProfileEditor = getEditor(p); + mAddingProfile = TextUtils.isEmpty(p.getName()); initViewFor(p); Parcel parcel = Parcel.obtain(); p.writeToParcel(parcel, 0); mOriginalProfileData = parcel.marshall(); + + registerForContextMenu(getListView()); + setHasOptionsMenu(true); } @Override - protected synchronized void onSaveInstanceState(Bundle outState) { + public synchronized void onSaveInstanceState(Bundle outState) { if (mProfileEditor == null) return; outState.putParcelable(KEY_PROFILE, getProfile()); } @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); menu.add(0, MENU_SAVE, 0, R.string.vpn_menu_done) .setIcon(android.R.drawable.ic_menu_save); menu.add(0, MENU_CANCEL, 0, mAddingProfile ? R.string.vpn_menu_cancel : R.string.vpn_menu_revert) .setIcon(android.R.drawable.ic_menu_close_clear_cancel); - return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case MENU_SAVE: - if (validateAndSetResult()) finish(); + if (validateAndSetResult()) finishFragment(); return true; case MENU_CANCEL: if (profileChanged()) { showDialog(CONFIRM_DIALOG_ID); } else { - finish(); + finishFragment(); } return true; } return super.onOptionsItemSelected(item); } + /* @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { @@ -117,7 +132,7 @@ public class VpnEditor extends PreferenceActivity { return true; } return super.onKeyDown(keyCode, event); - } + }*/ private void initViewFor(VpnProfile profile) { setTitle(profile); @@ -125,10 +140,11 @@ public class VpnEditor extends PreferenceActivity { } private void setTitle(VpnProfile profile) { + final Activity activity = getActivity(); String formatString = mAddingProfile - ? getString(R.string.vpn_edit_title_add) - : getString(R.string.vpn_edit_title_edit); - setTitle(String.format(formatString, + ? activity.getString(R.string.vpn_edit_title_add) + : activity.getString(R.string.vpn_edit_title_edit); + activity.setTitle(String.format(formatString, profile.getType().getDisplayName())); } @@ -140,7 +156,7 @@ public class VpnEditor extends PreferenceActivity { String errorMsg = mProfileEditor.validate(); if (errorMsg != null) { - Util.showErrorMessage(this, errorMsg); + Util.showErrorMessage(getActivity(), errorMsg); return false; } @@ -149,9 +165,9 @@ public class VpnEditor extends PreferenceActivity { } private void setResult(VpnProfile p) { - Intent intent = new Intent(this, VpnSettings.class); + Intent intent = new Intent(getActivity(), VpnSettings.class); intent.putExtra(VpnSettings.KEY_VPN_PROFILE, (Parcelable) p); - setResult(RESULT_OK, intent); + setResult(Activity.RESULT_OK, intent); } private VpnProfileEditor getEditor(VpnProfile p) { @@ -175,10 +191,9 @@ public class VpnEditor extends PreferenceActivity { @Override - protected Dialog onCreateDialog(int id) { - + public Dialog onCreateDialog(int id) { if (id == CONFIRM_DIALOG_ID) { - return new AlertDialog.Builder(this) + return new AlertDialog.Builder(getActivity()) .setTitle(android.R.string.dialog_alert_title) .setIcon(android.R.drawable.ic_dialog_alert) .setMessage(mAddingProfile @@ -187,7 +202,7 @@ public class VpnEditor extends PreferenceActivity { .setPositiveButton(R.string.vpn_yes_button, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int w) { - finish(); + finishFragment(); } }) .setNegativeButton(R.string.vpn_mistake_button, null) @@ -197,8 +212,9 @@ public class VpnEditor extends PreferenceActivity { return super.onCreateDialog(id); } + /* @Override - protected void onPrepareDialog(int id, Dialog dialog) { + public void onPrepareDialog(int id, Dialog dialog) { super.onPrepareDialog(id, dialog); if (id == CONFIRM_DIALOG_ID) { @@ -206,7 +222,7 @@ public class VpnEditor extends PreferenceActivity { ? getString(R.string.vpn_confirm_add_profile_cancellation) : getString(R.string.vpn_confirm_edit_profile_cancellation)); } - } + }*/ private VpnProfile getProfile() { return mProfileEditor.getProfile(); diff --git a/src/com/android/settings/vpn/VpnSettings.java b/src/com/android/settings/vpn/VpnSettings.java index 7b8d433ad..e9a4c3dc0 100644 --- a/src/com/android/settings/vpn/VpnSettings.java +++ b/src/com/android/settings/vpn/VpnSettings.java @@ -17,11 +17,14 @@ package com.android.settings.vpn; import com.android.settings.R; +import com.android.settings.SettingsPreferenceFragment; +import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; -import android.content.ComponentName; +import android.app.Fragment; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -37,12 +40,10 @@ import android.net.vpn.VpnType; import android.os.Bundle; import android.os.ConditionVariable; import android.os.IBinder; -import android.os.Parcelable; import android.preference.Preference; -import android.preference.PreferenceActivity; +import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceCategory; import android.preference.PreferenceScreen; -import android.preference.Preference.OnPreferenceClickListener; import android.security.Credentials; import android.security.KeyStore; import android.text.TextUtils; @@ -69,8 +70,8 @@ import java.util.Map; /** * The preference activity for configuring VPN settings. */ -public class VpnSettings extends PreferenceActivity implements - DialogInterface.OnClickListener { +public class VpnSettings extends SettingsPreferenceFragment + implements DialogInterface.OnClickListener { // Key to the field exchanged for profile editing. static final String KEY_VPN_PROFILE = "vpn_profile"; @@ -122,7 +123,7 @@ public class VpnSettings extends PreferenceActivity implements private KeyStore mKeyStore = KeyStore.getInstance(); - private VpnManager mVpnManager = new VpnManager(this); + private VpnManager mVpnManager; private ConnectivityReceiver mConnectivityReceiver = new ConnectivityReceiver(); @@ -137,7 +138,13 @@ public class VpnSettings extends PreferenceActivity implements public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.vpn_settings); + } + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mVpnManager = new VpnManager(getActivity()); // restore VpnProfile list and construct VpnPreference map mVpnListContainer = (PreferenceCategory) findPreference(PREF_VPN_LIST); @@ -168,22 +175,24 @@ public class VpnSettings extends PreferenceActivity implements if ((mUnlockAction != null) && isKeyStoreUnlocked()) { Runnable action = mUnlockAction; mUnlockAction = null; - runOnUiThread(action); + getActivity().runOnUiThread(action); } } @Override - protected void onDestroy() { - super.onDestroy(); + public void onDestroyView() { unregisterForContextMenu(getListView()); mVpnManager.unregisterConnectivityReceiver(mConnectivityReceiver); if ((mShowingDialog != null) && mShowingDialog.isShowing()) { mShowingDialog.dismiss(); } + // This should be called after the procedure above as ListView inside this Fragment + // will be deleted here. + super.onDestroyView(); } @Override - protected Dialog onCreateDialog (int id) { + public Dialog onCreateDialog (int id) { switch (id) { case DIALOG_CONNECT: return createConnectDialog(); @@ -203,13 +212,14 @@ public class VpnSettings extends PreferenceActivity implements } private Dialog createConnectDialog() { - return new AlertDialog.Builder(this) + final Activity activity = getActivity(); + return new AlertDialog.Builder(activity) .setView(mConnectingActor.createConnectView()) - .setTitle(String.format(getString(R.string.vpn_connect_to), + .setTitle(String.format(activity.getString(R.string.vpn_connect_to), mActiveProfile.getName())) - .setPositiveButton(getString(R.string.vpn_connect_button), + .setPositiveButton(activity.getString(R.string.vpn_connect_button), this) - .setNegativeButton(getString(android.R.string.cancel), + .setNegativeButton(activity.getString(android.R.string.cancel), this) .setOnCancelListener(new DialogInterface.OnCancelListener() { public void onCancel(DialogInterface dialog) { @@ -291,7 +301,7 @@ public class VpnSettings extends PreferenceActivity implements } private AlertDialog.Builder createCommonDialogBuilder() { - return new AlertDialog.Builder(this) + return new AlertDialog.Builder(getActivity()) .setTitle(android.R.string.dialog_alert_title) .setIcon(android.R.drawable.ic_dialog_alert) .setPositiveButton(R.string.vpn_yes_button, @@ -364,9 +374,9 @@ public class VpnSettings extends PreferenceActivity implements } @Override - protected void onActivityResult(final int requestCode, final int resultCode, + public void onActivityResult(final int requestCode, final int resultCode, final Intent data) { - if ((resultCode == RESULT_CANCELED) || (data == null)) { + if ((resultCode == Activity.RESULT_CANCELED) || (data == null)) { Log.d(TAG, "no result returned by editor"); return; } @@ -381,11 +391,12 @@ public class VpnSettings extends PreferenceActivity implements return; } + final Activity activity = getActivity(); int index = getProfileIndexFromId(p.getId()); if (checkDuplicateName(p, index)) { final VpnProfile profile = p; - Util.showErrorMessage(this, String.format( - getString(R.string.vpn_error_duplicate_name), + Util.showErrorMessage(activity, String.format( + activity.getString(R.string.vpn_error_duplicate_name), p.getName()), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int w) { @@ -407,30 +418,32 @@ public class VpnSettings extends PreferenceActivity implements try { if (index < 0) { addProfile(p); - Util.showShortToastMessage(this, String.format( - getString(R.string.vpn_profile_added), p.getName())); + Util.showShortToastMessage(activity, String.format( + activity.getString(R.string.vpn_profile_added), p.getName())); } else { replaceProfile(index, p); - Util.showShortToastMessage(this, String.format( - getString(R.string.vpn_profile_replaced), + Util.showShortToastMessage(activity, String.format( + activity.getString(R.string.vpn_profile_replaced), p.getName())); } } catch (IOException e) { final VpnProfile profile = p; - Util.showErrorMessage(this, e + ": " + e.getMessage(), + Util.showErrorMessage(activity, e + ": " + e.getMessage(), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int w) { startVpnEditor(profile); } }); } + + // Remove cached VpnEditor as it is needless anymore. } else { throw new RuntimeException("unknown request code: " + requestCode); } } // Called when the buttons on the connect dialog are clicked. - //@Override + @Override public synchronized void onClick(DialogInterface dialog, int which) { if (which == CONNECT_BUTTON) { Dialog d = (Dialog) dialog; @@ -440,12 +453,15 @@ public class VpnSettings extends PreferenceActivity implements removeDialog(DIALOG_CONNECT); return; } else { - dismissDialog(DIALOG_CONNECT); + // dismissDialog(DIALOG_CONNECT); + removeDialog(DIALOG_CONNECT); + + final Activity activity = getActivity(); // show error dialog - mShowingDialog = new AlertDialog.Builder(this) + mShowingDialog = new AlertDialog.Builder(activity) .setTitle(android.R.string.dialog_alert_title) .setIcon(android.R.drawable.ic_dialog_alert) - .setMessage(String.format(getString( + .setMessage(String.format(activity.getString( R.string.vpn_error_miss_entering), error)) .setPositiveButton(R.string.vpn_back_button, new DialogInterface.OnClickListener() { @@ -513,7 +529,7 @@ public class VpnSettings extends PreferenceActivity implements } } }; - mShowingDialog = new AlertDialog.Builder(this) + mShowingDialog = new AlertDialog.Builder(getActivity()) .setTitle(android.R.string.dialog_alert_title) .setIcon(android.R.drawable.ic_dialog_alert) .setMessage(R.string.vpn_confirm_profile_deletion) @@ -559,7 +575,7 @@ public class VpnSettings extends PreferenceActivity implements // Adds a preference in mVpnListContainer private VpnPreference addPreferenceFor( VpnProfile p, boolean addToContainer) { - VpnPreference pref = new VpnPreference(this, p); + VpnPreference pref = new VpnPreference(getActivity(), p); mVpnPreferenceMap.put(p.getName(), pref); if (addToContainer) mVpnListContainer.addPreference(pref); @@ -599,8 +615,8 @@ public class VpnSettings extends PreferenceActivity implements } private void startVpnTypeSelection() { - Intent intent = new Intent(this, VpnTypeSelection.class); - startActivityForResult(intent, REQUEST_SELECT_VPN_TYPE); + startFragment(this, VpnTypeSelection.class.getCanonicalName(), + REQUEST_SELECT_VPN_TYPE, null); } private boolean isKeyStoreUnlocked() { @@ -614,16 +630,14 @@ public class VpnSettings extends PreferenceActivity implements L2tpIpsecPskProfile pskProfile = (L2tpIpsecPskProfile) p; String presharedKey = pskProfile.getPresharedKey(); if (!TextUtils.isEmpty(presharedKey)) return true; - // pass through - + // $FALL-THROUGH$ case L2TP: L2tpProfile l2tpProfile = (L2tpProfile) p; if (l2tpProfile.isSecretEnabled() && !TextUtils.isEmpty(l2tpProfile.getSecretString())) { return true; } - // pass through - + // $FALL-THROUGH$ default: return false; } @@ -648,14 +662,15 @@ public class VpnSettings extends PreferenceActivity implements private boolean unlockKeyStore(VpnProfile p, Runnable action) { if (isKeyStoreUnlocked()) return true; mUnlockAction = action; - Credentials.getInstance().unlock(this); + Credentials.getInstance().unlock(getActivity()); return false; } private void startVpnEditor(final VpnProfile profile) { - Intent intent = new Intent(this, VpnEditor.class); - intent.putExtra(KEY_VPN_PROFILE, (Parcelable) profile); - startActivityForResult(intent, REQUEST_ADD_OR_EDIT_PROFILE); + Bundle args = new Bundle(); + args.putParcelable(KEY_VPN_PROFILE, profile); + startFragment(this, VpnEditor.class.getCanonicalName(), + REQUEST_ADD_OR_EDIT_PROFILE, args); } private synchronized void connect(final VpnProfile p) { @@ -714,7 +729,7 @@ public class VpnSettings extends PreferenceActivity implements case CONNECTING: mConnectingActor = getActor(p); - // pass through + // $FALL-THROUGH$ case DISCONNECTING: mActiveProfile = p; disableProfilePreferencesIfOneActive(); @@ -810,11 +825,6 @@ public class VpnSettings extends PreferenceActivity implements public int compare(VpnProfile p1, VpnProfile p2) { return p1.getName().compareTo(p2.getName()); } - - public boolean equals(VpnProfile p) { - // not used - return false; - } }); for (VpnProfile p : mVpnProfileList) { Preference pref = addPreferenceFor(p, false); @@ -855,20 +865,21 @@ public class VpnSettings extends PreferenceActivity implements } private String getProfileSummaryString(VpnProfile p) { + final Activity activity = getActivity(); switch (p.getState()) { case CONNECTING: - return getString(R.string.vpn_connecting); + return activity.getString(R.string.vpn_connecting); case DISCONNECTING: - return getString(R.string.vpn_disconnecting); + return activity.getString(R.string.vpn_disconnecting); case CONNECTED: - return getString(R.string.vpn_connected); + return activity.getString(R.string.vpn_connected); default: - return getString(R.string.vpn_connect_hint); + return activity.getString(R.string.vpn_connect_hint); } } private VpnProfileActor getActor(VpnProfile p) { - return new AuthenticationActor(this, p); + return new AuthenticationActor(getActivity(), p); } private VpnProfile createVpnProfile(String type) { @@ -938,8 +949,7 @@ public class VpnSettings extends PreferenceActivity implements Log.e(TAG, "keystore write failed: key=" + key); } pskProfile.setPresharedKey(key); - // pass through - + // $FALL-THROUGH$ case L2TP_IPSEC: case L2TP: L2tpProfile l2tpProfile = (L2tpProfile) p; @@ -1007,8 +1017,6 @@ public class VpnSettings extends PreferenceActivity implements // managing status check in a background thread private class StatusChecker { - private List<VpnProfile> mList; - synchronized void check(final List<VpnProfile> list) { final ConditionVariable cv = new ConditionVariable(); cv.close(); @@ -1027,7 +1035,7 @@ public class VpnSettings extends PreferenceActivity implements changeState(p, VpnState.IDLE); } } - VpnSettings.this.unbindService(this); + getActivity().unbindService(this); showPreferences(); } @@ -1035,7 +1043,7 @@ public class VpnSettings extends PreferenceActivity implements cv.open(); setDefaultState(list); - VpnSettings.this.unbindService(this); + getActivity().unbindService(this); showPreferences(); } }; diff --git a/src/com/android/settings/vpn/VpnTypeSelection.java b/src/com/android/settings/vpn/VpnTypeSelection.java index aa4bc5e30..5990ac0ac 100644 --- a/src/com/android/settings/vpn/VpnTypeSelection.java +++ b/src/com/android/settings/vpn/VpnTypeSelection.java @@ -17,13 +17,14 @@ package com.android.settings.vpn; import com.android.settings.R; +import com.android.settings.SettingsPreferenceFragment; +import android.app.Activity; import android.content.Intent; import android.net.vpn.VpnManager; import android.net.vpn.VpnType; import android.os.Bundle; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceScreen; import java.util.HashMap; @@ -32,7 +33,7 @@ import java.util.Map; /** * The activity to select a VPN type. */ -public class VpnTypeSelection extends PreferenceActivity { +public class VpnTypeSelection extends SettingsPreferenceFragment { private Map<String, VpnType> mTypeMap = new HashMap<String, VpnType>(); @Override @@ -46,19 +47,20 @@ public class VpnTypeSelection extends PreferenceActivity { @Override public boolean onPreferenceTreeClick(PreferenceScreen ps, Preference pref) { setResult(mTypeMap.get(pref.getTitle().toString())); - finish(); + finishFragment(); return true; } private void initTypeList() { PreferenceScreen root = getPreferenceScreen(); + final Activity activity = getActivity(); for (VpnType t : VpnManager.getSupportedVpnTypes()) { String displayName = t.getDisplayName(); String message = String.format( - getString(R.string.vpn_edit_title_add), displayName); + activity.getString(R.string.vpn_edit_title_add), displayName); mTypeMap.put(message, t); - Preference pref = new Preference(this); + Preference pref = new Preference(activity); pref.setTitle(message); pref.setSummary(t.getDescriptionId()); root.addPreference(pref); @@ -66,8 +68,8 @@ public class VpnTypeSelection extends PreferenceActivity { } private void setResult(VpnType type) { - Intent intent = new Intent(this, VpnSettings.class); + Intent intent = new Intent(getActivity(), VpnSettings.class); intent.putExtra(VpnSettings.KEY_VPN_TYPE, type.toString()); - setResult(RESULT_OK, intent); + setResult(Activity.RESULT_OK, intent); } } diff --git a/src/com/android/settings/wifi/AccessPoint.java b/src/com/android/settings/wifi/AccessPoint.java index 141c4129c..054c6ff93 100644 --- a/src/com/android/settings/wifi/AccessPoint.java +++ b/src/com/android/settings/wifi/AccessPoint.java @@ -20,28 +20,59 @@ import com.android.settings.R; import android.content.Context; import android.net.NetworkInfo.DetailedState; +import android.net.wifi.ScanResult; 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.ScanResult; import android.preference.Preference; -import android.text.TextUtils; import android.view.View; import android.widget.ImageView; +import java.util.Comparator; + class AccessPoint extends Preference { private static final int[] STATE_SECURED = {R.attr.state_encrypted}; private static final int[] STATE_NONE = {}; + public static final class Comparater + implements Comparator<AccessPoint> { + @Override + public int compare(AccessPoint accessPoint1, AccessPoint accessPoint2) { + // Active one goes first. + if (accessPoint1.mInfo != accessPoint2.mInfo) { + return (accessPoint1.mInfo != null) ? -1 : 1; + } + + // Reachable one goes before unreachable one. + if ((accessPoint1.mRssi ^ accessPoint2.mRssi) < 0) { + return (accessPoint1.mRssi != Integer.MAX_VALUE) ? -1 : 1; + } + // Configured one goes before unconfigured one. + if ((accessPoint1.networkId ^ accessPoint2.networkId) < 0) { + return (accessPoint1.networkId != -1) ? -1 : 1; + } + // Sort by signal strength. + int difference = WifiManager.compareSignalLevel( + accessPoint2.mRssi, accessPoint1.mRssi); + if (difference != 0) { + return difference; + } + // Sort by ssid. + return accessPoint1.ssid.compareToIgnoreCase(accessPoint2.ssid); + } + } + static final int SECURITY_NONE = 0; static final int SECURITY_WEP = 1; static final int SECURITY_PSK = 2; static final int SECURITY_EAP = 3; final String ssid; + final String bssid; final int security; final int networkId; + boolean wpsAvailable = false; private WifiConfiguration mConfig; private int mRssi; @@ -75,6 +106,7 @@ class AccessPoint extends Preference { super(context); setWidgetLayoutResource(R.layout.preference_widget_wifi_signal); ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID)); + bssid = config.BSSID; security = getSecurity(config); networkId = config.networkId; mConfig = config; @@ -85,7 +117,10 @@ class AccessPoint extends Preference { super(context); setWidgetLayoutResource(R.layout.preference_widget_wifi_signal); ssid = result.SSID; + bssid = result.BSSID; security = getSecurity(result); + wpsAvailable = security != SECURITY_NONE && security != SECURITY_EAP && + result.capabilities.contains("WPS"); networkId = -1; mRssi = result.level; } @@ -105,32 +140,6 @@ class AccessPoint extends Preference { super.onBindView(view); } - @Override - public int compareTo(Preference preference) { - if (!(preference instanceof AccessPoint)) { - return 1; - } - AccessPoint other = (AccessPoint) preference; - // Active one goes first. - if (mInfo != other.mInfo) { - return (mInfo != null) ? -1 : 1; - } - // Reachable one goes before unreachable one. - if ((mRssi ^ other.mRssi) < 0) { - return (mRssi != Integer.MAX_VALUE) ? -1 : 1; - } - // Configured one goes before unconfigured one. - if ((networkId ^ other.networkId) < 0) { - return (networkId != -1) ? -1 : 1; - } - // Sort by signal strength. - int difference = WifiManager.compareSignalLevel(other.mRssi, mRssi); - if (difference != 0) { - return difference; - } - // Sort by ssid. - return ssid.compareToIgnoreCase(other.ssid); - } boolean update(ScanResult result) { // We do not call refresh() since this is called before onBindView(). @@ -215,8 +224,13 @@ class AccessPoint extends Preference { if (security == SECURITY_NONE) { setSummary(status); } else { - String format = context.getString((status == null) ? - R.string.wifi_secured : R.string.wifi_secured_with_status); + String format; + if (wpsAvailable && mConfig == null) { + format = context.getString(R.string.wifi_secured_with_wps); + } else { + format = context.getString((status == null) ? + R.string.wifi_secured : R.string.wifi_secured_with_status); + } String[] type = context.getResources().getStringArray(R.array.wifi_security); setSummary(String.format(format, type[security], status)); } diff --git a/src/com/android/settings/wifi/AccessPointCategoryForSetupWizardXL.java b/src/com/android/settings/wifi/AccessPointCategoryForSetupWizardXL.java new file mode 100644 index 000000000..7a1623bc5 --- /dev/null +++ b/src/com/android/settings/wifi/AccessPointCategoryForSetupWizardXL.java @@ -0,0 +1,35 @@ +/* + * 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.wifi; + +import com.android.settings.ProgressCategoryBase; +import com.android.settings.R; + +import android.content.Context; +import android.util.AttributeSet; + +public class AccessPointCategoryForSetupWizardXL extends ProgressCategoryBase { + public AccessPointCategoryForSetupWizardXL(Context context, AttributeSet attrs) { + super(context, attrs); + setLayoutResource(R.layout.access_point_category_for_setup_wizard_xl); + } + + @Override + public void setProgress(boolean progressOn) { + notifyChanged(); + } +}
\ No newline at end of file diff --git a/src/com/android/settings/wifi/AdvancedSettings.java b/src/com/android/settings/wifi/AdvancedSettings.java index 636e1dffb..c88073d75 100644 --- a/src/com/android/settings/wifi/AdvancedSettings.java +++ b/src/com/android/settings/wifi/AdvancedSettings.java @@ -17,64 +17,39 @@ package com.android.settings.wifi; import com.android.settings.R; +import com.android.settings.SettingsPreferenceFragment; -import android.content.ContentResolver; +import android.app.Activity; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.EditTextPreference; +import android.os.SystemProperties; import android.preference.ListPreference; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.provider.Settings; -import android.provider.Settings.System; import android.text.TextUtils; -import android.view.KeyEvent; -import android.view.Menu; -import android.view.MenuItem; import android.widget.Toast; -import android.os.SystemProperties; -public class AdvancedSettings extends PreferenceActivity +public class AdvancedSettings extends SettingsPreferenceFragment implements Preference.OnPreferenceChangeListener { 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_USE_STATIC_IP = "use_static_ip"; private static final String KEY_NUM_CHANNELS = "num_channels"; private static final String KEY_SLEEP_POLICY = "sleep_policy"; - - private String[] mSettingNames = { - System.WIFI_STATIC_IP, System.WIFI_STATIC_GATEWAY, System.WIFI_STATIC_NETMASK, - System.WIFI_STATIC_DNS1, System.WIFI_STATIC_DNS2 - }; - - private String[] mPreferenceKeys = { - "ip_address", "gateway", "netmask", "dns1", "dns2" - }; - - private CheckBoxPreference mUseStaticIpCheckBox; - - private static final int MENU_ITEM_SAVE = Menu.FIRST; - private static final int MENU_ITEM_CANCEL = Menu.FIRST + 1; - + //Tracks ro.debuggable (1 on userdebug builds) private static int DEBUGGABLE; @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.wifi_advanced_settings); - - mUseStaticIpCheckBox = (CheckBoxPreference) findPreference(KEY_USE_STATIC_IP); - mUseStaticIpCheckBox.setOnPreferenceChangeListener(this); + } - for (int i = 0; i < mPreferenceKeys.length; i++) { - Preference preference = findPreference(mPreferenceKeys[i]); - preference.setOnPreferenceChangeListener(this); - } + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); DEBUGGABLE = SystemProperties.getInt("ro.debuggable", 0); @@ -98,10 +73,9 @@ public class AdvancedSettings extends PreferenceActivity } @Override - protected void onResume() { + public void onResume() { super.onResume(); - - updateUi(); + /** * Remove user control of regulatory domain * channel count settings in non userdebug builds @@ -117,7 +91,7 @@ public class AdvancedSettings extends PreferenceActivity ListPreference pref = (ListPreference) findPreference(KEY_NUM_CHANNELS); pref.setOnPreferenceChangeListener(this); - WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE); + WifiManager wifiManager = (WifiManager) getSystemService(Activity.WIFI_SERVICE); /* * Generate the list of valid channel counts to show in the ListPreference. * The values are numerical, so the only text to be localized is the @@ -125,7 +99,7 @@ public class AdvancedSettings extends PreferenceActivity */ int[] validChannelCounts = wifiManager.getValidChannelCounts(); if (validChannelCounts == null) { - Toast.makeText(this, R.string.wifi_setting_num_channels_error, + Toast.makeText(getActivity(), R.string.wifi_setting_num_channels_error, Toast.LENGTH_SHORT).show(); pref.setEnabled(false); return; @@ -135,8 +109,8 @@ public class AdvancedSettings extends PreferenceActivity for (int i = 0; i < validChannelCounts.length; i++) { entryValues[i] = String.valueOf(validChannelCounts[i]); - entries[i] = getString(R.string.wifi_setting_num_channels_channel_phrase, - validChannelCounts[i]); + entries[i] = getActivity().getString(R.string.wifi_setting_num_channels_channel_phrase, + validChannelCounts[i]); } pref.setEntries(entries); pref.setEntryValues(entryValues); @@ -155,16 +129,6 @@ public class AdvancedSettings extends PreferenceActivity pref.setValue(String.valueOf(value)); } - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - - if (keyCode == KeyEvent.KEYCODE_BACK) { - updateSettingsProvider(); - } - - return super.onKeyDown(keyCode, event); - } - public boolean onPreferenceChange(Preference preference, Object newValue) { String key = preference.getKey(); if (key == null) return true; @@ -172,13 +136,13 @@ public class AdvancedSettings extends PreferenceActivity if (key.equals(KEY_NUM_CHANNELS)) { try { int numChannels = Integer.parseInt((String) newValue); - WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE); + WifiManager wifiManager = (WifiManager) getSystemService(Activity.WIFI_SERVICE); if (!wifiManager.setNumAllowedChannels(numChannels, true)) { - Toast.makeText(this, R.string.wifi_setting_num_channels_error, + Toast.makeText(getActivity(), R.string.wifi_setting_num_channels_error, Toast.LENGTH_SHORT).show(); } } catch (NumberFormatException e) { - Toast.makeText(this, R.string.wifi_setting_num_channels_error, + Toast.makeText(getActivity(), R.string.wifi_setting_num_channels_error, Toast.LENGTH_SHORT).show(); return false; } @@ -188,134 +152,23 @@ public class AdvancedSettings extends PreferenceActivity Settings.System.putInt(getContentResolver(), Settings.System.WIFI_SLEEP_POLICY, Integer.parseInt(((String) newValue))); } catch (NumberFormatException e) { - Toast.makeText(this, R.string.wifi_setting_sleep_policy_error, + Toast.makeText(getActivity(), R.string.wifi_setting_sleep_policy_error, Toast.LENGTH_SHORT).show(); return false; } - - } else if (key.equals(KEY_USE_STATIC_IP)) { - boolean value = ((Boolean) newValue).booleanValue(); - - try { - Settings.System.putInt(getContentResolver(), - Settings.System.WIFI_USE_STATIC_IP, value ? 1 : 0); - } catch (NumberFormatException e) { - return false; - } - } else { - String value = (String) newValue; - - if (!isIpAddress(value)) { - Toast.makeText(this, R.string.wifi_ip_settings_invalid_ip, Toast.LENGTH_LONG).show(); - return false; - } - - preference.setSummary(value); - for (int i = 0; i < mSettingNames.length; i++) { - if (key.equals(mPreferenceKeys[i])) { - Settings.System.putString(getContentResolver(), mSettingNames[i], value); - break; - } - } } return true; } - private boolean isIpAddress(String value) { - - int start = 0; - int end = value.indexOf('.'); - int numBlocks = 0; - - while (start < value.length()) { - - if (end == -1) { - end = value.length(); - } - - try { - int block = Integer.parseInt(value.substring(start, end)); - if ((block > 255) || (block < 0)) { - return false; - } - } catch (NumberFormatException e) { - return false; - } - - numBlocks++; - - start = end + 1; - end = value.indexOf('.', start); - } - - return numBlocks == 4; - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - - menu.add(0, MENU_ITEM_SAVE, 0, R.string.wifi_ip_settings_menu_save) - .setIcon(android.R.drawable.ic_menu_save); - - menu.add(0, MENU_ITEM_CANCEL, 0, R.string.wifi_ip_settings_menu_cancel) - .setIcon(android.R.drawable.ic_menu_close_clear_cancel); - - return super.onCreateOptionsMenu(menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - switch (item.getItemId()) { - - case MENU_ITEM_SAVE: - updateSettingsProvider(); - finish(); - return true; - - case MENU_ITEM_CANCEL: - finish(); - return true; - } - - return super.onOptionsItemSelected(item); - } - - private void updateUi() { - ContentResolver contentResolver = getContentResolver(); - - mUseStaticIpCheckBox.setChecked(System.getInt(contentResolver, - System.WIFI_USE_STATIC_IP, 0) != 0); - - for (int i = 0; i < mSettingNames.length; i++) { - EditTextPreference preference = (EditTextPreference) findPreference(mPreferenceKeys[i]); - String settingValue = System.getString(contentResolver, mSettingNames[i]); - preference.setText(settingValue); - preference.setSummary(settingValue); - } - } - - private void updateSettingsProvider() { - ContentResolver contentResolver = getContentResolver(); - - System.putInt(contentResolver, System.WIFI_USE_STATIC_IP, - mUseStaticIpCheckBox.isChecked() ? 1 : 0); - - for (int i = 0; i < mSettingNames.length; i++) { - EditTextPreference preference = (EditTextPreference) findPreference(mPreferenceKeys[i]); - System.putString(contentResolver, mSettingNames[i], preference.getText()); - } - } - private void refreshWifiInfo() { - WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE); + WifiManager wifiManager = (WifiManager) getSystemService(Activity.WIFI_SERVICE); WifiInfo wifiInfo = wifiManager.getConnectionInfo(); Preference wifiMacAddressPref = findPreference(KEY_MAC_ADDRESS); String macAddress = wifiInfo == null ? null : wifiInfo.getMacAddress(); wifiMacAddressPref.setSummary(!TextUtils.isEmpty(macAddress) ? macAddress - : getString(R.string.status_unavailable)); + : getActivity().getString(R.string.status_unavailable)); Preference wifiIpAddressPref = findPreference(KEY_CURRENT_IP_ADDRESS); String ipAddress = null; @@ -329,7 +182,7 @@ public class AdvancedSettings extends PreferenceActivity } } wifiIpAddressPref.setSummary(ipAddress == null ? - getString(R.string.status_unavailable) : ipAddress); + getActivity().getString(R.string.status_unavailable) : ipAddress); } } diff --git a/src/com/android/settings/wifi/Summary.java b/src/com/android/settings/wifi/Summary.java index 6da2fa5db..d96d23ecd 100644 --- a/src/com/android/settings/wifi/Summary.java +++ b/src/com/android/settings/wifi/Summary.java @@ -20,7 +20,6 @@ import com.android.settings.R; import android.content.Context; import android.net.NetworkInfo.DetailedState; -import android.text.TextUtils; class Summary { static String get(Context context, String ssid, DetailedState state) { diff --git a/src/com/android/settings/wifi/WifiApDialog.java b/src/com/android/settings/wifi/WifiApDialog.java index 43289d2f9..fde6efc69 100644 --- a/src/com/android/settings/wifi/WifiApDialog.java +++ b/src/com/android/settings/wifi/WifiApDialog.java @@ -16,8 +16,6 @@ package com.android.settings.wifi; -import com.android.settings.R; - import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; @@ -28,16 +26,15 @@ import android.os.Bundle; import android.text.Editable; import android.text.InputType; import android.text.TextWatcher; -import android.util.Log; import android.view.View; -import android.view.ViewGroup; import android.widget.AdapterView; -import android.widget.ArrayAdapter; import android.widget.CheckBox; import android.widget.EditText; import android.widget.Spinner; import android.widget.TextView; +import com.android.settings.R; + /** * Dialog to configure the SSID and security settings * for Access Point operation @@ -97,6 +94,7 @@ class WifiApDialog extends AlertDialog implements View.OnClickListener, return null; } + @Override protected void onCreate(Bundle savedInstanceState) { mView = getLayoutInflater().inflate(R.layout.wifi_ap_dialog, null); @@ -167,7 +165,8 @@ class WifiApDialog extends AlertDialog implements View.OnClickListener, validate(); } - public void onItemSelected(AdapterView parent, View view, int position, long id) { + @Override + public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { if(position == OPEN_INDEX) mSecurityType = AccessPoint.SECURITY_NONE; else @@ -176,7 +175,8 @@ class WifiApDialog extends AlertDialog implements View.OnClickListener, validate(); } - public void onNothingSelected(AdapterView parent) { + @Override + public void onNothingSelected(AdapterView<?> parent) { } private void showSecurityFields() { diff --git a/src/com/android/settings/wifi/WifiApSettings.java b/src/com/android/settings/wifi/WifiApSettings.java index ca1b85688..7336c6c5e 100644 --- a/src/com/android/settings/wifi/WifiApSettings.java +++ b/src/com/android/settings/wifi/WifiApSettings.java @@ -17,29 +17,24 @@ package com.android.settings.wifi; import com.android.settings.R; +import com.android.settings.SettingsPreferenceFragment; + +import android.app.Activity; import android.app.Dialog; -import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; -import android.content.Intent; -import android.content.IntentFilter; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.PreferenceActivity; -import android.preference.PreferenceScreen; -import android.preference.CheckBoxPreference; -import android.provider.Settings; -import android.util.Log; import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiConfiguration.AuthAlgorithm; import android.net.wifi.WifiConfiguration.KeyMgmt; import android.net.wifi.WifiManager; import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.Preference; +import android.preference.PreferenceScreen; /* * Displays preferences for Tethering. */ -public class WifiApSettings extends PreferenceActivity +public class WifiApSettings extends SettingsPreferenceFragment implements DialogInterface.OnClickListener { private static final String WIFI_AP_SSID_AND_SECURITY = "wifi_ap_ssid_and_security"; @@ -61,26 +56,34 @@ public class WifiApSettings extends PreferenceActivity private WifiConfiguration mWifiConfig = null; @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.wifi_ap_settings); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + final Activity activity = getActivity(); mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); mWifiConfig = mWifiManager.getWifiApConfiguration(); mSecurityType = getResources().getStringArray(R.array.wifi_ap_security); - addPreferencesFromResource(R.xml.wifi_ap_settings); + mCreateNetwork = findPreference(WIFI_AP_SSID_AND_SECURITY); mEnableWifiAp = (CheckBoxPreference) findPreference(ENABLE_WIFI_AP); - mWifiApEnabler = new WifiApEnabler(this, mEnableWifiAp); + mWifiApEnabler = new WifiApEnabler(activity, mEnableWifiAp); if(mWifiConfig == null) { - String s = getString(com.android.internal.R.string.wifi_tether_configure_ssid_default); - mCreateNetwork.setSummary(String.format(getString(CONFIG_SUBTEXT), + final String s = activity.getString( + com.android.internal.R.string.wifi_tether_configure_ssid_default); + mCreateNetwork.setSummary(String.format(activity.getString(CONFIG_SUBTEXT), s, mSecurityType[OPEN_INDEX])); } else { - mCreateNetwork.setSummary(String.format(getString(CONFIG_SUBTEXT), + mCreateNetwork.setSummary(String.format(activity.getString(CONFIG_SUBTEXT), mWifiConfig.SSID, mWifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ? mSecurityType[WPA_INDEX] : mSecurityType[OPEN_INDEX])); @@ -88,22 +91,23 @@ public class WifiApSettings extends PreferenceActivity } @Override - protected Dialog onCreateDialog(int id) { + public Dialog onCreateDialog(int id) { if (id == DIALOG_AP_SETTINGS) { - mDialog = new WifiApDialog(this, this, mWifiConfig); + final Activity activity = getActivity(); + mDialog = new WifiApDialog(activity, this, mWifiConfig); return mDialog; } return null; } @Override - protected void onResume() { + public void onResume() { super.onResume(); mWifiApEnabler.resume(); } @Override - protected void onPause() { + public void onPause() { super.onPause(); mWifiApEnabler.pause(); } @@ -117,7 +121,6 @@ public class WifiApSettings extends PreferenceActivity } public void onClick(DialogInterface dialogInterface, int button) { - if (button == DialogInterface.BUTTON_POSITIVE) { mWifiConfig = mDialog.getConfig(); if (mWifiConfig != null) { @@ -135,7 +138,7 @@ public class WifiApSettings extends PreferenceActivity } else { mWifiManager.setWifiApConfiguration(mWifiConfig); } - mCreateNetwork.setSummary(String.format(getString(CONFIG_SUBTEXT), + mCreateNetwork.setSummary(String.format(getActivity().getString(CONFIG_SUBTEXT), mWifiConfig.SSID, mWifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ? mSecurityType[WPA_INDEX] : mSecurityType[OPEN_INDEX])); diff --git a/src/com/android/settings/wifi/WifiConfigController.java b/src/com/android/settings/wifi/WifiConfigController.java new file mode 100644 index 000000000..4c04fb553 --- /dev/null +++ b/src/com/android/settings/wifi/WifiConfigController.java @@ -0,0 +1,544 @@ +/* + * 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.wifi; + +import android.content.Context; +import android.content.DialogInterface; +import android.content.res.Resources; +import android.net.DhcpInfo; +import android.net.NetworkInfo.DetailedState; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiConfiguration.IpAssignment; +import android.net.wifi.WifiConfiguration.AuthAlgorithm; +import android.net.wifi.WifiConfiguration.KeyMgmt; +import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID; +import android.net.wifi.WifiInfo; +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.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.CheckBox; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.Toast; + +import com.android.settings.R; +import java.net.UnknownHostException; + +/** + * The class for allowing UIs like {@link WifiDialog} and {@link WifiConfigPreference} to + * share the logic for controlling buttons, text fields, etc. + */ +public class WifiConfigController implements TextWatcher, + View.OnClickListener, AdapterView.OnItemSelectedListener { + private static final String KEYSTORE_SPACE = "keystore://"; + + private final WifiConfigUiBase mConfigUi; + private final View mView; + private final AccessPoint mAccessPoint; + + private boolean mEdit; + + private TextView mSsidView; + + // e.g. AccessPoint.SECURITY_NONE + private int mAccessPointSecurity; + private TextView mPasswordView; + + private Spinner mSecuritySpinner; + private Spinner mEapMethodSpinner; + private Spinner mEapCaCertSpinner; + private Spinner mPhase2Spinner; + private Spinner mEapUserCertSpinner; + private TextView mEapIdentityView; + private TextView mEapAnonymousView; + + /* This value comes from "wifi_ip_settings" resource array */ + private static final int STATIC_IP = 1; + + /* These values come from "wifi_network_setup" resource array */ + public static final int MANUAL = 0; + public static final int WPS_PBC = 1; + public static final int WPS_PIN = 2; + + private Spinner mNetworkSetupSpinner; + private Spinner mIpSettingsSpinner; + private TextView mIpAddressView; + private TextView mGatewayView; + private TextView mNetmaskView; + private TextView mDns1View; + private TextView mDns2View; + + static boolean requireKeyStore(WifiConfiguration config) { + String values[] = {config.ca_cert.value(), config.client_cert.value(), + config.private_key.value()}; + for (String value : values) { + if (value != null && value.startsWith(KEYSTORE_SPACE)) { + return true; + } + } + return false; + } + + public WifiConfigController(WifiConfigUiBase parent, View view, AccessPoint accessPoint, + boolean edit, DialogInterface.OnClickListener listener) { + mConfigUi = parent; + + mView = view; + mAccessPoint = accessPoint; + mAccessPointSecurity = (accessPoint == null) ? AccessPoint.SECURITY_NONE : + accessPoint.security; + mEdit = edit; + + final Context context = mConfigUi.getContext(); + final Resources resources = context.getResources(); + + if (mAccessPoint == null) { + mConfigUi.setTitle(R.string.wifi_add_network); + mView.findViewById(R.id.type).setVisibility(View.VISIBLE); + mSsidView = (TextView) mView.findViewById(R.id.ssid); + mSsidView.addTextChangedListener(this); + mSecuritySpinner = ((Spinner) mView.findViewById(R.id.security)); + mSecuritySpinner.setOnItemSelectedListener(this); + mConfigUi.setSubmitButton(context.getString(R.string.wifi_save)); + } else { + mConfigUi.setTitle(mAccessPoint.ssid); + ViewGroup group = (ViewGroup) mView.findViewById(R.id.info); + + DetailedState state = mAccessPoint.getState(); + if (state != null) { + addRow(group, R.string.wifi_status, Summary.get(mConfigUi.getContext(), state)); + } + + String[] type = resources.getStringArray(R.array.wifi_security); + addRow(group, R.string.wifi_security, type[mAccessPoint.security]); + + int level = mAccessPoint.getLevel(); + if (level != -1) { + String[] signal = resources.getStringArray(R.array.wifi_signal); + addRow(group, R.string.wifi_signal, signal[level]); + } + + WifiInfo info = mAccessPoint.getInfo(); + if (info != null) { + addRow(group, R.string.wifi_speed, info.getLinkSpeed() + WifiInfo.LINK_SPEED_UNITS); + // TODO: fix the ip address for IPv6. + int address = info.getIpAddress(); + if (address != 0) { + addRow(group, R.string.wifi_ip_address, Formatter.formatIpAddress(address)); + } + } + + /* Show network setup options only for a new network */ + if (mAccessPoint.networkId == INVALID_NETWORK_ID && mAccessPoint.wpsAvailable) { + showNetworkSetupFields(); + } + + if (mAccessPoint.networkId == INVALID_NETWORK_ID || mEdit) { + showSecurityFields(); + showIpConfigFields(); + } + + if (mEdit) { + mConfigUi.setSubmitButton(context.getString(R.string.wifi_save)); + } else { + if (state == null && level != -1) { + mConfigUi.setSubmitButton(context.getString(R.string.wifi_connect)); + } else { + mView.findViewById(R.id.ip_fields).setVisibility(View.GONE); + } + if (mAccessPoint.networkId != INVALID_NETWORK_ID) { + mConfigUi.setForgetButton(context.getString(R.string.wifi_forget)); + } + } + } + + if (mAccessPoint != null && mAccessPoint.networkId != INVALID_NETWORK_ID) { + WifiConfiguration config = mAccessPoint.getConfig(); + if (config.ipAssignment == IpAssignment.STATIC) { + mIpSettingsSpinner.setSelection(STATIC_IP); + } + } + + mConfigUi.setCancelButton(context.getString(R.string.wifi_cancel)); + if (mConfigUi.getSubmitButton() != null) { + enableSubmitIfAppropriate(); + } + } + + private void addRow(ViewGroup group, int name, String value) { + View row = mConfigUi.getLayoutInflater().inflate(R.layout.wifi_dialog_row, group, false); + ((TextView) row.findViewById(R.id.name)).setText(name); + ((TextView) row.findViewById(R.id.value)).setText(value); + group.addView(row); + } + + /* show submit button if the password is valid */ + private void enableSubmitIfAppropriate() { + if ((mSsidView != null && mSsidView.length() == 0) || + ((mAccessPoint == null || mAccessPoint.networkId == INVALID_NETWORK_ID) && + ((mAccessPointSecurity == AccessPoint.SECURITY_WEP && mPasswordView.length() == 0) || + (mAccessPointSecurity == AccessPoint.SECURITY_PSK && mPasswordView.length() < 8)))) { + mConfigUi.getSubmitButton().setEnabled(false); + } else { + mConfigUi.getSubmitButton().setEnabled(true); + } + } + + /* package */ WifiConfiguration getConfig() { + if (mAccessPoint != null && mAccessPoint.networkId != INVALID_NETWORK_ID && !mEdit) { + return null; + } + + WifiConfiguration config = new WifiConfiguration(); + + if (mAccessPoint == null) { + config.SSID = AccessPoint.convertToQuotedString( + mSsidView.getText().toString()); + // If the user adds a network manually, assume that it is hidden. + config.hiddenSSID = true; + } else if (mAccessPoint.networkId == INVALID_NETWORK_ID) { + config.SSID = AccessPoint.convertToQuotedString( + mAccessPoint.ssid); + } else { + config.networkId = mAccessPoint.networkId; + } + + switch (mAccessPointSecurity) { + case AccessPoint.SECURITY_NONE: + config.allowedKeyManagement.set(KeyMgmt.NONE); + break; + + case AccessPoint.SECURITY_WEP: + config.allowedKeyManagement.set(KeyMgmt.NONE); + config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN); + config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED); + if (mPasswordView.length() != 0) { + int length = mPasswordView.length(); + String password = mPasswordView.getText().toString(); + // WEP-40, WEP-104, and 256-bit WEP (WEP-232?) + if ((length == 10 || length == 26 || length == 58) && + password.matches("[0-9A-Fa-f]*")) { + config.wepKeys[0] = password; + } else { + config.wepKeys[0] = '"' + password + '"'; + } + } + break; + + case AccessPoint.SECURITY_PSK: + config.allowedKeyManagement.set(KeyMgmt.WPA_PSK); + if (mPasswordView.length() != 0) { + String password = mPasswordView.getText().toString(); + if (password.matches("[0-9A-Fa-f]{64}")) { + config.preSharedKey = password; + } else { + config.preSharedKey = '"' + password + '"'; + } + } + break; + + case AccessPoint.SECURITY_EAP: + config.allowedKeyManagement.set(KeyMgmt.WPA_EAP); + config.allowedKeyManagement.set(KeyMgmt.IEEE8021X); + config.eap.setValue((String) mEapMethodSpinner.getSelectedItem()); + + config.phase2.setValue((mPhase2Spinner.getSelectedItemPosition() == 0) ? "" : + "auth=" + mPhase2Spinner.getSelectedItem()); + config.ca_cert.setValue((mEapCaCertSpinner.getSelectedItemPosition() == 0) ? "" : + KEYSTORE_SPACE + Credentials.CA_CERTIFICATE + + (String) mEapCaCertSpinner.getSelectedItem()); + config.client_cert.setValue((mEapUserCertSpinner.getSelectedItemPosition() == 0) ? + "" : KEYSTORE_SPACE + Credentials.USER_CERTIFICATE + + (String) mEapUserCertSpinner.getSelectedItem()); + config.private_key.setValue((mEapUserCertSpinner.getSelectedItemPosition() == 0) ? + "" : KEYSTORE_SPACE + Credentials.USER_PRIVATE_KEY + + (String) mEapUserCertSpinner.getSelectedItem()); + config.identity.setValue((mEapIdentityView.length() == 0) ? "" : + mEapIdentityView.getText().toString()); + config.anonymous_identity.setValue((mEapAnonymousView.length() == 0) ? "" : + mEapAnonymousView.getText().toString()); + if (mPasswordView.length() != 0) { + config.password.setValue(mPasswordView.getText().toString()); + } + break; + + default: + return null; + } + + config.ipAssignment = (mIpSettingsSpinner.getSelectedItemPosition() == STATIC_IP) ? + IpAssignment.STATIC : IpAssignment.DHCP; + + if (config.ipAssignment == IpAssignment.STATIC) { + //TODO: A better way to do this is to not dismiss the + //dialog as long as one of the fields is invalid + try { + config.ipConfig.ipAddress = stringToIpAddr(mIpAddressView.getText().toString()); + config.ipConfig.gateway = stringToIpAddr(mGatewayView.getText().toString()); + config.ipConfig.netmask = stringToIpAddr(mNetmaskView.getText().toString()); + config.ipConfig.dns1 = stringToIpAddr(mDns1View.getText().toString()); + if (mDns2View.getText() != null && mDns2View.getText().length() > 0) { + config.ipConfig.dns2 = stringToIpAddr(mDns2View.getText().toString()); + } + } catch (UnknownHostException e) { + Toast.makeText(mConfigUi.getContext(), R.string.wifi_ip_settings_invalid_ip, + Toast.LENGTH_LONG).show(); + return null; + } + } + + return config; + } + + int chosenNetworkSetupMethod() { + if (mNetworkSetupSpinner != null) { + return mNetworkSetupSpinner.getSelectedItemPosition(); + } + return MANUAL; + } + + int getWpsPin() { + try { + String wpsPin = ((TextView) mView.findViewById(R.id.wps_pin)).getText().toString(); + return Integer.parseInt(wpsPin); + } catch (NumberFormatException e) { + return -1; + } + } + + private void showSecurityFields() { + if (mAccessPointSecurity == AccessPoint.SECURITY_NONE) { + mView.findViewById(R.id.security_fields).setVisibility(View.GONE); + return; + } + mView.findViewById(R.id.security_fields).setVisibility(View.VISIBLE); + + if (mPasswordView == null) { + mPasswordView = (TextView) mView.findViewById(R.id.password); + mPasswordView.addTextChangedListener(this); + ((CheckBox) mView.findViewById(R.id.show_password)).setOnClickListener(this); + + if (mAccessPoint != null && mAccessPoint.networkId != INVALID_NETWORK_ID) { + mPasswordView.setHint(R.string.wifi_unchanged); + } + } + + if (mAccessPointSecurity != AccessPoint.SECURITY_EAP) { + mView.findViewById(R.id.eap).setVisibility(View.GONE); + return; + } + mView.findViewById(R.id.eap).setVisibility(View.VISIBLE); + + if (mEapMethodSpinner == null) { + mEapMethodSpinner = (Spinner) mView.findViewById(R.id.method); + mPhase2Spinner = (Spinner) mView.findViewById(R.id.phase2); + mEapCaCertSpinner = (Spinner) mView.findViewById(R.id.ca_cert); + mEapUserCertSpinner = (Spinner) mView.findViewById(R.id.user_cert); + mEapIdentityView = (TextView) mView.findViewById(R.id.identity); + mEapAnonymousView = (TextView) mView.findViewById(R.id.anonymous); + + loadCertificates(mEapCaCertSpinner, Credentials.CA_CERTIFICATE); + loadCertificates(mEapUserCertSpinner, Credentials.USER_PRIVATE_KEY); + + if (mAccessPoint != null && mAccessPoint.networkId != INVALID_NETWORK_ID) { + WifiConfiguration config = mAccessPoint.getConfig(); + setSelection(mEapMethodSpinner, config.eap.value()); + setSelection(mPhase2Spinner, config.phase2.value()); + setCertificate(mEapCaCertSpinner, Credentials.CA_CERTIFICATE, + config.ca_cert.value()); + setCertificate(mEapUserCertSpinner, Credentials.USER_PRIVATE_KEY, + config.private_key.value()); + mEapIdentityView.setText(config.identity.value()); + mEapAnonymousView.setText(config.anonymous_identity.value()); + } + } + } + + private void showNetworkSetupFields() { + mView.findViewById(R.id.setup_fields).setVisibility(View.VISIBLE); + + if (mNetworkSetupSpinner == null) { + mNetworkSetupSpinner = (Spinner) mView.findViewById(R.id.network_setup); + mNetworkSetupSpinner.setOnItemSelectedListener(this); + } + + int pos = mNetworkSetupSpinner.getSelectedItemPosition(); + + /* Show pin text input if needed */ + if (pos == WPS_PIN) { + mView.findViewById(R.id.wps_fields).setVisibility(View.VISIBLE); + } else { + mView.findViewById(R.id.wps_fields).setVisibility(View.GONE); + } + + /* show/hide manual security fields appropriately */ + if ((pos == WPS_PIN) || (pos == WPS_PBC)) { + mView.findViewById(R.id.security_fields).setVisibility(View.GONE); + } else { + mView.findViewById(R.id.security_fields).setVisibility(View.VISIBLE); + } + + } + + private void showIpConfigFields() { + WifiConfiguration config = null; + + mView.findViewById(R.id.ip_fields).setVisibility(View.VISIBLE); + + if (mIpSettingsSpinner == null) { + mIpSettingsSpinner = (Spinner) mView.findViewById(R.id.ip_settings); + mIpSettingsSpinner.setOnItemSelectedListener(this); + } + + if (mAccessPoint != null && mAccessPoint.networkId != INVALID_NETWORK_ID) { + config = mAccessPoint.getConfig(); + } + + if (mIpSettingsSpinner.getSelectedItemPosition() == STATIC_IP) { + mView.findViewById(R.id.staticip).setVisibility(View.VISIBLE); + if (mIpAddressView == null) { + mIpAddressView = (TextView) mView.findViewById(R.id.ipaddress); + mGatewayView = (TextView) mView.findViewById(R.id.gateway); + mNetmaskView = (TextView) mView.findViewById(R.id.netmask); + mDns1View = (TextView) mView.findViewById(R.id.dns1); + mDns2View = (TextView) mView.findViewById(R.id.dns2); + } + if (config != null) { + DhcpInfo ipConfig = config.ipConfig; + if (ipConfig != null && ipConfig.ipAddress != 0) { + mIpAddressView.setText(intToIpString(ipConfig.ipAddress)); + mGatewayView.setText(intToIpString(ipConfig.gateway)); + mNetmaskView.setText(intToIpString(ipConfig.netmask)); + mDns1View.setText(intToIpString(ipConfig.dns1)); + mDns2View.setText(intToIpString(ipConfig.dns2)); + } + } + } else { + mView.findViewById(R.id.staticip).setVisibility(View.GONE); + } + } + + + private void loadCertificates(Spinner spinner, String prefix) { + final Context context = mConfigUi.getContext(); + final String unspecified = context.getString(R.string.wifi_unspecified); + + String[] certs = KeyStore.getInstance().saw(prefix); + if (certs == null || certs.length == 0) { + certs = new String[] {unspecified}; + } else { + final String[] array = new String[certs.length + 1]; + array[0] = unspecified; + System.arraycopy(certs, 0, array, 1, certs.length); + certs = array; + } + + final ArrayAdapter<String> adapter = new ArrayAdapter<String>( + context, android.R.layout.simple_spinner_item, certs); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinner.setAdapter(adapter); + } + + private void setCertificate(Spinner spinner, String prefix, String cert) { + prefix = KEYSTORE_SPACE + prefix; + if (cert != null && cert.startsWith(prefix)) { + setSelection(spinner, cert.substring(prefix.length())); + } + } + + private void setSelection(Spinner spinner, String value) { + if (value != null) { + ArrayAdapter<String> adapter = (ArrayAdapter<String>) spinner.getAdapter(); + for (int i = adapter.getCount() - 1; i >= 0; --i) { + if (value.equals(adapter.getItem(i))) { + spinner.setSelection(i); + break; + } + } + } + } + + @Override + public void afterTextChanged(Editable s) { + enableSubmitIfAppropriate(); + } + + @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(View view) { + mPasswordView.setInputType( + InputType.TYPE_CLASS_TEXT | (((CheckBox) view).isChecked() ? + InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD : + InputType.TYPE_TEXT_VARIATION_PASSWORD)); + } + + @Override + public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { + if (parent == mSecuritySpinner) { + mAccessPointSecurity = position; + showSecurityFields(); + enableSubmitIfAppropriate(); + } else if (parent == mNetworkSetupSpinner){ + showNetworkSetupFields(); + } else { + showIpConfigFields(); + } + } + + @Override + public void onNothingSelected(AdapterView<?> parent) { + } + + /* TODO: should go away when we move to IPv6 based config storage */ + private static int stringToIpAddr(String addrString) throws UnknownHostException { + try { + String[] parts = addrString.split("\\."); + if (parts.length != 4) { + throw new UnknownHostException(addrString); + } + + int a = Integer.parseInt(parts[0]); + int b = Integer.parseInt(parts[1]) << 8; + int c = Integer.parseInt(parts[2]) << 16; + int d = Integer.parseInt(parts[3]) << 24; + + return a | b | c | d; + } catch (NumberFormatException e) { + throw new UnknownHostException(addrString); + } + } + + private static String intToIpString(int i) { + return (i & 0xFF) + "." + ((i >> 8 ) & 0xFF) + "." +((i >> 16 ) & 0xFF) + "." + + ((i >> 24 ) & 0xFF); + } + +} diff --git a/src/com/android/settings/wifi/WifiConfigPreference.java b/src/com/android/settings/wifi/WifiConfigPreference.java new file mode 100644 index 000000000..5a0982771 --- /dev/null +++ b/src/com/android/settings/wifi/WifiConfigPreference.java @@ -0,0 +1,129 @@ +/* + * 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.wifi; + +import com.android.settings.R; + +import android.content.Context; +import android.content.DialogInterface; +import android.preference.Preference; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; + +/** + * Preference letting users modify a setting for Wifi network. This work as an alternative UI + * for {@link WifiDialog} without shouwing popup Dialog. + */ +public class WifiConfigPreference extends Preference implements WifiConfigUiBase { + private WifiSettings mWifiSettings; + private View mView; + private final DialogInterface.OnClickListener mListener; + private WifiConfigController mController; + private AccessPoint mAccessPoint; + private boolean mEdit; + + private LayoutInflater mInflater; + + public WifiConfigPreference(WifiSettings wifiSettings, + DialogInterface.OnClickListener listener, + AccessPoint accessPoint, boolean edit) { + super(wifiSettings.getActivity()); + mWifiSettings = wifiSettings; + setLayoutResource(R.layout.wifi_config_preference); + mListener = listener; + mAccessPoint = accessPoint; + mEdit = edit; + mInflater = (LayoutInflater) + wifiSettings.getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + } + + @Override + protected View onCreateView(ViewGroup parent) { + // Called every time the list is created. + if (mView != null) { + // TODO: we need to re-forcus something. + return mView; + } + mView = mInflater.inflate(getLayoutResource(), parent, false); + mController = new WifiConfigController(this, mView, mAccessPoint, mEdit, mListener); + return mView; + } + + @Override + public WifiConfigController getController() { + return mController; + } + + public View findViewById(int id) { + return mView.findViewById(id); + } + + public AccessPoint getAccessPoint() { + return mAccessPoint; + } + + @Override + public boolean isEdit() { + return mEdit; + } + + @Override + public LayoutInflater getLayoutInflater() { + return mInflater; + } + + @Override + public Button getSubmitButton() { + return (Button)mWifiSettings.getActivity().findViewById(R.id.wifi_setup_connect); + } + + @Override + public Button getForgetButton() { + return (Button)mWifiSettings.getActivity().findViewById(R.id.wifi_setup_forget); + } + + @Override + public Button getCancelButton() { + return (Button)mWifiSettings.getActivity().findViewById(R.id.wifi_setup_cancel); + } + + @Override + public void setSubmitButton(CharSequence text) { + final Button button = (Button) + mWifiSettings.getActivity().findViewById(R.id.wifi_setup_connect); + button.setVisibility(View.VISIBLE); + + // test + mWifiSettings.getActivity().findViewById(R.id.wifi_setup_forget).setVisibility(View.GONE); + } + + @Override + public void setForgetButton(CharSequence text) { + final Button button = (Button) + mWifiSettings.getActivity().findViewById(R.id.wifi_setup_forget); + button.setVisibility(View.VISIBLE); + } + + @Override + public void setCancelButton(CharSequence text) { + final Button button = (Button) + mWifiSettings.getActivity().findViewById(R.id.wifi_setup_cancel); + button.setVisibility(View.VISIBLE); + } +}
\ No newline at end of file diff --git a/src/com/android/settings/wifi/WifiConfigUiBase.java b/src/com/android/settings/wifi/WifiConfigUiBase.java new file mode 100644 index 000000000..e17d49103 --- /dev/null +++ b/src/com/android/settings/wifi/WifiConfigUiBase.java @@ -0,0 +1,42 @@ +/* + * 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.wifi; + +import android.content.Context; +import android.view.LayoutInflater; +import android.widget.Button; + +/** + * Foundation interface glues between Activities and UIs like + * {@link WifiDialog} or {@link WifiConfigController}. + */ +public interface WifiConfigUiBase { + public Context getContext(); + public WifiConfigController getController(); + public LayoutInflater getLayoutInflater(); + public boolean isEdit(); + + public void setTitle(int id); + public void setTitle(CharSequence title); + + public void setSubmitButton(CharSequence text); + public void setForgetButton(CharSequence text); + public void setCancelButton(CharSequence text); + public Button getSubmitButton(); + public Button getForgetButton(); + public Button getCancelButton(); +}
\ No newline at end of file diff --git a/src/com/android/settings/wifi/WifiDialog.java b/src/com/android/settings/wifi/WifiDialog.java index a8bf717ca..b6356159d 100644 --- a/src/com/android/settings/wifi/WifiDialog.java +++ b/src/com/android/settings/wifi/WifiDialog.java @@ -21,149 +21,32 @@ import com.android.settings.R; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; -import android.content.res.Resources; -import android.net.NetworkInfo.DetailedState; -import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiConfiguration.AuthAlgorithm; -import android.net.wifi.WifiConfiguration.KeyMgmt; -import android.net.wifi.WifiInfo; import android.os.Bundle; -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.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.CheckBox; -import android.widget.Spinner; -import android.widget.TextView; - -class WifiDialog extends AlertDialog implements View.OnClickListener, - TextWatcher, AdapterView.OnItemSelectedListener { - private static final String KEYSTORE_SPACE = "keystore://"; +import android.widget.Button; +class WifiDialog extends AlertDialog implements WifiConfigUiBase { static final int BUTTON_SUBMIT = DialogInterface.BUTTON_POSITIVE; static final int BUTTON_FORGET = DialogInterface.BUTTON_NEUTRAL; - final boolean edit; + private final boolean mEdit; private final DialogInterface.OnClickListener mListener; private final AccessPoint mAccessPoint; private View mView; - private TextView mSsid; - private int mSecurity; - private TextView mPassword; - - private Spinner mEapMethod; - private Spinner mEapCaCert; - private Spinner mPhase2; - private Spinner mEapUserCert; - private TextView mEapIdentity; - private TextView mEapAnonymous; - - static boolean requireKeyStore(WifiConfiguration config) { - String values[] = {config.ca_cert.value(), config.client_cert.value(), - config.private_key.value()}; - for (String value : values) { - if (value != null && value.startsWith(KEYSTORE_SPACE)) { - return true; - } - } - return false; - } + private WifiConfigController mController; - WifiDialog(Context context, DialogInterface.OnClickListener listener, + public WifiDialog(Context context, DialogInterface.OnClickListener listener, AccessPoint accessPoint, boolean edit) { super(context); - this.edit = edit; + mEdit = edit; mListener = listener; mAccessPoint = accessPoint; - mSecurity = (accessPoint == null) ? AccessPoint.SECURITY_NONE : accessPoint.security; } - WifiConfiguration getConfig() { - if (mAccessPoint != null && mAccessPoint.networkId != -1 && !edit) { - return null; - } - - WifiConfiguration config = new WifiConfiguration(); - - if (mAccessPoint == null) { - config.SSID = AccessPoint.convertToQuotedString( - mSsid.getText().toString()); - // If the user adds a network manually, assume that it is hidden. - config.hiddenSSID = true; - } else if (mAccessPoint.networkId == -1) { - config.SSID = AccessPoint.convertToQuotedString( - mAccessPoint.ssid); - } else { - config.networkId = mAccessPoint.networkId; - } - - switch (mSecurity) { - case AccessPoint.SECURITY_NONE: - config.allowedKeyManagement.set(KeyMgmt.NONE); - return config; - - case AccessPoint.SECURITY_WEP: - config.allowedKeyManagement.set(KeyMgmt.NONE); - config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN); - config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED); - if (mPassword.length() != 0) { - int length = mPassword.length(); - String password = mPassword.getText().toString(); - // WEP-40, WEP-104, and 256-bit WEP (WEP-232?) - if ((length == 10 || length == 26 || length == 58) && - password.matches("[0-9A-Fa-f]*")) { - config.wepKeys[0] = password; - } else { - config.wepKeys[0] = '"' + password + '"'; - } - } - return config; - - case AccessPoint.SECURITY_PSK: - config.allowedKeyManagement.set(KeyMgmt.WPA_PSK); - if (mPassword.length() != 0) { - String password = mPassword.getText().toString(); - if (password.matches("[0-9A-Fa-f]{64}")) { - config.preSharedKey = password; - } else { - config.preSharedKey = '"' + password + '"'; - } - } - return config; - - case AccessPoint.SECURITY_EAP: - config.allowedKeyManagement.set(KeyMgmt.WPA_EAP); - config.allowedKeyManagement.set(KeyMgmt.IEEE8021X); - config.eap.setValue((String) mEapMethod.getSelectedItem()); - - config.phase2.setValue((mPhase2.getSelectedItemPosition() == 0) ? "" : - "auth=" + mPhase2.getSelectedItem()); - config.ca_cert.setValue((mEapCaCert.getSelectedItemPosition() == 0) ? "" : - KEYSTORE_SPACE + Credentials.CA_CERTIFICATE + - (String) mEapCaCert.getSelectedItem()); - config.client_cert.setValue((mEapUserCert.getSelectedItemPosition() == 0) ? "" : - KEYSTORE_SPACE + Credentials.USER_CERTIFICATE + - (String) mEapUserCert.getSelectedItem()); - config.private_key.setValue((mEapUserCert.getSelectedItemPosition() == 0) ? "" : - KEYSTORE_SPACE + Credentials.USER_PRIVATE_KEY + - (String) mEapUserCert.getSelectedItem()); - config.identity.setValue((mEapIdentity.length() == 0) ? "" : - mEapIdentity.getText().toString()); - config.anonymous_identity.setValue((mEapAnonymous.length() == 0) ? "" : - mEapAnonymous.getText().toString()); - if (mPassword.length() != 0) { - config.password.setValue(mPassword.getText().toString()); - } - return config; - } - return null; + @Override + public WifiConfigController getController() { + return mController; } @Override @@ -171,200 +54,42 @@ class WifiDialog extends AlertDialog implements View.OnClickListener, mView = getLayoutInflater().inflate(R.layout.wifi_dialog, null); setView(mView); setInverseBackgroundForced(true); - - Context context = getContext(); - Resources resources = context.getResources(); - - if (mAccessPoint == null) { - setTitle(R.string.wifi_add_network); - mView.findViewById(R.id.type).setVisibility(View.VISIBLE); - mSsid = (TextView) mView.findViewById(R.id.ssid); - mSsid.addTextChangedListener(this); - ((Spinner) mView.findViewById(R.id.security)).setOnItemSelectedListener(this); - setButton(BUTTON_SUBMIT, context.getString(R.string.wifi_save), mListener); - } else { - setTitle(mAccessPoint.ssid); - ViewGroup group = (ViewGroup) mView.findViewById(R.id.info); - - DetailedState state = mAccessPoint.getState(); - if (state != null) { - addRow(group, R.string.wifi_status, Summary.get(getContext(), state)); - } - - String[] type = resources.getStringArray(R.array.wifi_security); - addRow(group, R.string.wifi_security, type[mAccessPoint.security]); - - int level = mAccessPoint.getLevel(); - if (level != -1) { - String[] signal = resources.getStringArray(R.array.wifi_signal); - addRow(group, R.string.wifi_signal, signal[level]); - } - - WifiInfo info = mAccessPoint.getInfo(); - if (info != null) { - addRow(group, R.string.wifi_speed, info.getLinkSpeed() + WifiInfo.LINK_SPEED_UNITS); - // TODO: fix the ip address for IPv6. - int address = info.getIpAddress(); - if (address != 0) { - addRow(group, R.string.wifi_ip_address, Formatter.formatIpAddress(address)); - } - } - - if (mAccessPoint.networkId == -1 || edit) { - showSecurityFields(); - } - - if (edit) { - setButton(BUTTON_SUBMIT, context.getString(R.string.wifi_save), mListener); - } else { - if (state == null && level != -1) { - setButton(BUTTON_SUBMIT, context.getString(R.string.wifi_connect), mListener); - } - if (mAccessPoint.networkId != -1) { - setButton(BUTTON_FORGET, context.getString(R.string.wifi_forget), mListener); - } - } - } - - setButton(DialogInterface.BUTTON_NEGATIVE, - context.getString(R.string.wifi_cancel), mListener); - + mController = new WifiConfigController(this, mView, mAccessPoint, mEdit, mListener); super.onCreate(savedInstanceState); - - if (getButton(BUTTON_SUBMIT) != null) { - validate(); - } } - private void addRow(ViewGroup group, int name, String value) { - View row = getLayoutInflater().inflate(R.layout.wifi_dialog_row, group, false); - ((TextView) row.findViewById(R.id.name)).setText(name); - ((TextView) row.findViewById(R.id.value)).setText(value); - group.addView(row); - } - - private void validate() { - // TODO: make sure this is complete. - if ((mSsid != null && mSsid.length() == 0) || - ((mAccessPoint == null || mAccessPoint.networkId == -1) && - ((mSecurity == AccessPoint.SECURITY_WEP && mPassword.length() == 0) || - (mSecurity == AccessPoint.SECURITY_PSK && mPassword.length() < 8)))) { - getButton(BUTTON_SUBMIT).setEnabled(false); - } else { - getButton(BUTTON_SUBMIT).setEnabled(true); - } - } - - public void onClick(View view) { - mPassword.setInputType( - InputType.TYPE_CLASS_TEXT | (((CheckBox) view).isChecked() ? - InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD : - InputType.TYPE_TEXT_VARIATION_PASSWORD)); - } - - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } - - public void afterTextChanged(Editable editable) { - validate(); + @Override + public boolean isEdit() { + return mEdit; } - public void onItemSelected(AdapterView parent, View view, int position, long id) { - mSecurity = position; - showSecurityFields(); - validate(); + @Override + public Button getSubmitButton() { + return getButton(BUTTON_SUBMIT); } - public void onNothingSelected(AdapterView parent) { + @Override + public Button getForgetButton() { + return getButton(BUTTON_FORGET); } - private void showSecurityFields() { - if (mSecurity == AccessPoint.SECURITY_NONE) { - mView.findViewById(R.id.fields).setVisibility(View.GONE); - return; - } - mView.findViewById(R.id.fields).setVisibility(View.VISIBLE); - - if (mPassword == null) { - mPassword = (TextView) mView.findViewById(R.id.password); - mPassword.addTextChangedListener(this); - ((CheckBox) mView.findViewById(R.id.show_password)).setOnClickListener(this); - - if (mAccessPoint != null && mAccessPoint.networkId != -1) { - mPassword.setHint(R.string.wifi_unchanged); - } - } - - if (mSecurity != AccessPoint.SECURITY_EAP) { - mView.findViewById(R.id.eap).setVisibility(View.GONE); - return; - } - mView.findViewById(R.id.eap).setVisibility(View.VISIBLE); - - if (mEapMethod == null) { - mEapMethod = (Spinner) mView.findViewById(R.id.method); - mPhase2 = (Spinner) mView.findViewById(R.id.phase2); - mEapCaCert = (Spinner) mView.findViewById(R.id.ca_cert); - mEapUserCert = (Spinner) mView.findViewById(R.id.user_cert); - mEapIdentity = (TextView) mView.findViewById(R.id.identity); - mEapAnonymous = (TextView) mView.findViewById(R.id.anonymous); - - loadCertificates(mEapCaCert, Credentials.CA_CERTIFICATE); - loadCertificates(mEapUserCert, Credentials.USER_PRIVATE_KEY); - - if (mAccessPoint != null && mAccessPoint.networkId != -1) { - WifiConfiguration config = mAccessPoint.getConfig(); - setSelection(mEapMethod, config.eap.value()); - setSelection(mPhase2, config.phase2.value()); - setCertificate(mEapCaCert, Credentials.CA_CERTIFICATE, - config.ca_cert.value()); - setCertificate(mEapUserCert, Credentials.USER_PRIVATE_KEY, - config.private_key.value()); - mEapIdentity.setText(config.identity.value()); - mEapAnonymous.setText(config.anonymous_identity.value()); - } - } + @Override + public Button getCancelButton() { + return getButton(BUTTON_NEGATIVE); } - private void loadCertificates(Spinner spinner, String prefix) { - String[] certs = KeyStore.getInstance().saw(prefix); - Context context = getContext(); - String unspecified = context.getString(R.string.wifi_unspecified); - - if (certs == null || certs.length == 0) { - certs = new String[] {unspecified}; - } else { - String[] array = new String[certs.length + 1]; - array[0] = unspecified; - System.arraycopy(certs, 0, array, 1, certs.length); - certs = array; - } - - ArrayAdapter<String> adapter = new ArrayAdapter<String>( - context, android.R.layout.simple_spinner_item, certs); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - spinner.setAdapter(adapter); + @Override + public void setSubmitButton(CharSequence text) { + setButton(BUTTON_SUBMIT, text, mListener); } - private void setCertificate(Spinner spinner, String prefix, String cert) { - prefix = KEYSTORE_SPACE + prefix; - if (cert != null && cert.startsWith(prefix)) { - setSelection(spinner, cert.substring(prefix.length())); - } + @Override + public void setForgetButton(CharSequence text) { + setButton(BUTTON_FORGET, text, mListener); } - private void setSelection(Spinner spinner, String value) { - if (value != null) { - ArrayAdapter<String> adapter = (ArrayAdapter<String>) spinner.getAdapter(); - for (int i = adapter.getCount() - 1; i >= 0; --i) { - if (value.equals(adapter.getItem(i))) { - spinner.setSelection(i); - break; - } - } - } + @Override + public void setCancelButton(CharSequence text) { + setButton(BUTTON_NEGATIVE, text, mListener); } } diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java index d389cae14..a0b2132c2 100644 --- a/src/com/android/settings/wifi/WifiSettings.java +++ b/src/com/android/settings/wifi/WifiSettings.java @@ -16,21 +16,24 @@ package com.android.settings.wifi; -import com.android.settings.ProgressCategory; +import com.android.settings.ProgressCategoryBase; import com.android.settings.R; +import com.android.settings.SettingsPreferenceFragment; +import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; +import android.net.ConnectivityManager; import android.net.NetworkInfo; 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.WifiConfiguration.Status; +import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Bundle; @@ -38,30 +41,58 @@ import android.os.Handler; import android.os.Message; import android.preference.CheckBoxPreference; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceScreen; import android.provider.Settings.Secure; import android.security.Credentials; import android.security.KeyStore; -import android.text.TextUtils; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.Menu; +import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView.AdapterContextMenuInfo; +import android.widget.Button; import android.widget.Toast; -import java.util.ArrayList; +import java.util.Collection; import java.util.List; +import java.util.TreeSet; -public class WifiSettings extends PreferenceActivity implements DialogInterface.OnClickListener { +/** + * This currently provides three types of UI. + * + * Two are for phones with relatively small screens: "for SetupWizard" and "for usual Settings". + * Users just need to launch WifiSettings Activity as usual. The request will be appropriately + * handled by ActivityManager, and they will have appropriate look-and-feel with this fragment. + * + * Third type is for Setup Wizard with X-Large, landscape UI. Users need to launch + * {@link WifiSettingsForSetupWizardXL} Activity, which contains this fragment but also has + * other decorations specific to that screen. + */ +public class WifiSettings extends SettingsPreferenceFragment + implements DialogInterface.OnClickListener { 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; + // Indicates that this fragment is used as a part of Setup Wizard with XL screen settings. + // This fragment should show information which has been shown as Dialog in combined UI + // inside this fragment. + /* package */ static final String IN_XL_SETUP_WIZARD = "in_setup_wizard"; + + // this boolean extra specifies whether to disable the Next button when not connected + // Note: this is only effective in Setup Wizard with XL screen size. + private static final String EXTRA_ENABLE_NEXT_ON_CONNECT = "wifi_enable_next_on_connect"; + + // In SetupWizard XL, We limit the number of showable access points so that the + // ListView won't become larger than the screen. + // + // This constant doesn't affect other contexts other than SetupWizard XL. + private static int MAX_MENU_COUNT_IN_XL = 7; + private final IntentFilter mFilter; private final BroadcastReceiver mReceiver; private final Scanner mScanner; @@ -69,25 +100,38 @@ public class WifiSettings extends PreferenceActivity implements DialogInterface. private WifiManager mWifiManager; private WifiEnabler mWifiEnabler; private CheckBoxPreference mNotifyOpenNetworks; - private ProgressCategory mAccessPoints; + private ProgressCategoryBase mAccessPoints; private Preference mAddNetwork; + // An access point being editted is stored here. + private AccessPoint mSelectedAccessPoint; private DetailedState mLastState; private WifiInfo mLastInfo; - private int mLastPriority; - private boolean mResetNetworks = false; - private int mKeyStoreNetworkId = -1; + private int mKeyStoreNetworkId = INVALID_NETWORK_ID; + + // should Next button only be enabled when we have a connection? + private boolean mEnableNextOnConnection; + private boolean mInXlSetupWizard; - private AccessPoint mSelected; + + // TODO: merge into one + private WifiConfigPreference mConfigPreference; private WifiDialog mDialog; + // Used only in SetupWizard XL, which remembers the network a user selected and + // refrain other available networks when trying to connect it. + private AccessPoint mConnectingAccessPoint; + + private boolean mRefrainListUpdate; + public WifiSettings() { mFilter = new IntentFilter(); mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); mFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION); mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION); + mFilter.addAction(WifiManager.SUPPLICANT_CONFIG_CHANGED_ACTION); mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION); @@ -102,16 +146,40 @@ public class WifiSettings extends PreferenceActivity implements DialogInterface. } @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + public void onActivityCreated(Bundle savedInstanceState) { + // We don't call super.onActivityCreated() here, since it assumes we already set up + // Preference (probably in onCreate()), while WifiSettings exceptionally set it up in + // this method. mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); - if (getIntent().getBooleanExtra("only_access_points", false)) { + final Activity activity = getActivity(); + final Intent intent = activity.getIntent(); + + mInXlSetupWizard = intent.getBooleanExtra(IN_XL_SETUP_WIZARD, false); + + // if we're supposed to enable/disable the Next button based on our current connection + // state, start it off in the right state + mEnableNextOnConnection = intent.getBooleanExtra(EXTRA_ENABLE_NEXT_ON_CONNECT, false); + + if (mEnableNextOnConnection) { + if (mEnableNextOnConnection && 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(this, + mWifiEnabler = new WifiEnabler(activity, (CheckBoxPreference) findPreference("enable_wifi")); mNotifyOpenNetworks = (CheckBoxPreference) findPreference("notify_open_networks"); @@ -119,50 +187,85 @@ public class WifiSettings extends PreferenceActivity implements DialogInterface. Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0) == 1); } - mAccessPoints = (ProgressCategory) findPreference("access_points"); - mAccessPoints.setOrderingAsAdded(false); + // After confirming PreferenceScreen is available, we call super. + super.onActivityCreated(savedInstanceState); + + // This may be either ProgressCategory or AccessPointCategoryForXL. + final ProgressCategoryBase preference = + (ProgressCategoryBase) findPreference("access_points"); + mAccessPoints = preference; + mAccessPoints.setOrderingAsAdded(true); mAddNetwork = findPreference("add_network"); registerForContextMenu(getListView()); + setHasOptionsMenu(true); } @Override - protected void onResume() { + public void onResume() { super.onResume(); if (mWifiEnabler != null) { mWifiEnabler.resume(); } - registerReceiver(mReceiver, mFilter); - if (mKeyStoreNetworkId != -1 && KeyStore.getInstance().test() == KeyStore.NO_ERROR) { - connect(mKeyStoreNetworkId); + getActivity().registerReceiver(mReceiver, mFilter); + if (mKeyStoreNetworkId != INVALID_NETWORK_ID && + KeyStore.getInstance().test() == KeyStore.NO_ERROR) { + mWifiManager.connectNetwork(mKeyStoreNetworkId); + } + mKeyStoreNetworkId = INVALID_NETWORK_ID; + if (mInXlSetupWizard) { + // We show "Now scanning" + final int wifiState = mWifiManager.getWifiState(); + switch (wifiState) { + case WifiManager.WIFI_STATE_ENABLED: { + updateAccessPoints(); + break; + } + case WifiManager.WIFI_STATE_DISABLED: + case WifiManager.WIFI_STATE_DISABLING: + case WifiManager.WIFI_STATE_UNKNOWN: { + mWifiManager.setWifiEnabled(true); + } // $FALL-THROUGH$ + default: { + mAccessPoints.removeAll(); + Preference preference = new Preference(getActivity()); + preference.setLayoutResource(R.layout.preference_widget_shortcut); + preference.setSelectable(false); + preference.setTitle("Connecting"); + preference.setSummary("COONNECTING"); + mAccessPoints.addPreference(preference); + break; + } + } + } else { + updateAccessPoints(); } - mKeyStoreNetworkId = -1; } @Override - protected void onPause() { + public void onPause() { super.onPause(); if (mWifiEnabler != null) { mWifiEnabler.pause(); } - unregisterReceiver(mReceiver); + getActivity().unregisterReceiver(mReceiver); mScanner.pause(); if (mDialog != null) { mDialog.dismiss(); mDialog = null; } - if (mResetNetworks) { - enableNetworks(); - } } @Override - public boolean onCreateOptionsMenu(Menu menu) { - menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.wifi_menu_scan) - .setIcon(R.drawable.ic_menu_scan_network); - menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced) - .setIcon(android.R.drawable.ic_menu_manage); - return super.onCreateOptionsMenu(menu); + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + // We don't want menus in Setup Wizard XL. + if (!mInXlSetupWizard) { + menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.wifi_menu_scan) + .setIcon(R.drawable.ic_menu_scan_network); + menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced) + .setIcon(android.R.drawable.ic_menu_manage); + } + super.onCreateOptionsMenu(menu, inflater); } @Override @@ -174,7 +277,7 @@ public class WifiSettings extends PreferenceActivity implements DialogInterface. } return true; case MENU_ID_ADVANCED: - startActivity(new Intent(this, AdvancedSettings.class)); + startFragment(this, AdvancedSettings.class.getCanonicalName(), -1, null); return true; } return super.onOptionsItemSelected(item); @@ -187,16 +290,15 @@ public class WifiSettings extends PreferenceActivity implements DialogInterface. ((AdapterContextMenuInfo) info).position); if (preference instanceof AccessPoint) { - mSelected = (AccessPoint) preference; - menu.setHeaderTitle(mSelected.ssid); - if (mSelected.getLevel() != -1 && mSelected.getState() == null) { + mSelectedAccessPoint = (AccessPoint) preference; + menu.setHeaderTitle(mSelectedAccessPoint.ssid); + if (mSelectedAccessPoint.getLevel() != -1 + && mSelectedAccessPoint.getState() == null) { menu.add(Menu.NONE, MENU_ID_CONNECT, 0, R.string.wifi_menu_connect); } - if (mSelected.networkId != -1) { + if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) { menu.add(Menu.NONE, MENU_ID_FORGET, 0, R.string.wifi_menu_forget); - if (mSelected.security != AccessPoint.SECURITY_NONE) { - menu.add(Menu.NONE, MENU_ID_MODIFY, 0, R.string.wifi_menu_modify); - } + menu.add(Menu.NONE, MENU_ID_MODIFY, 0, R.string.wifi_menu_modify); } } } @@ -204,33 +306,34 @@ public class WifiSettings extends PreferenceActivity implements DialogInterface. @Override public boolean onContextItemSelected(MenuItem item) { - if (mSelected == null) { + if (mSelectedAccessPoint == null) { return super.onContextItemSelected(item); } switch (item.getItemId()) { - case MENU_ID_CONNECT: - if (mSelected.networkId != -1) { - if (!requireKeyStore(mSelected.getConfig())) { - connect(mSelected.networkId); + case MENU_ID_CONNECT: { + if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) { + if (!requireKeyStore(mSelectedAccessPoint.getConfig())) { + mWifiManager.connectNetwork(mSelectedAccessPoint.networkId); } - } else if (mSelected.security == AccessPoint.SECURITY_NONE) { + } else if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE) { // Shortcut for open networks. WifiConfiguration config = new WifiConfiguration(); - config.SSID = AccessPoint.convertToQuotedString(mSelected.ssid); + config.SSID = AccessPoint.convertToQuotedString(mSelectedAccessPoint.ssid); config.allowedKeyManagement.set(KeyMgmt.NONE); - int networkId = mWifiManager.addNetwork(config); - mWifiManager.enableNetwork(networkId, false); - connect(networkId); + mWifiManager.connectNetwork(config); } else { - showDialog(mSelected, false); + showConfigUi(mSelectedAccessPoint, true); } return true; - case MENU_ID_FORGET: - forget(mSelected.networkId); + } + case MENU_ID_FORGET: { + mWifiManager.forgetNetwork(mSelectedAccessPoint.networkId); return true; - case MENU_ID_MODIFY: - showDialog(mSelected, true); + } + case MENU_ID_MODIFY: { + showConfigUi(mSelectedAccessPoint, true); return true; + } } return super.onContextItemSelected(item); } @@ -238,11 +341,10 @@ public class WifiSettings extends PreferenceActivity implements DialogInterface. @Override public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) { if (preference instanceof AccessPoint) { - mSelected = (AccessPoint) preference; - showDialog(mSelected, false); + mSelectedAccessPoint = (AccessPoint) preference; + showConfigUi(mSelectedAccessPoint, false); } else if (preference == mAddNetwork) { - mSelected = null; - showDialog(null, true); + onAddNetworkPressed(); } else if (preference == mNotifyOpenNetworks) { Secure.putInt(getContentResolver(), Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, @@ -253,33 +355,54 @@ public class WifiSettings extends PreferenceActivity implements DialogInterface. return true; } - public void onClick(DialogInterface dialogInterface, int button) { - if (button == WifiDialog.BUTTON_FORGET && mSelected != null) { - forget(mSelected.networkId); - } else if (button == WifiDialog.BUTTON_SUBMIT && mDialog != null) { - WifiConfiguration config = mDialog.getConfig(); - - if (config == null) { - if (mSelected != null && !requireKeyStore(mSelected.getConfig())) { - connect(mSelected.networkId); - } - } else if (config.networkId != -1) { - if (mSelected != null) { - mWifiManager.updateNetwork(config); - saveNetworks(); - } - } else { - int networkId = mWifiManager.addNetwork(config); - if (networkId != -1) { - mWifiManager.enableNetwork(networkId, false); - config.networkId = networkId; - if (mDialog.edit || requireKeyStore(config)) { - saveNetworks(); - } else { - connect(networkId); - } - } - } + /** + * Called when a user clicks "Add network" preference or relevant button. + */ + private void showConfigUi(AccessPoint accessPoint, boolean edit) { + synchronized (this) { + mRefrainListUpdate = false; + } + if (mInXlSetupWizard) { + final Activity activity = getActivity(); + activity.findViewById(R.id.wifi_setup_connect).setVisibility(View.VISIBLE); + activity.findViewById(R.id.wifi_setup_cancel).setVisibility(View.VISIBLE); + showConfigPreference(accessPoint, edit); + } else { + showDialog(accessPoint, edit); + } + } + + private void showConfigPreference(AccessPoint accessPoint, boolean edit) { + // We don't want to show more than one WifiConfigPreference + if (mConfigPreference != null) { + mAccessPoints.removePreference(mConfigPreference); + } + + mConfigPreference = new WifiConfigPreference(this, this, accessPoint, edit); + toggleButtonsVisibility(false); + final Activity activity = getActivity(); + if (activity instanceof WifiSettingsForSetupWizardXL) { + ((WifiSettingsForSetupWizardXL)activity).onWifiConfigPreferenceAttached(edit); + } + updateAccessPoints(); + mScanner.pause(); + } + + private void toggleButtonsVisibility(boolean firstLayout) { + final Activity activity = getActivity(); + if (firstLayout) { + activity.findViewById(R.id.wifi_setup_add_network).setVisibility(View.VISIBLE); + activity.findViewById(R.id.wifi_setup_refresh_list).setVisibility(View.VISIBLE); + activity.findViewById(R.id.wifi_setup_skip_or_next).setVisibility(View.VISIBLE); + activity.findViewById(R.id.wifi_setup_connect).setVisibility(View.GONE); + activity.findViewById(R.id.wifi_setup_forget).setVisibility(View.GONE); + activity.findViewById(R.id.wifi_setup_cancel).setVisibility(View.GONE); + } else { + activity.findViewById(R.id.wifi_setup_add_network).setVisibility(View.GONE); + activity.findViewById(R.id.wifi_setup_refresh_list).setVisibility(View.GONE); + activity.findViewById(R.id.wifi_setup_skip_or_next).setVisibility(View.GONE); + + // made visible from controller. } } @@ -287,99 +410,70 @@ public class WifiSettings extends PreferenceActivity implements DialogInterface. if (mDialog != null) { mDialog.dismiss(); } - mDialog = new WifiDialog(this, this, accessPoint, edit); + mDialog = new WifiDialog(getActivity(), this, accessPoint, edit); mDialog.show(); } private boolean requireKeyStore(WifiConfiguration config) { - if (WifiDialog.requireKeyStore(config) && + if (WifiConfigController.requireKeyStore(config) && KeyStore.getInstance().test() != KeyStore.NO_ERROR) { mKeyStoreNetworkId = config.networkId; - Credentials.getInstance().unlock(this); + Credentials.getInstance().unlock(getActivity()); return true; } return false; } - private void forget(int networkId) { - mWifiManager.removeNetwork(networkId); - saveNetworks(); - } - - private void connect(int networkId) { - if (networkId == -1) { - return; - } - - // Reset the priority of each network if it goes too high. - if (mLastPriority > 1000000) { - for (int i = mAccessPoints.getPreferenceCount() - 1; i >= 0; --i) { - AccessPoint accessPoint = (AccessPoint) mAccessPoints.getPreference(i); - if (accessPoint.networkId != -1) { - WifiConfiguration config = new WifiConfiguration(); - config.networkId = accessPoint.networkId; - config.priority = 0; - mWifiManager.updateNetwork(config); - } + /** + * Shows the latest access points available with supplimental information like + * the strength of network and the security for it. + */ + private void updateAccessPoints() { + synchronized (this) { + if (mRefrainListUpdate) { + return; } - mLastPriority = 0; } - // Set to the highest priority and save the configuration. - WifiConfiguration config = new WifiConfiguration(); - config.networkId = networkId; - config.priority = ++mLastPriority; - mWifiManager.updateNetwork(config); - saveNetworks(); - - // Connect to network by disabling others. - mWifiManager.enableNetwork(networkId, true); - mWifiManager.reconnect(); - mResetNetworks = true; - } - - private void enableNetworks() { - for (int i = mAccessPoints.getPreferenceCount() - 1; i >= 0; --i) { - WifiConfiguration config = ((AccessPoint) mAccessPoints.getPreference(i)).getConfig(); - if (config != null && config.status != Status.ENABLED) { - mWifiManager.enableNetwork(config.networkId, false); + mAccessPoints.removeAll(); + if (mConnectingAccessPoint != null) { + mAccessPoints.addPreference(mConnectingAccessPoint); + } else if (mConfigPreference != null) { + final AccessPoint parent = mConfigPreference.getAccessPoint(); + if (parent != null) { + parent.setSelectable(false); + mAccessPoints.addPreference(parent); + } + mAccessPoints.addPreference(mConfigPreference); + } else { + // AccessPoints are automatically sorted with TreeSet. + final Collection<AccessPoint> accessPoints = constructAccessPoints(); + + int count = MAX_MENU_COUNT_IN_XL; + for (AccessPoint accessPoint : accessPoints) { + mAccessPoints.addPreference(accessPoint); + count--; + if (count <= 0) { + break; + } } } - mResetNetworks = false; } - private void saveNetworks() { - // Always save the configuration with all networks enabled. - enableNetworks(); - mWifiManager.saveConfiguration(); - updateAccessPoints(); - } - - private void updateAccessPoints() { - List<AccessPoint> accessPoints = new ArrayList<AccessPoint>(); + private Collection<AccessPoint> constructAccessPoints() { + Collection<AccessPoint> accessPoints = + new TreeSet<AccessPoint>(new AccessPoint.Comparater()); - List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks(); + final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks(); if (configs != null) { - mLastPriority = 0; for (WifiConfiguration config : configs) { - if (config.priority > mLastPriority) { - mLastPriority = config.priority; - } - - // Shift the status to make enableNetworks() more efficient. - if (config.status == Status.CURRENT) { - config.status = Status.ENABLED; - } else if (mResetNetworks && config.status == Status.DISABLED) { - config.status = Status.CURRENT; - } - - AccessPoint accessPoint = new AccessPoint(this, config); + AccessPoint accessPoint = new AccessPoint(getActivity(), config); accessPoint.update(mLastInfo, mLastState); accessPoints.add(accessPoint); } } - List<ScanResult> results = mWifiManager.getScanResults(); + final List<ScanResult> results = mWifiManager.getScanResults(); if (results != null) { for (ScanResult result : results) { // Ignore hidden and ad-hoc networks. @@ -395,15 +489,12 @@ public class WifiSettings extends PreferenceActivity implements DialogInterface. } } if (!found) { - accessPoints.add(new AccessPoint(this, result)); + accessPoints.add(new AccessPoint(getActivity(), result)); } } } - mAccessPoints.removeAll(); - for (AccessPoint accessPoint : accessPoints) { - mAccessPoints.addPreference(accessPoint); - } + return accessPoints; } private void handleEvent(Intent intent) { @@ -411,19 +502,17 @@ public class WifiSettings extends PreferenceActivity implements DialogInterface. if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) { updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN)); - } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) { - updateAccessPoints(); - } else if (WifiManager.NETWORK_IDS_CHANGED_ACTION.equals(action)) { - if (mSelected != null && mSelected.networkId != -1) { - mSelected = null; - } - updateAccessPoints(); + } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action) || + WifiManager.SUPPLICANT_CONFIG_CHANGED_ACTION.equals(action)) { + updateAccessPoints(); } else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) { updateConnectionState(WifiInfo.getDetailedStateOf((SupplicantState) intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE))); } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) { - updateConnectionState(((NetworkInfo) intent.getParcelableExtra( - WifiManager.EXTRA_NETWORK_INFO)).getDetailedState()); + NetworkInfo info = (NetworkInfo) intent.getParcelableExtra( + WifiManager.EXTRA_NETWORK_INFO); + changeNextButtonState(info.isConnected()); + updateConnectionState(info.getDetailedState()); } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) { updateConnectionState(null); } @@ -448,20 +537,23 @@ public class WifiSettings extends PreferenceActivity implements DialogInterface. } for (int i = mAccessPoints.getPreferenceCount() - 1; i >= 0; --i) { - ((AccessPoint) mAccessPoints.getPreference(i)).update(mLastInfo, mLastState); + // Maybe there's a WifiConfigPreference + Preference preference = mAccessPoints.getPreference(i); + if (preference instanceof AccessPoint) { + final AccessPoint accessPoint = (AccessPoint) preference; + accessPoint.update(mLastInfo, mLastState); + } } - if (mResetNetworks && (state == DetailedState.CONNECTED || - state == DetailedState.DISCONNECTED || state == DetailedState.FAILED)) { - updateAccessPoints(); - enableNetworks(); + final Activity activity = getActivity(); + if (activity instanceof WifiSettingsForSetupWizardXL) { + ((WifiSettingsForSetupWizardXL)activity).updateConnectionState(mLastState); } } private void updateWifiState(int state) { if (state == WifiManager.WIFI_STATE_ENABLED) { mScanner.resume(); - updateAccessPoints(); } else { mScanner.pause(); mAccessPoints.removeAll(); @@ -472,6 +564,9 @@ public class WifiSettings extends PreferenceActivity implements DialogInterface. private int mRetry = 0; void resume() { + synchronized (WifiSettings.this) { + mRefrainListUpdate = false; + } if (!hasMessages(0)) { sendEmptyMessage(0); } @@ -480,6 +575,9 @@ public class WifiSettings extends PreferenceActivity implements DialogInterface. void pause() { mRetry = 0; mAccessPoints.setProgress(false); + synchronized (WifiSettings.this) { + mRefrainListUpdate = true; + } removeMessages(0); } @@ -489,12 +587,132 @@ public class WifiSettings extends PreferenceActivity implements DialogInterface. mRetry = 0; } else if (++mRetry >= 3) { mRetry = 0; - Toast.makeText(WifiSettings.this, R.string.wifi_fail_to_scan, + Toast.makeText(getActivity(), R.string.wifi_fail_to_scan, Toast.LENGTH_LONG).show(); return; } mAccessPoints.setProgress(mRetry != 0); - sendEmptyMessageDelayed(0, 6000); + // Combo scans can take 5-6s to complete. Increase interval to 10s. + sendEmptyMessageDelayed(0, 10000); + } + } + + private void changeNextButtonState(boolean wifiAvailable) { + if (mInXlSetupWizard) { + final Button button = + (Button)getActivity().findViewById(R.id.wifi_setup_skip_or_next); + button.setEnabled(true); + if (wifiAvailable) { + button.setText(R.string.wifi_setup_next); + } else { + button.setText(R.string.wifi_setup_skip); + } + } else if (mEnableNextOnConnection && hasNextButton()) { + // Assumes layout for phones has next button inside it. + getNextButton().setEnabled(wifiAvailable); + } + } + + public void onClick(DialogInterface dialogInterface, int button) { + if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) { + forget(); + } else if (button == WifiDialog.BUTTON_SUBMIT) { + submit(); + } + } + + /* package */ void submit() { + final WifiConfigUiBase uiBase = (mDialog != null ? mDialog : mConfigPreference); + final WifiConfigController configController = uiBase.getController(); + + switch(configController.chosenNetworkSetupMethod()) { + case WifiConfigController.WPS_PBC: + mWifiManager.startWpsPbc(mSelectedAccessPoint.bssid); + break; + case WifiConfigController.WPS_PIN: + int apPin = configController.getWpsPin(); + mWifiManager.startWpsPin(mSelectedAccessPoint.bssid, apPin); + break; + case WifiConfigController.MANUAL: + final WifiConfiguration config = configController.getConfig(); + + if (config == null) { + if (mSelectedAccessPoint != null + && !requireKeyStore(mSelectedAccessPoint.getConfig())) { + mWifiManager.connectNetwork(mSelectedAccessPoint.networkId); + } + } else if (config.networkId != INVALID_NETWORK_ID) { + if (mSelectedAccessPoint != null) { + mWifiManager.saveNetwork(config); + } + } else { + if (uiBase.isEdit() || requireKeyStore(config)) { + mWifiManager.saveNetwork(config); + } else { + mWifiManager.connectNetwork(config); + } + } + break; + } + + if (mInXlSetupWizard && mConfigPreference != null) { + mConnectingAccessPoint = mSelectedAccessPoint; + mConnectingAccessPoint.setSelectable(false); + } + + detachConfigPreference(); + } + + /* package */ void forget() { + mWifiManager.forgetNetwork(mSelectedAccessPoint.networkId); + + detachConfigPreference(); + + changeNextButtonState(false); + + final Activity activity = getActivity(); + if (activity instanceof WifiSettingsForSetupWizardXL) { + ((WifiSettingsForSetupWizardXL)activity).onForget(); + } + } + + /* package */ void refreshAccessPoints() { + if (mWifiManager.isWifiEnabled()) { + mScanner.resume(); + } + + mConfigPreference = null; + mConnectingAccessPoint = null; + mAccessPoints.removeAll(); + + final Activity activity = getActivity(); + if (activity instanceof WifiSettingsForSetupWizardXL) { + ((WifiSettingsForSetupWizardXL)activity).onRefreshAccessPoints(); + } + } + + /* package */ void detachConfigPreference() { + if (mConfigPreference != null) { + if (mWifiManager.isWifiEnabled()) { + mScanner.resume(); + } + mAccessPoints.removePreference(mConfigPreference); + mConfigPreference = null; + updateAccessPoints(); + toggleButtonsVisibility(true); + } + } + + /* package */ void onAddNetworkPressed() { + mSelectedAccessPoint = null; + showConfigUi(null, true); + } + + /* package */ int getAccessPointsCount() { + if (mAccessPoints != null) { + return mAccessPoints.getPreferenceCount(); + } else { + return 0; } } } diff --git a/src/com/android/settings/wifi/WifiSettingsForSetupWizardXL.java b/src/com/android/settings/wifi/WifiSettingsForSetupWizardXL.java new file mode 100644 index 000000000..4870bfe5f --- /dev/null +++ b/src/com/android/settings/wifi/WifiSettingsForSetupWizardXL.java @@ -0,0 +1,209 @@ +/* + * 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.wifi; + +import com.android.settings.R; + +import android.app.Activity; +import android.app.StatusBarManager; +import android.content.Context; +import android.net.NetworkInfo.DetailedState; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.Window; +import android.widget.Button; +import android.widget.ProgressBar; +import android.widget.TextView; + +import java.util.EnumMap; + +/** + * WifiSetings Activity specific for SetupWizard with X-Large screen size. + */ +public class WifiSettingsForSetupWizardXL extends Activity implements OnClickListener { + private static final String TAG = WifiSettingsForSetupWizardXL.class.getSimpleName(); + + private static final EnumMap<DetailedState, DetailedState> stateMap = + new EnumMap<DetailedState, DetailedState>(DetailedState.class); + + static { + stateMap.put(DetailedState.IDLE, DetailedState.DISCONNECTED); + stateMap.put(DetailedState.SCANNING, DetailedState.SCANNING); + stateMap.put(DetailedState.CONNECTING, DetailedState.CONNECTING); + stateMap.put(DetailedState.AUTHENTICATING, DetailedState.CONNECTING); + stateMap.put(DetailedState.OBTAINING_IPADDR, DetailedState.CONNECTING); + stateMap.put(DetailedState.CONNECTED, DetailedState.CONNECTED); + stateMap.put(DetailedState.SUSPENDED, DetailedState.SUSPENDED); // ? + stateMap.put(DetailedState.DISCONNECTING, DetailedState.DISCONNECTED); + stateMap.put(DetailedState.DISCONNECTED, DetailedState.DISCONNECTED); + stateMap.put(DetailedState.FAILED, DetailedState.DISCONNECTED); + } + + private TextView mProgressText; + private ProgressBar mProgressBar; + private WifiSettings mWifiSettings; + private TextView mStatusText; + + private StatusBarManager mStatusBarManager; + + // This count reduces every time when there's a notification about WiFi status change. + // During the term this is >0, The system shows the message "connecting", regardless + // of the actual WiFi status. After this count's becoming 0, the status message correctly + // reflects what WiFi Picker told it. This is a tweak for letting users not confused + // with instable WiFi state during the first scan. + private int mIgnoringWifiNotificationCount = 5; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + setContentView(R.layout.wifi_settings_for_setup_wizard_xl); + mWifiSettings = + (WifiSettings)getFragmentManager().findFragmentById(R.id.wifi_setup_fragment); + setup(); + // XXX: should we use method? + getIntent().putExtra(WifiSettings.IN_XL_SETUP_WIZARD, true); + + mStatusBarManager = (StatusBarManager)getSystemService(Context.STATUS_BAR_SERVICE); + if (mStatusBarManager != null) { + mStatusBarManager.disable(StatusBarManager.DISABLE_EXPAND + | StatusBarManager.DISABLE_NOTIFICATION_ICONS + | StatusBarManager.DISABLE_NOTIFICATION_ALERTS + | StatusBarManager.DISABLE_SYSTEM_INFO + | StatusBarManager.DISABLE_NAVIGATION); + } else { + Log.e(TAG, "StatusBarManager isn't available."); + } + } + + @Override + public void onDestroy() { + if (mStatusBarManager != null) { + mStatusBarManager.disable(StatusBarManager.DISABLE_NONE); + } + super.onDestroy(); + } + + public void setup() { + mProgressText = (TextView)findViewById(R.id.scanning_progress_text); + mProgressText.setText(Summary.get(this, DetailedState.SCANNING)); + mProgressBar = (ProgressBar)findViewById(R.id.scanning_progress_bar); + mProgressBar.setMax(2); + mProgressBar.setIndeterminate(true); + mStatusText = (TextView)findViewById(R.id.wifi_setup_status); + + ((Button)findViewById(R.id.wifi_setup_refresh_list)).setOnClickListener(this); + ((Button)findViewById(R.id.wifi_setup_add_network)).setOnClickListener(this); + ((Button)findViewById(R.id.wifi_setup_skip_or_next)).setOnClickListener(this); + ((Button)findViewById(R.id.wifi_setup_connect)).setOnClickListener(this); + ((Button)findViewById(R.id.wifi_setup_forget)).setOnClickListener(this); + ((Button)findViewById(R.id.wifi_setup_cancel)).setOnClickListener(this); + } + + @Override + public void onClick(View view) { + final int id = view.getId(); + switch (id) { + case R.id.wifi_setup_refresh_list: + mWifiSettings.refreshAccessPoints(); + break; + case R.id.wifi_setup_add_network: + mWifiSettings.onAddNetworkPressed(); + break; + case R.id.wifi_setup_skip_or_next: + setResult(Activity.RESULT_OK); + finish(); + break; + case R.id.wifi_setup_connect: + mWifiSettings.submit(); + break; + case R.id.wifi_setup_forget: + mWifiSettings.forget(); + break; + case R.id.wifi_setup_cancel: + mStatusText.setText(R.string.wifi_setup_status_select_network); + mWifiSettings.detachConfigPreference(); + break; + } + } + + // Called from WifiSettings + public void updateConnectionState(DetailedState originalState) { + final DetailedState state = stateMap.get(originalState); + 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) { + mProgressBar.setIndeterminate(true); + mProgressText.setText(Summary.get(this, DetailedState.SCANNING)); + } else { + // Users already already connected to a network, or see available networks. + mProgressBar.setIndeterminate(false); + } + break; + } + case CONNECTING: { + mProgressBar.setIndeterminate(false); + mProgressBar.setProgress(1); + mStatusText.setText(R.string.wifi_setup_status_connecting); + mProgressText.setText(Summary.get(this, state)); + break; + } + case CONNECTED: { + mProgressBar.setIndeterminate(false); + mProgressBar.setProgress(2); + mStatusText.setText(R.string.wifi_setup_status_connected); + mProgressText.setText(Summary.get(this, state)); + setResult(Activity.RESULT_OK); + finish(); + break; + } + default: // Not connected. + if (mWifiSettings.getAccessPointsCount() == 0 && + mIgnoringWifiNotificationCount > 0) { + mIgnoringWifiNotificationCount--; + mProgressBar.setIndeterminate(true); + mProgressText.setText(Summary.get(this, DetailedState.SCANNING)); + return; + } else { + mProgressBar.setIndeterminate(false); + mProgressBar.setProgress(0); + mStatusText.setText(R.string.wifi_setup_status_select_network); + mProgressText.setText(getString(R.string.wifi_setup_not_connected)); + } + break; + } + } + + public void onWifiConfigPreferenceAttached(boolean isNewNetwork) { + mStatusText.setText(R.string.wifi_setup_status_edit_network); + } + + public void onForget() { + mProgressBar.setIndeterminate(false); + mProgressBar.setProgress(0); + mProgressText.setText(getString(R.string.wifi_setup_not_connected)); + } + + public void onRefreshAccessPoints() { + mProgressBar.setIndeterminate(true); + mProgressText.setText(Summary.get(this, DetailedState.SCANNING)); + } +} |