diff options
Diffstat (limited to 'src/com/android')
110 files changed, 6086 insertions, 3665 deletions
diff --git a/src/com/android/settings/ButtonBarHandler.java b/src/com/android/settings/ButtonBarHandler.java index d61da1336..85e39d1a1 100644 --- a/src/com/android/settings/ButtonBarHandler.java +++ b/src/com/android/settings/ButtonBarHandler.java @@ -19,7 +19,7 @@ import android.widget.Button; /** * Interface letting {@link SettingsPreferenceFragment} access to bottom bar inside - * {@link android.preference.PreferenceActivity}. + * {@link SettingsActivity}. */ public interface ButtonBarHandler { public boolean hasNextButton(); diff --git a/src/com/android/settings/ChooseLockGeneric.java b/src/com/android/settings/ChooseLockGeneric.java index 49de36686..9c9fab1ff 100644 --- a/src/com/android/settings/ChooseLockGeneric.java +++ b/src/com/android/settings/ChooseLockGeneric.java @@ -17,7 +17,6 @@ package com.android.settings; import android.app.Activity; -import android.app.Fragment; import android.app.PendingIntent; import android.app.admin.DevicePolicyManager; import android.content.Context; @@ -28,7 +27,6 @@ import android.os.Process; import android.os.UserHandle; import android.os.UserManager; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceScreen; import android.security.KeyStore; import android.util.EventLog; @@ -38,13 +36,12 @@ import android.view.ViewGroup; import android.widget.ListView; import com.android.internal.widget.LockPatternUtils; -import com.android.settings.ConfirmLockPattern.ConfirmLockPatternFragment; import java.util.List; import libcore.util.MutableBoolean; -public class ChooseLockGeneric extends PreferenceActivity { +public class ChooseLockGeneric extends SettingsActivity { @Override public Intent getIntent() { @@ -229,20 +226,7 @@ public class ChooseLockGeneric extends PreferenceActivity { private int upgradeQuality(int quality, MutableBoolean allowBiometric) { quality = upgradeQualityForDPM(quality); quality = upgradeQualityForKeyStore(quality); - int encryptionQuality = upgradeQualityForEncryption(quality); - if (encryptionQuality > quality) { - //The first case checks whether biometric is allowed, prior to the user making - //their selection from the list - if (allowBiometric != null) { - allowBiometric.value = quality <= - DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK; - } else if (quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) { - //When the user has selected biometric we shouldn't change that due to - //encryption - return quality; - } - } - return encryptionQuality; + return quality; } private int upgradeQualityForDPM(int quality) { @@ -254,27 +238,6 @@ public class ChooseLockGeneric extends PreferenceActivity { return quality; } - /** - * Mix in "encryption minimums" to any given quality value. This prevents users - * from downgrading the pattern/pin/password to a level below the minimums. - * - * ASSUMPTION: Setting quality is sufficient (e.g. minimum lengths will be set - * appropriately.) - */ - private int upgradeQualityForEncryption(int quality) { - // Don't upgrade quality for secondary users. Encryption requirements don't apply. - if (!Process.myUserHandle().equals(UserHandle.OWNER)) return quality; - int encryptionStatus = mDPM.getStorageEncryptionStatus(); - boolean encrypted = (encryptionStatus == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE) - || (encryptionStatus == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVATING); - if (encrypted) { - if (quality < CryptKeeperSettings.MIN_PASSWORD_QUALITY) { - quality = CryptKeeperSettings.MIN_PASSWORD_QUALITY; - } - } - return quality; - } - private int upgradeQualityForKeyStore(int quality) { if (!mKeyStore.isEmpty()) { if (quality < CredentialStorage.MIN_PASSWORD_QUALITY) { diff --git a/src/com/android/settings/ChooseLockPassword.java b/src/com/android/settings/ChooseLockPassword.java index f43738f04..4d102a46c 100644 --- a/src/com/android/settings/ChooseLockPassword.java +++ b/src/com/android/settings/ChooseLockPassword.java @@ -19,7 +19,6 @@ package com.android.settings; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.PasswordEntryKeyboardHelper; import com.android.internal.widget.PasswordEntryKeyboardView; -import com.android.settings.ChooseLockGeneric.ChooseLockGenericFragment; import android.app.Activity; import android.app.Fragment; @@ -29,7 +28,6 @@ import android.inputmethodservice.KeyboardView; import android.os.Bundle; import android.os.Handler; import android.os.Message; -import android.preference.PreferenceActivity; import android.text.Editable; import android.text.InputType; import android.text.Selection; @@ -41,13 +39,12 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.View.OnClickListener; -import android.view.accessibility.AccessibilityEvent; import android.view.inputmethod.EditorInfo; import android.widget.Button; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; -public class ChooseLockPassword extends PreferenceActivity { +public class ChooseLockPassword extends SettingsActivity { 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"; @@ -79,7 +76,7 @@ public class ChooseLockPassword extends PreferenceActivity { //WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); super.onCreate(savedInstanceState); CharSequence msg = getText(R.string.lockpassword_choose_your_password_header); - showBreadCrumbs(msg, msg); + setTitle(msg); } public static class ChooseLockPasswordFragment extends Fragment @@ -137,9 +134,6 @@ public class ChooseLockPassword extends PreferenceActivity { R.string.lockpassword_confirm_pins_dont_match, R.string.lockpassword_continue_label); - /** - * @param headerMessage The message displayed at the top. - */ Stage(int hintInAlpha, int hintInNumeric, int nextButtonText) { this.alphaHint = hintInAlpha; this.numericHint = hintInNumeric; @@ -235,13 +229,12 @@ public class ChooseLockPassword extends PreferenceActivity { updateStage(mUiStage); } } - // Update the breadcrumb (title) if this is embedded in a PreferenceActivity - if (activity instanceof PreferenceActivity) { - final PreferenceActivity preferenceActivity = (PreferenceActivity) activity; + if (activity instanceof SettingsActivity) { + final SettingsActivity sa = (SettingsActivity) activity; int id = mIsAlphaMode ? R.string.lockpassword_choose_your_password_header : R.string.lockpassword_choose_your_pin_header; CharSequence title = getText(id); - preferenceActivity.showBreadCrumbs(title, title); + sa.setTitle(title); } return view; diff --git a/src/com/android/settings/ChooseLockPattern.java b/src/com/android/settings/ChooseLockPattern.java index 328312c2f..5a939aac5 100644 --- a/src/com/android/settings/ChooseLockPattern.java +++ b/src/com/android/settings/ChooseLockPattern.java @@ -22,7 +22,6 @@ import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternView; import com.android.internal.widget.LockPatternView.Cell; -import com.android.settings.ChooseLockGeneric.ChooseLockGenericFragment; import static com.android.internal.widget.LockPatternView.DisplayMode; @@ -30,7 +29,6 @@ import android.app.Activity; import android.app.Fragment; import android.content.Intent; import android.os.Bundle; -import android.preference.PreferenceActivity; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; @@ -49,7 +47,7 @@ import java.util.List; * - asks for confirmation / restart * - saves chosen password when confirmed */ -public class ChooseLockPattern extends PreferenceActivity { +public class ChooseLockPattern extends SettingsActivity { /** * Used by the choose lock pattern wizard to indicate the wizard is * finished, and each activity in the wizard should finish. @@ -80,7 +78,7 @@ public class ChooseLockPattern extends PreferenceActivity { // requestWindowFeature(Window.FEATURE_NO_TITLE); super.onCreate(savedInstanceState); CharSequence msg = getText(R.string.lockpassword_choose_your_pattern_header); - showBreadCrumbs(msg, msg); + setTitle(msg); } @Override diff --git a/src/com/android/settings/ConfirmLockPassword.java b/src/com/android/settings/ConfirmLockPassword.java index d7402daff..72da15674 100644 --- a/src/com/android/settings/ConfirmLockPassword.java +++ b/src/com/android/settings/ConfirmLockPassword.java @@ -19,7 +19,6 @@ package com.android.settings; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.PasswordEntryKeyboardHelper; import com.android.internal.widget.PasswordEntryKeyboardView; -import com.android.settings.ChooseLockGeneric.ChooseLockGenericFragment; import android.app.Activity; import android.app.Fragment; @@ -27,7 +26,6 @@ import android.app.admin.DevicePolicyManager; import android.content.Intent; import android.os.Bundle; import android.os.Handler; -import android.preference.PreferenceActivity; import android.text.Editable; import android.text.InputType; import android.text.TextWatcher; @@ -36,13 +34,12 @@ import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; -import android.view.accessibility.AccessibilityEvent; import android.view.inputmethod.EditorInfo; import android.widget.Button; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; -public class ConfirmLockPassword extends PreferenceActivity { +public class ConfirmLockPassword extends SettingsActivity { @Override public Intent getIntent() { @@ -65,7 +62,7 @@ public class ConfirmLockPassword extends PreferenceActivity { //WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); super.onCreate(savedInstanceState); CharSequence msg = getText(R.string.lockpassword_confirm_your_password_header); - showBreadCrumbs(msg, msg); + setTitle(msg); } public static class ConfirmLockPasswordFragment extends Fragment implements OnClickListener, @@ -127,13 +124,12 @@ public class ConfirmLockPassword extends PreferenceActivity { mPasswordEntry.setInputType(isAlpha ? currentType : (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD)); - // Update the breadcrumb (title) if this is embedded in a PreferenceActivity - if (activity instanceof PreferenceActivity) { - final PreferenceActivity preferenceActivity = (PreferenceActivity) activity; + if (activity instanceof SettingsActivity) { + final SettingsActivity sa = (SettingsActivity) activity; int id = isAlpha ? R.string.lockpassword_confirm_your_password_header : R.string.lockpassword_confirm_your_pin_header; CharSequence title = getText(id); - preferenceActivity.showBreadCrumbs(title, title); + sa.setTitle(title); } return view; diff --git a/src/com/android/settings/ConfirmLockPattern.java b/src/com/android/settings/ConfirmLockPattern.java index 3a1f06c33..81b325b7c 100644 --- a/src/com/android/settings/ConfirmLockPattern.java +++ b/src/com/android/settings/ConfirmLockPattern.java @@ -27,7 +27,6 @@ import android.content.Intent; import android.os.CountDownTimer; import android.os.SystemClock; import android.os.Bundle; -import android.preference.PreferenceActivity; import android.widget.TextView; import android.view.LayoutInflater; import android.view.View; @@ -41,7 +40,7 @@ import java.util.List; * Sets an activity result of {@link Activity#RESULT_OK} when the user * successfully confirmed their pattern. */ -public class ConfirmLockPattern extends PreferenceActivity { +public class ConfirmLockPattern extends SettingsActivity { /** * Names of {@link CharSequence} fields within the originating {@link Intent} @@ -65,7 +64,7 @@ public class ConfirmLockPattern extends PreferenceActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); CharSequence msg = getText(R.string.lockpassword_confirm_your_pattern_header); - showBreadCrumbs(msg, msg); + setTitle(msg); } @Override diff --git a/src/com/android/settings/CreateShortcut.java b/src/com/android/settings/CreateShortcut.java index f71df1d6f..0bf265f5f 100644 --- a/src/com/android/settings/CreateShortcut.java +++ b/src/com/android/settings/CreateShortcut.java @@ -19,7 +19,6 @@ package com.android.settings; import android.app.LauncherActivity; import android.content.Intent; import android.content.pm.ResolveInfo; -import android.os.Bundle; import android.view.View; import android.widget.ListView; diff --git a/src/com/android/settings/CryptKeeper.java b/src/com/android/settings/CryptKeeper.java index 23ec70ede..d974d005d 100644 --- a/src/com/android/settings/CryptKeeper.java +++ b/src/com/android/settings/CryptKeeper.java @@ -34,6 +34,7 @@ import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; import android.os.storage.IMountService; +import android.os.storage.StorageManager; import android.provider.Settings; import android.telephony.TelephonyManager; import android.text.Editable; @@ -60,6 +61,9 @@ import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.telephony.ITelephony; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneConstants; +import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.LockPatternView; +import com.android.internal.widget.LockPatternView.Cell; import java.util.List; @@ -113,6 +117,7 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList private int mCooldown; PowerManager.WakeLock mWakeLock; private EditText mPasswordEntry; + private LockPatternView mLockPatternView; /** Number of calls to {@link #notifyUser()} to ignore before notifying. */ private int mNotificationCountdown = 0; @@ -175,7 +180,9 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList final TextView status = (TextView) findViewById(R.id.status); status.setText(R.string.try_again); // Reenable the password entry - mPasswordEntry.setEnabled(true); + if (mPasswordEntry != null) { + mPasswordEntry.setEnabled(true); + } } } } @@ -354,7 +361,21 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList setContentView(R.layout.crypt_keeper_progress); encryptionProgressInit(); } else if (mValidationComplete || isDebugView(FORCE_VIEW_PASSWORD)) { - setContentView(R.layout.crypt_keeper_password_entry); + final IMountService service = getMountService(); + int type = StorageManager.CRYPT_TYPE_PASSWORD; + try { + type = service.getPasswordType(); + } catch (Exception e) { + Log.e(TAG, "Error while getting type - showing default dialog" + e); + } + + if(type == StorageManager.CRYPT_TYPE_PIN) { + setContentView(R.layout.crypt_keeper_pin_entry); + } else if (type == StorageManager.CRYPT_TYPE_PATTERN) { + setContentView(R.layout.crypt_keeper_pattern_entry); + } else { + setContentView(R.layout.crypt_keeper_password_entry); + } passwordEntryInit(); } else if (!mValidationRequested) { // We're supposed to be encrypted, but no validation has been done. @@ -503,14 +524,44 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList } } - private void passwordEntryInit() { + protected LockPatternView.OnPatternListener mChooseNewLockPatternListener = + new LockPatternView.OnPatternListener() { + + @Override + public void onPatternStart() { + } + + @Override + public void onPatternCleared() { + } + + @Override + public void onPatternDetected(List<LockPatternView.Cell> pattern) { + new DecryptTask().execute(LockPatternUtils.patternToString(pattern)); + } + + @Override + public void onPatternCellAdded(List<Cell> pattern) { + } + }; + + private void passwordEntryInit() { + // Password/pin case mPasswordEntry = (EditText) findViewById(R.id.passwordEntry); - mPasswordEntry.setOnEditorActionListener(this); - mPasswordEntry.requestFocus(); - // Become quiet when the user interacts with the Edit text screen. - mPasswordEntry.setOnKeyListener(this); - mPasswordEntry.setOnTouchListener(this); - mPasswordEntry.addTextChangedListener(this); + if (mPasswordEntry != null){ + mPasswordEntry.setOnEditorActionListener(this); + mPasswordEntry.requestFocus(); + // Become quiet when the user interacts with the Edit text screen. + mPasswordEntry.setOnKeyListener(this); + mPasswordEntry.setOnTouchListener(this); + mPasswordEntry.addTextChangedListener(this); + } + + // Pattern case + mLockPatternView = (LockPatternView) findViewById(R.id.lockPattern); + if (mLockPatternView != null) { + mLockPatternView.setOnPatternListener(mChooseNewLockPatternListener); + } // Disable the Emergency call button if the device has no voice telephone capability final TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); diff --git a/src/com/android/settings/CryptKeeperSettings.java b/src/com/android/settings/CryptKeeperSettings.java index 58d97a82f..cedf53034 100644 --- a/src/com/android/settings/CryptKeeperSettings.java +++ b/src/com/android/settings/CryptKeeperSettings.java @@ -30,7 +30,6 @@ import android.content.res.Resources; import android.os.BatteryManager; import android.os.Bundle; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; @@ -201,6 +200,6 @@ public class CryptKeeperSettings extends Fragment { preference.setFragment(CryptKeeperConfirm.class.getName()); preference.setTitle(R.string.crypt_keeper_confirm_title); preference.getExtras().putString("password", password); - ((PreferenceActivity) getActivity()).onPreferenceStartFragment(null, preference); + ((SettingsActivity) getActivity()).onPreferenceStartFragment(null, preference); } } diff --git a/src/com/android/settings/DataUsageSummary.java b/src/com/android/settings/DataUsageSummary.java index db1ae29db..396c76a31 100644 --- a/src/com/android/settings/DataUsageSummary.java +++ b/src/com/android/settings/DataUsageSummary.java @@ -87,8 +87,6 @@ import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; import android.preference.Preference; -import android.preference.PreferenceActivity; -import android.provider.Settings; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.text.format.DateUtils; @@ -548,8 +546,8 @@ public class DataUsageSummary extends Fragment { return true; } case R.id.data_usage_menu_metered: { - final PreferenceActivity activity = (PreferenceActivity) getActivity(); - activity.startPreferencePanel(DataUsageMeteredSettings.class.getCanonicalName(), null, + final SettingsActivity sa = (SettingsActivity) getActivity(); + sa.startPreferencePanel(DataUsageMeteredSettings.class.getCanonicalName(), null, R.string.data_usage_metered_title, null, this, 0); return true; } @@ -575,11 +573,6 @@ public class DataUsageSummary extends Fragment { TrafficStats.closeQuietly(mStatsSession); - if (this.isRemoving()) { - getFragmentManager() - .popBackStack(TAG_APP_DETAILS, FragmentManager.POP_BACK_STACK_INCLUSIVE); - } - super.onDestroy(); } @@ -886,14 +879,16 @@ public class DataUsageSummary extends Fragment { private boolean getDataRoaming() { final ContentResolver resolver = getActivity().getContentResolver(); - return Settings.Global.getInt(resolver, Settings.Global.DATA_ROAMING, 0) != 0; + return android.provider.Settings.Global.getInt(resolver, + android.provider.Settings.Global.DATA_ROAMING, 0) != 0; } private void setDataRoaming(boolean enabled) { // TODO: teach telephony DataConnectionTracker to watch and apply // updates when changed. final ContentResolver resolver = getActivity().getContentResolver(); - Settings.Global.putInt(resolver, Settings.Global.DATA_ROAMING, enabled ? 1 : 0); + android.provider.Settings.Global.putInt(resolver, + android.provider.Settings.Global.DATA_ROAMING, enabled ? 1 : 0); mMenuDataRoaming.setChecked(enabled); } diff --git a/src/com/android/settings/DevelopmentSettings.java b/src/com/android/settings/DevelopmentSettings.java index d971bd224..15d5a5fc3 100644 --- a/src/com/android/settings/DevelopmentSettings.java +++ b/src/com/android/settings/DevelopmentSettings.java @@ -59,6 +59,7 @@ import android.view.Gravity; import android.view.HardwareRenderer; import android.view.IWindowManager; import android.view.View; +import android.view.accessibility.AccessibilityManager; import android.widget.CompoundButton; import android.widget.Switch; import android.widget.TextView; @@ -113,6 +114,7 @@ public class DevelopmentSettings extends RestrictedSettingsFragment private static final String SHOW_TOUCHES_KEY = "show_touches"; private static final String SHOW_SCREEN_UPDATES_KEY = "show_screen_updates"; private static final String DISABLE_OVERLAYS_KEY = "disable_overlays"; + private static final String SIMULATE_COLOR_SPACE = "simulate_color_space"; private static final String SHOW_CPU_USAGE_KEY = "show_cpu_usage"; private static final String FORCE_HARDWARE_UI_KEY = "force_hw_ui"; private static final String FORCE_MSAA_KEY = "force_msaa"; @@ -139,6 +141,8 @@ public class DevelopmentSettings extends RestrictedSettingsFragment private static final String SHOW_ALL_ANRS_KEY = "show_all_anrs"; + private static final String PROCESS_STATS = "proc_stats"; + private static final String TAG_CONFIRM_ENFORCE = "confirm_enforce"; private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive"; @@ -192,12 +196,15 @@ public class DevelopmentSettings extends RestrictedSettingsFragment private ListPreference mAnimatorDurationScale; private ListPreference mOverlayDisplayDevices; private ListPreference mOpenGLTraces; + private ListPreference mSimulateColorSpace; private CheckBoxPreference mImmediatelyDestroyActivities; private ListPreference mAppProcessLimit; private CheckBoxPreference mShowAllANRs; + private PreferenceScreen mProcessStats; + private final ArrayList<Preference> mAllPrefs = new ArrayList<Preference>(); private final ArrayList<CheckBoxPreference> mResetCbPrefs = new ArrayList<CheckBoxPreference>(); @@ -243,6 +250,7 @@ public class DevelopmentSettings extends RestrictedSettingsFragment debugDebuggingCategory.removePreference(mClearAdbKeys); } } + mAllPrefs.add(mClearAdbKeys); mEnableTerminal = findAndInitCheckboxPref(ENABLE_TERMINAL); if (!isPackageInstalled(getActivity(), TERMINAL_APP_PACKAGE)) { debugDebuggingCategory.removePreference(mEnableTerminal); @@ -296,11 +304,13 @@ public class DevelopmentSettings extends RestrictedSettingsFragment mAnimatorDurationScale = addListPreference(ANIMATOR_DURATION_SCALE_KEY); mOverlayDisplayDevices = addListPreference(OVERLAY_DISPLAY_DEVICES_KEY); mOpenGLTraces = addListPreference(OPENGL_TRACES_KEY); + mSimulateColorSpace = addListPreference(SIMULATE_COLOR_SPACE); mImmediatelyDestroyActivities = (CheckBoxPreference) findPreference( IMMEDIATELY_DESTROY_ACTIVITIES_KEY); mAllPrefs.add(mImmediatelyDestroyActivities); mResetCbPrefs.add(mImmediatelyDestroyActivities); + mAppProcessLimit = addListPreference(APP_PROCESS_LIMIT_KEY); mShowAllANRs = (CheckBoxPreference) findPreference( @@ -319,6 +329,9 @@ public class DevelopmentSettings extends RestrictedSettingsFragment mAllPrefs.add(hdcpChecking); removePreferenceForProduction(hdcpChecking); } + + mProcessStats = (PreferenceScreen) findPreference(PROCESS_STATS); + mAllPrefs.add(mProcessStats); } private ListPreference addListPreference(String prefKey) { @@ -497,6 +510,7 @@ public class DevelopmentSettings extends RestrictedSettingsFragment updateBugreportOptions(); updateForceRtlOptions(); updateWifiDisplayCertificationOptions(); + updateSimulateColorSpace(); } private void resetDangerousOptions() { @@ -933,6 +947,40 @@ public class DevelopmentSettings extends RestrictedSettingsFragment pokeSystemProperties(); } + private void updateSimulateColorSpace() { + final ContentResolver cr = getContentResolver(); + final boolean enabled = Settings.Secure.getInt( + cr, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0) != 0; + if (enabled) { + final String mode = Integer.toString(Settings.Secure.getInt( + cr, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, + AccessibilityManager.DALTONIZER_DISABLED)); + mSimulateColorSpace.setValue(mode); + final int index = mSimulateColorSpace.findIndexOfValue(mode); + if (index < 0) { + // We're using a mode controlled by accessibility preferences. + mSimulateColorSpace.setSummary(getString(R.string.daltonizer_type_overridden, + getString(R.string.accessibility_display_daltonizer_preference_title))); + } else { + mSimulateColorSpace.setSummary("%s"); + } + } else { + mSimulateColorSpace.setValue( + Integer.toString(AccessibilityManager.DALTONIZER_DISABLED)); + } + } + + private void writeSimulateColorSpace(Object value) { + final ContentResolver cr = getContentResolver(); + final int newMode = Integer.parseInt(value.toString()); + if (newMode < 0) { + Settings.Secure.putInt(cr, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0); + } else { + Settings.Secure.putInt(cr, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 1); + Settings.Secure.putInt(cr, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, newMode); + } + } + private void updateForceRtlOptions() { updateCheckBox(mForceRtlLayout, Settings.Global.getInt(getActivity().getContentResolver(), Settings.Global.DEVELOPMENT_FORCE_RTL, 0) != 0); @@ -1311,6 +1359,9 @@ public class DevelopmentSettings extends RestrictedSettingsFragment } else if (preference == mAppProcessLimit) { writeAppProcessLimitOptions(newValue); return true; + } else if (preference == mSimulateColorSpace) { + writeSimulateColorSpace(newValue); + return true; } return false; } diff --git a/src/com/android/settings/DeviceAdminAdd.java b/src/com/android/settings/DeviceAdminAdd.java index 623403800..8bfdf927c 100644 --- a/src/com/android/settings/DeviceAdminAdd.java +++ b/src/com/android/settings/DeviceAdminAdd.java @@ -38,6 +38,7 @@ import android.os.Handler; import android.os.RemoteCallback; import android.os.RemoteException; import android.text.TextUtils.TruncateAt; +import android.util.EventLog; import android.util.Log; import android.view.Display; import android.view.View; @@ -55,19 +56,19 @@ import java.util.List; public class DeviceAdminAdd extends Activity { static final String TAG = "DeviceAdminAdd"; - + static final int DIALOG_WARNING = 1; private static final int MAX_ADD_MSG_LINES_PORTRAIT = 5; private static final int MAX_ADD_MSG_LINES_LANDSCAPE = 2; private static final int MAX_ADD_MSG_LINES = 15; - + Handler mHandler; - + DevicePolicyManager mDPM; DeviceAdminInfo mDeviceAdmin; CharSequence mAddMsgText; - + ImageView mAdminIcon; TextView mAdminName; TextView mAdminDescription; @@ -78,19 +79,19 @@ public class DeviceAdminAdd extends Activity { ViewGroup mAdminPolicies; Button mActionButton; Button mCancelButton; - + final ArrayList<View> mAddingPolicies = new ArrayList<View>(); final ArrayList<View> mActivePolicies = new ArrayList<View>(); - + boolean mAdding; boolean mRefreshing; - + @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); mHandler = new Handler(getMainLooper()); - + mDPM = (DevicePolicyManager)getSystemService(Context.DEVICE_POLICY_SERVICE); if ((getIntent().getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { @@ -98,7 +99,7 @@ public class DeviceAdminAdd extends Activity { finish(); return; } - + ComponentName cn = (ComponentName)getIntent().getParcelableExtra( DevicePolicyManager.EXTRA_DEVICE_ADMIN); if (cn == null) { @@ -163,7 +164,7 @@ public class DeviceAdminAdd extends Activity { finish(); return; } - + // This admin already exists, an we have two options at this point. If new policy // bits are set, show the user the new list. If nothing has changed, simply return // "OK" immediately. @@ -189,7 +190,7 @@ public class DeviceAdminAdd extends Activity { mAddMsgText = getIntent().getCharSequenceExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION); setContentView(R.layout.device_admin_add); - + mAdminIcon = (ImageView)findViewById(R.id.admin_icon); mAdminName = (TextView)findViewById(R.id.admin_name); mAdminDescription = (TextView)findViewById(R.id.admin_description); @@ -210,6 +211,8 @@ public class DeviceAdminAdd extends Activity { mCancelButton = (Button) findViewById(R.id.cancel_button); mCancelButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { + EventLog.writeEvent(EventLogTags.EXP_DET_DEVICE_ADMIN_DECLINED_BY_USER, + mDeviceAdmin.getActivityInfo().applicationInfo.uid); finish(); } }); @@ -220,6 +223,8 @@ public class DeviceAdminAdd extends Activity { try { mDPM.setActiveAdmin(mDeviceAdmin.getComponent(), mRefreshing); setResult(Activity.RESULT_OK); + EventLog.writeEvent(EventLogTags.EXP_DET_DEVICE_ADMIN_ACTIVATED_BY_USER, + mDeviceAdmin.getActivityInfo().applicationInfo.uid); } catch (RuntimeException e) { // Something bad happened... could be that it was // already set, though. @@ -264,13 +269,13 @@ public class DeviceAdminAdd extends Activity { } }); } - + @Override protected void onResume() { super.onResume(); updateInterface(); } - + @Override protected Dialog onCreateDialog(int id, Bundle args) { switch (id) { @@ -291,17 +296,17 @@ public class DeviceAdminAdd extends Activity { } default: return super.onCreateDialog(id, args); - + } } - + static void setViewVisibility(ArrayList<View> views, int visibility) { final int N = views.size(); for (int i=0; i<N; i++) { views.get(i).setVisibility(visibility); } } - + void updateInterface() { mAdminIcon.setImageDrawable(mDeviceAdmin.loadIcon(getPackageManager())); mAdminName.setText(mDeviceAdmin.loadLabel(getPackageManager())); diff --git a/src/com/android/settings/DisplaySettings.java b/src/com/android/settings/DisplaySettings.java index b0c944d23..afe5b6819 100644 --- a/src/com/android/settings/DisplaySettings.java +++ b/src/com/android/settings/DisplaySettings.java @@ -25,7 +25,9 @@ import android.content.ContentResolver; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; +import android.database.ContentObserver; import android.os.Bundle; +import android.os.Handler; import android.os.RemoteException; import android.preference.CheckBoxPreference; import android.preference.ListPreference; @@ -36,9 +38,6 @@ import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.Log; -import com.android.internal.view.RotationPolicy; -import com.android.settings.DreamSettings; - import java.util.ArrayList; public class DisplaySettings extends SettingsPreferenceFragment implements @@ -49,47 +48,26 @@ public class DisplaySettings extends SettingsPreferenceFragment implements private static final int FALLBACK_SCREEN_TIMEOUT_VALUE = 30000; private static final String KEY_SCREEN_TIMEOUT = "screen_timeout"; - private static final String KEY_ACCELEROMETER = "accelerometer"; private static final String KEY_FONT_SIZE = "font_size"; - private static final String KEY_NOTIFICATION_PULSE = "notification_pulse"; private static final String KEY_SCREEN_SAVER = "screensaver"; private static final int DLG_GLOBAL_CHANGE_WARNING = 1; - private CheckBoxPreference mAccelerometer; private WarnedListPreference mFontSizePref; - private CheckBoxPreference mNotificationPulse; private final Configuration mCurConfig = new Configuration(); + private final Handler mHandler = new Handler(); private ListPreference mScreenTimeoutPreference; private Preference mScreenSaverPreference; - private final RotationPolicy.RotationPolicyListener mRotationPolicyListener = - new RotationPolicy.RotationPolicyListener() { - @Override - public void onChange() { - updateAccelerometerRotationCheckbox(); - } - }; - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - ContentResolver resolver = getActivity().getContentResolver(); + final ContentResolver resolver = getActivity().getContentResolver(); addPreferencesFromResource(R.xml.display_settings); - mAccelerometer = (CheckBoxPreference) findPreference(KEY_ACCELEROMETER); - mAccelerometer.setPersistent(false); - if (!RotationPolicy.isRotationSupported(getActivity()) - || RotationPolicy.isRotationLockToggleSupported(getActivity())) { - // If rotation lock is supported, then we do not provide this option in - // Display settings. However, is still available in Accessibility settings, - // if the device supports rotation. - getPreferenceScreen().removePreference(mAccelerometer); - } - mScreenSaverPreference = findPreference(KEY_SCREEN_SAVER); if (mScreenSaverPreference != null && getResources().getBoolean( @@ -108,20 +86,6 @@ public class DisplaySettings extends SettingsPreferenceFragment implements mFontSizePref = (WarnedListPreference) findPreference(KEY_FONT_SIZE); mFontSizePref.setOnPreferenceChangeListener(this); mFontSizePref.setOnPreferenceClickListener(this); - mNotificationPulse = (CheckBoxPreference) findPreference(KEY_NOTIFICATION_PULSE); - if (mNotificationPulse != null - && getResources().getBoolean( - com.android.internal.R.bool.config_intrusiveNotificationLed) == false) { - getPreferenceScreen().removePreference(mNotificationPulse); - } else { - try { - mNotificationPulse.setChecked(Settings.System.getInt(resolver, - Settings.System.NOTIFICATION_LIGHT_PULSE) == 1); - mNotificationPulse.setOnPreferenceChangeListener(this); - } catch (SettingNotFoundException snfe) { - Log.e(TAG, Settings.System.NOTIFICATION_LIGHT_PULSE + " not found"); - } - } } private void updateTimeoutPreferenceDescription(long currentTimeout) { @@ -226,21 +190,10 @@ public class DisplaySettings extends SettingsPreferenceFragment implements public void onResume() { super.onResume(); - RotationPolicy.registerRotationPolicyListener(getActivity(), - mRotationPolicyListener); - updateState(); } @Override - public void onPause() { - super.onPause(); - - RotationPolicy.unregisterRotationPolicyListener(getActivity(), - mRotationPolicyListener); - } - - @Override public Dialog onCreateDialog(int dialogId) { if (dialogId == DLG_GLOBAL_CHANGE_WARNING) { return Utils.buildGlobalChangeWarningDialog(getActivity(), @@ -255,7 +208,6 @@ public class DisplaySettings extends SettingsPreferenceFragment implements } private void updateState() { - updateAccelerometerRotationCheckbox(); readFontSizePreference(mFontSizePref); updateScreenSaverSummary(); } @@ -267,12 +219,6 @@ public class DisplaySettings extends SettingsPreferenceFragment implements } } - private void updateAccelerometerRotationCheckbox() { - if (getActivity() == null) return; - - mAccelerometer.setChecked(!RotationPolicy.isRotationLocked(getActivity())); - } - public void writeFontSizePreference(Object objValue) { try { mCurConfig.fontScale = Float.parseFloat(objValue.toString()); @@ -284,15 +230,6 @@ public class DisplaySettings extends SettingsPreferenceFragment implements @Override public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { - if (preference == mAccelerometer) { - RotationPolicy.setRotationLockForAccessibility( - getActivity(), !mAccelerometer.isChecked()); - } else if (preference == mNotificationPulse) { - boolean value = mNotificationPulse.isChecked(); - Settings.System.putInt(getContentResolver(), Settings.System.NOTIFICATION_LIGHT_PULSE, - value ? 1 : 0); - return true; - } return super.onPreferenceTreeClick(preferenceScreen, preference); } diff --git a/src/com/android/settings/DreamSettings.java b/src/com/android/settings/DreamSettings.java index cb91f397a..f4e251ec3 100644 --- a/src/com/android/settings/DreamSettings.java +++ b/src/com/android/settings/DreamSettings.java @@ -26,7 +26,6 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; -import android.preference.PreferenceActivity; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; diff --git a/src/com/android/settings/EventLogTags.logtags b/src/com/android/settings/EventLogTags.logtags index 3e87c5369..b21623c11 100644 --- a/src/com/android/settings/EventLogTags.logtags +++ b/src/com/android/settings/EventLogTags.logtags @@ -4,3 +4,9 @@ option java_package com.android.settings # log the type of screen lock when user sets lock screen 90200 lock_screen_type (type|3) + +# log whether user accepted and activated device admin +90201 exp_det_device_admin_activated_by_user (app_signature|3) + +# log whether user declined activation of device admin +90202 exp_det_device_admin_declined_by_user (app_signature|3) diff --git a/src/com/android/settings/HomeSettings.java b/src/com/android/settings/HomeSettings.java index eb659e27e..8697062fd 100644 --- a/src/com/android/settings/HomeSettings.java +++ b/src/com/android/settings/HomeSettings.java @@ -23,7 +23,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; @@ -138,7 +137,7 @@ public class HomeSettings extends SettingsPreferenceFragment { if (mPrefs.size() < 2) { if (mShowNotice) { mShowNotice = false; - Settings.requestHomeNotice(); + SettingsActivity.requestHomeNotice(); } finishFragment(); } diff --git a/src/com/android/settings/MasterClear.java b/src/com/android/settings/MasterClear.java index 262aca37e..eaf29c6e7 100644 --- a/src/com/android/settings/MasterClear.java +++ b/src/com/android/settings/MasterClear.java @@ -31,7 +31,6 @@ import android.os.Environment; import android.os.SystemProperties; import android.os.UserManager; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -115,7 +114,7 @@ public class MasterClear extends Fragment { preference.setFragment(MasterClearConfirm.class.getName()); preference.setTitle(R.string.master_clear_confirm_title); preference.getExtras().putBoolean(ERASE_EXTERNAL_EXTRA, mExternalStorage.isChecked()); - ((PreferenceActivity) getActivity()).onPreferenceStartFragment(null, preference); + ((SettingsActivity) getActivity()).onPreferenceStartFragment(null, preference); } /** diff --git a/src/com/android/settings/NfcLockFragment.java b/src/com/android/settings/NfcLockFragment.java new file mode 100644 index 000000000..b7bf6f768 --- /dev/null +++ b/src/com/android/settings/NfcLockFragment.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2013 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.AlertDialog; +import android.content.DialogInterface; +import android.nfc.NfcAdapter; +import android.nfc.NfcUnlock; +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.Preference; + +import android.preference.PreferenceCategory; +import android.util.Log; +import com.android.internal.widget.LockPatternUtils; + +import java.text.DateFormat; +import java.util.Date; + +import static android.preference.Preference.OnPreferenceClickListener; + +public class NfcLockFragment extends SettingsPreferenceFragment { + + private static final String NFC_PAIRING = "nfc_pairing"; + private static final String NFC_UNLOCK_ENABLED = "nfc_unlock_enabled"; + private static final String TAGS_CATEGORY = "nfc_unlock_tags_category"; + private static final String TAG_FORMAT = "Tag # %d"; + + private NfcUnlock mNfcUnlock; + private LockPatternUtils mLockPatternUtils; + private NfcAdapter mNfcAdapter; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mNfcUnlock = NfcUnlock.getInstance(NfcAdapter.getDefaultAdapter(getActivity())); + mLockPatternUtils = new LockPatternUtils(getActivity()); + mNfcAdapter = NfcAdapter.getDefaultAdapter(getActivity()); + addPreferencesFromResource(R.xml.security_settings_nfc_unlock); + } + + @Override + public void onResume() { + super.onResume(); + + boolean prefsEnabled = (mLockPatternUtils.isLockPasswordEnabled() || + mLockPatternUtils.isLockPatternEnabled()) && mNfcAdapter.isEnabled(); + CheckBoxPreference unlockPref = (CheckBoxPreference) findPreference(NFC_UNLOCK_ENABLED); + unlockPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + mNfcUnlock.setNfcUnlockEnabled((Boolean) newValue); + return true; + } + }); + Preference pairingPref = findPreference(NFC_PAIRING); + unlockPref.setEnabled(prefsEnabled); + pairingPref.setEnabled(prefsEnabled); + + long[] tagRegistryTimes = mNfcUnlock.getTagRegistryTimes(); + unlockPref.setChecked(mNfcUnlock.getNfcUnlockEnabled()); + + final PreferenceCategory pairedTags = (PreferenceCategory) findPreference(TAGS_CATEGORY); + pairedTags.setEnabled(prefsEnabled); + + loadTagList(tagRegistryTimes, pairedTags); + } + + private void loadTagList(long[] tagRegistryTimes, final PreferenceCategory pairedTags) { + pairedTags.removeAll(); + + for (int i = 0; i < tagRegistryTimes.length; i++) { + + final Preference thisPreference = new Preference(getActivity()); + final long timestamp = tagRegistryTimes[i]; + + thisPreference.setTitle(String.format(TAG_FORMAT, i)); + thisPreference.setSummary( + DateFormat.getDateTimeInstance().format(new Date(tagRegistryTimes[i]))); + thisPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + + AlertDialog.Builder deleteDialogBuilder = new AlertDialog.Builder(getActivity()); + + deleteDialogBuilder.setTitle(thisPreference.getTitle()); + deleteDialogBuilder.setItems(new String[] {"Delete"}, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (which == 0) { + if (mNfcUnlock.deregisterTag(timestamp)) { + loadTagList(mNfcUnlock.getTagRegistryTimes(), + pairedTags); + } + } + } + }); + + + deleteDialogBuilder.show(); + + return true; + } + }); + + + pairedTags.addPreference(thisPreference); + } + } +} diff --git a/src/com/android/settings/NotificationSettings.java b/src/com/android/settings/NotificationSettings.java new file mode 100644 index 000000000..c8ba39a00 --- /dev/null +++ b/src/com/android/settings/NotificationSettings.java @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2014 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.ContentResolver; +import android.content.pm.PackageManager; +import android.database.ContentObserver; +import android.media.RingtoneManager; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.preference.CheckBoxPreference; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceClickListener; +import android.preference.PreferenceGroup; +import android.preference.PreferenceScreen; +import android.provider.Settings; +import android.util.Log; + +public class NotificationSettings extends SettingsPreferenceFragment implements + Preference.OnPreferenceChangeListener, OnPreferenceClickListener { + private static final String TAG = "NotificationSettings"; + + private static final String KEY_NOTIFICATION_SOUND = "notification_sound"; + private static final String KEY_ZEN_MODE = "zen_mode"; + private static final String KEY_NOTIFICATION_ACCESS = "manage_notification_access"; + private static final String KEY_LOCK_SCREEN_NOTIFICATIONS = "toggle_lock_screen_notifications"; + private static final String KEY_HEADS_UP = "heads_up"; + private static final String KEY_NOTIFICATION_PULSE = "notification_pulse"; + + private static final String KEY_SECURITY_CATEGORY = "category_security"; + private static final String KEY_TWEAKS_CATEGORY = "category_tweaks"; // power toys, eng only + + private static final int MSG_UPDATE_SOUND_SUMMARY = 2; + + private PackageManager mPM; + + private Preference mNotificationSoundPreference; + private Preference mNotificationAccess; + private CheckBoxPreference mLockscreenNotifications; + private CheckBoxPreference mHeadsUp; + private CheckBoxPreference mNotificationPulse; + + private final Runnable mRingtoneLookupRunnable = new Runnable() { + @Override + public void run() { + if (mNotificationSoundPreference != null) { + final CharSequence summary = SoundSettings.updateRingtoneName( + getActivity(), RingtoneManager.TYPE_NOTIFICATION); + if (summary != null) { + mHandler.sendMessage( + mHandler.obtainMessage(MSG_UPDATE_SOUND_SUMMARY, summary)); + } + } + } + }; + + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_UPDATE_SOUND_SUMMARY: + mNotificationSoundPreference.setSummary((CharSequence) msg.obj); + break; + } + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + final ContentResolver resolver = getActivity().getContentResolver(); + + mPM = getActivity().getPackageManager(); + + addPreferencesFromResource(R.xml.notification_settings); + + final PreferenceScreen root = getPreferenceScreen(); + final PreferenceGroup securityCategory = (PreferenceGroup) + root.findPreference(KEY_SECURITY_CATEGORY); + + PreferenceGroup tweaksCategory = (PreferenceGroup) + root.findPreference(KEY_TWEAKS_CATEGORY); + + if (tweaksCategory != null + && !(Build.TYPE.equals("eng") || Build.TYPE.equals("userdebug"))) { + root.removePreference(tweaksCategory); + tweaksCategory = null; + } + + mNotificationSoundPreference = findPreference(KEY_NOTIFICATION_SOUND); + + mNotificationAccess = findPreference(KEY_NOTIFICATION_ACCESS); + refreshNotificationListeners(); + + mLockscreenNotifications + = (CheckBoxPreference) root.findPreference(KEY_LOCK_SCREEN_NOTIFICATIONS); + if (mLockscreenNotifications != null) { + if (!getDeviceLockscreenNotificationsEnabled()) { + if (securityCategory != null) { + securityCategory.removePreference(mLockscreenNotifications); + } + } else { + mLockscreenNotifications.setChecked(getLockscreenAllowPrivateNotifications()); + } + } + + mHeadsUp = (CheckBoxPreference) findPreference(KEY_HEADS_UP); + if (mHeadsUp != null) { + updateHeadsUpMode(resolver); + mHeadsUp.setOnPreferenceChangeListener(this); + resolver.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED), + false, new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange) { + updateHeadsUpMode(resolver); + } + }); + } + mNotificationPulse = (CheckBoxPreference) findPreference(KEY_NOTIFICATION_PULSE); + + if (mNotificationPulse != null + && getResources().getBoolean( + com.android.internal.R.bool.config_intrusiveNotificationLed) == false) { + getPreferenceScreen().removePreference(mNotificationPulse); + } else { + try { + mNotificationPulse.setChecked(Settings.System.getInt(resolver, + Settings.System.NOTIFICATION_LIGHT_PULSE) == 1); + mNotificationPulse.setOnPreferenceChangeListener(this); + } catch (Settings.SettingNotFoundException snfe) { + Log.e(TAG, Settings.System.NOTIFICATION_LIGHT_PULSE + " not found"); + } + } + } + + @Override + public void onResume() { + super.onResume(); + + refreshNotificationListeners(); + lookupRingtoneNames(); + } + + @Override + public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { + final String key = preference.getKey(); + + if (KEY_LOCK_SCREEN_NOTIFICATIONS.equals(key)) { + Settings.Secure.putInt(getContentResolver(), + Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, + mLockscreenNotifications.isChecked() ? 1 : 0); + } else if (KEY_HEADS_UP.equals(key)) { + setHeadsUpMode(getContentResolver(), mHeadsUp.isChecked()); + } else if (KEY_NOTIFICATION_PULSE.equals(key)) { + Settings.System.putInt(getContentResolver(), + Settings.System.NOTIFICATION_LIGHT_PULSE, + mNotificationPulse.isChecked() ? 1 : 0); + } else { + return super.onPreferenceTreeClick(preferenceScreen, preference); + } + + return true; + } + + @Override + public boolean onPreferenceChange(Preference preference, Object objValue) { + return true; + } + + @Override + public boolean onPreferenceClick(Preference preference) { + return false; + } + + // === Heads-up notifications === + + private void updateHeadsUpMode(ContentResolver resolver) { + mHeadsUp.setChecked(Settings.Global.HEADS_UP_ON == Settings.Global.getInt(resolver, + Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, Settings.Global.HEADS_UP_OFF)); + } + + private void setHeadsUpMode(ContentResolver resolver, boolean value) { + Settings.Global.putInt(resolver, Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, + value ? Settings.Global.HEADS_UP_ON : Settings.Global.HEADS_UP_OFF); + } + + // === Lockscreen (public / private) notifications === + + private boolean getDeviceLockscreenNotificationsEnabled() { + return 0 != Settings.Global.getInt(getContentResolver(), + Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0); + } + + private boolean getLockscreenAllowPrivateNotifications() { + return 0 != Settings.Secure.getInt(getContentResolver(), + Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0); + } + + // === Notification listeners === + + private int getNumEnabledNotificationListeners() { + final String flat = Settings.Secure.getString(getContentResolver(), + Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); + if (flat == null || "".equals(flat)) return 0; + final String[] components = flat.split(":"); + return components.length; + } + + private void refreshNotificationListeners() { + if (mNotificationAccess != null) { + final PreferenceGroup securityCategory + = (PreferenceGroup) getPreferenceScreen().findPreference(KEY_SECURITY_CATEGORY); + + final int total = NotificationAccessSettings.getListenersCount(mPM); + if (total == 0) { + if (securityCategory != null) { + securityCategory.removePreference(mNotificationAccess); + } + } else { + final int n = getNumEnabledNotificationListeners(); + if (n == 0) { + mNotificationAccess.setSummary(getResources().getString( + R.string.manage_notification_access_summary_zero)); + } else { + mNotificationAccess.setSummary(String.format(getResources().getQuantityString( + R.plurals.manage_notification_access_summary_nonzero, + n, n))); + } + } + } + } + + // === Ringtone === + + private void lookupRingtoneNames() { + new Thread(mRingtoneLookupRunnable).start(); + } +} diff --git a/src/com/android/settings/PairNfcDevice.java b/src/com/android/settings/PairNfcDevice.java new file mode 100644 index 000000000..7c75b9f61 --- /dev/null +++ b/src/com/android/settings/PairNfcDevice.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2013 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.AlertDialog; +import android.app.PendingIntent; +import android.content.DialogInterface; +import android.content.Intent; +import android.nfc.NfcAdapter; +import android.nfc.NfcUnlock; +import android.nfc.Tag; +import android.os.Bundle; +import android.os.Handler; +import android.os.PowerManager; +import android.provider.Settings; +import android.view.Menu; +import android.widget.ImageView; +import android.widget.TextView; + +public class PairNfcDevice extends Activity { + private static String TAG = PairNfcDevice.class.getName(); + + private TextView mStatusText; + private ImageView mStatusImage; + + private PendingIntent mPendingIntent; + private NfcAdapter mAdapter; + + private Handler mHandler = new Handler(); + private PowerManager.WakeLock mWakeLock; + + private NfcUnlock mNfcUnlock; + + // If pairing fails, we immediately get a new intent that would not leave time for the user to + // read the error message. So we'll just drop it and the user has to try again. + // TEST + private boolean mWaitingForDeviceDelayed; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_nfc_pairing); + + mStatusText = (TextView) findViewById(R.id.status_text); + mStatusImage = (ImageView) findViewById(R.id.status_image); + + mAdapter = NfcAdapter.getDefaultAdapter(this); + mPendingIntent = PendingIntent.getActivity( + this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0); + + mNfcUnlock = NfcUnlock.getInstance(mAdapter); + + setWaitingForDeviceMode(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + return false; + } + + public void onPause() { + super.onPause(); + mAdapter.disableForegroundDispatch(this); + if (mWakeLock != null) { + mWakeLock.release(); + } + } + + public void onResume() { + super.onResume(); + + if (!mAdapter.isEnabled()) { + // We need the user to start NFC. + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); + dialogBuilder.setMessage(R.string.enable_nfc); + dialogBuilder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + startActivity(new Intent(Settings.ACTION_NFC_SETTINGS)); + } + }); + dialogBuilder.show(); + } + + mAdapter.enableForegroundDispatch(this, mPendingIntent, null, null); + + if (mWakeLock == null) { + PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE); + mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, TAG); + } + mWakeLock.acquire(); + } + + @Override + public void onNewIntent(Intent intent) { + Tag tag = (Tag) intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); + if (!mWaitingForDeviceDelayed) { + processTag(tag); + } + } + + private void processTag(Tag tag) { + if (mNfcUnlock.registerTag(tag)) { + setPairingSucceededMode(); + } else { + setPairingFailedMode(); + } + } + + private void setWaitingForDeviceModeDelayed(int delayInMs) { + mWaitingForDeviceDelayed = true; + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + mWaitingForDeviceDelayed = false; + setWaitingForDeviceMode(); + } + }, delayInMs); + } + + private void setWaitingForDeviceMode() { + mStatusImage.setImageResource(R.drawable.no_ring_detected); + mStatusText.setText(R.string.status_no_ring_detected); + } + + private void setPairingFailedMode() { + setErrorMode(R.string.status_error_pairing_failed); + } + + private void setPairingSucceededMode() { + mStatusImage.setImageResource(R.drawable.ring_paired); + mStatusText.setText(R.string.status_device_paired); + + // Automatically quit. + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + finish(); + } + }, 1000); + } + + private void setErrorMode(int errorMsgResourceId) { + mStatusText.setText(errorMsgResourceId); + setWaitingForDeviceModeDelayed(2500); + } +} diff --git a/src/com/android/settings/ProxySelector.java b/src/com/android/settings/ProxySelector.java index 21e717a4a..052ea0abb 100644 --- a/src/com/android/settings/ProxySelector.java +++ b/src/com/android/settings/ProxySelector.java @@ -45,8 +45,6 @@ import android.widget.EditText; import android.widget.TextView; import java.net.InetSocketAddress; -import java.util.regex.Matcher; -import java.util.regex.Pattern; public class ProxySelector extends Fragment implements DialogCreatable { private static final String TAG = "ProxySelector"; @@ -58,22 +56,6 @@ public class ProxySelector extends Fragment implements DialogCreatable { Button mClearButton; Button mDefaultButton; - // Allows underscore char to supports proxies that do not - // follow the spec - private static final String HC = "a-zA-Z0-9\\_"; - - // Matches blank input, ips, and domain names - private static final String HOSTNAME_REGEXP = - "^$|^[" + HC + "]+(\\-[" + HC + "]+)*(\\.[" + HC + "]+(\\-[" + HC + "]+)*)*$"; - private static final Pattern HOSTNAME_PATTERN; - private static final String EXCLUSION_REGEXP = - "$|^(\\*)?\\.?[" + HC + "]+(\\-[" + HC + "]+)*(\\.[" + HC + "]+(\\-[" + HC + "]+)*)*$"; - private static final Pattern EXCLUSION_PATTERN; - static { - HOSTNAME_PATTERN = Pattern.compile(HOSTNAME_REGEXP); - EXCLUSION_PATTERN = Pattern.compile(EXCLUSION_REGEXP); - } - private static final int ERROR_DIALOG_ID = 0; private SettingsDialogFragment mDialogFragment; @@ -203,35 +185,24 @@ public class ProxySelector extends Fragment implements DialogCreatable { * @return 0 on success, string resource ID on failure */ public static int validate(String hostname, String port, String exclList) { - Matcher match = HOSTNAME_PATTERN.matcher(hostname); - String exclListArray[] = exclList.split(","); - - if (!match.matches()) return R.string.proxy_error_invalid_host; - - for (String excl : exclListArray) { - Matcher m = EXCLUSION_PATTERN.matcher(excl); - if (!m.matches()) return R.string.proxy_error_invalid_exclusion_list; - } - - if (hostname.length() > 0 && port.length() == 0) { - return R.string.proxy_error_empty_port; - } - - if (port.length() > 0) { - if (hostname.length() == 0) { + switch (Proxy.validate(hostname, port, exclList)) { + case Proxy.PROXY_VALID: + return 0; + case Proxy.PROXY_HOSTNAME_EMPTY: return R.string.proxy_error_empty_host_set_port; - } - int portVal = -1; - try { - portVal = Integer.parseInt(port); - } catch (NumberFormatException ex) { + case Proxy.PROXY_HOSTNAME_INVALID: + return R.string.proxy_error_invalid_host; + case Proxy.PROXY_PORT_EMPTY: + return R.string.proxy_error_empty_port; + case Proxy.PROXY_PORT_INVALID: return R.string.proxy_error_invalid_port; - } - if (portVal <= 0 || portVal > 0xFFFF) { - return R.string.proxy_error_invalid_port; - } + case Proxy.PROXY_EXCLLIST_INVALID: + return R.string.proxy_error_invalid_exclusion_list; + default: + // should neven happen + Log.e(TAG, "Unknown proxy settings error"); + return -1; } - return 0; } /** @@ -245,7 +216,7 @@ public class ProxySelector extends Fragment implements DialogCreatable { int port = 0; int result = validate(hostname, portStr, exclList); - if (result > 0) { + if (result != 0) { showDialog(ERROR_DIALOG_ID); return false; } diff --git a/src/com/android/settings/SecuritySettings.java b/src/com/android/settings/SecuritySettings.java index e4dcea1db..c4f0c7f42 100644 --- a/src/com/android/settings/SecuritySettings.java +++ b/src/com/android/settings/SecuritySettings.java @@ -29,6 +29,7 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; +import android.nfc.NfcUnlock; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; @@ -57,6 +58,7 @@ public class SecuritySettings extends RestrictedSettingsFragment // Lock Settings private static final String KEY_UNLOCK_SET_OR_CHANGE = "unlock_set_or_change"; + private static final String KEY_NFC_UNLOCK_SET_OR_CHANGE = "nfc_unlock_set_or_change"; private static final String KEY_BIOMETRIC_WEAK_IMPROVE_MATCHING = "biometric_weak_improve_matching"; private static final String KEY_BIOMETRIC_WEAK_LIVELINESS = "biometric_weak_liveliness"; @@ -82,7 +84,6 @@ public class SecuritySettings extends RestrictedSettingsFragment private static final String KEY_TOGGLE_VERIFY_APPLICATIONS = "toggle_verify_applications"; private static final String KEY_POWER_INSTANTLY_LOCKS = "power_button_instantly_locks"; private static final String KEY_CREDENTIALS_MANAGER = "credentials_management"; - private static final String KEY_NOTIFICATION_ACCESS = "manage_notification_access"; private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive"; private PackageManager mPM; @@ -106,8 +107,6 @@ public class SecuritySettings extends RestrictedSettingsFragment private CheckBoxPreference mPowerButtonInstantlyLocks; private CheckBoxPreference mEnableKeyguardWidgets; - private Preference mNotificationAccess; - private boolean mIsPrimary; public SecuritySettings() { @@ -225,6 +224,19 @@ public class SecuritySettings extends RestrictedSettingsFragment } } + // don't display NFC unlock settings if the prop is not enabled + if (!NfcUnlock.getPropertyEnabled()) { + PreferenceGroup securityCategory = + (PreferenceGroup) root.findPreference(KEY_SECURITY_CATEGORY); + + if (securityCategory != null) { + Preference preference = root.findPreference(KEY_NFC_UNLOCK_SET_OR_CHANGE); + if (preference != null) { + securityCategory.removePreference(preference); + } + } + } + // Append the rest of the settings addPreferencesFromResource(R.xml.security_settings_misc); @@ -313,41 +325,14 @@ public class SecuritySettings extends RestrictedSettingsFragment } } - mNotificationAccess = findPreference(KEY_NOTIFICATION_ACCESS); - if (mNotificationAccess != null) { - final int total = NotificationAccessSettings.getListenersCount(mPM); - if (total == 0) { - if (deviceAdminCategory != null) { - deviceAdminCategory.removePreference(mNotificationAccess); - } - } else { - final int n = getNumEnabledNotificationListeners(); - if (n == 0) { - mNotificationAccess.setSummary(getResources().getString( - R.string.manage_notification_access_summary_zero)); - } else { - mNotificationAccess.setSummary(String.format(getResources().getQuantityString( - R.plurals.manage_notification_access_summary_nonzero, - n, n))); - } - } - } - if (shouldBePinProtected(RESTRICTIONS_PIN_SET)) { protectByRestrictions(mToggleAppInstallation); protectByRestrictions(mToggleVerifyApps); protectByRestrictions(mResetCredentials); protectByRestrictions(root.findPreference(KEY_CREDENTIALS_INSTALL)); } - return root; - } - private int getNumEnabledNotificationListeners() { - final String flat = Settings.Secure.getString(getContentResolver(), - Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); - if (flat == null || "".equals(flat)) return 0; - final String[] components = flat.split(":"); - return components.length; + return root; } private boolean isNonMarketAppsAllowed() { @@ -520,6 +505,9 @@ public class SecuritySettings extends RestrictedSettingsFragment if (KEY_UNLOCK_SET_OR_CHANGE.equals(key)) { startFragment(this, "com.android.settings.ChooseLockGeneric$ChooseLockGenericFragment", SET_OR_CHANGE_LOCK_METHOD_REQUEST, null); + } else if (KEY_NFC_UNLOCK_SET_OR_CHANGE.equals(key)) { + startFragment(this, "com.android.settings.NfcLockFragment", + SET_OR_CHANGE_LOCK_METHOD_REQUEST, null); } else if (KEY_BIOMETRIC_WEAK_IMPROVE_MATCHING.equals(key)) { ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(this.getActivity(), this); diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java index f6f49b86e..0d5839b2d 100644 --- a/src/com/android/settings/Settings.java +++ b/src/com/android/settings/Settings.java @@ -16,1105 +16,78 @@ package com.android.settings; -import android.accounts.Account; -import android.accounts.AccountManager; -import android.accounts.OnAccountsUpdateListener; -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.DialogFragment; -import android.app.admin.DevicePolicyManager; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.SharedPreferences; -import android.content.pm.ActivityInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.ResolveInfo; -import android.graphics.drawable.Drawable; -import android.nfc.NfcAdapter; -import android.os.Bundle; -import android.os.INetworkManagementService; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.UserHandle; -import android.os.UserManager; -import android.preference.Preference; -import android.preference.PreferenceActivity; -import android.preference.PreferenceFragment; -import android.text.TextUtils; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.ListAdapter; -import android.widget.Switch; -import android.widget.TextView; - -import com.android.internal.util.ArrayUtils; -import com.android.settings.accessibility.AccessibilitySettings; -import com.android.settings.accessibility.CaptionPropertiesFragment; -import com.android.settings.accessibility.ToggleAccessibilityServicePreferenceFragment; -import com.android.settings.accounts.AccountSyncSettings; -import com.android.settings.accounts.AuthenticatorHelper; -import com.android.settings.accounts.ManageAccountsSettings; import com.android.settings.applications.AppOpsSummary; -import com.android.settings.applications.ManageApplications; -import com.android.settings.applications.ProcessStatsUi; -import com.android.settings.bluetooth.BluetoothEnabler; -import com.android.settings.bluetooth.BluetoothSettings; -import com.android.settings.deviceinfo.Memory; -import com.android.settings.deviceinfo.UsbSettings; -import com.android.settings.fuelgauge.PowerUsageSummary; -import com.android.settings.inputmethod.InputMethodAndLanguageSettings; -import com.android.settings.inputmethod.KeyboardLayoutPickerFragment; -import com.android.settings.inputmethod.SpellCheckersSettings; -import com.android.settings.inputmethod.UserDictionaryList; -import com.android.settings.location.LocationSettings; -import com.android.settings.nfc.AndroidBeam; -import com.android.settings.nfc.PaymentSettings; -import com.android.settings.print.PrintJobSettingsFragment; -import com.android.settings.print.PrintServiceSettingsFragment; -import com.android.settings.print.PrintSettingsFragment; -import com.android.settings.tts.TextToSpeechSettings; -import com.android.settings.users.UserSettings; -import com.android.settings.vpn2.VpnSettings; -import com.android.settings.wfd.WifiDisplaySettings; -import com.android.settings.wifi.AdvancedWifiSettings; -import com.android.settings.wifi.WifiEnabler; -import com.android.settings.wifi.WifiSettings; -import com.android.settings.wifi.p2p.WifiP2pSettings; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; /** - * Top-level settings activity to handle single pane and double pane UI layout. + * Top-level Settings activity */ -public class Settings extends PreferenceActivity - implements ButtonBarHandler, OnAccountsUpdateListener { - - private static final String LOG_TAG = "Settings"; - - private static final String META_DATA_KEY_HEADER_ID = - "com.android.settings.TOP_LEVEL_HEADER_ID"; - private static final String META_DATA_KEY_FRAGMENT_CLASS = - "com.android.settings.FRAGMENT_CLASS"; - private static final String META_DATA_KEY_PARENT_TITLE = - "com.android.settings.PARENT_FRAGMENT_TITLE"; - private static final String META_DATA_KEY_PARENT_FRAGMENT_CLASS = - "com.android.settings.PARENT_FRAGMENT_CLASS"; - - private static final String EXTRA_UI_OPTIONS = "settings:ui_options"; - - private static final String SAVE_KEY_CURRENT_HEADER = "com.android.settings.CURRENT_HEADER"; - private static final String SAVE_KEY_PARENT_HEADER = "com.android.settings.PARENT_HEADER"; - - static final int DIALOG_ONLY_ONE_HOME = 1; - - private static boolean sShowNoHomeNotice = false; - - private String mFragmentClass; - private int mTopLevelHeaderId; - private Header mFirstHeader; - private Header mCurrentHeader; - private Header mParentHeader; - private boolean mInLocalHeaderSwitch; - - // Show only these settings for restricted users - private int[] SETTINGS_FOR_RESTRICTED = { - R.id.wireless_section, - R.id.wifi_settings, - R.id.bluetooth_settings, - R.id.data_usage_settings, - R.id.wireless_settings, - R.id.device_section, - R.id.sound_settings, - R.id.display_settings, - R.id.storage_settings, - R.id.application_settings, - R.id.battery_settings, - R.id.personal_section, - R.id.location_settings, - R.id.security_settings, - R.id.language_settings, - R.id.user_settings, - R.id.account_settings, - R.id.account_add, - R.id.system_section, - R.id.date_time_settings, - R.id.about_settings, - R.id.accessibility_settings, - R.id.print_settings, - R.id.nfc_payment_settings, - R.id.home_settings - }; - - private SharedPreferences mDevelopmentPreferences; - private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener; - - // TODO: Update Call Settings based on airplane mode state. - - protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>(); - - private AuthenticatorHelper mAuthenticatorHelper; - private Header mLastHeader; - private boolean mListeningToAccountUpdates; - - private boolean mBatteryPresent = true; - private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() { - - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (Intent.ACTION_BATTERY_CHANGED.equals(action)) { - boolean batteryPresent = Utils.isBatteryPresent(intent); - - if (mBatteryPresent != batteryPresent) { - mBatteryPresent = batteryPresent; - invalidateHeaders(); - } - } - } - }; - - @Override - protected void onCreate(Bundle savedInstanceState) { - if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) { - getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0)); - } - - mAuthenticatorHelper = new AuthenticatorHelper(); - mAuthenticatorHelper.updateAuthDescriptions(this); - mAuthenticatorHelper.onAccountsUpdated(this, null); - - mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE, - Context.MODE_PRIVATE); - - getMetaData(); - mInLocalHeaderSwitch = true; - super.onCreate(savedInstanceState); - mInLocalHeaderSwitch = false; - - if (!onIsHidingHeaders() && onIsMultiPane()) { - highlightHeader(mTopLevelHeaderId); - // Force the title so that it doesn't get overridden by a direct launch of - // a specific settings screen. - setTitle(R.string.settings_label); - } - - // Retrieve any saved state - if (savedInstanceState != null) { - mCurrentHeader = savedInstanceState.getParcelable(SAVE_KEY_CURRENT_HEADER); - mParentHeader = savedInstanceState.getParcelable(SAVE_KEY_PARENT_HEADER); - } - - // If the current header was saved, switch to it - if (savedInstanceState != null && mCurrentHeader != null) { - //switchToHeaderLocal(mCurrentHeader); - showBreadCrumbs(mCurrentHeader.title, null); - } - - if (mParentHeader != null) { - setParentTitle(mParentHeader.title, null, new OnClickListener() { - @Override - public void onClick(View v) { - switchToParent(mParentHeader.fragment); - } - }); - } - - // Override up navigation for multi-pane, since we handle it in the fragment breadcrumbs - if (onIsMultiPane()) { - getActionBar().setDisplayHomeAsUpEnabled(false); - getActionBar().setHomeButtonEnabled(false); - } - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - - // Save the current fragment, if it is the same as originally launched - if (mCurrentHeader != null) { - outState.putParcelable(SAVE_KEY_CURRENT_HEADER, mCurrentHeader); - } - if (mParentHeader != null) { - outState.putParcelable(SAVE_KEY_PARENT_HEADER, mParentHeader); - } - } - - @Override - public void onResume() { - super.onResume(); - - mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() { - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - invalidateHeaders(); - } - }; - mDevelopmentPreferences.registerOnSharedPreferenceChangeListener( - mDevelopmentPreferencesListener); - - ListAdapter listAdapter = getListAdapter(); - if (listAdapter instanceof HeaderAdapter) { - ((HeaderAdapter) listAdapter).resume(); - } - invalidateHeaders(); - - registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); - } - - @Override - public void onPause() { - super.onPause(); - - unregisterReceiver(mBatteryInfoReceiver); - - ListAdapter listAdapter = getListAdapter(); - if (listAdapter instanceof HeaderAdapter) { - ((HeaderAdapter) listAdapter).pause(); - } - - mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener( - mDevelopmentPreferencesListener); - mDevelopmentPreferencesListener = null; - } - - @Override - public void onDestroy() { - super.onDestroy(); - if (mListeningToAccountUpdates) { - AccountManager.get(this).removeOnAccountsUpdatedListener(this); - } - } - - @Override - public boolean onIsMultiPane() { - return false; - } - - private static final String[] ENTRY_FRAGMENTS = { - WirelessSettings.class.getName(), - WifiSettings.class.getName(), - AdvancedWifiSettings.class.getName(), - BluetoothSettings.class.getName(), - TetherSettings.class.getName(), - WifiP2pSettings.class.getName(), - VpnSettings.class.getName(), - DateTimeSettings.class.getName(), - LocalePicker.class.getName(), - InputMethodAndLanguageSettings.class.getName(), - SpellCheckersSettings.class.getName(), - UserDictionaryList.class.getName(), - UserDictionarySettings.class.getName(), - SoundSettings.class.getName(), - DisplaySettings.class.getName(), - DeviceInfoSettings.class.getName(), - ManageApplications.class.getName(), - ProcessStatsUi.class.getName(), - NotificationStation.class.getName(), - LocationSettings.class.getName(), - SecuritySettings.class.getName(), - PrivacySettings.class.getName(), - DeviceAdminSettings.class.getName(), - AccessibilitySettings.class.getName(), - CaptionPropertiesFragment.class.getName(), - TextToSpeechSettings.class.getName(), - Memory.class.getName(), - DevelopmentSettings.class.getName(), - UsbSettings.class.getName(), - AndroidBeam.class.getName(), - WifiDisplaySettings.class.getName(), - PowerUsageSummary.class.getName(), - AccountSyncSettings.class.getName(), - CryptKeeperSettings.class.getName(), - DataUsageSummary.class.getName(), - DreamSettings.class.getName(), - UserSettings.class.getName(), - NotificationAccessSettings.class.getName(), - ManageAccountsSettings.class.getName(), - PrintSettingsFragment.class.getName(), - PrintJobSettingsFragment.class.getName(), - TrustedCredentialsSettings.class.getName(), - PaymentSettings.class.getName(), - KeyboardLayoutPickerFragment.class.getName() - }; - - @Override - protected boolean isValidFragment(String fragmentName) { - // Almost all fragments are wrapped in this, - // except for a few that have their own activities. - for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) { - if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true; - } - return false; - } - - private void switchToHeaderLocal(Header header) { - mInLocalHeaderSwitch = true; - switchToHeader(header); - mInLocalHeaderSwitch = false; - } - - @Override - public void switchToHeader(Header header) { - if (!mInLocalHeaderSwitch) { - mCurrentHeader = null; - mParentHeader = null; - } - super.switchToHeader(header); - } - - /** - * Switch to parent fragment and store the grand parent's info - * @param className name of the activity wrapper for the parent fragment. - */ - private void switchToParent(String className) { - final ComponentName cn = new ComponentName(this, className); - try { - final PackageManager pm = getPackageManager(); - final ActivityInfo parentInfo = pm.getActivityInfo(cn, PackageManager.GET_META_DATA); - - if (parentInfo != null && parentInfo.metaData != null) { - String fragmentClass = parentInfo.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS); - CharSequence fragmentTitle = parentInfo.loadLabel(pm); - Header parentHeader = new Header(); - parentHeader.fragment = fragmentClass; - parentHeader.title = fragmentTitle; - mCurrentHeader = parentHeader; - - switchToHeaderLocal(parentHeader); - highlightHeader(mTopLevelHeaderId); - - mParentHeader = new Header(); - mParentHeader.fragment - = parentInfo.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS); - mParentHeader.title = parentInfo.metaData.getString(META_DATA_KEY_PARENT_TITLE); - } - } catch (NameNotFoundException nnfe) { - Log.w(LOG_TAG, "Could not find parent activity : " + className); - } - } - - @Override - public void onNewIntent(Intent intent) { - super.onNewIntent(intent); - - // If it is not launched from history, then reset to top-level - if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) { - if (mFirstHeader != null && !onIsHidingHeaders() && onIsMultiPane()) { - switchToHeaderLocal(mFirstHeader); - } - getListView().setSelectionFromTop(0, 0); - } - } - - private void highlightHeader(int id) { - if (id != 0) { - Integer index = mHeaderIndexMap.get(id); - if (index != null) { - getListView().setItemChecked(index, true); - if (isMultiPane()) { - getListView().smoothScrollToPosition(index); - } - } - } - } - - @Override - public Intent getIntent() { - Intent superIntent = super.getIntent(); - String startingFragment = getStartingFragmentClass(superIntent); - // This is called from super.onCreate, isMultiPane() is not yet reliable - // Do not use onIsHidingHeaders either, which relies itself on this method - if (startingFragment != null && !onIsMultiPane()) { - Intent modIntent = new Intent(superIntent); - modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment); - Bundle args = superIntent.getExtras(); - if (args != null) { - args = new Bundle(args); - } else { - args = new Bundle(); - } - args.putParcelable("intent", superIntent); - modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras()); - return modIntent; - } - return superIntent; - } - - /** - * Checks if the component name in the intent is different from the Settings class and - * returns the class name to load as a fragment. - */ - protected String getStartingFragmentClass(Intent intent) { - if (mFragmentClass != null) return mFragmentClass; - - String intentClass = intent.getComponent().getClassName(); - if (intentClass.equals(getClass().getName())) return null; - - if ("com.android.settings.ManageApplications".equals(intentClass) - || "com.android.settings.RunningServices".equals(intentClass) - || "com.android.settings.applications.StorageUse".equals(intentClass)) { - // Old names of manage apps. - intentClass = com.android.settings.applications.ManageApplications.class.getName(); - } - - return intentClass; - } - - /** - * Override initial header when an activity-alias is causing Settings to be launched - * for a specific fragment encoded in the android:name parameter. - */ - @Override - public Header onGetInitialHeader() { - String fragmentClass = getStartingFragmentClass(super.getIntent()); - if (fragmentClass != null) { - Header header = new Header(); - header.fragment = fragmentClass; - header.title = getTitle(); - header.fragmentArguments = getIntent().getExtras(); - mCurrentHeader = header; - return header; - } - - return mFirstHeader; - } - - @Override - public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args, - int titleRes, int shortTitleRes) { - Intent intent = super.onBuildStartFragmentIntent(fragmentName, args, - titleRes, shortTitleRes); - - // Some fragments want split ActionBar; these should stay in sync with - // uiOptions for fragments also defined as activities in manifest. - if (WifiSettings.class.getName().equals(fragmentName) || - WifiP2pSettings.class.getName().equals(fragmentName) || - BluetoothSettings.class.getName().equals(fragmentName) || - DreamSettings.class.getName().equals(fragmentName) || - LocationSettings.class.getName().equals(fragmentName) || - ToggleAccessibilityServicePreferenceFragment.class.getName().equals(fragmentName) || - PrintSettingsFragment.class.getName().equals(fragmentName) || - PrintServiceSettingsFragment.class.getName().equals(fragmentName)) { - intent.putExtra(EXTRA_UI_OPTIONS, ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW); - } - - intent.setClass(this, SubSettings.class); - return intent; - } - - /** - * Populate the activity with the top-level headers. - */ - @Override - public void onBuildHeaders(List<Header> headers) { - if (!onIsHidingHeaders()) { - loadHeadersFromResource(R.xml.settings_headers, headers); - updateHeaderList(headers); - } - } - - private void updateHeaderList(List<Header> target) { - final boolean showDev = mDevelopmentPreferences.getBoolean( - DevelopmentSettings.PREF_SHOW, - android.os.Build.TYPE.equals("eng")); - int i = 0; - - final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); - mHeaderIndexMap.clear(); - while (i < target.size()) { - Header header = target.get(i); - // Ids are integers, so downcasting - int id = (int) header.id; - if (id == R.id.operator_settings || id == R.id.manufacturer_settings) { - Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header); - } else if (id == R.id.wifi_settings) { - // Remove WiFi Settings if WiFi service is not available. - if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) { - target.remove(i); - } - } else if (id == R.id.bluetooth_settings) { - // Remove Bluetooth Settings if Bluetooth service is not available. - if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) { - target.remove(i); - } - } else if (id == R.id.data_usage_settings) { - // Remove data usage when kernel module not enabled - final INetworkManagementService netManager = INetworkManagementService.Stub - .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)); - try { - if (!netManager.isBandwidthControlEnabled()) { - target.remove(i); - } - } catch (RemoteException e) { - // ignored - } - } else if (id == R.id.battery_settings) { - // Remove battery settings when battery is not available. (e.g. TV) - - if (!mBatteryPresent) { - target.remove(i); - } - } else if (id == R.id.account_settings) { - int headerIndex = i + 1; - i = insertAccountsHeaders(target, headerIndex); - } else if (id == R.id.home_settings) { - if (!updateHomeSettingHeaders(header)) { - target.remove(i); - } - } else if (id == R.id.user_settings) { - if (!UserHandle.MU_ENABLED - || !UserManager.supportsMultipleUsers() - || Utils.isMonkeyRunning()) { - target.remove(i); - } - } else if (id == R.id.nfc_payment_settings) { - if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) { - target.remove(i); - } else { - // Only show if NFC is on and we have the HCE feature - NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this); - if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature( - PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) { - target.remove(i); - } - } - } else if (id == R.id.development_settings) { - if (!showDev) { - target.remove(i); - } - } else if (id == R.id.account_add) { - if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) { - target.remove(i); - } - } - - if (i < target.size() && target.get(i) == header - && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0 - && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) { - target.remove(i); - } - - // Increment if the current one wasn't removed by the Utils code. - if (i < target.size() && target.get(i) == header) { - // Hold on to the first header, when we need to reset to the top-level - if (mFirstHeader == null && - HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) { - mFirstHeader = header; - } - mHeaderIndexMap.put(id, i); - i++; - } - } - } - - private int insertAccountsHeaders(List<Header> target, int headerIndex) { - String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes(); - List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length); - for (String accountType : accountTypes) { - CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType); - if (label == null) { - continue; - } - - Account[] accounts = AccountManager.get(this).getAccountsByType(accountType); - boolean skipToAccount = accounts.length == 1 - && !mAuthenticatorHelper.hasAccountPreferences(accountType); - Header accHeader = new Header(); - accHeader.title = label; - if (accHeader.extras == null) { - accHeader.extras = new Bundle(); - } - if (skipToAccount) { - accHeader.breadCrumbTitleRes = R.string.account_sync_settings_title; - accHeader.breadCrumbShortTitleRes = R.string.account_sync_settings_title; - accHeader.fragment = AccountSyncSettings.class.getName(); - accHeader.fragmentArguments = new Bundle(); - // Need this for the icon - accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType); - accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]); - accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY, - accounts[0]); - } else { - accHeader.breadCrumbTitle = label; - accHeader.breadCrumbShortTitle = label; - accHeader.fragment = ManageAccountsSettings.class.getName(); - accHeader.fragmentArguments = new Bundle(); - accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType); - accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, - accountType); - if (!isMultiPane()) { - accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL, - label.toString()); - } - } - accountHeaders.add(accHeader); - mAuthenticatorHelper.preloadDrawableForType(this, accountType); - } - - // Sort by label - Collections.sort(accountHeaders, new Comparator<Header>() { - @Override - public int compare(Header h1, Header h2) { - return h1.title.toString().compareTo(h2.title.toString()); - } - }); - - for (Header header : accountHeaders) { - target.add(headerIndex++, header); - } - if (!mListeningToAccountUpdates) { - AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true); - mListeningToAccountUpdates = true; - } - return headerIndex; - } - - private boolean updateHomeSettingHeaders(Header header) { - // Once we decide to show Home settings, keep showing it forever - SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE); - if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) { - return true; - } - - try { - final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>(); - getPackageManager().getHomeActivities(homeApps); - if (homeApps.size() < 2) { - // When there's only one available home app, omit this settings - // category entirely at the top level UI. If the user just - // uninstalled the penultimate home app candidiate, we also - // now tell them about why they aren't seeing 'Home' in the list. - if (sShowNoHomeNotice) { - sShowNoHomeNotice = false; - NoHomeDialogFragment.show(this); - } - return false; - } else { - // Okay, we're allowing the Home settings category. Tell it, when - // invoked via this front door, that we'll need to be told about the - // case when the user uninstalls all but one home app. - if (header.fragmentArguments == null) { - header.fragmentArguments = new Bundle(); - } - header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true); - } - } catch (Exception e) { - // Can't look up the home activity; bail on configuring the icon - Log.w(LOG_TAG, "Problem looking up home activity!", e); - } - - sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply(); - return true; - } - - private void getMetaData() { - try { - ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(), - PackageManager.GET_META_DATA); - if (ai == null || ai.metaData == null) return; - mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID); - mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS); - - // Check if it has a parent specified and create a Header object - final int parentHeaderTitleRes = ai.metaData.getInt(META_DATA_KEY_PARENT_TITLE); - String parentFragmentClass = ai.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS); - if (parentFragmentClass != null) { - mParentHeader = new Header(); - mParentHeader.fragment = parentFragmentClass; - if (parentHeaderTitleRes != 0) { - mParentHeader.title = getResources().getString(parentHeaderTitleRes); - } - } - } catch (NameNotFoundException nnfe) { - // No recovery - } - } - - @Override - public boolean hasNextButton() { - return super.hasNextButton(); - } - - @Override - public Button getNextButton() { - return super.getNextButton(); - } - - public static class NoHomeDialogFragment extends DialogFragment { - public static void show(Activity parent) { - final NoHomeDialogFragment dialog = new NoHomeDialogFragment(); - dialog.show(parent.getFragmentManager(), null); - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - return new AlertDialog.Builder(getActivity()) - .setMessage(R.string.only_one_home_message) - .setPositiveButton(android.R.string.ok, null) - .create(); - } - } - - private static class HeaderAdapter extends ArrayAdapter<Header> { - static final int HEADER_TYPE_CATEGORY = 0; - static final int HEADER_TYPE_NORMAL = 1; - static final int HEADER_TYPE_SWITCH = 2; - static final int HEADER_TYPE_BUTTON = 3; - private static final int HEADER_TYPE_COUNT = HEADER_TYPE_BUTTON + 1; - - private final WifiEnabler mWifiEnabler; - private final BluetoothEnabler mBluetoothEnabler; - private AuthenticatorHelper mAuthHelper; - private DevicePolicyManager mDevicePolicyManager; - - private static class HeaderViewHolder { - ImageView icon; - TextView title; - TextView summary; - Switch switch_; - ImageButton button_; - View divider_; - } - - private LayoutInflater mInflater; - - static int getHeaderType(Header header) { - if (header.fragment == null && header.intent == null) { - return HEADER_TYPE_CATEGORY; - } else if (header.id == R.id.wifi_settings || header.id == R.id.bluetooth_settings) { - return HEADER_TYPE_SWITCH; - } else if (header.id == R.id.security_settings) { - return HEADER_TYPE_BUTTON; - } else { - return HEADER_TYPE_NORMAL; - } - } - - @Override - public int getItemViewType(int position) { - Header header = getItem(position); - return getHeaderType(header); - } - - @Override - public boolean areAllItemsEnabled() { - return false; // because of categories - } - - @Override - public boolean isEnabled(int position) { - return getItemViewType(position) != HEADER_TYPE_CATEGORY; - } - - @Override - public int getViewTypeCount() { - return HEADER_TYPE_COUNT; - } - - @Override - public boolean hasStableIds() { - return true; - } - - public HeaderAdapter(Context context, List<Header> objects, - AuthenticatorHelper authenticatorHelper, DevicePolicyManager dpm) { - super(context, 0, objects); - - mAuthHelper = authenticatorHelper; - mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - - // Temp Switches provided as placeholder until the adapter replaces these with actual - // Switches inflated from their layouts. Must be done before adapter is set in super - mWifiEnabler = new WifiEnabler(context, new Switch(context)); - mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context)); - mDevicePolicyManager = dpm; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - HeaderViewHolder holder; - Header header = getItem(position); - int headerType = getHeaderType(header); - View view = null; - - if (convertView == null) { - holder = new HeaderViewHolder(); - switch (headerType) { - case HEADER_TYPE_CATEGORY: - view = new TextView(getContext(), null, - android.R.attr.listSeparatorTextViewStyle); - holder.title = (TextView) view; - break; - - case HEADER_TYPE_SWITCH: - view = mInflater.inflate(R.layout.preference_header_switch_item, parent, - false); - holder.icon = (ImageView) view.findViewById(R.id.icon); - holder.title = (TextView) - view.findViewById(com.android.internal.R.id.title); - holder.summary = (TextView) - view.findViewById(com.android.internal.R.id.summary); - holder.switch_ = (Switch) view.findViewById(R.id.switchWidget); - break; - - case HEADER_TYPE_BUTTON: - view = mInflater.inflate(R.layout.preference_header_button_item, parent, - false); - holder.icon = (ImageView) view.findViewById(R.id.icon); - holder.title = (TextView) - view.findViewById(com.android.internal.R.id.title); - holder.summary = (TextView) - view.findViewById(com.android.internal.R.id.summary); - holder.button_ = (ImageButton) view.findViewById(R.id.buttonWidget); - holder.divider_ = view.findViewById(R.id.divider); - break; - - case HEADER_TYPE_NORMAL: - view = mInflater.inflate( - R.layout.preference_header_item, parent, - false); - holder.icon = (ImageView) view.findViewById(R.id.icon); - holder.title = (TextView) - view.findViewById(com.android.internal.R.id.title); - holder.summary = (TextView) - view.findViewById(com.android.internal.R.id.summary); - break; - } - view.setTag(holder); - } else { - view = convertView; - holder = (HeaderViewHolder) view.getTag(); - } - - // All view fields must be updated every time, because the view may be recycled - switch (headerType) { - case HEADER_TYPE_CATEGORY: - holder.title.setText(header.getTitle(getContext().getResources())); - break; - - case HEADER_TYPE_SWITCH: - // Would need a different treatment if the main menu had more switches - if (header.id == R.id.wifi_settings) { - mWifiEnabler.setSwitch(holder.switch_); - } else { - mBluetoothEnabler.setSwitch(holder.switch_); - } - updateCommonHeaderView(header, holder); - break; - - case HEADER_TYPE_BUTTON: - if (header.id == R.id.security_settings) { - boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled(); - if (hasCert) { - holder.button_.setVisibility(View.VISIBLE); - holder.divider_.setVisibility(View.VISIBLE); - boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null; - if (isManaged) { - holder.button_.setImageResource(R.drawable.ic_settings_about); - } else { - holder.button_.setImageResource( - android.R.drawable.stat_notify_error); - } - holder.button_.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent( - android.provider.Settings.ACTION_MONITORING_CERT_INFO); - getContext().startActivity(intent); - } - }); - } else { - holder.button_.setVisibility(View.GONE); - holder.divider_.setVisibility(View.GONE); - } - } - updateCommonHeaderView(header, holder); - break; - - case HEADER_TYPE_NORMAL: - updateCommonHeaderView(header, holder); - break; - } - - return view; - } - - private void updateCommonHeaderView(Header header, HeaderViewHolder holder) { - if (header.extras != null - && header.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) { - String accType = header.extras.getString( - ManageAccountsSettings.KEY_ACCOUNT_TYPE); - Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType); - setHeaderIcon(holder, icon); - } else { - holder.icon.setImageResource(header.iconRes); - } - holder.title.setText(header.getTitle(getContext().getResources())); - CharSequence summary = header.getSummary(getContext().getResources()); - if (!TextUtils.isEmpty(summary)) { - holder.summary.setVisibility(View.VISIBLE); - holder.summary.setText(summary); - } else { - holder.summary.setVisibility(View.GONE); - } - } - - private void setHeaderIcon(HeaderViewHolder holder, Drawable icon) { - ViewGroup.LayoutParams lp = holder.icon.getLayoutParams(); - lp.width = getContext().getResources().getDimensionPixelSize( - R.dimen.header_icon_width); - lp.height = lp.width; - holder.icon.setLayoutParams(lp); - holder.icon.setImageDrawable(icon); - } - - public void resume() { - mWifiEnabler.resume(); - mBluetoothEnabler.resume(); - } - - public void pause() { - mWifiEnabler.pause(); - mBluetoothEnabler.pause(); - } - } - - @Override - public void onHeaderClick(Header header, int position) { - boolean revert = false; - if (header.id == R.id.account_add) { - revert = true; - } - - super.onHeaderClick(header, position); - - if (revert && mLastHeader != null) { - highlightHeader((int) mLastHeader.id); - } else { - mLastHeader = header; - } - } - - @Override - public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) { - // Override the fragment title for Wallpaper settings - int titleRes = pref.getTitleRes(); - if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) { - titleRes = R.string.wallpaper_settings_fragment_title; - } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName()) - && UserHandle.myUserId() != UserHandle.USER_OWNER) { - if (UserManager.get(this).isLinkedUser()) { - titleRes = R.string.profile_info_settings_title; - } else { - titleRes = R.string.user_info_settings_title; - } - } - startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(), - null, 0); - return true; - } - - @Override - public boolean shouldUpRecreateTask(Intent targetIntent) { - return super.shouldUpRecreateTask(new Intent(this, Settings.class)); - } - - @Override - public void setListAdapter(ListAdapter adapter) { - if (adapter == null) { - super.setListAdapter(null); - } else { - DevicePolicyManager dpm = - (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); - super.setListAdapter(new HeaderAdapter(this, getHeaders(), mAuthenticatorHelper, dpm)); - } - } - - @Override - public void onAccountsUpdated(Account[] accounts) { - // TODO: watch for package upgrades to invalidate cache; see 7206643 - mAuthenticatorHelper.updateAuthDescriptions(this); - mAuthenticatorHelper.onAccountsUpdated(this, accounts); - invalidateHeaders(); - } - - public static void requestHomeNotice() { - sShowNoHomeNotice = true; - } +public class Settings extends SettingsActivity { /* - * Settings subclasses for launching independently. - */ - public static class BluetoothSettingsActivity extends Settings { /* empty */ } - public static class WirelessSettingsActivity extends Settings { /* empty */ } - public static class TetherSettingsActivity extends Settings { /* empty */ } - public static class VpnSettingsActivity extends Settings { /* empty */ } - public static class DateTimeSettingsActivity extends Settings { /* empty */ } - public static class StorageSettingsActivity extends Settings { /* empty */ } - public static class WifiSettingsActivity extends Settings { /* empty */ } - public static class WifiP2pSettingsActivity extends Settings { /* empty */ } - public static class InputMethodAndLanguageSettingsActivity extends Settings { /* empty */ } - public static class KeyboardLayoutPickerActivity extends Settings { /* empty */ } - public static class InputMethodAndSubtypeEnablerActivity extends Settings { /* empty */ } - public static class SpellCheckersSettingsActivity extends Settings { /* empty */ } - public static class LocalePickerActivity extends Settings { /* empty */ } - public static class UserDictionarySettingsActivity extends Settings { /* empty */ } - public static class SoundSettingsActivity extends Settings { /* empty */ } - public static class DisplaySettingsActivity extends Settings { /* empty */ } - public static class DeviceInfoSettingsActivity extends Settings { /* empty */ } - public static class ApplicationSettingsActivity extends Settings { /* empty */ } - public static class ManageApplicationsActivity extends Settings { /* empty */ } - public static class AppOpsSummaryActivity extends Settings { + * Settings subclasses for launching independently. + */ + public static class BluetoothSettingsActivity extends SettingsActivity { /* empty */ } + public static class WirelessSettingsActivity extends SettingsActivity { /* empty */ } + public static class TetherSettingsActivity extends SettingsActivity { /* empty */ } + public static class VpnSettingsActivity extends SettingsActivity { /* empty */ } + public static class DateTimeSettingsActivity extends SettingsActivity { /* empty */ } + public static class StorageSettingsActivity extends SettingsActivity { /* empty */ } + public static class WifiSettingsActivity extends SettingsActivity { /* empty */ } + public static class WifiP2pSettingsActivity extends SettingsActivity { /* empty */ } + public static class InputMethodAndLanguageSettingsActivity extends SettingsActivity { /* empty */ } + public static class KeyboardLayoutPickerActivity extends SettingsActivity { /* empty */ } + public static class InputMethodAndSubtypeEnablerActivity extends SettingsActivity { /* empty */ } + public static class SpellCheckersSettingsActivity extends SettingsActivity { /* empty */ } + public static class LocalePickerActivity extends SettingsActivity { /* empty */ } + public static class UserDictionarySettingsActivity extends SettingsActivity { /* empty */ } + public static class SoundSettingsActivity extends SettingsActivity { /* empty */ } + public static class DisplaySettingsActivity extends SettingsActivity { /* empty */ } + public static class DeviceInfoSettingsActivity extends SettingsActivity { /* empty */ } + public static class ApplicationSettingsActivity extends SettingsActivity { /* empty */ } + public static class ManageApplicationsActivity extends SettingsActivity { /* empty */ } + public static class AppOpsSummaryActivity extends SettingsActivity { @Override public boolean isValidFragment(String className) { if (AppOpsSummary.class.getName().equals(className)) { return true; } return super.isValidFragment(className); - } + } } - public static class StorageUseActivity extends Settings { /* empty */ } - public static class DevelopmentSettingsActivity extends Settings { /* empty */ } - public static class AccessibilitySettingsActivity extends Settings { /* empty */ } - public static class CaptioningSettingsActivity extends Settings { /* empty */ } - public static class SecuritySettingsActivity extends Settings { /* empty */ } - public static class LocationSettingsActivity extends Settings { /* empty */ } - public static class PrivacySettingsActivity extends Settings { /* empty */ } - public static class RunningServicesActivity extends Settings { /* empty */ } - public static class ManageAccountsSettingsActivity extends Settings { /* empty */ } - public static class PowerUsageSummaryActivity extends Settings { /* empty */ } - public static class AccountSyncSettingsActivity extends Settings { /* empty */ } - public static class AccountSyncSettingsInAddAccountActivity extends Settings { /* empty */ } - public static class CryptKeeperSettingsActivity extends Settings { /* empty */ } - public static class DeviceAdminSettingsActivity extends Settings { /* empty */ } - public static class DataUsageSummaryActivity extends Settings { /* empty */ } - public static class AdvancedWifiSettingsActivity extends Settings { /* empty */ } - public static class TextToSpeechSettingsActivity extends Settings { /* empty */ } - public static class AndroidBeamSettingsActivity extends Settings { /* empty */ } - public static class WifiDisplaySettingsActivity extends Settings { /* empty */ } - public static class DreamSettingsActivity extends Settings { /* empty */ } - public static class NotificationStationActivity extends Settings { /* empty */ } - public static class UserSettingsActivity extends Settings { /* empty */ } - public static class NotificationAccessSettingsActivity extends Settings { /* empty */ } - public static class UsbSettingsActivity extends Settings { /* empty */ } - public static class TrustedCredentialsSettingsActivity extends Settings { /* empty */ } - public static class PaymentSettingsActivity extends Settings { /* empty */ } - public static class PrintSettingsActivity extends Settings { /* empty */ } - public static class PrintJobSettingsActivity extends Settings { /* empty */ } + public static class StorageUseActivity extends SettingsActivity { /* empty */ } + public static class DevelopmentSettingsActivity extends SettingsActivity { /* empty */ } + public static class AccessibilitySettingsActivity extends SettingsActivity { /* empty */ } + public static class CaptioningSettingsActivity extends SettingsActivity { /* empty */ } + public static class AccessibilityInversionSettingsActivity extends SettingsActivity { /* empty */ } + public static class AccessibilityContrastSettingsActivity extends SettingsActivity { /* empty */ } + public static class AccessibilityDaltonizerSettingsActivity extends SettingsActivity { /* empty */ } + public static class SecuritySettingsActivity extends SettingsActivity { /* empty */ } + public static class LocationSettingsActivity extends SettingsActivity { /* empty */ } + public static class PrivacySettingsActivity extends SettingsActivity { /* empty */ } + public static class RunningServicesActivity extends SettingsActivity { /* empty */ } + public static class ManageAccountsSettingsActivity extends SettingsActivity { /* empty */ } + public static class PowerUsageSummaryActivity extends SettingsActivity { /* empty */ } + public static class AccountSyncSettingsActivity extends SettingsActivity { /* empty */ } + public static class AccountSyncSettingsInAddAccountActivity extends SettingsActivity { /* empty */ } + public static class CryptKeeperSettingsActivity extends SettingsActivity { /* empty */ } + public static class DeviceAdminSettingsActivity extends SettingsActivity { /* empty */ } + public static class DataUsageSummaryActivity extends SettingsActivity { /* empty */ } + public static class AdvancedWifiSettingsActivity extends SettingsActivity { /* empty */ } + public static class TextToSpeechSettingsActivity extends SettingsActivity { /* empty */ } + public static class AndroidBeamSettingsActivity extends SettingsActivity { /* empty */ } + public static class WifiDisplaySettingsActivity extends SettingsActivity { /* empty */ } + public static class DreamSettingsActivity extends SettingsActivity { /* empty */ } + public static class NotificationStationActivity extends SettingsActivity { /* empty */ } + public static class UserSettingsActivity extends SettingsActivity { /* empty */ } + public static class NotificationAccessSettingsActivity extends SettingsActivity { /* empty */ } + public static class UsbSettingsActivity extends SettingsActivity { /* empty */ } + public static class TrustedCredentialsSettingsActivity extends SettingsActivity { /* empty */ } + public static class PaymentSettingsActivity extends SettingsActivity { /* empty */ } + public static class PrintSettingsActivity extends SettingsActivity { /* empty */ } + public static class PrintJobSettingsActivity extends SettingsActivity { /* empty */ } + public static class ZenModeSettingsActivity extends SettingsActivity { /* empty */ } + public static class NotificationSettingsActivity extends SettingsActivity { /* empty */ } + + public static class TopLevelSettings extends SettingsActivity { /* empty */ } } + diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java new file mode 100644 index 000000000..e49d8277e --- /dev/null +++ b/src/com/android/settings/SettingsActivity.java @@ -0,0 +1,1872 @@ +/* + * Copyright (C) 2014 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.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.OnAccountsUpdateListener; +import android.app.ActionBar; +import android.app.Activity; +import android.app.Fragment; +import android.app.FragmentManager; +import android.app.FragmentTransaction; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.app.admin.DevicePolicyManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ResolveInfo; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.graphics.drawable.Drawable; +import android.nfc.NfcAdapter; +import android.os.Bundle; +import android.os.Handler; +import android.os.INetworkManagementService; +import android.os.Message; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.os.UserManager; +import android.preference.Preference; +import android.preference.PreferenceFragment; +import android.preference.PreferenceManager; +import android.preference.PreferenceScreen; +import android.support.v4.app.ActionBarDrawerToggle; +import android.support.v4.widget.DrawerLayout; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.util.Log; +import android.util.TypedValue; +import android.util.Xml; +import android.view.LayoutInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.AbsListView; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.Switch; +import android.widget.TextView; + +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.XmlUtils; +import com.android.settings.accessibility.AccessibilitySettings; +import com.android.settings.accessibility.CaptionPropertiesFragment; +import com.android.settings.accounts.AccountSyncSettings; +import com.android.settings.accounts.AuthenticatorHelper; +import com.android.settings.accounts.ManageAccountsSettings; +import com.android.settings.applications.ManageApplications; +import com.android.settings.applications.ProcessStatsUi; +import com.android.settings.bluetooth.BluetoothEnabler; +import com.android.settings.bluetooth.BluetoothSettings; +import com.android.settings.dashboard.DashboardSummary; +import com.android.settings.deviceinfo.Memory; +import com.android.settings.deviceinfo.UsbSettings; +import com.android.settings.fuelgauge.PowerUsageSummary; +import com.android.settings.indexer.Index; +import com.android.settings.indexer.IndexableRef; +import com.android.settings.inputmethod.InputMethodAndLanguageSettings; +import com.android.settings.inputmethod.KeyboardLayoutPickerFragment; +import com.android.settings.inputmethod.SpellCheckersSettings; +import com.android.settings.inputmethod.UserDictionaryList; +import com.android.settings.location.LocationSettings; +import com.android.settings.net.DataUsageMeteredSettings; +import com.android.settings.nfc.AndroidBeam; +import com.android.settings.nfc.PaymentSettings; +import com.android.settings.print.PrintJobSettingsFragment; +import com.android.settings.print.PrintSettingsFragment; +import com.android.settings.tts.TextToSpeechSettings; +import com.android.settings.users.UserSettings; +import com.android.settings.vpn2.VpnSettings; +import com.android.settings.wfd.WifiDisplaySettings; +import com.android.settings.wifi.AdvancedWifiSettings; +import com.android.settings.wifi.WifiEnabler; +import com.android.settings.wifi.WifiSettings; +import com.android.settings.wifi.p2p.WifiP2pSettings; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; + +public class SettingsActivity extends Activity + implements PreferenceManager.OnPreferenceTreeClickListener, + PreferenceFragment.OnPreferenceStartFragmentCallback, + ButtonBarHandler, OnAccountsUpdateListener, FragmentManager.OnBackStackChangedListener { + + private static final String LOG_TAG = "Settings"; + + // Constants for state save/restore + private static final String SAVE_KEY_HEADERS_TAG = ":settings:headers"; + private static final String SAVE_KEY_CURRENT_HEADER_TAG = ":settings:cur_header"; + + /** + * When starting this activity, the invoking Intent can contain this extra + * string to specify which fragment should be initially displayed. + * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity + * will call isValidFragment() to confirm that the fragment class name is valid for this + * activity. + */ + public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment"; + + /** + * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT}, + * this extra can also be specified to supply a Bundle of arguments to pass + * to that fragment when it is instantiated during the initial creation + * of the activity. + */ + public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args"; + + /** + * When starting this activity, the invoking Intent can contain this extra + * boolean that the header list should not be displayed. This is most often + * used in conjunction with {@link #EXTRA_SHOW_FRAGMENT} to launch + * the activity to display a specific fragment that the user has navigated + * to. + */ + public static final String EXTRA_NO_HEADERS = ":settings:no_headers"; + + public static final String BACK_STACK_PREFS = ":settings:prefs"; + + // extras that allow any preference activity to be launched as part of a wizard + + // show Back and Next buttons? takes boolean parameter + // Back will then return RESULT_CANCELED and Next RESULT_OK + protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar"; + + // add a Skip button? + private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip"; + + // specify custom text for the Back or Next buttons, or cause a button to not appear + // at all by setting it to null + protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text"; + protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text"; + + /** + * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT}, + * this extra can also be specify to supply the title to be shown for + * that fragment. + */ + protected static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title"; + + private static final String META_DATA_KEY_HEADER_ID = + "com.android.settings.TOP_LEVEL_HEADER_ID"; + + private static final String META_DATA_KEY_FRAGMENT_CLASS = + "com.android.settings.FRAGMENT_CLASS"; + + private static final String EXTRA_UI_OPTIONS = "settings:ui_options"; + + private static boolean sShowNoHomeNotice = false; + + private String mFragmentClass; + private int mTopLevelHeaderId; + private Header mFirstHeader; + private Header mSelectedHeader; + private Header mCurrentHeader; + + private CharSequence mInitialTitle; + private Header mInitialHeader; + + // Show only these settings for restricted users + private int[] SETTINGS_FOR_RESTRICTED = { + R.id.wireless_section, + R.id.wifi_settings, + R.id.bluetooth_settings, + R.id.data_usage_settings, + R.id.wireless_settings, + R.id.device_section, + R.id.sound_settings, + R.id.display_settings, + R.id.storage_settings, + R.id.application_settings, + R.id.battery_settings, + R.id.personal_section, + R.id.location_settings, + R.id.security_settings, + R.id.language_settings, + R.id.user_settings, + R.id.account_settings, + R.id.account_add, + R.id.system_section, + R.id.date_time_settings, + R.id.about_settings, + R.id.accessibility_settings, + R.id.print_settings, + R.id.nfc_payment_settings, + R.id.home_settings, + R.id.dashboard + }; + + private static final String[] ENTRY_FRAGMENTS = { + WirelessSettings.class.getName(), + WifiSettings.class.getName(), + AdvancedWifiSettings.class.getName(), + BluetoothSettings.class.getName(), + TetherSettings.class.getName(), + WifiP2pSettings.class.getName(), + VpnSettings.class.getName(), + DateTimeSettings.class.getName(), + LocalePicker.class.getName(), + InputMethodAndLanguageSettings.class.getName(), + SpellCheckersSettings.class.getName(), + UserDictionaryList.class.getName(), + UserDictionarySettings.class.getName(), + SoundSettings.class.getName(), + DisplaySettings.class.getName(), + DeviceInfoSettings.class.getName(), + ManageApplications.class.getName(), + ProcessStatsUi.class.getName(), + NotificationStation.class.getName(), + LocationSettings.class.getName(), + SecuritySettings.class.getName(), + PrivacySettings.class.getName(), + DeviceAdminSettings.class.getName(), + AccessibilitySettings.class.getName(), + CaptionPropertiesFragment.class.getName(), + com.android.settings.accessibility.ToggleInversionPreferenceFragment.class.getName(), + com.android.settings.accessibility.ToggleContrastPreferenceFragment.class.getName(), + com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(), + TextToSpeechSettings.class.getName(), + Memory.class.getName(), + DevelopmentSettings.class.getName(), + UsbSettings.class.getName(), + AndroidBeam.class.getName(), + WifiDisplaySettings.class.getName(), + PowerUsageSummary.class.getName(), + AccountSyncSettings.class.getName(), + CryptKeeperSettings.class.getName(), + DataUsageSummary.class.getName(), + DreamSettings.class.getName(), + UserSettings.class.getName(), + NotificationAccessSettings.class.getName(), + ManageAccountsSettings.class.getName(), + PrintSettingsFragment.class.getName(), + PrintJobSettingsFragment.class.getName(), + TrustedCredentialsSettings.class.getName(), + PaymentSettings.class.getName(), + KeyboardLayoutPickerFragment.class.getName(), + DashboardSummary.class.getName(), + ZenModeSettings.class.getName() + }; + + private SharedPreferences mDevelopmentPreferences; + private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener; + + // TODO: Update Call Settings based on airplane mode state. + + protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>(); + + private AuthenticatorHelper mAuthenticatorHelper; + private boolean mListeningToAccountUpdates; + + private Button mNextButton; + + private boolean mBatteryPresent = true; + private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() { + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (Intent.ACTION_BATTERY_CHANGED.equals(action)) { + boolean batteryPresent = Utils.isBatteryPresent(intent); + + if (mBatteryPresent != batteryPresent) { + mBatteryPresent = batteryPresent; + invalidateHeaders(); + } + } + } + }; + + private final ArrayList<Header> mHeaders = new ArrayList<Header>(); + private HeaderAdapter mHeaderAdapter; + + private DrawerLayout mDrawerLayout; + private ListView mDrawer; + private ActionBarDrawerToggle mDrawerToggle; + private ActionBar mActionBar; + + private static final int MSG_BUILD_HEADERS = 1; + private Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_BUILD_HEADERS: { + mHeaders.clear(); + onBuildHeaders(mHeaders); + mHeaderAdapter.notifyDataSetChanged(); + if (mCurrentHeader != null) { + Header mappedHeader = findBestMatchingHeader(mCurrentHeader, mHeaders); + if (mappedHeader != null) { + setSelectedHeader(mappedHeader); + } + } + } break; + } + } + }; + + private static int NO_DATA_RES_ID = 0; + + /** + * Indexable data description. + * + * Known restriction: we are only searching (for now) the first level of Settings. + */ + private static IndexableRef[] INDEXABLE_REFS = new IndexableRef[] { + new IndexableRef(1, NO_DATA_RES_ID, + WifiSettings.class.getName(), + R.drawable.ic_settings_wireless), + new IndexableRef(2, R.xml.bluetooth_settings, + BluetoothSettings.class.getName(), + R.drawable.ic_settings_bluetooth2), + new IndexableRef(3, R.xml.data_usage_metered_prefs, + DataUsageMeteredSettings.class.getName(), + R.drawable.ic_settings_data_usage), + new IndexableRef(4, R.xml.wireless_settings, + WirelessSettings.class.getName(), + R.drawable.empty_icon), + new IndexableRef(5, R.xml.home_selection, + HomeSettings.class.getName(), + R.drawable.ic_settings_home), + new IndexableRef(6, R.xml.sound_settings, + SoundSettings.class.getName(), + R.drawable.ic_settings_sound), + new IndexableRef(7, R.xml.display_settings, + DisplaySettings.class.getName(), + R.drawable.ic_settings_display), + new IndexableRef(7, NO_DATA_RES_ID, + WallpaperTypeSettings.class.getName(), + R.drawable.ic_settings_display), + new IndexableRef(8, R.xml.device_info_memory, + Memory.class.getName(), + R.drawable.ic_settings_storage), + new IndexableRef(9, R.xml.power_usage_summary, + PowerUsageSummary.class.getName(), + R.drawable.ic_settings_battery), + new IndexableRef(10, R.xml.user_settings, + UserSettings.class.getName(), + R.drawable.ic_settings_multiuser), + new IndexableRef(11, R.xml.location_settings, + LocationSettings.class.getName(), + R.drawable.ic_settings_location), + new IndexableRef(12, R.xml.security_settings, + SecuritySettings.class.getName(), + R.drawable.ic_settings_security), + new IndexableRef(13, R.xml.language_settings, + InputMethodAndLanguageSettings.class.getName(), + R.drawable.ic_settings_language), + new IndexableRef(14, R.xml.privacy_settings, + PrivacySettings.class.getName(), + R.drawable.ic_settings_backup), + new IndexableRef(15, R.xml.date_time_prefs, + DateTimeSettings.class.getName(), + R.drawable.ic_settings_date_time), + new IndexableRef(16, R.xml.accessibility_settings, + AccessibilitySettings.class.getName(), + R.drawable.ic_settings_accessibility), + new IndexableRef(17, R.xml.print_settings, + PrintSettingsFragment.class.getName(), + com.android.internal.R.drawable.ic_print), + new IndexableRef(18, R.xml.development_prefs, + DevelopmentSettings.class.getName(), + R.drawable.ic_settings_development), + new IndexableRef(19, R.xml.device_info_settings, + DeviceInfoSettings.class.getName(), + R.drawable.ic_settings_about), + }; + + @Override + public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) { + // Override the fragment title for Wallpaper settings + int titleRes = pref.getTitleRes(); + if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) { + titleRes = R.string.wallpaper_settings_fragment_title; + } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName()) + && UserHandle.myUserId() != UserHandle.USER_OWNER) { + if (UserManager.get(this).isLinkedUser()) { + titleRes = R.string.profile_info_settings_title; + } else { + titleRes = R.string.user_info_settings_title; + } + } + startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(), + null, 0); + return true; + } + + @Override + public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { + return false; + } + + private class DrawerListener implements DrawerLayout.DrawerListener { + @Override + public void onDrawerOpened(View drawerView) { + mDrawerToggle.onDrawerOpened(drawerView); + } + + @Override + public void onDrawerClosed(View drawerView) { + mDrawerToggle.onDrawerClosed(drawerView); + // Cannot process clicks when the App is finishing + if (isFinishing() || mSelectedHeader == null) { + return; + } + switchToHeader(mSelectedHeader, false, false); + mSelectedHeader = null; + } + + @Override + public void onDrawerSlide(View drawerView, float slideOffset) { + mDrawerToggle.onDrawerSlide(drawerView, slideOffset); + } + + @Override + public void onDrawerStateChanged(int newState) { + mDrawerToggle.onDrawerStateChanged(newState); + } + } + + private class DrawerItemClickListener implements ListView.OnItemClickListener { + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + mDrawerLayout.closeDrawer(mDrawer); + onListItemClick((ListView)parent, view, position, id); + } + } + + private Header findBestMatchingHeader(Header current, ArrayList<Header> from) { + ArrayList<Header> matches = new ArrayList<Header>(); + for (int j=0; j<from.size(); j++) { + Header oh = from.get(j); + if (current == oh || (current.id != HEADER_ID_UNDEFINED && current.id == oh.id)) { + // Must be this one. + matches.clear(); + matches.add(oh); + break; + } + if (current.fragment != null) { + if (current.fragment.equals(oh.fragment)) { + matches.add(oh); + } + } else if (current.intent != null) { + if (current.intent.equals(oh.intent)) { + matches.add(oh); + } + } else if (current.title != null) { + if (current.title.equals(oh.title)) { + matches.add(oh); + } + } + } + final int NM = matches.size(); + if (NM == 1) { + return matches.get(0); + } else if (NM > 1) { + for (int j=0; j<NM; j++) { + Header oh = matches.get(j); + if (current.fragmentArguments != null && + current.fragmentArguments.equals(oh.fragmentArguments)) { + return oh; + } + if (current.extras != null && current.extras.equals(oh.extras)) { + return oh; + } + if (current.title != null && current.title.equals(oh.title)) { + return oh; + } + } + } + return null; + } + + private void invalidateHeaders() { + if (!mHandler.hasMessages(MSG_BUILD_HEADERS)) { + mHandler.sendEmptyMessage(MSG_BUILD_HEADERS); + } + } + + @Override + protected void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + + // Sync the toggle state after onRestoreInstanceState has occurred. + mDrawerToggle.syncState(); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + mDrawerToggle.onConfigurationChanged(newConfig); + Index.getInstance(this).update(); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (mDrawerToggle.onOptionsItemSelected(item)) { + return true; + } + return super.onOptionsItemSelected(item); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) { + getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0)); + } + + Index.getInstance(this).addIndexableData(INDEXABLE_REFS); + Index.getInstance(this).update(); + + mAuthenticatorHelper = new AuthenticatorHelper(); + mAuthenticatorHelper.updateAuthDescriptions(this); + mAuthenticatorHelper.onAccountsUpdated(this, null); + + DevicePolicyManager dpm = + (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); + + mHeaderAdapter= new HeaderAdapter(this, mHeaders, mAuthenticatorHelper, dpm); + + mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE, + Context.MODE_PRIVATE); + + getMetaData(); + + super.onCreate(savedInstanceState); + + setContentView(R.layout.settings_main); + + getFragmentManager().addOnBackStackChangedListener(this); + + mActionBar = getActionBar(); + mActionBar.setDisplayHomeAsUpEnabled(true); + mActionBar.setHomeButtonEnabled(true); + + mDrawer = (ListView) findViewById(R.id.headers_drawer); + mDrawer.setAdapter(mHeaderAdapter); + mDrawer.setOnItemClickListener(new DrawerItemClickListener()); + mDrawer.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE); + + mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); + mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, + R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close); + + if (savedInstanceState != null) { + // We are restarting from a previous saved state; used that to + // initialize, instead of starting fresh. + mInitialTitle = getTitle(); + + ArrayList<Header> headers = + savedInstanceState.getParcelableArrayList(SAVE_KEY_HEADERS_TAG); + if (headers != null) { + mHeaders.addAll(headers); + int curHeader = savedInstanceState.getInt(SAVE_KEY_CURRENT_HEADER_TAG, + (int) HEADER_ID_UNDEFINED); + if (curHeader >= 0 && curHeader < mHeaders.size()) { + setSelectedHeader(mHeaders.get(curHeader)); + mInitialHeader = mCurrentHeader; + } + setTitleFromBackStack(); + } + } else { + String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT); + Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS); + + // We need to build the Headers in all cases + onBuildHeaders(mHeaders); + + if (initialFragment != null) { + // If we are just showing a fragment, we want to run in + // new fragment mode, but don't need to compute and show + // the headers. + final int initialTitleResId = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE, 0); + mInitialTitle = (initialTitleResId > 0) ? getText(initialTitleResId) : getTitle(); + setTitle(mInitialTitle); + switchToHeaderInner(initialFragment, initialArguments, true, false, mInitialTitle); + setSelectedHeaderById(mTopLevelHeaderId); + mInitialHeader = mCurrentHeader; + } else { + // If there are headers, then at this point we need to show + // them and, depending on the screen, we may also show in-line + // the currently selected preference fragment. + if (mHeaders.size() > 0) { + mInitialHeader = onGetInitialHeader(); + mInitialTitle = getHeaderTitle(mInitialHeader); + switchToHeader(mInitialHeader, false, true); + } + } + } + + // see if we should show Back/Next buttons + Intent intent = getIntent(); + if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) { + + View buttonBar = findViewById(com.android.internal.R.id.button_bar); + if (buttonBar != null) { + buttonBar.setVisibility(View.VISIBLE); + + Button backButton = (Button)findViewById(com.android.internal.R.id.back_button); + backButton.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + setResult(RESULT_CANCELED); + finish(); + } + }); + Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button); + skipButton.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + setResult(RESULT_OK); + finish(); + } + }); + mNextButton = (Button)findViewById(com.android.internal.R.id.next_button); + mNextButton.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + setResult(RESULT_OK); + 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); + } + } + 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); + } + } + if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) { + skipButton.setVisibility(View.VISIBLE); + } + } + } + + if (!onIsHidingHeaders()) { + highlightHeader(mTopLevelHeaderId); + } + } + + public Header onGetInitialHeader() { + String fragmentClass = getStartingFragmentClass(super.getIntent()); + if (fragmentClass != null) { + Header header = new Header(); + header.fragment = fragmentClass; + header.title = getTitle(); + header.fragmentArguments = getIntent().getExtras(); + return header; + } + + return mFirstHeader; + } + + @Override + public void onBackPressed() { + if (mDrawerLayout.isDrawerOpen(mDrawer)) { + mDrawerLayout.closeDrawer(mDrawer); + return; + } + super.onBackPressed(); + } + + @Override + public void onBackStackChanged() { + if (setTitleFromBackStack() == 0) { + setSelectedHeaderById(mInitialHeader.id); + } + } + + private int setTitleFromBackStack() { + final int count = getFragmentManager().getBackStackEntryCount(); + + if (count == 0) { + setTitle(mInitialTitle); + return 0; + } + + FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1); + setTitleFromBackStackEntry(bse); + + return count; + } + + private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) { + final CharSequence title; + final int titleRes = bse.getBreadCrumbTitleRes(); + if (titleRes > 0) { + title = getText(titleRes); + } else { + title = bse.getBreadCrumbTitle(); + } + if (title != null) { + setTitle(title); + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + if (mHeaders.size() > 0) { + outState.putParcelableArrayList(SAVE_KEY_HEADERS_TAG, mHeaders); + if (mCurrentHeader != null) { + int index = mHeaders.indexOf(mCurrentHeader); + if (index >= 0) { + outState.putInt(SAVE_KEY_CURRENT_HEADER_TAG, index); + } + } + } + } + + @Override + public void onResume() { + super.onResume(); + + mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() { + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + invalidateHeaders(); + } + }; + mDevelopmentPreferences.registerOnSharedPreferenceChangeListener( + mDevelopmentPreferencesListener); + + mHeaderAdapter.resume(this); + invalidateHeaders(); + + registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + + mDrawerLayout.setDrawerListener(new DrawerListener()); + } + + @Override + public void onPause() { + super.onPause(); + + mDrawerLayout.setDrawerListener(null); + + unregisterReceiver(mBatteryInfoReceiver); + + mHeaderAdapter.pause(); + + mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener( + mDevelopmentPreferencesListener); + + mDevelopmentPreferencesListener = null; + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (mListeningToAccountUpdates) { + AccountManager.get(this).removeOnAccountsUpdatedListener(this); + } + } + + protected boolean isValidFragment(String fragmentName) { + // Almost all fragments are wrapped in this, + // except for a few that have their own activities. + for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) { + if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true; + } + return false; + } + + private CharSequence getHeaderTitle(Header header) { + if (header == null || header.fragment == null) return getTitle(); + final CharSequence title; + if (header.fragment.equals(DashboardSummary.class.getName())) { + title = getResources().getString(R.string.settings_label); + } else { + title = header.getTitle(getResources()); + } + return title; + } + + private void setSelectedHeaderById(long headerId) { + final int count = mHeaders.size(); + for (int n = 0; n < count; n++) { + Header h = mHeaders.get(n); + if (h.id == headerId) { + setSelectedHeader(h); + return; + } + } + } + + /** + * As the Headers can be rebuilt, their references can change, so use this method with caution! + */ + private void setSelectedHeader(Header header) { + if (header == null) { + mCurrentHeader = null; + return; + } + // Update selected Header into Drawer only if it is not "Add Account" + if (header.id == R.id.account_add) { + mDrawer.clearChoices(); + return; + } + mCurrentHeader = header; + int index = mHeaders.indexOf(header); + if (index >= 0) { + mDrawer.setItemChecked(index, true); + } else { + mDrawer.clearChoices(); + } + } + + private void highlightHeader(int id) { + if (id != 0) { + Integer index = mHeaderIndexMap.get(id); + if (index != null) { + mDrawer.setItemChecked(index, true); + if (mDrawer.getVisibility() == View.VISIBLE) { + mDrawer.smoothScrollToPosition(index); + } + } + } + } + + /** + * When in two-pane mode, switch to the fragment pane to show the given + * preference fragment. + * + * @param header The new header to display. + * @param validate true means that the fragment's Header needs to be validated. + * @param initial true means that it is the initial Header. + */ + private void switchToHeader(Header header, boolean validate, boolean initial) { + if (header == null) { + return; + } + // For switching to another Header it should be a different one + if (mCurrentHeader == null || header.id != mCurrentHeader.id) { + if (header.fragment != null) { + boolean addToBackStack; + if (initial) { + addToBackStack = false; + } else { + if (header.id != mInitialHeader.id) { + addToBackStack = true; + } else { + addToBackStack = (mTopLevelHeaderId > 0); + } + } + switchToHeaderInner(header.fragment, header.fragmentArguments, validate, + addToBackStack, getHeaderTitle(header)); + setSelectedHeader(header); + } else if (header.intent != null) { + setSelectedHeader(header); + startActivity(header.intent); + } else { + throw new IllegalStateException( + "Can't switch to header that has no Fragment nor Intent"); + } + } + } + + /** + * Switch the fragment pane to show the given preference fragment. + * + * (used for initial fragment) + * + * @param fragmentName The name of the fragment to display. + * @param args Optional arguments to supply to the fragment. + * @param validate true means that the fragment's Header needs to be validated. + * @param title The title of the fragment to display. + */ + private void switchToHeader(String fragmentName, Bundle args, boolean validate, + CharSequence title) { + setSelectedHeader(null); + switchToHeaderInner(fragmentName, args, validate, false, title); + } + + /** + * Switch to a specific Header with taking care of validation, Title and BackStack + */ + private void switchToHeaderInner(String fragmentName, Bundle args, boolean validate, + boolean addToBackStack, CharSequence title) { + getFragmentManager().popBackStack(BACK_STACK_PREFS, + FragmentManager.POP_BACK_STACK_INCLUSIVE); + if (validate && !isValidFragment(fragmentName)) { + throw new IllegalArgumentException("Invalid fragment for this activity: " + + fragmentName); + } + Fragment f = Fragment.instantiate(this, fragmentName, args); + FragmentTransaction transaction = getFragmentManager().beginTransaction(); + transaction.replace(R.id.prefs, f); + transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); + if (addToBackStack) { + transaction.addToBackStack(BACK_STACK_PREFS); + } + if (title != null) { + transaction.setBreadCrumbTitle(title); + } + transaction.commitAllowingStateLoss(); + } + + @Override + public void onNewIntent(Intent intent) { + super.onNewIntent(intent); + + // If it is not launched from history, then reset to top-level + if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) { + if (mDrawer != null) { + mDrawer.setSelectionFromTop(0, 0); + } + } + } + + /** + * Called to determine whether the header list should be hidden. + * The default implementation returns the + * value given in {@link #EXTRA_NO_HEADERS} or false if it is not supplied. + * This is set to false, for example, when the activity is being re-launched + * to show a particular preference activity. + */ + public boolean onIsHidingHeaders() { + return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false); + } + + @Override + public Intent getIntent() { + Intent superIntent = super.getIntent(); + String startingFragment = getStartingFragmentClass(superIntent); + // This is called from super.onCreate, isMultiPane() is not yet reliable + // Do not use onIsHidingHeaders either, which relies itself on this method + if (startingFragment != null) { + Intent modIntent = new Intent(superIntent); + modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment); + Bundle args = superIntent.getExtras(); + if (args != null) { + args = new Bundle(args); + } else { + args = new Bundle(); + } + args.putParcelable("intent", superIntent); + modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras()); + return modIntent; + } + return superIntent; + } + + /** + * Checks if the component name in the intent is different from the Settings class and + * returns the class name to load as a fragment. + */ + private String getStartingFragmentClass(Intent intent) { + if (mFragmentClass != null) return mFragmentClass; + + String intentClass = intent.getComponent().getClassName(); + if (intentClass.equals(getClass().getName())) return null; + + if ("com.android.settings.ManageApplications".equals(intentClass) + || "com.android.settings.RunningServices".equals(intentClass) + || "com.android.settings.applications.StorageUse".equals(intentClass)) { + // Old names of manage apps. + intentClass = com.android.settings.applications.ManageApplications.class.getName(); + } + + return intentClass; + } + + /** + * Start a new fragment containing a preference panel. If the preferences + * are being displayed in multi-pane mode, the given fragment class will + * be instantiated and placed in the appropriate pane. If running in + * single-pane mode, a new activity will be launched in which to show the + * fragment. + * + * @param fragmentClass Full name of the class implementing the fragment. + * @param args Any desired arguments to supply to the fragment. + * @param titleRes Optional resource identifier of the title of this + * fragment. + * @param titleText Optional text of the title of this fragment. + * @param resultTo Optional fragment that result data should be sent to. + * If non-null, resultTo.onActivityResult() will be called when this + * preference panel is done. The launched panel must use + * {@link #finishPreferencePanel(Fragment, int, Intent)} when done. + * @param resultRequestCode If resultTo is non-null, this is the caller's + * request code to be received with the resut. + */ + public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes, + CharSequence titleText, Fragment resultTo, + int resultRequestCode) { + startWithFragment(fragmentClass, args, resultTo, resultRequestCode, titleRes, titleText); + } + + /** + * Called by a preference panel fragment to finish itself. + * + * @param caller The fragment that is asking to be finished. + * @param resultCode Optional result code to send back to the original + * launching fragment. + * @param resultData Optional result data to send back to the original + * launching fragment. + */ + public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) { + setResult(resultCode, resultData); + } + + /** + * Start a new fragment. + * + * @param fragment The fragment to start + * @param push If true, the current fragment will be pushed onto the back stack. If false, + * the current fragment will be replaced. + */ + public void startPreferenceFragment(Fragment fragment, boolean push) { + FragmentTransaction transaction = getFragmentManager().beginTransaction(); + transaction.replace(R.id.prefs, fragment); + if (push) { + transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); + transaction.addToBackStack(BACK_STACK_PREFS); + } else { + transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); + } + transaction.commitAllowingStateLoss(); + } + + /** + * Start a new fragment. + * + * @param fragmentName The name of the fragment to display. + * @param args Optional arguments to supply to the fragment. + * @param resultTo Option fragment that should receive the result of + * the activity launch. + * @param resultRequestCode If resultTo is non-null, this is the request code in which to + * report the result. + * @param titleRes Resource ID of string to display for the title of. If the Resource ID is a + * valid one then it will be used to get the title. Otherwise the titleText + * argument will be used as the title. + * @param titleText string to display for the title of. + */ + private void startWithFragment(String fragmentName, Bundle args, Fragment resultTo, + int resultRequestCode, int titleRes, CharSequence titleText) { + final CharSequence cs; + if (titleRes != 0) { + cs = getText(titleRes); + } else { + cs = titleText; + } + + Fragment f = Fragment.instantiate(this, fragmentName, args); + if (resultTo != null) { + f.setTargetFragment(resultTo, resultRequestCode); + } + FragmentTransaction transaction = getFragmentManager().beginTransaction(); + transaction.replace(R.id.prefs, f); + transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); + transaction.addToBackStack(BACK_STACK_PREFS); + transaction.setBreadCrumbTitle(cs); + transaction.commitAllowingStateLoss(); + } + + /** + * Called when the activity needs its list of headers build. + * + * @param headers The list in which to place the headers. + */ + private void onBuildHeaders(List<Header> headers) { + loadHeadersFromResource(R.xml.settings_headers, headers); + updateHeaderList(headers); + } + + /** + * Parse the given XML file as a header description, adding each + * parsed Header into the target list. + * + * @param resid The XML resource to load and parse. + * @param target The list in which the parsed headers should be placed. + */ + private void loadHeadersFromResource(int resid, List<Header> target) { + XmlResourceParser parser = null; + try { + parser = getResources().getXml(resid); + AttributeSet attrs = Xml.asAttributeSet(parser); + + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && type != XmlPullParser.START_TAG) { + // Parse next until start tag is found + } + + String nodeName = parser.getName(); + if (!"preference-headers".equals(nodeName)) { + throw new RuntimeException( + "XML document must start with <preference-headers> tag; found" + + nodeName + " at " + parser.getPositionDescription()); + } + + Bundle curBundle = null; + + final int outerDepth = parser.getDepth(); + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + nodeName = parser.getName(); + if ("header".equals(nodeName)) { + Header header = new Header(); + + TypedArray sa = obtainStyledAttributes( + attrs, com.android.internal.R.styleable.PreferenceHeader); + header.id = sa.getResourceId( + com.android.internal.R.styleable.PreferenceHeader_id, + (int)HEADER_ID_UNDEFINED); + TypedValue tv = sa.peekValue( + com.android.internal.R.styleable.PreferenceHeader_title); + if (tv != null && tv.type == TypedValue.TYPE_STRING) { + if (tv.resourceId != 0) { + header.titleRes = tv.resourceId; + } else { + header.title = tv.string; + } + } + tv = sa.peekValue( + com.android.internal.R.styleable.PreferenceHeader_summary); + if (tv != null && tv.type == TypedValue.TYPE_STRING) { + if (tv.resourceId != 0) { + header.summaryRes = tv.resourceId; + } else { + header.summary = tv.string; + } + } + header.iconRes = sa.getResourceId( + com.android.internal.R.styleable.PreferenceHeader_icon, 0); + header.fragment = sa.getString( + com.android.internal.R.styleable.PreferenceHeader_fragment); + sa.recycle(); + + if (curBundle == null) { + curBundle = new Bundle(); + } + + final int innerDepth = parser.getDepth(); + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + String innerNodeName = parser.getName(); + if (innerNodeName.equals("extra")) { + getResources().parseBundleExtra("extra", attrs, curBundle); + XmlUtils.skipCurrentTag(parser); + + } else if (innerNodeName.equals("intent")) { + header.intent = Intent.parseIntent(getResources(), parser, attrs); + + } else { + XmlUtils.skipCurrentTag(parser); + } + } + + if (curBundle.size() > 0) { + header.fragmentArguments = curBundle; + curBundle = null; + } + + target.add(header); + } else { + XmlUtils.skipCurrentTag(parser); + } + } + + } catch (XmlPullParserException e) { + throw new RuntimeException("Error parsing headers", e); + } catch (IOException e) { + throw new RuntimeException("Error parsing headers", e); + } finally { + if (parser != null) parser.close(); + } + } + + private void updateHeaderList(List<Header> target) { + final boolean showDev = mDevelopmentPreferences.getBoolean( + DevelopmentSettings.PREF_SHOW, + android.os.Build.TYPE.equals("eng")); + int i = 0; + + final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); + mHeaderIndexMap.clear(); + while (i < target.size()) { + Header header = target.get(i); + // Ids are integers, so downcasting + int id = (int) header.id; + if (id == R.id.operator_settings || id == R.id.manufacturer_settings) { + Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header); + } else if (id == R.id.wifi_settings) { + // Remove WiFi Settings if WiFi service is not available. + if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) { + target.remove(i); + } + } else if (id == R.id.bluetooth_settings) { + // Remove Bluetooth Settings if Bluetooth service is not available. + if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) { + target.remove(i); + } + } else if (id == R.id.data_usage_settings) { + // Remove data usage when kernel module not enabled + final INetworkManagementService netManager = INetworkManagementService.Stub + .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)); + try { + if (!netManager.isBandwidthControlEnabled()) { + target.remove(i); + } + } catch (RemoteException e) { + // ignored + } + } else if (id == R.id.battery_settings) { + // Remove battery settings when battery is not available. (e.g. TV) + + if (!mBatteryPresent) { + target.remove(i); + } + } else if (id == R.id.account_settings) { + int headerIndex = i + 1; + i = insertAccountsHeaders(target, headerIndex); + } else if (id == R.id.home_settings) { + if (!updateHomeSettingHeaders(header)) { + target.remove(i); + } + } else if (id == R.id.user_settings) { + if (!UserHandle.MU_ENABLED + || !UserManager.supportsMultipleUsers() + || Utils.isMonkeyRunning()) { + target.remove(i); + } + } else if (id == R.id.nfc_payment_settings) { + if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) { + target.remove(i); + } else { + // Only show if NFC is on and we have the HCE feature + NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this); + if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature( + PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) { + target.remove(i); + } + } + } else if (id == R.id.development_settings) { + if (!showDev) { + target.remove(i); + } + } else if (id == R.id.account_add) { + if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) { + target.remove(i); + } + } + + if (i < target.size() && target.get(i) == header + && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0 + && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) { + target.remove(i); + } + + // Increment if the current one wasn't removed by the Utils code. + if (i < target.size() && target.get(i) == header) { + // Hold on to the first header, when we need to reset to the top-level + if (mFirstHeader == null && + HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) { + mFirstHeader = header; + } + mHeaderIndexMap.put(id, i); + i++; + } + } + } + + private int insertAccountsHeaders(List<Header> target, int headerIndex) { + String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes(); + List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length); + for (String accountType : accountTypes) { + CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType); + if (label == null) { + continue; + } + + Account[] accounts = AccountManager.get(this).getAccountsByType(accountType); + boolean skipToAccount = accounts.length == 1 + && !mAuthenticatorHelper.hasAccountPreferences(accountType); + Header accHeader = new Header(); + accHeader.title = label; + if (accHeader.extras == null) { + accHeader.extras = new Bundle(); + } + if (skipToAccount) { + accHeader.fragment = AccountSyncSettings.class.getName(); + accHeader.fragmentArguments = new Bundle(); + // Need this for the icon + accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType); + accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]); + accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY, + accounts[0]); + } else { + accHeader.fragment = ManageAccountsSettings.class.getName(); + accHeader.fragmentArguments = new Bundle(); + accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType); + accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, + accountType); + accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL, + label.toString()); + } + accountHeaders.add(accHeader); + mAuthenticatorHelper.preloadDrawableForType(this, accountType); + } + + // Sort by label + Collections.sort(accountHeaders, new Comparator<Header>() { + @Override + public int compare(Header h1, Header h2) { + return h1.title.toString().compareTo(h2.title.toString()); + } + }); + + for (Header header : accountHeaders) { + target.add(headerIndex++, header); + } + if (!mListeningToAccountUpdates) { + AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true); + mListeningToAccountUpdates = true; + } + return headerIndex; + } + + private boolean updateHomeSettingHeaders(Header header) { + // Once we decide to show Home settings, keep showing it forever + SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE); + if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) { + return true; + } + + try { + final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>(); + getPackageManager().getHomeActivities(homeApps); + if (homeApps.size() < 2) { + // When there's only one available home app, omit this settings + // category entirely at the top level UI. If the user just + // uninstalled the penultimate home app candidiate, we also + // now tell them about why they aren't seeing 'Home' in the list. + if (sShowNoHomeNotice) { + sShowNoHomeNotice = false; + NoHomeDialogFragment.show(this); + } + return false; + } else { + // Okay, we're allowing the Home settings category. Tell it, when + // invoked via this front door, that we'll need to be told about the + // case when the user uninstalls all but one home app. + if (header.fragmentArguments == null) { + header.fragmentArguments = new Bundle(); + } + header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true); + } + } catch (Exception e) { + // Can't look up the home activity; bail on configuring the icon + Log.w(LOG_TAG, "Problem looking up home activity!", e); + } + + sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply(); + return true; + } + + private void getMetaData() { + try { + ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(), + PackageManager.GET_META_DATA); + if (ai == null || ai.metaData == null) return; + mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID); + mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS); + } catch (NameNotFoundException nnfe) { + // No recovery + Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString()); + } + } + + // give subclasses access to the Next button + public boolean hasNextButton() { + return mNextButton != null; + } + + public Button getNextButton() { + return mNextButton; + } + + public static class NoHomeDialogFragment extends DialogFragment { + public static void show(Activity parent) { + final NoHomeDialogFragment dialog = new NoHomeDialogFragment(); + dialog.show(parent.getFragmentManager(), null); + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + return new AlertDialog.Builder(getActivity()) + .setMessage(R.string.only_one_home_message) + .setPositiveButton(android.R.string.ok, null) + .create(); + } + } + + private static class HeaderAdapter extends ArrayAdapter<Header> { + static final int HEADER_TYPE_CATEGORY = 0; + static final int HEADER_TYPE_NORMAL = 1; + static final int HEADER_TYPE_SWITCH = 2; + static final int HEADER_TYPE_BUTTON = 3; + private static final int HEADER_TYPE_COUNT = HEADER_TYPE_BUTTON + 1; + + private final WifiEnabler mWifiEnabler; + private final BluetoothEnabler mBluetoothEnabler; + private AuthenticatorHelper mAuthHelper; + private DevicePolicyManager mDevicePolicyManager; + + private static class HeaderViewHolder { + ImageView mIcon; + TextView mTitle; + TextView mSummary; + Switch mSwitch; + ImageButton mButton; + View mDivider; + } + + private LayoutInflater mInflater; + + static int getHeaderType(Header header) { + if (header.fragment == null && header.intent == null) { + return HEADER_TYPE_CATEGORY; + } else if (header.id == R.id.security_settings) { + return HEADER_TYPE_BUTTON; + } else { + return HEADER_TYPE_NORMAL; + } + } + + @Override + public int getItemViewType(int position) { + Header header = getItem(position); + return getHeaderType(header); + } + + @Override + public boolean areAllItemsEnabled() { + return false; // because of categories + } + + @Override + public boolean isEnabled(int position) { + return getItemViewType(position) != HEADER_TYPE_CATEGORY; + } + + @Override + public int getViewTypeCount() { + return HEADER_TYPE_COUNT; + } + + @Override + public boolean hasStableIds() { + return true; + } + + public HeaderAdapter(Context context, List<Header> objects, + AuthenticatorHelper authenticatorHelper, DevicePolicyManager dpm) { + super(context, 0, objects); + + mAuthHelper = authenticatorHelper; + mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + // Temp Switches provided as placeholder until the adapter replaces these with actual + // Switches inflated from their layouts. Must be done before adapter is set in super + mWifiEnabler = new WifiEnabler(context, new Switch(context)); + mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context)); + mDevicePolicyManager = dpm; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + HeaderViewHolder holder; + Header header = getItem(position); + int headerType = getHeaderType(header); + View view = null; + + if (convertView == null) { + holder = new HeaderViewHolder(); + switch (headerType) { + case HEADER_TYPE_CATEGORY: + view = new TextView(getContext(), null, + android.R.attr.listSeparatorTextViewStyle); + holder.mTitle = (TextView) view; + break; + + case HEADER_TYPE_SWITCH: + view = mInflater.inflate(R.layout.preference_header_switch_item, parent, + false); + holder.mIcon = (ImageView) view.findViewById(R.id.icon); + holder.mTitle = (TextView) + view.findViewById(com.android.internal.R.id.title); + holder.mSummary = (TextView) + view.findViewById(com.android.internal.R.id.summary); + holder.mSwitch = (Switch) view.findViewById(R.id.switchWidget); + break; + + case HEADER_TYPE_BUTTON: + view = mInflater.inflate(R.layout.preference_header_button_item, parent, + false); + holder.mIcon = (ImageView) view.findViewById(R.id.icon); + holder.mTitle = (TextView) + view.findViewById(com.android.internal.R.id.title); + holder.mSummary = (TextView) + view.findViewById(com.android.internal.R.id.summary); + holder.mButton = (ImageButton) view.findViewById(R.id.buttonWidget); + holder.mDivider = view.findViewById(R.id.divider); + break; + + case HEADER_TYPE_NORMAL: + view = mInflater.inflate( + R.layout.preference_header_item, parent, + false); + holder.mIcon = (ImageView) view.findViewById(R.id.icon); + holder.mTitle = (TextView) + view.findViewById(com.android.internal.R.id.title); + holder.mSummary = (TextView) + view.findViewById(com.android.internal.R.id.summary); + break; + } + view.setTag(holder); + } else { + view = convertView; + holder = (HeaderViewHolder) view.getTag(); + } + + // All view fields must be updated every time, because the view may be recycled + switch (headerType) { + case HEADER_TYPE_CATEGORY: + holder.mTitle.setText(header.getTitle(getContext().getResources())); + break; + + case HEADER_TYPE_SWITCH: + // Would need a different treatment if the main menu had more switches + if (header.id == R.id.wifi_settings) { + mWifiEnabler.setSwitch(holder.mSwitch); + } else { + mBluetoothEnabler.setSwitch(holder.mSwitch); + } + updateCommonHeaderView(header, holder); + break; + + case HEADER_TYPE_BUTTON: + if (header.id == R.id.security_settings) { + boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled(); + if (hasCert) { + holder.mButton.setVisibility(View.VISIBLE); + holder.mDivider.setVisibility(View.VISIBLE); + boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null; + if (isManaged) { + holder.mButton.setImageResource(R.drawable.ic_settings_about); + } else { + holder.mButton.setImageResource( + android.R.drawable.stat_notify_error); + } + holder.mButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent( + android.provider.Settings.ACTION_MONITORING_CERT_INFO); + getContext().startActivity(intent); + } + }); + } else { + holder.mButton.setVisibility(View.GONE); + holder.mDivider.setVisibility(View.GONE); + } + } + updateCommonHeaderView(header, holder); + break; + + case HEADER_TYPE_NORMAL: + updateCommonHeaderView(header, holder); + break; + } + + return view; + } + + private void updateCommonHeaderView(Header header, HeaderViewHolder holder) { + if (header.extras != null + && header.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) { + String accType = header.extras.getString( + ManageAccountsSettings.KEY_ACCOUNT_TYPE); + Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType); + setHeaderIcon(holder, icon); + } else { + if (header.iconRes > 0) { + holder.mIcon.setImageResource(header.iconRes); + } else { + holder.mIcon.setImageDrawable(null); + } + } + if (holder.mIcon != null) { + if (header.iconRes > 0) { + holder.mIcon.setBackgroundResource(R.color.background_drawer_icon); + } else { + holder.mIcon.setBackground(null); + } + } + holder.mTitle.setText(header.getTitle(getContext().getResources())); + CharSequence summary = header.getSummary(getContext().getResources()); + if (!TextUtils.isEmpty(summary)) { + holder.mSummary.setVisibility(View.VISIBLE); + holder.mSummary.setText(summary); + } else { + holder.mSummary.setVisibility(View.GONE); + } + } + + private void setHeaderIcon(HeaderViewHolder holder, Drawable icon) { + ViewGroup.LayoutParams lp = holder.mIcon.getLayoutParams(); + lp.width = getContext().getResources().getDimensionPixelSize( + R.dimen.header_icon_width); + lp.height = lp.width; + holder.mIcon.setLayoutParams(lp); + holder.mIcon.setImageDrawable(icon); + } + + public void resume(Context context) { + mWifiEnabler.resume(); + mBluetoothEnabler.resume(context); + } + + public void pause() { + mWifiEnabler.pause(); + mBluetoothEnabler.pause(); + } + } + + private void onListItemClick(ListView l, View v, int position, long id) { + if (!isResumed()) { + return; + } + Object item = mHeaderAdapter.getItem(position); + if (item instanceof Header) { + mSelectedHeader = (Header) item; + } + } + + @Override + public boolean shouldUpRecreateTask(Intent targetIntent) { + return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class)); + } + + @Override + public void onAccountsUpdated(Account[] accounts) { + // TODO: watch for package upgrades to invalidate cache; see 7206643 + mAuthenticatorHelper.updateAuthDescriptions(this); + mAuthenticatorHelper.onAccountsUpdated(this, accounts); + invalidateHeaders(); + } + + public static void requestHomeNotice() { + sShowNoHomeNotice = true; + } + + /** + * Default value for {@link Header#id Header.id} indicating that no + * identifier value is set. All other values (including those below -1) + * are valid. + */ + private static final long HEADER_ID_UNDEFINED = -1; + + /** + * Description of a single Header item that the user can select. + */ + static final class Header implements Parcelable { + /** + * Identifier for this header, to correlate with a new list when + * it is updated. The default value is + * {@link SettingsActivity#HEADER_ID_UNDEFINED}, meaning no id. + * @attr ref android.R.styleable#PreferenceHeader_id + */ + public long id = HEADER_ID_UNDEFINED; + + /** + * Resource ID of title of the header that is shown to the user. + * @attr ref android.R.styleable#PreferenceHeader_title + */ + public int titleRes; + + /** + * Title of the header that is shown to the user. + * @attr ref android.R.styleable#PreferenceHeader_title + */ + public CharSequence title; + + /** + * Resource ID of optional summary describing what this header controls. + * @attr ref android.R.styleable#PreferenceHeader_summary + */ + public int summaryRes; + + /** + * Optional summary describing what this header controls. + * @attr ref android.R.styleable#PreferenceHeader_summary + */ + public CharSequence summary; + + /** + * Optional icon resource to show for this header. + * @attr ref android.R.styleable#PreferenceHeader_icon + */ + public int iconRes; + + /** + * Full class name of the fragment to display when this header is + * selected. + * @attr ref android.R.styleable#PreferenceHeader_fragment + */ + public String fragment; + + /** + * Optional arguments to supply to the fragment when it is + * instantiated. + */ + public Bundle fragmentArguments; + + /** + * Intent to launch when the preference is selected. + */ + public Intent intent; + + /** + * Optional additional data for use by subclasses of the activity + */ + public Bundle extras; + + public Header() { + // Empty + } + + /** + * Return the currently set title. If {@link #titleRes} is set, + * this resource is loaded from <var>res</var> and returned. Otherwise + * {@link #title} is returned. + */ + public CharSequence getTitle(Resources res) { + if (titleRes != 0) { + return res.getText(titleRes); + } + return title; + } + + /** + * Return the currently set summary. If {@link #summaryRes} is set, + * this resource is loaded from <var>res</var> and returned. Otherwise + * {@link #summary} is returned. + */ + public CharSequence getSummary(Resources res) { + if (summaryRes != 0) { + return res.getText(summaryRes); + } + return summary; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(id); + dest.writeInt(titleRes); + TextUtils.writeToParcel(title, dest, flags); + dest.writeInt(summaryRes); + TextUtils.writeToParcel(summary, dest, flags); + dest.writeInt(iconRes); + dest.writeString(fragment); + dest.writeBundle(fragmentArguments); + if (intent != null) { + dest.writeInt(1); + intent.writeToParcel(dest, flags); + } else { + dest.writeInt(0); + } + dest.writeBundle(extras); + } + + public void readFromParcel(Parcel in) { + id = in.readLong(); + titleRes = in.readInt(); + title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + summaryRes = in.readInt(); + summary = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + iconRes = in.readInt(); + fragment = in.readString(); + fragmentArguments = in.readBundle(); + if (in.readInt() != 0) { + intent = Intent.CREATOR.createFromParcel(in); + } + extras = in.readBundle(); + } + + Header(Parcel in) { + readFromParcel(in); + } + + public static final Creator<Header> CREATOR = new Creator<Header>() { + public Header createFromParcel(Parcel source) { + return new Header(source); + } + public Header[] newArray(int size) { + return new Header[size]; + } + }; + } +} diff --git a/src/com/android/settings/SettingsPreferenceFragment.java b/src/com/android/settings/SettingsPreferenceFragment.java index 0a382b578..35893ffaf 100644 --- a/src/com/android/settings/SettingsPreferenceFragment.java +++ b/src/com/android/settings/SettingsPreferenceFragment.java @@ -22,12 +22,9 @@ import android.app.Fragment; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; -import android.content.Intent; import android.content.pm.PackageManager; -import android.net.Uri; import android.os.Bundle; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceFragment; import android.text.TextUtils; import android.util.Log; @@ -147,7 +144,7 @@ public class SettingsPreferenceFragment extends PreferenceFragment implements Di Log.e(TAG, "Old dialog fragment not null!"); } mDialogFragment = new SettingsDialogFragment(this, dialogId); - mDialogFragment.show(getActivity().getFragmentManager(), Integer.toString(dialogId)); + mDialogFragment.show(getChildFragmentManager(), Integer.toString(dialogId)); } public Dialog onCreateDialog(int dialogId) { @@ -236,17 +233,15 @@ public class SettingsPreferenceFragment extends PreferenceFragment implements Di public Dialog onCreateDialog(Bundle savedInstanceState) { if (savedInstanceState != null) { mDialogId = savedInstanceState.getInt(KEY_DIALOG_ID, 0); + mParentFragment = getParentFragment(); int mParentFragmentId = savedInstanceState.getInt(KEY_PARENT_FRAGMENT_ID, -1); - if (mParentFragmentId > -1) { - mParentFragment = getFragmentManager().findFragmentById(mParentFragmentId); - if (!(mParentFragment instanceof DialogCreatable)) { - throw new IllegalArgumentException( - (mParentFragment != null - ? mParentFragment.getClass().getName() - : mParentFragmentId) - + " must implement " - + DialogCreatable.class.getName()); - } + if (!(mParentFragment instanceof DialogCreatable)) { + throw new IllegalArgumentException( + (mParentFragment != null + ? mParentFragment.getClass().getName() + : mParentFragmentId) + + " must implement " + + DialogCreatable.class.getName()); } // This dialog fragment could be created from non-SettingsPreferenceFragment if (mParentFragment instanceof SettingsPreferenceFragment) { @@ -305,13 +300,13 @@ public class SettingsPreferenceFragment extends PreferenceFragment implements Di public boolean startFragment( Fragment caller, String fragmentClass, int requestCode, Bundle extras) { - if (getActivity() instanceof PreferenceActivity) { - PreferenceActivity preferenceActivity = (PreferenceActivity)getActivity(); - preferenceActivity.startPreferencePanel(fragmentClass, extras, + if (getActivity() instanceof SettingsActivity) { + SettingsActivity sa = (SettingsActivity) getActivity(); + sa.startPreferencePanel(fragmentClass, extras, R.string.lock_settings_picker_title, null, caller, requestCode); return true; } else { - Log.w(TAG, "Parent isn't PreferenceActivity, thus there's no way to launch the " + Log.w(TAG, "Parent isn't Settings activity, thus there's no way to launch the " + "given Fragment (name: " + fragmentClass + ", requestCode: " + requestCode + ")"); return false; diff --git a/src/com/android/settings/SoundSettings.java b/src/com/android/settings/SoundSettings.java index 28d93f16a..45fd0dbee 100644 --- a/src/com/android/settings/SoundSettings.java +++ b/src/com/android/settings/SoundSettings.java @@ -248,8 +248,13 @@ public class SoundSettings extends SettingsPreferenceFragment implements private void updateRingtoneName(int type, Preference preference, int msg) { if (preference == null) return; - Context context = getActivity(); - if (context == null) return; + final CharSequence summary = updateRingtoneName(getActivity(), type); + if (summary == null) return; + mHandler.sendMessage(mHandler.obtainMessage(msg, summary)); + } + + public static CharSequence updateRingtoneName(Context context, int type) { + if (context == null) return null; Uri ringtoneUri = RingtoneManager.getActualDefaultRingtoneUri(context, type); CharSequence summary = context.getString(com.android.internal.R.string.ringtone_unknown); // Is it a silent ringtone? @@ -270,7 +275,7 @@ public class SoundSettings extends SettingsPreferenceFragment implements // Unknown title for the ringtone } } - mHandler.sendMessage(mHandler.obtainMessage(msg, summary)); + return summary; } private void lookupRingtoneNames() { diff --git a/src/com/android/settings/SubSettings.java b/src/com/android/settings/SubSettings.java index 34e9ba3a7..04955b2b0 100644 --- a/src/com/android/settings/SubSettings.java +++ b/src/com/android/settings/SubSettings.java @@ -16,16 +16,13 @@ package com.android.settings; -import android.app.Fragment; import android.util.Log; -import com.android.settings.ChooseLockGeneric.ChooseLockGenericFragment; - /** * Stub class for showing sub-settings; we can't use the main Settings class * since for our app it is a special singleTask class. */ -public class SubSettings extends Settings { +public class SubSettings extends SettingsActivity { @Override public boolean onNavigateUp() { diff --git a/src/com/android/settings/TrustedCredentialsSettings.java b/src/com/android/settings/TrustedCredentialsSettings.java index cdb96cbbc..65d09348c 100644 --- a/src/com/android/settings/TrustedCredentialsSettings.java +++ b/src/com/android/settings/TrustedCredentialsSettings.java @@ -35,15 +35,20 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.ArrayAdapter; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.CheckBox; import android.widget.FrameLayout; +import android.widget.LinearLayout; import android.widget.ListView; import android.widget.ProgressBar; +import android.widget.Spinner; import android.widget.TabHost; import android.widget.TextView; import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collections; @@ -360,10 +365,42 @@ public class TrustedCredentialsSettings extends Fragment { } private void showCertDialog(final CertHolder certHolder) { - View view = certHolder.mSslCert.inflateCertificateView(getActivity()); AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setTitle(com.android.internal.R.string.ssl_certificate); - builder.setView(view); + + final ArrayList<View> views = new ArrayList<View>(); + final ArrayList<String> titles = new ArrayList<String>(); + addCertChain(certHolder, views, titles); + + ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(getActivity(), + android.R.layout.simple_spinner_item, + titles); + arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + Spinner spinner = new Spinner(getActivity()); + spinner.setAdapter(arrayAdapter); + spinner.setOnItemSelectedListener(new OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView<?> parent, View view, int position, + long id) { + for(int i = 0; i < views.size(); i++) { + views.get(i).setVisibility(i == position ? View.VISIBLE : View.GONE); + } + } + @Override + public void onNothingSelected(AdapterView<?> parent) { } + }); + + LinearLayout container = new LinearLayout(getActivity()); + container.setOrientation(LinearLayout.VERTICAL); + container.addView(spinner); + for (int i = 0; i < views.size(); ++i) { + View certificateView = views.get(i); + if (i != 0) { + certificateView.setVisibility(View.GONE); + } + container.addView(certificateView); + } + builder.setView(container); builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { dialog.dismiss(); @@ -371,6 +408,7 @@ public class TrustedCredentialsSettings extends Fragment { }); final Dialog certDialog = builder.create(); + View view = views.get(0); ViewGroup body = (ViewGroup) view.findViewById(com.android.internal.R.id.body); LayoutInflater inflater = LayoutInflater.from(getActivity()); Button removeButton = (Button) inflater.inflate(R.layout.trusted_credential_details, @@ -409,6 +447,27 @@ public class TrustedCredentialsSettings extends Fragment { certDialog.show(); } + private void addCertChain(final CertHolder certHolder, final ArrayList<View> views, + final ArrayList<String> titles) { + + List<X509Certificate> certificates = null; + try { + certificates = mStore.getCertificateChain(certHolder.mX509Cert); + } catch (CertificateException ex) { + return; + } + for (X509Certificate certificate : certificates) { + addCertDetails(certificate, views, titles); + } + } + + private void addCertDetails(X509Certificate certificate, final ArrayList<View> views, + final ArrayList<String> titles) { + SslCertificate sslCert = new SslCertificate(certificate); + views.add(sslCert.inflateCertificateView(getActivity())); + titles.add(sslCert.getIssuedTo().getCName()); + } + @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_PIN_CHALLENGE) { diff --git a/src/com/android/settings/UserDictionarySettings.java b/src/com/android/settings/UserDictionarySettings.java index da120048d..1e9fd0aee 100644 --- a/src/com/android/settings/UserDictionarySettings.java +++ b/src/com/android/settings/UserDictionarySettings.java @@ -192,9 +192,8 @@ public class UserDictionarySettings extends ListFragment { args.putString(UserDictionaryAddWordContents.EXTRA_WORD, editingWord); args.putString(UserDictionaryAddWordContents.EXTRA_SHORTCUT, editingShortcut); args.putString(UserDictionaryAddWordContents.EXTRA_LOCALE, mLocale); - android.preference.PreferenceActivity pa = - (android.preference.PreferenceActivity)getActivity(); - pa.startPreferencePanel( + SettingsActivity sa = (SettingsActivity) getActivity(); + sa.startPreferencePanel( com.android.settings.inputmethod.UserDictionaryAddWordFragment.class.getName(), args, R.string.user_dict_settings_add_dialog_title, null, null, 0); } diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index b9e729c78..bf44835b1 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -16,7 +16,6 @@ package com.android.settings; -import android.app.Activity; import android.app.ActivityManager; import android.app.AlertDialog; import android.app.Dialog; @@ -40,31 +39,23 @@ import android.net.LinkProperties; import android.net.Uri; import android.os.BatteryManager; import android.os.Bundle; -import android.os.ParcelFileDescriptor; import android.os.UserHandle; import android.os.UserManager; import android.preference.Preference; -import android.preference.PreferenceActivity.Header; import android.preference.PreferenceFrameLayout; import android.preference.PreferenceGroup; import android.provider.ContactsContract.CommonDataKinds; -import android.provider.ContactsContract.CommonDataKinds.Phone; -import android.provider.ContactsContract.CommonDataKinds.StructuredName; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Data; import android.provider.ContactsContract.Profile; import android.provider.ContactsContract.RawContacts; import android.telephony.TelephonyManager; import android.text.TextUtils; -import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.ListView; import android.widget.TabWidget; -import com.android.settings.users.ProfileUpdateReceiver; - -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.InetAddress; @@ -246,7 +237,7 @@ public class Utils { } public static boolean updateHeaderToSpecificActivityFromMetaDataOrRemove(Context context, - List<Header> target, Header header) { + List<SettingsActivity.Header> target, SettingsActivity.Header header) { Intent intent = header.intent; if (intent != null) { diff --git a/src/com/android/settings/WallpaperTypeSettings.java b/src/com/android/settings/WallpaperTypeSettings.java index fa5f0ac73..f46315a1d 100644 --- a/src/com/android/settings/WallpaperTypeSettings.java +++ b/src/com/android/settings/WallpaperTypeSettings.java @@ -16,18 +16,23 @@ package com.android.settings; -import android.app.Activity; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Bundle; import android.preference.Preference; import android.preference.PreferenceScreen; +import com.android.settings.indexer.Indexable; +import com.android.settings.indexer.IndexableData; +import com.android.settings.indexer.IndexableRef; +import java.util.ArrayList; import java.util.List; -public class WallpaperTypeSettings extends SettingsPreferenceFragment { +public class WallpaperTypeSettings extends SettingsPreferenceFragment implements Indexable { + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -38,9 +43,9 @@ public class WallpaperTypeSettings extends SettingsPreferenceFragment { private void populateWallpaperTypes() { // Search for activities that satisfy the ACTION_SET_WALLPAPER action - Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER); + final Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER); final PackageManager pm = getPackageManager(); - List<ResolveInfo> rList = pm.queryIntentActivities(intent, + final List<ResolveInfo> rList = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); final PreferenceScreen parent = getPreferenceScreen(); @@ -58,4 +63,42 @@ public class WallpaperTypeSettings extends SettingsPreferenceFragment { parent.addPreference(pref); } } + + public static final IndexDataProvider INDEX_DATA_PROVIDER = + new IndexDataProvider() { + @Override + public List<IndexableRef> getRefsToIndex(Context context) { + return null; + } + + @Override + public List<IndexableData> getRawDataToIndex(Context context) { + final List<IndexableData> result = new ArrayList<IndexableData>(); + + final Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER); + final PackageManager pm = context.getPackageManager(); + final List<ResolveInfo> rList = pm.queryIntentActivities(intent, + PackageManager.MATCH_DEFAULT_ONLY); + + // Add indexable data for each of the matching activities + for (ResolveInfo info : rList) { + Intent prefIntent = new Intent(intent); + prefIntent.setComponent(new ComponentName( + info.activityInfo.packageName, info.activityInfo.name)); + CharSequence label = info.loadLabel(pm); + if (label == null) label = info.activityInfo.packageName; + + IndexableData data = new IndexableData(); + data.title = label.toString(); + data.fragmentTitle = context.getResources().getString( + R.string.wallpaper_settings_fragment_title); + data.intentAction = intent.getAction(); + data.intentTargetPackage = info.activityInfo.packageName; + data.intentTargetClass = info.activityInfo.name; + result.add(data); + } + + return result; + } + }; } diff --git a/src/com/android/settings/WirelessSettings.java b/src/com/android/settings/WirelessSettings.java index 65127b577..59074e583 100644 --- a/src/com/android/settings/WirelessSettings.java +++ b/src/com/android/settings/WirelessSettings.java @@ -80,6 +80,7 @@ public class WirelessSettings extends RestrictedSettingsFragment private ConnectivityManager mCm; private TelephonyManager mTm; + private PackageManager mPm; private static final int MANAGE_MOBILE_PLAN_DIALOG_ID = 1; private static final String SAVED_MANAGE_MOBILE_PLAN_MSG = "mManageMobilePlanMessage"; @@ -91,7 +92,7 @@ public class WirelessSettings extends RestrictedSettingsFragment } /** * Invoked on each preference click in this hierarchy, overrides - * PreferenceActivity's implementation. Used to make sure we track the + * PreferenceFragment's implementation. Used to make sure we track the * preference click events. */ @Override @@ -124,6 +125,15 @@ public class WirelessSettings extends RestrictedSettingsFragment NetworkInfo ni = mCm.getProvisioningOrActiveNetworkInfo(); if (mTm.hasIccCard() && (ni != null)) { + // Check for carrier apps that can handle provisioning first + Intent provisioningIntent = new Intent(TelephonyIntents.ACTION_CARRIER_SETUP); + provisioningIntent.addCategory(TelephonyIntents.CATEGORY_MCCMNC_PREFIX + + mTm.getSimOperator()); + if (mPm.resolveActivity(provisioningIntent, 0 /* flags */) != null) { + startActivity(provisioningIntent); + return; + } + // Get provisioning URL String url = mCm.getMobileProvisioningUrl(); if (!TextUtils.isEmpty(url)) { @@ -192,15 +202,14 @@ public class WirelessSettings extends RestrictedSettingsFragment CharSequence[] entryValues = new CharSequence[count]; Drawable[] entryImages = new Drawable[count]; - PackageManager packageManager = getPackageManager(); int i = 0; for (SmsApplicationData smsApplicationData : smsApplications) { entries[i] = smsApplicationData.mApplicationName; entryValues[i] = smsApplicationData.mPackageName; try { - entryImages[i] = packageManager.getApplicationIcon(smsApplicationData.mPackageName); + entryImages[i] = mPm.getApplicationIcon(smsApplicationData.mPackageName); } catch (NameNotFoundException e) { - entryImages[i] = packageManager.getDefaultActivityIcon(); + entryImages[i] = mPm.getDefaultActivityIcon(); } i++; } @@ -260,6 +269,7 @@ public class WirelessSettings extends RestrictedSettingsFragment mCm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); mTm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); + mPm = getPackageManager(); addPreferencesFromResource(R.xml.wireless_settings); @@ -352,7 +362,7 @@ public class WirelessSettings extends RestrictedSettingsFragment } // Remove Airplane Mode settings if it's a stationary device such as a TV. - if (getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION)) { + if (mPm.hasSystemFeature(PackageManager.FEATURE_TELEVISION)) { removePreference(KEY_TOGGLE_AIRPLANE); } @@ -380,8 +390,7 @@ public class WirelessSettings extends RestrictedSettingsFragment com.android.internal.R.bool.config_cellBroadcastAppLinks); try { if (isCellBroadcastAppLinkEnabled) { - PackageManager pm = getPackageManager(); - if (pm.getApplicationEnabledSetting("com.android.cellbroadcastreceiver") + if (mPm.getApplicationEnabledSetting("com.android.cellbroadcastreceiver") == PackageManager.COMPONENT_ENABLED_STATE_DISABLED) { isCellBroadcastAppLinkEnabled = false; // CMAS app disabled } diff --git a/src/com/android/settings/ZenModeListPreference.java b/src/com/android/settings/ZenModeListPreference.java new file mode 100644 index 000000000..e405e0fe5 --- /dev/null +++ b/src/com/android/settings/ZenModeListPreference.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2014 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.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.os.Handler; +import android.preference.ListPreference; +import android.preference.Preference; +import android.provider.Settings; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.ImageView; + +public class ZenModeListPreference extends ListPreference { + private static final String TAG = "ZenModeListPreference"; + private static final boolean DEBUG = false; + + private final Context mContext; + private final Handler mHandler = new Handler(); + private final ContentResolver mResolver; + + private ImageView mConfigure; + private int mMode; + + public ZenModeListPreference(Context context, AttributeSet attrs) { + super(context, attrs); + if (DEBUG) Log.d(TAG, "new ZenModeListPreference()"); + mContext = context; + mResolver = context.getContentResolver(); + setWidgetLayoutResource(R.layout.preference_zen_mode); + } + + public void init() { + if (DEBUG) Log.d(TAG, "init"); + loadZenModeSetting("init"); + setOnPreferenceChangeListener(new OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (DEBUG) Log.d(TAG, "onPreferenceChange " + newValue); + final boolean updateWithNewValue = saveZenModeSetting((String)newValue); + return updateWithNewValue; + } + }); + mResolver.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.ZEN_MODE), + false, new SettingsObserver()); + } + + @Override + protected void onBindView(View view) { + if (DEBUG) Log.d(TAG, "onBindView"); + super.onBindView(view); + mConfigure = (ImageView)view.findViewById(R.id.configure_zen_mode); + updateConfigureVisibility(); + mConfigure.setOnClickListener(new OnClickListener(){ + @Override + public void onClick(View v) { + if (mMode != Settings.Global.ZEN_MODE_LIMITED) return; + if (mContext instanceof SettingsActivity) { + SettingsActivity sa = (SettingsActivity)mContext; + sa.startPreferencePanel(ZenModeSettings.class.getName(), + null, R.string.zen_mode_settings_title, null, null, 0); + } + } + }); + } + + private void updateConfigureVisibility() { + if (mConfigure != null) { + final boolean limited = mMode == Settings.Global.ZEN_MODE_LIMITED; + mConfigure.setVisibility(limited ? View.VISIBLE : View.GONE); + } + } + + private void loadZenModeSetting(String reason) { + if (DEBUG) Log.d(TAG, "loadZenModeSetting " + reason); + mMode = Settings.Global.getInt(mResolver, + Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF); + setValue(Integer.toString(mMode)); + updateConfigureVisibility(); + } + + private boolean saveZenModeSetting(String value) { + if (DEBUG) Log.d(TAG, "saveZenModeSetting " + value); + try { + final int v = Integer.valueOf(value); + checkZenMode(v); + return Settings.Global.putInt(mResolver, Settings.Global.ZEN_MODE, v); + } catch (Throwable t) { + Log.w(TAG, "Failed to update zen mode with value: " + value, t); + return false; + } + } + + private static void checkZenMode(int mode) { + if (mode < Settings.Global.ZEN_MODE_OFF || mode > Settings.Global.ZEN_MODE_FULL) { + throw new IllegalArgumentException("Invalid zen mode: " + mode); + } + } + + private final class SettingsObserver extends ContentObserver { + public SettingsObserver() { + super(mHandler); + } + + @Override + public void onChange(boolean selfChange) { + loadZenModeSetting("change"); + } + } +} diff --git a/src/com/android/settings/ZenModeSettings.java b/src/com/android/settings/ZenModeSettings.java new file mode 100644 index 000000000..15c3a5ce2 --- /dev/null +++ b/src/com/android/settings/ZenModeSettings.java @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2014 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.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.util.Log; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.RelativeLayout; +import android.widget.ScrollView; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.Toast; + +public class ZenModeSettings extends SettingsPreferenceFragment { + private static final String TAG = "ZenModeSettings"; + private static final boolean DEBUG = false; + + private ZenModeConfigView mConfig; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + final Context context = getActivity(); + final ScrollView sv = new ScrollView(context); + sv.setVerticalScrollBarEnabled(false); + sv.setHorizontalScrollBarEnabled(false); + mConfig = new ZenModeConfigView(context); + sv.addView(mConfig); + return sv; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + mConfig.resetBackground(); + } + + public static final class ZenModeConfigView extends LinearLayout { + private static final Typeface LIGHT = + Typeface.create("sans-serif-light", Typeface.NORMAL); + private static final int BG_COLOR = 0xffe7e8e9; + private final Context mContext; + + private Drawable mOldBackground; + private Toast mToast; + + public ZenModeConfigView(Context context) { + super(context); + mContext = context; + setOrientation(VERTICAL); + + int p = getResources().getDimensionPixelSize(R.dimen.content_margin_left); + TextView tv = addHeader("When on"); + tv.setPadding(0, p / 2, 0, p / 4); + addBuckets(); + tv = addHeader("Automatically turn on"); + tv.setPadding(0, p / 2, 0, p / 4); + addTriggers(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mOldBackground = getParentView().getBackground(); + if (DEBUG) Log.d(TAG, "onAttachedToWindow mOldBackground=" + mOldBackground); + getParentView().setBackgroundColor(BG_COLOR); + } + + public void resetBackground() { + if (DEBUG) Log.d(TAG, "resetBackground"); + getParentView().setBackground(mOldBackground); + } + + private View getParentView() { + return (View)getParent().getParent(); + } + + private TextView addHeader(String text) { + TextView tv = new TextView(mContext); + tv.setTypeface(LIGHT); + tv.setTextColor(0x7f000000); + tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, tv.getTextSize() * 1.5f); + tv.setText(text); + addView(tv); + return tv; + } + + private void addTriggers() { + addView(new TriggerView("While driving")); + addView(new TriggerView("While in meetings")); + addView(new TriggerView("During a set time period")); + } + + private void addBuckets() { + LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.WRAP_CONTENT); + BucketView callView = new BucketView(android.R.drawable.ic_menu_call, "Calls", 0, + "Block all", "Starred contacts only", "Allow all"); + addView(callView, lp); + lp.topMargin = 4; + BucketView msgView = new BucketView(android.R.drawable.ic_menu_myplaces, + "Text & SMS Messages", 0, + "Block all", "Starred contacts only", "Allow all"); + addView(msgView, lp); + BucketView alarmView = new BucketView(android.R.drawable.ic_menu_agenda, + "Alarms & Timers", 1, + "Block all", "Allow all"); + addView(alarmView, lp); + BucketView otherView = new BucketView(android.R.drawable.ic_menu_info_details, + "Other Interruptions", 0, + "Block all", "Block all except..."); + addView(otherView, lp); + } + + private void notImplemented() { + if (mToast != null) mToast.cancel(); + mToast = Toast.makeText(mContext, "Not implemented", Toast.LENGTH_SHORT); + mToast.show(); + } + + private class BucketView extends RelativeLayout { + private final BucketSpinner mSpinner; + + public BucketView(int icon, String category, int defaultValue, String... values) { + super(ZenModeConfigView.this.mContext); + + setBackgroundColor(0xffffffff); + final int p = getResources().getDimensionPixelSize(R.dimen.content_margin_left); + + final ImageView iv = new ImageView(mContext); + iv.setId(android.R.id.icon); + iv.setImageResource(icon); + iv.setAlpha(.5f); + + final int size = mContext.getResources() + .getDimensionPixelSize(R.dimen.app_icon_size); + LayoutParams lp = new LayoutParams(size, size); + lp.addRule(CENTER_VERTICAL); + lp.leftMargin = 16; + lp.rightMargin = 16; + addView(iv, lp); + + TextView tv = new TextView(mContext); + tv.setPadding(4, 0, 0, 0); + tv.setId(android.R.id.title); + tv.setTextColor(0xff000000); + tv.setText(category); + tv.setAllCaps(true); + lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + lp.addRule(RIGHT_OF, iv.getId()); + lp.topMargin = p / 2; + addView(tv, lp); + + mSpinner = new BucketSpinner(defaultValue, values); + lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + lp.addRule(RIGHT_OF, iv.getId()); + lp.addRule(BELOW, tv.getId()); + addView(mSpinner, lp); + } + } + + private class BucketSpinner extends Spinner { + private final Bitmap mArrow; + + public BucketSpinner(int defaultValue, String... values) { + super(ZenModeConfigView.this.mContext); + setGravity(Gravity.LEFT); + mArrow = BitmapFactory.decodeResource(getResources(), + R.drawable.spinner_default_holo_dark_am_no_underline); + setPadding(0, 0, getPaddingRight(), getPaddingBottom()); + setBackgroundColor(0x00000000); + final ArrayAdapter<String> adapter = new ArrayAdapter<String>(mContext, 0) { + @Override + public View getView(int position, View convertView, ViewGroup parent) { + return getDropDownView(position, convertView, parent); + } + + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + final TextView tv = convertView != null ? (TextView) convertView + : new TextView(ZenModeConfigView.this.mContext); + tv.setText(getItem(position)); + if (convertView == null) { + tv.setTypeface(LIGHT); + tv.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL); + tv.setTextColor(0xff000000); + tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, tv.getTextSize() * 1.5f); + final int p = (int) tv.getTextSize() / 2; + if (parent instanceof ListView) { + final ListView lv = (ListView)parent; + lv.setDividerHeight(0); + tv.setBackgroundColor(BG_COLOR); + tv.setPadding(p, p, p, p); + } else { + tv.setPadding(0, 0, p, 0); + } + } + return tv; + } + }; + adapter.addAll(values); + setAdapter(adapter); + setSelection(defaultValue, true); + } + + @Override + protected void onDraw(Canvas canvas) { + final TextView tv = (TextView)getSelectedView(); + final int w = (int)tv.getLayout().getLineWidth(0); + final int left = w - mArrow.getWidth() / 4; + final int top = getHeight() - mArrow.getHeight(); + canvas.drawBitmap(mArrow, left, top, null); + super.onDraw(canvas); + } + + @Override + public void setSelection(int position) { + if (position != getSelectedItemPosition()) { + notImplemented(); + } + } + } + + private class TriggerView extends RelativeLayout { + public TriggerView(String text) { + super(ZenModeConfigView.this.mContext); + + setBackgroundColor(0xffffffff); + final int p = getResources().getDimensionPixelSize(R.dimen.content_margin_left); + final int p2 = p / 4; + setPadding(p2, p2, p2, p2); + + final CheckBox cb = new CheckBox(mContext); + cb.setId(android.R.id.checkbox); + cb.setOnCheckedChangeListener(new OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + cb.setChecked(false); + notImplemented(); + } + } + }); + LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); + lp.addRule(ALIGN_PARENT_RIGHT); + addView(cb, lp); + + final TextView tv = new TextView(mContext); + tv.setText(text); + tv.setTypeface(LIGHT); + tv.setTextColor(0xff000000); + tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, tv.getTextSize() * 1.5f); + final int p3 = p / 2; + tv.setPadding(p3, 0, p3, 0); + lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + lp.addRule(LEFT_OF, cb.getId()); + lp.addRule(CENTER_VERTICAL); + addView(tv, lp); + } + } + } +} diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java index 4d24d0968..9c1e30040 100644 --- a/src/com/android/settings/accessibility/AccessibilitySettings.java +++ b/src/com/android/settings/accessibility/AccessibilitySettings.java @@ -18,14 +18,8 @@ package com.android.settings.accessibility; import android.accessibilityservice.AccessibilityServiceInfo; import android.app.ActivityManagerNative; -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.SharedPreferences; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.res.Configuration; @@ -33,7 +27,6 @@ import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; -import android.os.SystemProperties; import android.preference.CheckBoxPreference; import android.preference.ListPreference; import android.preference.Preference; @@ -42,7 +35,6 @@ import android.preference.PreferenceScreen; import android.provider.Settings; import android.text.TextUtils; import android.text.TextUtils.SimpleStringSplitter; -import android.util.Log; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.View; @@ -68,20 +60,11 @@ import java.util.Set; */ public class AccessibilitySettings extends SettingsPreferenceFragment implements DialogCreatable, Preference.OnPreferenceChangeListener { - private static final String LOG_TAG = "AccessibilitySettings"; - - private static final String DEFAULT_SCREENREADER_MARKET_LINK = - "market://search?q=pname:com.google.android.marvin.talkback"; private static final float LARGE_FONT_SCALE = 1.3f; - private static final String SYSTEM_PROPERTY_MARKET_URL = "ro.screenreader.market"; - static final char ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR = ':'; - private static final String KEY_INSTALL_ACCESSIBILITY_SERVICE_OFFERED_ONCE = - "key_install_accessibility_service_offered_once"; - // Preference categories private static final String SERVICES_CATEGORY = "services_category"; private static final String SYSTEM_CATEGORY = "system_category"; @@ -103,6 +86,12 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements "captioning_preference_screen"; private static final String DISPLAY_MAGNIFICATION_PREFERENCE_SCREEN = "screen_magnification_preference_screen"; + private static final String DISPLAY_CONTRAST_PREFERENCE_SCREEN = + "contrast_preference_screen"; + private static final String DISPLAY_INVERSION_PREFERENCE_SCREEN = + "inversion_preference_screen"; + private static final String DISPLAY_DALTONIZER_PREFERENCE_SCREEN = + "daltonizer_preference_screen"; // Extras passed to sub-fragments. static final String EXTRA_PREFERENCE_KEY = "preference_key"; @@ -119,9 +108,6 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements // presentation. private static final long DELAY_UPDATE_SERVICES_MILLIS = 1000; - // Dialog IDs. - private static final int DIALOG_ID_NO_ACCESSIBILITY_SERVICES = 1; - // Auxiliary members. final static SimpleStringSplitter sStringColonSplitter = new SimpleStringSplitter(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR); @@ -198,6 +184,9 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements private PreferenceScreen mCaptioningPreferenceScreen; private PreferenceScreen mDisplayMagnificationPreferenceScreen; private PreferenceScreen mGlobalGesturePreferenceScreen; + private PreferenceScreen mDisplayInversionPreferenceScreen; + private PreferenceScreen mDisplayContrastPreferenceScreen; + private PreferenceScreen mDisplayDaltonizerPreferenceScreen; private int mLongPressTimeoutDefault; @@ -214,8 +203,6 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements loadInstalledServices(); updateAllPreferences(); - offerInstallAccessibilitySerivceOnce(); - mSettingsPackageMonitor.register(getActivity(), getActivity().getMainLooper(), false); mSettingsContentObserver.register(getContentResolver()); if (RotationPolicy.isRotationSupported(getActivity())) { @@ -375,6 +362,14 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements mDisplayMagnificationPreferenceScreen = (PreferenceScreen) findPreference( DISPLAY_MAGNIFICATION_PREFERENCE_SCREEN); + // Display color adjustments. + mDisplayContrastPreferenceScreen = (PreferenceScreen) findPreference( + DISPLAY_CONTRAST_PREFERENCE_SCREEN); + mDisplayInversionPreferenceScreen = (PreferenceScreen) findPreference( + DISPLAY_INVERSION_PREFERENCE_SCREEN); + mDisplayDaltonizerPreferenceScreen = (PreferenceScreen) findPreference( + DISPLAY_DALTONIZER_PREFERENCE_SCREEN); + // Global gesture. mGlobalGesturePreferenceScreen = (PreferenceScreen) findPreference(ENABLE_ACCESSIBILITY_GESTURE_PREFERENCE_SCREEN); @@ -518,25 +513,16 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements mSelectLongPressTimeoutPreference.setValue(value); mSelectLongPressTimeoutPreference.setSummary(mLongPressTimeoutValuetoTitleMap.get(value)); - // Captioning. - final boolean captioningEnabled = Settings.Secure.getInt(getContentResolver(), - Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED, 0) == 1; - if (captioningEnabled) { - mCaptioningPreferenceScreen.setSummary(R.string.accessibility_feature_state_on); - } else { - mCaptioningPreferenceScreen.setSummary(R.string.accessibility_feature_state_off); - } - - // Screen magnification. - final boolean magnificationEnabled = Settings.Secure.getInt(getContentResolver(), - Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0) == 1; - if (magnificationEnabled) { - mDisplayMagnificationPreferenceScreen.setSummary( - R.string.accessibility_feature_state_on); - } else { - mDisplayMagnificationPreferenceScreen.setSummary( - R.string.accessibility_feature_state_off); - } + updateFeatureSummary(Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED, + mCaptioningPreferenceScreen); + updateFeatureSummary(Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, + mDisplayMagnificationPreferenceScreen); + updateFeatureSummary(Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, + mDisplayInversionPreferenceScreen); + updateFeatureSummary(Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED, + mDisplayContrastPreferenceScreen); + updateFeatureSummary(Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, + mDisplayDaltonizerPreferenceScreen); // Global gesture final boolean globalGestureEnabled = Settings.Global.getInt(getContentResolver(), @@ -550,6 +536,12 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements } } + private void updateFeatureSummary(String prefKey, Preference pref) { + final boolean enabled = Settings.Secure.getInt(getContentResolver(), prefKey, 0) == 1; + pref.setSummary(enabled ? R.string.accessibility_feature_state_on + : R.string.accessibility_feature_state_off); + } + private void updateLockScreenRotationCheckbox() { Context context = getActivity(); if (context != null) { @@ -558,72 +550,6 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements } } - private void offerInstallAccessibilitySerivceOnce() { - // There is always one preference - if no services it is just a message. - if (mServicesCategory.getPreference(0) != mNoServicesMessagePreference) { - return; - } - SharedPreferences preferences = getActivity().getPreferences(Context.MODE_PRIVATE); - final boolean offerInstallService = !preferences.getBoolean( - KEY_INSTALL_ACCESSIBILITY_SERVICE_OFFERED_ONCE, false); - if (offerInstallService) { - String screenreaderMarketLink = SystemProperties.get( - SYSTEM_PROPERTY_MARKET_URL, - DEFAULT_SCREENREADER_MARKET_LINK); - Uri marketUri = Uri.parse(screenreaderMarketLink); - Intent marketIntent = new Intent(Intent.ACTION_VIEW, marketUri); - - if (getPackageManager().resolveActivity(marketIntent, 0) == null) { - // Don't show the dialog if no market app is found/installed. - return; - } - - preferences.edit().putBoolean(KEY_INSTALL_ACCESSIBILITY_SERVICE_OFFERED_ONCE, - true).commit(); - // Notify user that they do not have any accessibility - // services installed and direct them to Market to get TalkBack. - showDialog(DIALOG_ID_NO_ACCESSIBILITY_SERVICES); - } - } - - @Override - public Dialog onCreateDialog(int dialogId) { - switch (dialogId) { - case DIALOG_ID_NO_ACCESSIBILITY_SERVICES: - return new AlertDialog.Builder(getActivity()) - .setTitle(R.string.accessibility_service_no_apps_title) - .setMessage(R.string.accessibility_service_no_apps_message) - .setPositiveButton(android.R.string.ok, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - // dismiss the dialog before launching - // the activity otherwise the dialog - // removal occurs after - // onSaveInstanceState which triggers an - // exception - removeDialog(DIALOG_ID_NO_ACCESSIBILITY_SERVICES); - String screenreaderMarketLink = SystemProperties.get( - SYSTEM_PROPERTY_MARKET_URL, - DEFAULT_SCREENREADER_MARKET_LINK); - Uri marketUri = Uri.parse(screenreaderMarketLink); - Intent marketIntent = new Intent(Intent.ACTION_VIEW, - marketUri); - try { - startActivity(marketIntent); - } catch (ActivityNotFoundException anfe) { - Log.w(LOG_TAG, "Couldn't start play store activity", - anfe); - } - } - }) - .setNegativeButton(android.R.string.cancel, null) - .create(); - default: - return null; - } - } - private void loadInstalledServices() { Set<ComponentName> installedServices = sInstalledServices; installedServices.clear(); diff --git a/src/com/android/settings/accessibility/CaptionPropertiesFragment.java b/src/com/android/settings/accessibility/CaptionPropertiesFragment.java index 8a08d90ed..49e31cfe5 100644 --- a/src/com/android/settings/accessibility/CaptionPropertiesFragment.java +++ b/src/com/android/settings/accessibility/CaptionPropertiesFragment.java @@ -54,6 +54,8 @@ public class CaptionPropertiesFragment extends SettingsPreferenceFragment private static final String PREF_BACKGROUND_OPACITY = "captioning_background_opacity"; private static final String PREF_FOREGROUND_COLOR = "captioning_foreground_color"; private static final String PREF_FOREGROUND_OPACITY = "captioning_foreground_opacity"; + private static final String PREF_WINDOW_COLOR = "captioning_window_color"; + private static final String PREF_WINDOW_OPACITY = "captioning_window_opacity"; private static final String PREF_EDGE_COLOR = "captioning_edge_color"; private static final String PREF_EDGE_TYPE = "captioning_edge_type"; private static final String PREF_FONT_SIZE = "captioning_font_size"; @@ -66,6 +68,8 @@ public class CaptionPropertiesFragment extends SettingsPreferenceFragment private CaptioningManager mCaptioningManager; private SubtitleView mPreviewText; + private View mPreviewWindow; + private ToggleSwitch mToggleSwitch; // Standard options. private LocalePreference mLocale; @@ -80,6 +84,8 @@ public class CaptionPropertiesFragment extends SettingsPreferenceFragment private ColorPreference mEdgeColor; private ColorPreference mBackgroundColor; private ColorPreference mBackgroundOpacity; + private ColorPreference mWindowColor; + private ColorPreference mWindowOpacity; private PreferenceCategory mCustom; private boolean mShowingCustom; @@ -119,12 +125,38 @@ public class CaptionPropertiesFragment extends SettingsPreferenceFragment public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); + final boolean enabled = mCaptioningManager.isEnabled(); mPreviewText = (SubtitleView) view.findViewById(R.id.preview_text); + mPreviewText.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE); + + final Context context = view.getContext(); + final int padding = context.getResources().getDimensionPixelSize( + R.dimen.action_bar_switch_padding); + mToggleSwitch = new ToggleSwitch(context); + mToggleSwitch.setPaddingRelative(0, 0, padding, 0); + mToggleSwitch.setCheckedInternal(enabled); + + mPreviewWindow = view.findViewById(R.id.preview_window); + + getPreferenceScreen().setEnabled(enabled); - installActionBarToggleSwitch(); refreshPreviewText(); } + @Override + public void onResume() { + super.onResume(); + + installActionBarToggleSwitch(); + } + + @Override + public void onPause() { + removeActionBarToggleSwitch(); + + super.onPause(); + } + private void refreshPreviewText() { final Context context = getActivity(); if (context == null) { @@ -145,6 +177,9 @@ public class CaptionPropertiesFragment extends SettingsPreferenceFragment } else { preview.setText(R.string.captioning_preview_text); } + + final CaptionStyle style = mCaptioningManager.getUserStyle(); + mPreviewWindow.setBackgroundColor(style.windowColor); } } @@ -167,39 +202,41 @@ public class CaptionPropertiesFragment extends SettingsPreferenceFragment } } - private void installActionBarToggleSwitch() { - final Activity activity = getActivity(); - final ToggleSwitch toggleSwitch = new ToggleSwitch(activity); - - final int padding = getResources().getDimensionPixelSize( - R.dimen.action_bar_switch_padding); - toggleSwitch.setPaddingRelative(0, 0, padding, 0); - - final ActionBar actionBar = activity.getActionBar(); - actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, ActionBar.DISPLAY_SHOW_CUSTOM); - - final ActionBar.LayoutParams params = new ActionBar.LayoutParams( - ActionBar.LayoutParams.WRAP_CONTENT, ActionBar.LayoutParams.WRAP_CONTENT, - Gravity.CENTER_VERTICAL | Gravity.END); - actionBar.setCustomView(toggleSwitch, params); - - final boolean enabled = mCaptioningManager.isEnabled(); - getPreferenceScreen().setEnabled(enabled); - mPreviewText.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE); - toggleSwitch.setCheckedInternal(enabled); - toggleSwitch.setOnBeforeCheckedChangeListener(new OnBeforeCheckedChangeListener() { + protected void onInstallActionBarToggleSwitch() { + mToggleSwitch.setOnBeforeCheckedChangeListener(new OnBeforeCheckedChangeListener() { @Override public boolean onBeforeCheckedChanged(ToggleSwitch toggleSwitch, boolean checked) { toggleSwitch.setCheckedInternal(checked); Settings.Secure.putInt(getActivity().getContentResolver(), Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED, checked ? 1 : 0); getPreferenceScreen().setEnabled(checked); - mPreviewText.setVisibility(checked ? View.VISIBLE : View.INVISIBLE); + if (mPreviewText != null) { + mPreviewText.setVisibility(checked ? View.VISIBLE : View.INVISIBLE); + } return false; } }); } + private void installActionBarToggleSwitch() { + final ActionBar ab = getActivity().getActionBar(); + final ActionBar.LayoutParams params = new ActionBar.LayoutParams( + ActionBar.LayoutParams.WRAP_CONTENT, ActionBar.LayoutParams.WRAP_CONTENT, + Gravity.CENTER_VERTICAL | Gravity.END); + ab.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, ActionBar.DISPLAY_SHOW_CUSTOM); + ab.setCustomView(mToggleSwitch, params); + + onInstallActionBarToggleSwitch(); + } + + private void removeActionBarToggleSwitch() { + mToggleSwitch.setOnBeforeCheckedChangeListener(null); + + final ActionBar ab = getActivity().getActionBar(); + ab.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_CUSTOM); + ab.setCustomView(null); + } + private void initializeAllPreferences() { mLocale = (LocalePreference) findPreference(PREF_LOCALE); mFontSize = (ListPreference) findPreference(PREF_FONT_SIZE); @@ -246,6 +283,14 @@ public class CaptionPropertiesFragment extends SettingsPreferenceFragment mBackgroundOpacity.setTitles(opacityTitles); mBackgroundOpacity.setValues(opacityValues); + mWindowColor = (ColorPreference) mCustom.findPreference(PREF_WINDOW_COLOR); + mWindowColor.setTitles(bgColorTitles); + mWindowColor.setValues(bgColorValues); + + mWindowOpacity = (ColorPreference) mCustom.findPreference(PREF_WINDOW_OPACITY); + mWindowOpacity.setTitles(opacityTitles); + mWindowOpacity.setValues(opacityValues); + mEdgeType = (EdgeTypePreference) mCustom.findPreference(PREF_EDGE_TYPE); mTypeface = (ListPreference) mCustom.findPreference(PREF_TYPEFACE); } @@ -257,6 +302,8 @@ public class CaptionPropertiesFragment extends SettingsPreferenceFragment mEdgeColor.setOnValueChangedListener(this); mBackgroundColor.setOnValueChangedListener(this); mBackgroundOpacity.setOnValueChangedListener(this); + mWindowColor.setOnValueChangedListener(this); + mWindowOpacity.setOnValueChangedListener(this); mEdgeType.setOnValueChangedListener(this); mTypeface.setOnPreferenceChangeListener(this); @@ -278,6 +325,7 @@ public class CaptionPropertiesFragment extends SettingsPreferenceFragment parseColorOpacity(mForegroundColor, mForegroundOpacity, attrs.foregroundColor); parseColorOpacity(mBackgroundColor, mBackgroundOpacity, attrs.backgroundColor); + parseColorOpacity(mWindowColor, mWindowOpacity, attrs.windowColor); final String rawTypeface = attrs.mRawTypeface; mTypeface.setValue(rawTypeface == null ? "" : rawTypeface); @@ -334,6 +382,10 @@ public class CaptionPropertiesFragment extends SettingsPreferenceFragment final int merged = mergeColorOpacity(mBackgroundColor, mBackgroundOpacity); Settings.Secure.putInt( cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR, merged); + } else if (mWindowColor == preference || mWindowOpacity == preference) { + final int merged = mergeColorOpacity(mWindowColor, mWindowOpacity); + Settings.Secure.putInt( + cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_WINDOW_COLOR, merged); } else if (mEdgeColor == preference) { Settings.Secure.putInt(cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_EDGE_COLOR, value); } else if (mPreset == preference) { diff --git a/src/com/android/settings/accessibility/EdgeTypePreference.java b/src/com/android/settings/accessibility/EdgeTypePreference.java index ad71a765c..3bff704f3 100644 --- a/src/com/android/settings/accessibility/EdgeTypePreference.java +++ b/src/com/android/settings/accessibility/EdgeTypePreference.java @@ -34,7 +34,7 @@ public class EdgeTypePreference extends ListDialogPreference { private static final int DEFAULT_FOREGROUND_COLOR = Color.WHITE; private static final int DEFAULT_BACKGROUND_COLOR = Color.TRANSPARENT; private static final int DEFAULT_EDGE_COLOR = Color.BLACK; - private static final float DEFAULT_FONT_SIZE = 96f; + private static final float DEFAULT_FONT_SIZE = 32f; public EdgeTypePreference(Context context, AttributeSet attrs) { super(context, attrs); @@ -57,7 +57,9 @@ public class EdgeTypePreference extends ListDialogPreference { preview.setForegroundColor(DEFAULT_FOREGROUND_COLOR); preview.setBackgroundColor(DEFAULT_BACKGROUND_COLOR); - preview.setTextSize(DEFAULT_FONT_SIZE); + + final float density = getContext().getResources().getDisplayMetrics().density; + preview.setTextSize(DEFAULT_FONT_SIZE * density); final int value = getValueAt(index); preview.setEdgeType(value); diff --git a/src/com/android/settings/accessibility/PresetPreference.java b/src/com/android/settings/accessibility/PresetPreference.java index 84aba6c7e..9f1aee37c 100644 --- a/src/com/android/settings/accessibility/PresetPreference.java +++ b/src/com/android/settings/accessibility/PresetPreference.java @@ -27,7 +27,7 @@ import com.android.internal.widget.SubtitleView; import com.android.settings.R; public class PresetPreference extends ListDialogPreference { - private static final float DEFAULT_FONT_SIZE = 96f; + private static final float DEFAULT_FONT_SIZE = 32f; private final CaptioningManager mCaptioningManager; @@ -54,7 +54,8 @@ public class PresetPreference extends ListDialogPreference { CaptionPropertiesFragment.applyCaptionProperties( mCaptioningManager, previewText, value); - previewText.setTextSize(DEFAULT_FONT_SIZE); + final float density = getContext().getResources().getDisplayMetrics().density; + previewText.setTextSize(DEFAULT_FONT_SIZE * density); final CharSequence title = getTitleAt(index); if (title != null) { diff --git a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java index 0c568f063..db473f8a8 100644 --- a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java @@ -154,41 +154,44 @@ public class ToggleAccessibilityServicePreferenceFragment public Dialog onCreateDialog(int dialogId) { switch (dialogId) { case DIALOG_ID_ENABLE_WARNING: { - mShownDialogId = DIALOG_ID_ENABLE_WARNING; - AccessibilityServiceInfo info = getAccessibilityServiceInfo(); - if (info == null) { - return null; + mShownDialogId = DIALOG_ID_ENABLE_WARNING; + AccessibilityServiceInfo info = getAccessibilityServiceInfo(); + if (info == null) { + return null; + } + AlertDialog ad = new AlertDialog.Builder(getActivity()) + .setTitle(getString(R.string.enable_service_title, + info.getResolveInfo().loadLabel(getPackageManager()))) + .setIconAttribute(android.R.attr.alertDialogIcon) + .setView(createEnableDialogContentView(info)) + .setCancelable(true) + .setPositiveButton(android.R.string.ok, this) + .setNegativeButton(android.R.string.cancel, this) + .create(); + ad.create(); + ad.getButton(AlertDialog.BUTTON_POSITIVE).setFilterTouchesWhenObscured(true); + return ad; } - return new AlertDialog.Builder(getActivity()) - .setTitle(getString(R.string.enable_service_title, - info.getResolveInfo().loadLabel(getPackageManager()))) - .setIconAttribute(android.R.attr.alertDialogIcon) - .setView(createEnableDialogContentView(info)) - .setCancelable(true) - .setPositiveButton(android.R.string.ok, this) - .setNegativeButton(android.R.string.cancel, this) - .create(); - } case DIALOG_ID_DISABLE_WARNING: { - mShownDialogId = DIALOG_ID_DISABLE_WARNING; - AccessibilityServiceInfo info = getAccessibilityServiceInfo(); - if (info == null) { - return null; + mShownDialogId = DIALOG_ID_DISABLE_WARNING; + AccessibilityServiceInfo info = getAccessibilityServiceInfo(); + if (info == null) { + return null; + } + return new AlertDialog.Builder(getActivity()) + .setTitle(getString(R.string.disable_service_title, + info.getResolveInfo().loadLabel(getPackageManager()))) + .setIconAttribute(android.R.attr.alertDialogIcon) + .setMessage(getString(R.string.disable_service_message, + info.getResolveInfo().loadLabel(getPackageManager()))) + .setCancelable(true) + .setPositiveButton(android.R.string.ok, this) + .setNegativeButton(android.R.string.cancel, this) + .create(); } - return new AlertDialog.Builder(getActivity()) - .setTitle(getString(R.string.disable_service_title, - info.getResolveInfo().loadLabel(getPackageManager()))) - .setIconAttribute(android.R.attr.alertDialogIcon) - .setMessage(getString(R.string.disable_service_message, - info.getResolveInfo().loadLabel(getPackageManager()))) - .setCancelable(true) - .setPositiveButton(android.R.string.ok, this) - .setNegativeButton(android.R.string.cancel, this) - .create(); - } default: { - throw new IllegalArgumentException(); - } + throw new IllegalArgumentException(); + } } } diff --git a/src/com/android/settings/accessibility/ToggleContrastPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleContrastPreferenceFragment.java new file mode 100644 index 000000000..d1e30f8d7 --- /dev/null +++ b/src/com/android/settings/accessibility/ToggleContrastPreferenceFragment.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2013 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.accessibility; + +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.Preference; +import android.preference.PreferenceScreen; +import android.preference.SeekBarPreference; +import android.provider.Settings; +import android.view.View; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; + +import com.android.settings.R; +import com.android.settings.accessibility.ToggleSwitch.OnBeforeCheckedChangeListener; + +public class ToggleContrastPreferenceFragment extends ToggleFeaturePreferenceFragment + implements Preference.OnPreferenceChangeListener { + private static final String ENABLED = Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED; + private static final String QUICK_SETTING_ENABLED = + Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_QUICK_SETTING_ENABLED; + + private CheckBoxPreference mEnableQuickSetting; + private SeekBarPreference mBrightness; + private SeekBarPreference mContrast; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + addPreferencesFromResource(R.xml.accessibility_contrast_settings); + + mEnableQuickSetting = (CheckBoxPreference) findPreference("enable_quick_setting"); + mBrightness = (SeekBarPreference) findPreference("brightness"); + mBrightness.setMax(1000); + mContrast = (SeekBarPreference) findPreference("contrast"); + mContrast.setMax(1000); + + initPreferences(); + } + + @Override + protected void onPreferenceToggled(String preferenceKey, boolean enabled) { + Settings.Secure.putInt(getContentResolver(), ENABLED, enabled ? 1 : 0); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (preference == mEnableQuickSetting) { + Settings.Secure.putInt( + getContentResolver(), QUICK_SETTING_ENABLED, ((Boolean) newValue) ? 1 : 0); + } else if (preference == mBrightness) { + final int progress = (Integer) newValue; + final float value = progress / 1000f - 0.5f; + Settings.Secure.putFloat( + getContentResolver(), Settings.Secure.ACCESSIBILITY_DISPLAY_BRIGHTNESS, value); + } else if (preference == mContrast) { + final int progress = (Integer) newValue; + final float value = progress / 1000f * 10f + 1f; + Settings.Secure.putFloat( + getContentResolver(), Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST, value); + } + + return true; + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + setTitle(getString(R.string.accessibility_display_contrast_preference_title)); + } + + @Override + protected void onInstallActionBarToggleSwitch() { + super.onInstallActionBarToggleSwitch(); + + mToggleSwitch.setCheckedInternal( + Settings.Secure.getInt(getContentResolver(), ENABLED, 0) == 1); + mToggleSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton button, boolean checked) { + onPreferenceToggled(mPreferenceKey, checked); + } + }); + } + + private void initPreferences() { + mEnableQuickSetting.setChecked( + Settings.Secure.getInt(getContentResolver(), QUICK_SETTING_ENABLED, 0) == 1); + mEnableQuickSetting.setOnPreferenceChangeListener(this); + + final float brightness = Settings.Secure.getFloat( + getContentResolver(), Settings.Secure.ACCESSIBILITY_DISPLAY_BRIGHTNESS, 0); + final float contrast = Settings.Secure.getFloat( + getContentResolver(), Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST, 2); + + // Available brightness range is -0.5 to 0.5. + mBrightness.setProgress((int) (1000 * (brightness + 0.5f))); + mBrightness.setOnPreferenceChangeListener(this); + + // Available contrast range is 1 to 10. + mContrast.setProgress((int) (1000 * (contrast - 1f) / 10f)); + mContrast.setOnPreferenceChangeListener(this); + } +} diff --git a/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java new file mode 100644 index 000000000..384aafc66 --- /dev/null +++ b/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2013 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.accessibility; + +import android.accessibilityservice.AccessibilityService; +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceChangeListener; +import android.preference.PreferenceScreen; +import android.preference.SeekBarPreference; +import android.provider.Settings; +import android.view.View; +import android.view.accessibility.AccessibilityManager; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; + +import com.android.settings.R; +import com.android.settings.accessibility.ToggleSwitch.OnBeforeCheckedChangeListener; + +public class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePreferenceFragment + implements Preference.OnPreferenceChangeListener { + private static final String ENABLED = Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED; + private static final String TYPE = Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER; + private static final String QUICK_SETTING_ENABLED = + Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_QUICK_SETTING_ENABLED; + private static final int DEFAULT_TYPE = AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY; + + private CheckBoxPreference mEnableQuickSetting; + private ListPreference mType; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + addPreferencesFromResource(R.xml.accessibility_daltonizer_settings); + + mEnableQuickSetting = (CheckBoxPreference) findPreference("enable_quick_setting"); + mType = (ListPreference) findPreference("type"); + + initPreferences(); + } + + @Override + protected void onPreferenceToggled(String preferenceKey, boolean enabled) { + Settings.Secure.putInt(getContentResolver(), ENABLED, enabled ? 1 : 0); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (preference == mEnableQuickSetting) { + Settings.Secure.putInt( + getContentResolver(), QUICK_SETTING_ENABLED, ((Boolean) newValue) ? 1 : 0); + } else if (preference == mType) { + Settings.Secure.putInt(getContentResolver(), TYPE, Integer.parseInt((String) newValue)); + preference.setSummary("%s"); + } + + return true; + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + setTitle(getString(R.string.accessibility_display_daltonizer_preference_title)); + } + + @Override + protected void onInstallActionBarToggleSwitch() { + super.onInstallActionBarToggleSwitch(); + + mToggleSwitch.setCheckedInternal( + Settings.Secure.getInt(getContentResolver(), ENABLED, 0) == 1); + mToggleSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton button, boolean checked) { + onPreferenceToggled(mPreferenceKey, checked); + } + }); + } + + private void initPreferences() { + mEnableQuickSetting.setChecked( + Settings.Secure.getInt(getContentResolver(), QUICK_SETTING_ENABLED, 0) == 1); + mEnableQuickSetting.setOnPreferenceChangeListener(this); + + final String value = Integer.toString( + Settings.Secure.getInt(getContentResolver(), TYPE, DEFAULT_TYPE)); + mType.setValue(value); + mType.setOnPreferenceChangeListener(this); + final int index = mType.findIndexOfValue(value); + if (index < 0) { + // We're using a mode controlled by developer preferences. + mType.setSummary(getString(R.string.daltonizer_type_overridden, + getString(R.string.simulate_color_space))); + } + } +} diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java index 2fbbabdb0..03f907263 100644 --- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java @@ -18,12 +18,12 @@ package com.android.settings.accessibility; import android.app.ActionBar; import android.app.Activity; +import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceScreen; import android.view.Gravity; import android.view.Menu; @@ -35,6 +35,7 @@ import android.view.accessibility.AccessibilityManager; import android.widget.TextView; import com.android.settings.R; +import com.android.settings.SettingsActivity; import com.android.settings.SettingsPreferenceFragment; public abstract class ToggleFeaturePreferenceFragment @@ -86,18 +87,28 @@ public abstract class ToggleFeaturePreferenceFragment @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - onInstallActionBarToggleSwitch(); + + final Context context = view.getContext(); + final int padding = context.getResources().getDimensionPixelSize( + R.dimen.action_bar_switch_padding); + mToggleSwitch = new ToggleSwitch(context); + mToggleSwitch.setPaddingRelative(0, 0, padding, 0); + onProcessArguments(getArguments()); - // Set a transparent drawable to prevent use of the default one. - getListView().setSelector(new ColorDrawable(Color.TRANSPARENT)); - getListView().setDivider(null); } @Override - public void onDestroyView() { - getActivity().getActionBar().setCustomView(null); - mToggleSwitch.setOnBeforeCheckedChangeListener(null); - super.onDestroyView(); + public void onResume() { + super.onResume(); + + installActionBarToggleSwitch(); + } + + @Override + public void onPause() { + removeActionBarToggleSwitch(); + + super.onPause(); } protected abstract void onPreferenceToggled(String preferenceKey, boolean enabled); @@ -111,37 +122,66 @@ public abstract class ToggleFeaturePreferenceFragment } protected void onInstallActionBarToggleSwitch() { - mToggleSwitch = createAndAddActionBarToggleSwitch(getActivity()); + // Implement this to set a checked listener. } - private ToggleSwitch createAndAddActionBarToggleSwitch(Activity activity) { - ToggleSwitch toggleSwitch = new ToggleSwitch(activity); - final int padding = activity.getResources().getDimensionPixelSize( - R.dimen.action_bar_switch_padding); - toggleSwitch.setPaddingRelative(0, 0, padding, 0); - activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, - ActionBar.DISPLAY_SHOW_CUSTOM); - activity.getActionBar().setCustomView(toggleSwitch, - new ActionBar.LayoutParams(ActionBar.LayoutParams.WRAP_CONTENT, - ActionBar.LayoutParams.WRAP_CONTENT, - Gravity.CENTER_VERTICAL | Gravity.END)); - return toggleSwitch; + private void installActionBarToggleSwitch() { + final ActionBar ab = getActivity().getActionBar(); + final ActionBar.LayoutParams params = new ActionBar.LayoutParams( + ActionBar.LayoutParams.WRAP_CONTENT, ActionBar.LayoutParams.WRAP_CONTENT, + Gravity.CENTER_VERTICAL | Gravity.END); + ab.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, ActionBar.DISPLAY_SHOW_CUSTOM); + ab.setCustomView(mToggleSwitch, params); + + onInstallActionBarToggleSwitch(); + } + + private void removeActionBarToggleSwitch() { + mToggleSwitch.setOnBeforeCheckedChangeListener(null); + + final ActionBar ab = getActivity().getActionBar(); + ab.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_CUSTOM); + ab.setCustomView(null); + } + + public void setTitle(String title) { + final SettingsActivity activity = (SettingsActivity) getActivity(); + if (!activity.onIsHidingHeaders()) { + getActivity().setTitle(title); + } } protected void onProcessArguments(Bundle arguments) { + if (arguments == null) { + getPreferenceScreen().removePreference(mSummaryPreference); + return; + } + // Key. mPreferenceKey = arguments.getString(AccessibilitySettings.EXTRA_PREFERENCE_KEY); + // Enabled. - final boolean enabled = arguments.getBoolean(AccessibilitySettings.EXTRA_CHECKED); - mToggleSwitch.setCheckedInternal(enabled); + if (arguments.containsKey(AccessibilitySettings.EXTRA_CHECKED)) { + final boolean enabled = arguments.getBoolean(AccessibilitySettings.EXTRA_CHECKED); + mToggleSwitch.setCheckedInternal(enabled); + } + // Title. - PreferenceActivity activity = (PreferenceActivity) getActivity(); - if (!activity.onIsMultiPane() || activity.onIsHidingHeaders()) { - String title = arguments.getString(AccessibilitySettings.EXTRA_TITLE); - getActivity().setTitle(title); + if (arguments.containsKey(AccessibilitySettings.EXTRA_TITLE)) { + setTitle(arguments.getString(AccessibilitySettings.EXTRA_TITLE)); } + // Summary. - CharSequence summary = arguments.getCharSequence(AccessibilitySettings.EXTRA_SUMMARY); - mSummaryPreference.setSummary(summary); + if (arguments.containsKey(AccessibilitySettings.EXTRA_SUMMARY)) { + final CharSequence summary = arguments.getCharSequence( + AccessibilitySettings.EXTRA_SUMMARY); + mSummaryPreference.setSummary(summary); + + // Set a transparent drawable to prevent use of the default one. + getListView().setSelector(new ColorDrawable(Color.TRANSPARENT)); + getListView().setDivider(null); + } else { + getPreferenceScreen().removePreference(mSummaryPreference); + } } } diff --git a/src/com/android/settings/accessibility/ToggleInversionPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleInversionPreferenceFragment.java new file mode 100644 index 000000000..bfb250059 --- /dev/null +++ b/src/com/android/settings/accessibility/ToggleInversionPreferenceFragment.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2013 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.accessibility; + +import android.accessibilityservice.AccessibilityService; +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.PreferenceScreen; +import android.preference.SeekBarPreference; +import android.provider.Settings; +import android.view.View; +import android.view.accessibility.AccessibilityManager; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; + +import com.android.settings.R; +import com.android.settings.accessibility.ToggleSwitch.OnBeforeCheckedChangeListener; + +public class ToggleInversionPreferenceFragment extends ToggleFeaturePreferenceFragment + implements Preference.OnPreferenceChangeListener { + private static final String ENABLED = Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED; + private static final String TYPE = Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION; + private static final String QUICK_SETTING_ENABLED = + Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_QUICK_SETTING_ENABLED; + private static final int DEFAULT_TYPE = AccessibilityManager.INVERSION_STANDARD; + + private CheckBoxPreference mEnableQuickSetting; + private ListPreference mType; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + addPreferencesFromResource(R.xml.accessibility_inversion_settings); + + mEnableQuickSetting = (CheckBoxPreference) findPreference("enable_quick_setting"); + mType = (ListPreference) findPreference("type"); + + initPreferences(); + } + + @Override + protected void onPreferenceToggled(String preferenceKey, boolean enabled) { + Settings.Secure.putInt(getContentResolver(), ENABLED, enabled ? 1 : 0); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (preference == mEnableQuickSetting) { + Settings.Secure.putInt( + getContentResolver(), QUICK_SETTING_ENABLED, ((Boolean) newValue) ? 1 : 0); + } else if (preference == mType) { + Settings.Secure.putInt(getContentResolver(), TYPE, Integer.parseInt((String) newValue)); + } + + return true; + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + setTitle(getString(R.string.accessibility_display_inversion_preference_title)); + } + + @Override + protected void onInstallActionBarToggleSwitch() { + super.onInstallActionBarToggleSwitch(); + + mToggleSwitch.setCheckedInternal( + Settings.Secure.getInt(getContentResolver(), ENABLED, 0) == 1); + mToggleSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton button, boolean checked) { + onPreferenceToggled(mPreferenceKey, checked); + } + }); + } + + private void initPreferences() { + mEnableQuickSetting.setChecked( + Settings.Secure.getInt(getContentResolver(), QUICK_SETTING_ENABLED, 0) == 1); + mEnableQuickSetting.setOnPreferenceChangeListener(this); + + mType.setValue( + Integer.toString(Settings.Secure.getInt(getContentResolver(), TYPE, DEFAULT_TYPE))); + mType.setOnPreferenceChangeListener(this); + } +} diff --git a/src/com/android/settings/accounts/AccountPreferenceBase.java b/src/com/android/settings/accounts/AccountPreferenceBase.java index 2759a8f16..f5332633a 100644 --- a/src/com/android/settings/accounts/AccountPreferenceBase.java +++ b/src/com/android/settings/accounts/AccountPreferenceBase.java @@ -38,7 +38,6 @@ import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; -import android.preference.PreferenceActivity; import android.preference.PreferenceScreen; import android.text.format.DateFormat; import android.util.Log; diff --git a/src/com/android/settings/accounts/AddAccountSettings.java b/src/com/android/settings/accounts/AddAccountSettings.java index 39906eb30..34b6c8d64 100644 --- a/src/com/android/settings/accounts/AddAccountSettings.java +++ b/src/com/android/settings/accounts/AddAccountSettings.java @@ -31,6 +31,7 @@ import android.util.Log; import android.widget.Toast; import com.android.settings.R; +import com.android.settings.Settings; import com.android.settings.Utils; import java.io.IOException; diff --git a/src/com/android/settings/accounts/ManageAccountsSettings.java b/src/com/android/settings/accounts/ManageAccountsSettings.java index 184f68092..b5a6579e3 100644 --- a/src/com/android/settings/accounts/ManageAccountsSettings.java +++ b/src/com/android/settings/accounts/ManageAccountsSettings.java @@ -31,7 +31,6 @@ import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceScreen; import android.util.Log; import android.view.LayoutInflater; @@ -45,6 +44,7 @@ import android.widget.TextView; import com.android.settings.AccountPreference; import com.android.settings.R; +import com.android.settings.SettingsActivity; import com.android.settings.Utils; import com.android.settings.location.LocationSettings; @@ -146,7 +146,7 @@ public class ManageAccountsSettings extends AccountPreferenceBase private void startAccountSettings(AccountPreference acctPref) { Bundle args = new Bundle(); args.putParcelable(AccountSyncSettings.ACCOUNT_KEY, acctPref.getAccount()); - ((PreferenceActivity) getActivity()).startPreferencePanel( + ((SettingsActivity) getActivity()).startPreferencePanel( AccountSyncSettings.class.getCanonicalName(), args, R.string.account_sync_settings_title, acctPref.getAccount().name, this, REQUEST_SHOW_SYNC_SETTINGS); @@ -368,7 +368,7 @@ public class ManageAccountsSettings extends AccountPreferenceBase @Override public boolean onPreferenceClick(Preference preference) { - ((PreferenceActivity) getActivity()).startPreferencePanel( + ((SettingsActivity) getActivity()).startPreferencePanel( mClass, null, mTitleRes, null, null, 0); // Hack: announce that the Google account preferences page is launching the location // settings diff --git a/src/com/android/settings/accounts/SyncSettingsActivity.java b/src/com/android/settings/accounts/SyncSettingsActivity.java index 96f16d684..9b1bc32e7 100644 --- a/src/com/android/settings/accounts/SyncSettingsActivity.java +++ b/src/com/android/settings/accounts/SyncSettingsActivity.java @@ -16,22 +16,20 @@ package com.android.settings.accounts; -import android.app.Fragment; import android.content.Intent; -import android.preference.PreferenceActivity; -import com.android.settings.ChooseLockGeneric.ChooseLockGenericFragment; +import com.android.settings.SettingsActivity; /** * Launcher activity for the SyncSettings fragment. * */ -public class SyncSettingsActivity extends PreferenceActivity { +public class SyncSettingsActivity extends SettingsActivity { @Override public Intent getIntent() { Intent modIntent = new Intent(super.getIntent()); - modIntent.putExtra(EXTRA_SHOW_FRAGMENT, SyncSettings.class.getName()); - modIntent.putExtra(EXTRA_NO_HEADERS, true); + modIntent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, SyncSettings.class.getName()); + modIntent.putExtra(SettingsActivity.EXTRA_NO_HEADERS, true); return modIntent; } diff --git a/src/com/android/settings/applications/AppOpsCategory.java b/src/com/android/settings/applications/AppOpsCategory.java index 125a43b9d..03ebb9e5e 100644 --- a/src/com/android/settings/applications/AppOpsCategory.java +++ b/src/com/android/settings/applications/AppOpsCategory.java @@ -28,7 +28,6 @@ import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.os.Bundle; -import android.preference.PreferenceActivity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -40,6 +39,7 @@ import android.widget.TextView; import java.util.List; import com.android.settings.R; +import com.android.settings.SettingsActivity; import com.android.settings.applications.AppOpsState.AppOpEntry; public class AppOpsCategory extends ListFragment implements @@ -333,8 +333,8 @@ public class AppOpsCategory extends ListFragment implements Bundle args = new Bundle(); args.putString(AppOpsDetails.ARG_PACKAGE_NAME, mCurrentPkgName); - PreferenceActivity pa = (PreferenceActivity)getActivity(); - pa.startPreferencePanel(AppOpsDetails.class.getName(), args, + SettingsActivity sa = (SettingsActivity) getActivity(); + sa.startPreferencePanel(AppOpsDetails.class.getName(), args, R.string.app_ops_settings, null, this, RESULT_APP_DETAILS); } diff --git a/src/com/android/settings/applications/AppOpsDetails.java b/src/com/android/settings/applications/AppOpsDetails.java index 1e2ac7d63..d9dec1958 100644 --- a/src/com/android/settings/applications/AppOpsDetails.java +++ b/src/com/android/settings/applications/AppOpsDetails.java @@ -28,7 +28,6 @@ import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; import android.content.res.Resources; import android.os.Bundle; -import android.preference.PreferenceActivity; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -40,6 +39,7 @@ import android.widget.Switch; import android.widget.TextView; import com.android.settings.R; +import com.android.settings.SettingsActivity; import com.android.settings.Utils; import java.util.List; @@ -161,8 +161,8 @@ public class AppOpsDetails extends Fragment { private void setIntentAndFinish(boolean finish, boolean appChanged) { Intent intent = new Intent(); intent.putExtra(ManageApplications.APP_CHG, appChanged); - PreferenceActivity pa = (PreferenceActivity)getActivity(); - pa.finishPreferencePanel(this, Activity.RESULT_OK, intent); + SettingsActivity sa = (SettingsActivity)getActivity(); + sa.finishPreferencePanel(this, Activity.RESULT_OK, intent); } /** Called when the activity is first created. */ diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java index 6729ea947..b1145a11c 100755 --- a/src/com/android/settings/applications/InstalledAppDetails.java +++ b/src/com/android/settings/applications/InstalledAppDetails.java @@ -19,6 +19,7 @@ package com.android.settings.applications; import com.android.internal.telephony.ISms; import com.android.internal.telephony.SmsUsageMonitor; import com.android.settings.R; +import com.android.settings.SettingsActivity; import com.android.settings.Utils; import com.android.settings.applications.ApplicationsState.AppEntry; @@ -57,7 +58,6 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; -import android.preference.PreferenceActivity; import android.text.SpannableString; import android.text.TextUtils; import android.text.format.Formatter; @@ -926,8 +926,8 @@ public class InstalledAppDetails extends Fragment if(localLOGV) Log.i(TAG, "appChanged="+appChanged); Intent intent = new Intent(); intent.putExtra(ManageApplications.APP_CHG, appChanged); - PreferenceActivity pa = (PreferenceActivity)getActivity(); - pa.finishPreferencePanel(this, Activity.RESULT_OK, intent); + SettingsActivity sa = (SettingsActivity)getActivity(); + sa.finishPreferencePanel(this, Activity.RESULT_OK, intent); } private void refreshSizeInfo() { diff --git a/src/com/android/settings/applications/InstalledAppDetailsTop.java b/src/com/android/settings/applications/InstalledAppDetailsTop.java index 44a88fb5e..e731c1c12 100644 --- a/src/com/android/settings/applications/InstalledAppDetailsTop.java +++ b/src/com/android/settings/applications/InstalledAppDetailsTop.java @@ -1,12 +1,25 @@ +/* + * 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.applications; -import android.app.Fragment; import android.content.Intent; -import android.preference.PreferenceActivity; - -import com.android.settings.ChooseLockGeneric.ChooseLockGenericFragment; +import com.android.settings.SettingsActivity; -public class InstalledAppDetailsTop extends PreferenceActivity { +public class InstalledAppDetailsTop extends SettingsActivity { @Override public Intent getIntent() { @@ -21,5 +34,4 @@ public class InstalledAppDetailsTop extends PreferenceActivity { if (InstalledAppDetails.class.getName().equals(fragmentName)) return true; return false; } - } diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java index 8b4be0141..d68b6a998 100644 --- a/src/com/android/settings/applications/ManageApplications.java +++ b/src/com/android/settings/applications/ManageApplications.java @@ -29,7 +29,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; @@ -44,9 +43,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; -import android.preference.PreferenceActivity; import android.preference.PreferenceFrameLayout; -import android.provider.Settings; import android.support.v4.view.PagerAdapter; import android.support.v4.view.PagerTabStrip; import android.support.v4.view.ViewPager; @@ -72,6 +69,7 @@ import android.widget.TextView; import com.android.internal.app.IMediaContainerService; import com.android.internal.content.PackageHelper; import com.android.settings.R; +import com.android.settings.SettingsActivity; import com.android.settings.Settings.RunningServicesActivity; import com.android.settings.Settings.StorageUseActivity; import com.android.settings.applications.ApplicationsState.AppEntry; @@ -854,7 +852,7 @@ public class ManageApplications extends Fragment implements || className.endsWith(".StorageUse")) { mSortOrder = SORT_ORDER_SIZE; defaultListType = LIST_TYPE_ALL; - } else if (Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS.equals(action)) { + } else if (android.provider.Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS.equals(action)) { // Select the all-apps list, with the default sorting defaultListType = LIST_TYPE_ALL; } @@ -1037,8 +1035,8 @@ public class ManageApplications extends Fragment implements Bundle args = new Bundle(); args.putString(InstalledAppDetails.ARG_PACKAGE_NAME, mCurrentPkgName); - PreferenceActivity pa = (PreferenceActivity)getActivity(); - pa.startPreferencePanel(InstalledAppDetails.class.getName(), args, + SettingsActivity sa = (SettingsActivity) getActivity(); + sa.startPreferencePanel(InstalledAppDetails.class.getName(), args, R.string.application_info_label, null, this, INSTALLED_APP_DETAILS); } diff --git a/src/com/android/settings/applications/ProcStatsEntry.java b/src/com/android/settings/applications/ProcStatsEntry.java index 0821ced23..87024783f 100644 --- a/src/com/android/settings/applications/ProcStatsEntry.java +++ b/src/com/android/settings/applications/ProcStatsEntry.java @@ -23,6 +23,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.ArrayMap; import android.util.Log; +import android.util.SparseArray; import com.android.internal.app.ProcessStats; import java.util.ArrayList; @@ -109,22 +110,26 @@ public final class ProcStatsEntry implements Parcelable { // See if there is one significant package that was running here. ArrayList<ProcStatsEntry> subProcs = new ArrayList<ProcStatsEntry>(); for (int ipkg=0; ipkg<mPackages.size(); ipkg++) { - ProcessStats.PackageState pkgState = stats.mPackages.get(mPackages.get(ipkg), mUid); - if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ", pkg " - + mPackages.get(ipkg) + ":"); - if (pkgState == null) { - Log.w(TAG, "No package state found for " + mPackages.get(ipkg) + "/" - + mUid + " in process " + mName); - continue; - } - ProcessStats.ProcessState pkgProc = pkgState.mProcesses.get(mName); - if (pkgProc == null) { - Log.w(TAG, "No process " + mName + " found in package state " - + mPackages.get(ipkg) + "/" + mUid); - continue; + SparseArray<ProcessStats.PackageState> vpkgs + = stats.mPackages.get(mPackages.get(ipkg), mUid); + for (int ivers=0; ivers<vpkgs.size(); ivers++) { + ProcessStats.PackageState pkgState = vpkgs.valueAt(ivers); + if (DEBUG) Log.d(TAG, "Eval pkg of " + mName + ", pkg " + + pkgState + ":"); + if (pkgState == null) { + Log.w(TAG, "No package state found for " + mPackages.get(ipkg) + "/" + + mUid + " in process " + mName); + continue; + } + ProcessStats.ProcessState pkgProc = pkgState.mProcesses.get(mName); + if (pkgProc == null) { + Log.w(TAG, "No process " + mName + " found in package state " + + mPackages.get(ipkg) + "/" + mUid); + continue; + } + subProcs.add(new ProcStatsEntry(pkgProc, pkgState.mPackageName, totals, useUss, + weightWithTime)); } - subProcs.add(new ProcStatsEntry(pkgProc, pkgState.mPackageName, totals, useUss, - weightWithTime)); } if (subProcs.size() > 1) { Collections.sort(subProcs, compare); diff --git a/src/com/android/settings/applications/ProcessStatsDetail.java b/src/com/android/settings/applications/ProcessStatsDetail.java index 326ca7bb3..5d0660a90 100644 --- a/src/com/android/settings/applications/ProcessStatsDetail.java +++ b/src/com/android/settings/applications/ProcessStatsDetail.java @@ -30,7 +30,6 @@ import android.net.Uri; import android.os.Bundle; import android.os.Process; import android.os.UserHandle; -import android.preference.PreferenceActivity; import android.text.format.Formatter; import android.view.LayoutInflater; import android.view.View; @@ -161,7 +160,6 @@ public class ProcessStatsDetail extends Fragment implements Button.OnClickListen } private void doAction(int action) { - PreferenceActivity pa = (PreferenceActivity)getActivity(); switch (action) { case ACTION_FORCE_STOP: killProcesses(); diff --git a/src/com/android/settings/applications/ProcessStatsUi.java b/src/com/android/settings/applications/ProcessStatsUi.java index 8322ea352..9deb22ddd 100644 --- a/src/com/android/settings/applications/ProcessStatsUi.java +++ b/src/com/android/settings/applications/ProcessStatsUi.java @@ -25,7 +25,6 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserManager; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceFragment; import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; @@ -40,6 +39,7 @@ import com.android.internal.app.IProcessStats; import com.android.internal.app.ProcessMap; import com.android.internal.app.ProcessStats; import com.android.settings.R; +import com.android.settings.SettingsActivity; import com.android.settings.fuelgauge.Utils; import java.io.IOException; @@ -192,7 +192,7 @@ public class ProcessStatsUi extends PreferenceFragment args.putBoolean(ProcessStatsDetail.EXTRA_USE_USS, mUseUss); args.putLong(ProcessStatsDetail.EXTRA_MAX_WEIGHT, mMaxWeight); args.putLong(ProcessStatsDetail.EXTRA_TOTAL_TIME, mTotalTime); - ((PreferenceActivity) getActivity()).startPreferencePanel( + ((SettingsActivity) getActivity()).startPreferencePanel( ProcessStatsDetail.class.getName(), args, R.string.details_title, null, null, 0); return super.onPreferenceTreeClick(preferenceScreen, preference); @@ -466,33 +466,36 @@ public class ProcessStatsUi extends PreferenceFragment final ProcessMap<ProcStatsEntry> entriesMap = new ProcessMap<ProcStatsEntry>(); for (int ipkg=0, N=mStats.mPackages.getMap().size(); ipkg<N; ipkg++) { - final SparseArray<ProcessStats.PackageState> pkgUids + final SparseArray<SparseArray<ProcessStats.PackageState>> pkgUids = mStats.mPackages.getMap().valueAt(ipkg); for (int iu=0; iu<pkgUids.size(); iu++) { - final ProcessStats.PackageState st = pkgUids.valueAt(iu); - for (int iproc=0; iproc<st.mProcesses.size(); iproc++) { - final ProcessStats.ProcessState pkgProc = st.mProcesses.valueAt(iproc); - final ProcessStats.ProcessState proc = mStats.mProcesses.get(pkgProc.mName, - pkgProc.mUid); - if (proc == null) { - Log.w(TAG, "No process found for pkg " + st.mPackageName - + "/" + st.mUid + " proc name " + pkgProc.mName); - continue; - } - ProcStatsEntry ent = entriesMap.get(proc.mName, proc.mUid); - if (ent == null) { - ent = new ProcStatsEntry(proc, st.mPackageName, totals, mUseUss, - mStatsType == MENU_TYPE_BACKGROUND); - if (ent.mDuration > 0) { - if (DEBUG) Log.d(TAG, "Adding proc " + proc.mName + "/" - + proc.mUid + ": time=" + makeDuration(ent.mDuration) + " (" - + ((((double)ent.mDuration) / memTotalTime) * 100) + "%)" - + " pss=" + ent.mAvgPss); - entriesMap.put(proc.mName, proc.mUid, ent); - entries.add(ent); + final SparseArray<ProcessStats.PackageState> vpkgs = pkgUids.valueAt(iu); + for (int iv=0; iv<vpkgs.size(); iv++) { + final ProcessStats.PackageState st = vpkgs.valueAt(iv); + for (int iproc=0; iproc<st.mProcesses.size(); iproc++) { + final ProcessStats.ProcessState pkgProc = st.mProcesses.valueAt(iproc); + final ProcessStats.ProcessState proc = mStats.mProcesses.get(pkgProc.mName, + pkgProc.mUid); + if (proc == null) { + Log.w(TAG, "No process found for pkg " + st.mPackageName + + "/" + st.mUid + " proc name " + pkgProc.mName); + continue; + } + ProcStatsEntry ent = entriesMap.get(proc.mName, proc.mUid); + if (ent == null) { + ent = new ProcStatsEntry(proc, st.mPackageName, totals, mUseUss, + mStatsType == MENU_TYPE_BACKGROUND); + if (ent.mDuration > 0) { + if (DEBUG) Log.d(TAG, "Adding proc " + proc.mName + "/" + + proc.mUid + ": time=" + makeDuration(ent.mDuration) + " (" + + ((((double)ent.mDuration) / memTotalTime) * 100) + "%)" + + " pss=" + ent.mAvgPss); + entriesMap.put(proc.mName, proc.mUid, ent); + entries.add(ent); + } + } else { + ent.addPackage(st.mPackageName); } - } else { - ent.addPackage(st.mPackageName); } } } @@ -503,21 +506,25 @@ public class ProcessStatsUi extends PreferenceFragment // Add in service info. if (mStatsType == MENU_TYPE_BACKGROUND) { for (int ip=0, N=mStats.mPackages.getMap().size(); ip<N; ip++) { - SparseArray<ProcessStats.PackageState> uids = mStats.mPackages.getMap().valueAt(ip); + SparseArray<SparseArray<ProcessStats.PackageState>> uids + = mStats.mPackages.getMap().valueAt(ip); for (int iu=0; iu<uids.size(); iu++) { - ProcessStats.PackageState ps = uids.valueAt(iu); - for (int is=0, NS=ps.mServices.size(); is<NS; is++) { - ProcessStats.ServiceState ss = ps.mServices.valueAt(is); - if (ss.mProcessName != null) { - ProcStatsEntry ent = entriesMap.get(ss.mProcessName, uids.keyAt(iu)); - if (ent != null) { - if (DEBUG) Log.d(TAG, "Adding service " + ps.mPackageName - + "/" + ss.mName + "/" + uids.keyAt(iu) + " to proc " - + ss.mProcessName); - ent.addService(ss); - } else { - Log.w(TAG, "No process " + ss.mProcessName + "/" + uids.keyAt(iu) - + " for service " + ss.mName); + SparseArray<ProcessStats.PackageState> vpkgs = uids.valueAt(iu); + for (int iv=0; iv<vpkgs.size(); iv++) { + ProcessStats.PackageState ps = vpkgs.valueAt(iv); + for (int is=0, NS=ps.mServices.size(); is<NS; is++) { + ProcessStats.ServiceState ss = ps.mServices.valueAt(is); + if (ss.mProcessName != null) { + ProcStatsEntry ent = entriesMap.get(ss.mProcessName, uids.keyAt(iu)); + if (ent != null) { + if (DEBUG) Log.d(TAG, "Adding service " + ps.mPackageName + + "/" + ss.mName + "/" + uids.keyAt(iu) + " to proc " + + ss.mProcessName); + ent.addService(ss); + } else { + Log.w(TAG, "No process " + ss.mProcessName + "/" + uids.keyAt(iu) + + " for service " + ss.mName); + } } } } diff --git a/src/com/android/settings/applications/RunningProcessesView.java b/src/com/android/settings/applications/RunningProcessesView.java index 8eb0496cd..caa545076 100644 --- a/src/com/android/settings/applications/RunningProcessesView.java +++ b/src/com/android/settings/applications/RunningProcessesView.java @@ -28,7 +28,6 @@ import android.content.pm.PackageManager; import android.os.Bundle; import android.os.SystemClock; import android.os.UserHandle; -import android.preference.PreferenceActivity; import android.text.format.DateUtils; import android.text.format.Formatter; import android.util.AttributeSet; @@ -42,6 +41,7 @@ import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import android.widget.AbsListView.RecyclerListener; +import com.android.settings.SettingsActivity; import java.util.ArrayList; import java.util.Collections; @@ -397,9 +397,9 @@ public class RunningProcessesView extends FrameLayout } args.putInt(RunningServiceDetails.KEY_USER_ID, mi.mUserId); args.putBoolean(RunningServiceDetails.KEY_BACKGROUND, mAdapter.mShowBackground); - - PreferenceActivity pa = (PreferenceActivity)mOwner.getActivity(); - pa.startPreferencePanel(RunningServiceDetails.class.getName(), args, + + SettingsActivity sa = (SettingsActivity) mOwner.getActivity(); + sa.startPreferencePanel(RunningServiceDetails.class.getName(), args, R.string.runningservicedetails_settings_title, null, null, 0); } } diff --git a/src/com/android/settings/bluetooth/BluetoothAdvertisingEnabler.java b/src/com/android/settings/bluetooth/BluetoothAdvertisingEnabler.java deleted file mode 100644 index b9e4ee2e8..000000000 --- a/src/com/android/settings/bluetooth/BluetoothAdvertisingEnabler.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.bluetooth; - -import android.content.Context; -import android.preference.PreferenceScreen; - -import com.android.settings.R; - -/** - * BluetoothAdvertisingEnabler helps manager change of bluetooth advertising preferences. - */ -final class BluetoothAdvertisingEnabler { - - private final Context mContext; - private final PreferenceScreen mBluetoothAdvertisingPreference; - - public BluetoothAdvertisingEnabler(Context context, PreferenceScreen bluetoothBroadcast) { - mContext = context; - mBluetoothAdvertisingPreference = bluetoothBroadcast; - } - - public void resume() { - boolean isBroadcastingEnable = LocalBluetoothPreferences.isAdvertisingEnabled(mContext); - handleAdvertisingStateChange(isBroadcastingEnable); - } - - private void handleAdvertisingStateChange(boolean isBroadcastingEnable) { - mBluetoothAdvertisingPreference.setSummary(isBroadcastingEnable ? - R.string.bluetooth_broadcasting_state_on : - R.string.bluetooth_broadcasting_state_off); - } -} diff --git a/src/com/android/settings/bluetooth/BluetoothAdvertisingFragment.java b/src/com/android/settings/bluetooth/BluetoothAdvertisingFragment.java deleted file mode 100644 index ea6adb046..000000000 --- a/src/com/android/settings/bluetooth/BluetoothAdvertisingFragment.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.bluetooth; - -import android.app.ActionBar; -import android.app.Activity; -import android.app.Fragment; -import android.bluetooth.BluetoothAdapter; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.preference.PreferenceActivity; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CompoundButton; -import android.widget.Switch; - -import com.android.settings.R; - -/** - * Fragment to display and let the user change advertising preference. - */ -public class BluetoothAdvertisingFragment extends Fragment - implements CompoundButton.OnCheckedChangeListener { - - private static final String TAG = "BluetoothAdvertisingFragment"; - private View mView; - private Switch mActionBarSwitch; - private Activity mActivity; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mActivity = getActivity(); - mActionBarSwitch = new Switch(mActivity); - if (mActivity instanceof PreferenceActivity) { - final int padding = mActivity.getResources().getDimensionPixelSize( - R.dimen.action_bar_switch_padding); - mActionBarSwitch.setPaddingRelative(0, 0, padding, 0); - mActivity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, - ActionBar.DISPLAY_SHOW_CUSTOM); - mActivity.getActionBar().setCustomView(mActionBarSwitch, new ActionBar.LayoutParams( - ActionBar.LayoutParams.WRAP_CONTENT, - ActionBar.LayoutParams.WRAP_CONTENT, - Gravity.CENTER_VERTICAL | Gravity.END)); - mActivity.getActionBar().setTitle(R.string.bluetooth_broadcasting); - } - mActionBarSwitch.setChecked( - LocalBluetoothPreferences.isAdvertisingEnabled(mActivity.getApplicationContext())); - - mActionBarSwitch.setOnCheckedChangeListener(this); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - mView = inflater.inflate(R.layout.bluetooth_advertising, container, false); - initView(mView); - return mView; - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - getActivity().getActionBar().setCustomView(null); - } - - private void initView(View view) { - mActionBarSwitch.setOnCheckedChangeListener(this); - } - - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean desiredState) { - mActionBarSwitch.setChecked(desiredState); - Context context = getActivity(); - LocalBluetoothPreferences.setAdvertisingEnabled(context, desiredState); - if (!desiredState) { - LocalBluetoothAdapter adapter = - LocalBluetoothManager.getInstance(context).getBluetoothAdapter(); - // Stop advertising if advertising is in process. - if (adapter.isAdvertising()) { - Intent intent = new Intent(BluetoothAdapter.ACTION_STOP_ADVERTISING); - getActivity().startActivity(intent); - } - } - } -} diff --git a/src/com/android/settings/bluetooth/BluetoothDiscoverableEnabler.java b/src/com/android/settings/bluetooth/BluetoothDiscoverableEnabler.java index a040bf293..17da0a783 100755 --- a/src/com/android/settings/bluetooth/BluetoothDiscoverableEnabler.java +++ b/src/com/android/settings/bluetooth/BluetoothDiscoverableEnabler.java @@ -25,10 +25,13 @@ import android.content.SharedPreferences; import android.os.Handler; import android.os.SystemProperties; import android.preference.Preference; -import android.util.Log; +import android.text.format.DateUtils; import com.android.settings.R; +import android.text.format.Time; +import android.util.Log; + /** * BluetoothDiscoverableEnabler is a helper to manage the "Discoverable" * checkbox. It sets/unsets discoverability and keeps track of how much time @@ -57,11 +60,9 @@ final class BluetoothDiscoverableEnabler implements Preference.OnPreferenceClick static final int DEFAULT_DISCOVERABLE_TIMEOUT = DISCOVERABLE_TIMEOUT_TWO_MINUTES; - private final Context mContext; + private Context mContext; private final Handler mUiHandler; private final Preference mDiscoveryPreference; - // Preference for visibility time out. Not final as it needs to be set through setter. - private Preference mVisibilityTimeoutPreference; private final LocalBluetoothAdapter mLocalAdapter; @@ -91,9 +92,8 @@ final class BluetoothDiscoverableEnabler implements Preference.OnPreferenceClick } }; - BluetoothDiscoverableEnabler(Context context, LocalBluetoothAdapter adapter, + BluetoothDiscoverableEnabler(LocalBluetoothAdapter adapter, Preference discoveryPreference) { - mContext = context; mUiHandler = new Handler(); mLocalAdapter = adapter; mDiscoveryPreference = discoveryPreference; @@ -101,20 +101,19 @@ final class BluetoothDiscoverableEnabler implements Preference.OnPreferenceClick discoveryPreference.setPersistent(false); } - public void setVisibilityPreference(Preference visibilityPreference) { - mVisibilityTimeoutPreference = visibilityPreference; - } - - public void resume() { + public void resume(Context context) { if (mLocalAdapter == null) { return; } + if (mContext != context) { + mContext = context; + } + IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); mContext.registerReceiver(mReceiver, filter); mDiscoveryPreference.setOnPreferenceClickListener(this); handleModeChanged(mLocalAdapter.getScanMode()); - updateVisibilityTimeoutDisplay(); } public void pause() { @@ -125,9 +124,6 @@ final class BluetoothDiscoverableEnabler implements Preference.OnPreferenceClick mUiHandler.removeCallbacks(mUpdateCountdownSummaryRunnable); mContext.unregisterReceiver(mReceiver); mDiscoveryPreference.setOnPreferenceClickListener(null); - if (mVisibilityTimeoutPreference != null) { - mVisibilityTimeoutPreference.setOnPreferenceClickListener(null); - } } public boolean onPreferenceClick(Preference preference) { @@ -150,28 +146,14 @@ final class BluetoothDiscoverableEnabler implements Preference.OnPreferenceClick if (timeout > 0) { BluetoothDiscoverableTimeoutReceiver.setDiscoverableAlarm(mContext, endTimestamp); + } else { + BluetoothDiscoverableTimeoutReceiver.cancelDiscoverableAlarm(mContext); } + } else { mLocalAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE); BluetoothDiscoverableTimeoutReceiver.cancelDiscoverableAlarm(mContext); } - updateVisibilityTimeoutDisplay(); - } - - // Update visibility timeout preference. - private void updateVisibilityTimeoutDisplay() { - if (mVisibilityTimeoutPreference == null) { - return; - } - int index = getDiscoverableTimeoutIndex(); - - String visibilitySummary = ""; - CharSequence[] timeoutChoices = - mContext.getResources().getTextArray(R.array.bluetooth_visibility_timeout_entries); - if (index >= 0 && index < timeoutChoices.length) { - visibilitySummary = timeoutChoices[index].toString(); - } - mVisibilityTimeoutPreference.setSummary(visibilitySummary); } private void updateTimerDisplay(int timeout) { diff --git a/src/com/android/settings/bluetooth/BluetoothEnabler.java b/src/com/android/settings/bluetooth/BluetoothEnabler.java index df13eefbd..1a450d441 100644 --- a/src/com/android/settings/bluetooth/BluetoothEnabler.java +++ b/src/com/android/settings/bluetooth/BluetoothEnabler.java @@ -35,7 +35,7 @@ import com.android.settings.WirelessSettings; * preference reflects the current state. */ public final class BluetoothEnabler implements CompoundButton.OnCheckedChangeListener { - private final Context mContext; + private Context mContext; private Switch mSwitch; private boolean mValidListener; private final LocalBluetoothAdapter mLocalAdapter; @@ -67,12 +67,16 @@ public final class BluetoothEnabler implements CompoundButton.OnCheckedChangeLis mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); } - public void resume() { + public void resume(Context context) { if (mLocalAdapter == null) { mSwitch.setEnabled(false); return; } + if (mContext != context) { + mContext = context; + } + // Bluetooth state is not sticky, so set it manually handleStateChanged(mLocalAdapter.getBluetoothState()); diff --git a/src/com/android/settings/bluetooth/BluetoothLocalDevicePreference.java b/src/com/android/settings/bluetooth/BluetoothLocalDevicePreference.java deleted file mode 100644 index 403c3b4b9..000000000 --- a/src/com/android/settings/bluetooth/BluetoothLocalDevicePreference.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.bluetooth; - -import android.content.Context; -import android.preference.Preference; -import android.view.View; -import android.view.View.OnClickListener; -import android.widget.ImageView; - -import com.android.settings.R; - -/** - * A preference screen to show information for local device. - */ -public class BluetoothLocalDevicePreference extends Preference { - private static OnClickListener mSettingsListener; - - public BluetoothLocalDevicePreference(Context context, OnClickListener settingsListener) { - super(context); - mSettingsListener = settingsListener; - setWidgetLayoutResource(R.layout.preference_bluetooth); - } - - @Override - protected void onBindView(View view) { - ImageView deviceDetails = (ImageView) view.findViewById( - R.id.deviceDetails); - deviceDetails.setOnClickListener(mSettingsListener); - super.onBindView(view); - } -} diff --git a/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java b/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java index 0af9c4ed7..bf0356c31 100644 --- a/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java +++ b/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java @@ -26,7 +26,6 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; -import android.preference.PreferenceActivity; import android.text.Editable; import android.text.InputFilter; import android.text.TextWatcher; @@ -179,9 +178,8 @@ public final class BluetoothNameDialogFragment extends DialogFragment implements mDeviceNameUpdated = true; mDeviceNameEdited = false; mDeviceNameView.setText(mLocalAdapter.getName()); + getActivity().setTitle(mLocalAdapter.getName()); } - PreferenceActivity activity = (PreferenceActivity)getActivity(); - activity.showBreadCrumbs(mLocalAdapter.getName(), ""); } public void afterTextChanged(Editable s) { diff --git a/src/com/android/settings/bluetooth/BluetoothSettings.java b/src/com/android/settings/bluetooth/BluetoothSettings.java index 4cb36c884..acd9dada7 100755 --- a/src/com/android/settings/bluetooth/BluetoothSettings.java +++ b/src/com/android/settings/bluetooth/BluetoothSettings.java @@ -28,7 +28,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceCategory; import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; @@ -42,6 +41,7 @@ import android.widget.Switch; import android.widget.TextView; import com.android.settings.R; +import com.android.settings.SettingsActivity; /** * BluetoothSettings is the Settings screen for Bluetooth configuration and @@ -51,23 +51,25 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment { private static final String TAG = "BluetoothSettings"; private static final int MENU_ID_SCAN = Menu.FIRST; - private static final int MENU_ID_SHOW_RECEIVED = Menu.FIRST + 1; + private static final int MENU_ID_RENAME_DEVICE = Menu.FIRST + 1; + private static final int MENU_ID_VISIBILITY_TIMEOUT = Menu.FIRST + 2; + private static final int MENU_ID_SHOW_RECEIVED = Menu.FIRST + 3; /* Private intent to show the list of received files */ private static final String BTOPP_ACTION_OPEN_RECEIVED_FILES = "android.btopp.intent.action.OPEN_RECEIVED_FILES"; private BluetoothEnabler mBluetoothEnabler; - private BluetoothDiscoverableEnabler mDiscoverableEnabler; private PreferenceGroup mPairedDevicesCategory; - private PreferenceGroup mAvailableDevicesCategory; private boolean mAvailableDevicesCategoryIsPresent; + private boolean mActivityStarted; private TextView mEmptyView; + private Switch mSwitch; private final IntentFilter mIntentFilter; @@ -103,32 +105,45 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment { mEmptyView = (TextView) getView().findViewById(android.R.id.empty); getListView().setEmptyView(mEmptyView); + + final Activity activity = getActivity(); + final int padding = activity.getResources().getDimensionPixelSize( + R.dimen.action_bar_switch_padding); + mSwitch = new Switch(activity); + mSwitch.setPaddingRelative(0, 0, padding, 0); + + mBluetoothEnabler = new BluetoothEnabler(activity, mSwitch); } @Override - void addPreferencesForActivity() { - addPreferencesFromResource(R.xml.bluetooth_settings); + public void onStart() { + super.onStart(); + + final SettingsActivity activity = (SettingsActivity) getActivity(); + + if (!activity.onIsHidingHeaders()) { + activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, + ActionBar.DISPLAY_SHOW_CUSTOM); + activity.getActionBar().setCustomView(mSwitch, new ActionBar.LayoutParams( + ActionBar.LayoutParams.WRAP_CONTENT, + ActionBar.LayoutParams.WRAP_CONTENT, + Gravity.CENTER_VERTICAL | Gravity.END)); + } + } - Activity activity = getActivity(); - - Switch actionBarSwitch = new Switch(activity); - - if (activity instanceof PreferenceActivity) { - PreferenceActivity preferenceActivity = (PreferenceActivity) activity; - if (preferenceActivity.onIsHidingHeaders() || !preferenceActivity.onIsMultiPane()) { - final int padding = activity.getResources().getDimensionPixelSize( - R.dimen.action_bar_switch_padding); - actionBarSwitch.setPaddingRelative(0, 0, padding, 0); - activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, - ActionBar.DISPLAY_SHOW_CUSTOM); - activity.getActionBar().setCustomView(actionBarSwitch, new ActionBar.LayoutParams( - ActionBar.LayoutParams.WRAP_CONTENT, - ActionBar.LayoutParams.WRAP_CONTENT, - Gravity.CENTER_VERTICAL | Gravity.END)); - } + @Override + public void onStop() { + super.onStop(); + final SettingsActivity activity = (SettingsActivity) getActivity(); + if (!activity.onIsHidingHeaders()) { + activity.getActionBar().setDisplayOptions(0, ActionBar.DISPLAY_SHOW_CUSTOM); + activity.getActionBar().setCustomView(null); } + } - mBluetoothEnabler = new BluetoothEnabler(activity, actionBarSwitch); + @Override + void addPreferencesForActivity() { + addPreferencesFromResource(R.xml.bluetooth_settings); setHasOptionsMenu(true); } @@ -138,12 +153,12 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment { // resume BluetoothEnabler before calling super.onResume() so we don't get // any onDeviceAdded() callbacks before setting up view in updateContent() if (mBluetoothEnabler != null) { - mBluetoothEnabler.resume(); + mBluetoothEnabler.resume(getActivity()); } super.onResume(); if (mDiscoverableEnabler != null) { - mDiscoverableEnabler.resume(); + mDiscoverableEnabler.resume(getActivity()); } getActivity().registerReceiver(mReceiver, mIntentFilter); if (mLocalAdapter != null) { @@ -176,6 +191,12 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment { menu.add(Menu.NONE, MENU_ID_SCAN, 0, textId) .setEnabled(bluetoothIsEnabled && !isDiscovering) .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + menu.add(Menu.NONE, MENU_ID_RENAME_DEVICE, 0, R.string.bluetooth_rename_device) + .setEnabled(bluetoothIsEnabled) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); + menu.add(Menu.NONE, MENU_ID_VISIBILITY_TIMEOUT, 0, R.string.bluetooth_visibility_timeout) + .setEnabled(bluetoothIsEnabled) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); menu.add(Menu.NONE, MENU_ID_SHOW_RECEIVED, 0, R.string.bluetooth_show_received_files) .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); super.onCreateOptionsMenu(menu, inflater); @@ -190,6 +211,16 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment { } return true; + case MENU_ID_RENAME_DEVICE: + new BluetoothNameDialogFragment().show( + getFragmentManager(), "rename device"); + return true; + + case MENU_ID_VISIBILITY_TIMEOUT: + new BluetoothVisibilityTimeoutFragment().show( + getFragmentManager(), "visibility timeout"); + return true; + case MENU_ID_SHOW_RECEIVED: Intent intent = new Intent(BTOPP_ACTION_OPEN_RECEIVED_FILES); getActivity().sendBroadcast(intent); @@ -234,8 +265,7 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment { // This device if (mMyDevicePreference == null) { - mMyDevicePreference = new BluetoothLocalDevicePreference( - getActivity(), mLocalDeviceProfilesListener); + mMyDevicePreference = new Preference(getActivity()); } mMyDevicePreference.setTitle(mLocalAdapter.getName()); if (getResources().getBoolean(com.android.internal.R.bool.config_voice_capable)) { @@ -249,9 +279,9 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment { if (!isRestrictedAndNotPinProtected()) { if (mDiscoverableEnabler == null) { - mDiscoverableEnabler = new BluetoothDiscoverableEnabler(getActivity(), - mLocalAdapter, mMyDevicePreference); - mDiscoverableEnabler.resume(); + mDiscoverableEnabler = new BluetoothDiscoverableEnabler(mLocalAdapter, + mMyDevicePreference); + mDiscoverableEnabler.resume(getActivity()); LocalBluetoothManager.getInstance(getActivity()).setDiscoverableEnabler( mDiscoverableEnabler); } @@ -343,18 +373,6 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment { updateContent(mLocalAdapter.getBluetoothState(), false); } - // Listener for local device profile fragment. - private final View.OnClickListener mLocalDeviceProfilesListener = new View.OnClickListener() { - public void onClick(View v) { - if (isRestrictedAndNotPinProtected()) return; - - ((PreferenceActivity) getActivity()).startPreferencePanel( - LocalDeviceProfilesSettings.class.getName(), null, - 0, mLocalAdapter.getName(), null, 0); - } - }; - - private final View.OnClickListener mDeviceProfilesListener = new View.OnClickListener() { public void onClick(View v) { // User clicked on advanced options icon for a device in the list @@ -366,7 +384,7 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment { Bundle args = new Bundle(1); args.putParcelable(DeviceProfilesSettings.EXTRA_DEVICE, device.getDevice()); - ((PreferenceActivity) getActivity()).startPreferencePanel( + ((SettingsActivity) getActivity()).startPreferencePanel( DeviceProfilesSettings.class.getName(), args, R.string.bluetooth_device_advanced_title, null, null, 0); } else { diff --git a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java index e2faf7fd5..9ced992ca 100644 --- a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java +++ b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java @@ -25,9 +25,7 @@ import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; import android.util.Log; -import com.android.settings.ProgressCategory; import com.android.settings.RestrictedSettingsFragment; -import com.android.settings.SettingsPreferenceFragment; import java.util.Collection; import java.util.WeakHashMap; diff --git a/src/com/android/settings/bluetooth/LocalBluetoothAdapter.java b/src/com/android/settings/bluetooth/LocalBluetoothAdapter.java index 041ecb7bb..013171c14 100644 --- a/src/com/android/settings/bluetooth/LocalBluetoothAdapter.java +++ b/src/com/android/settings/bluetooth/LocalBluetoothAdapter.java @@ -118,9 +118,6 @@ public final class LocalBluetoothAdapter { return mAdapter.isDiscovering(); } - boolean isAdvertising() { - return mAdapter.isAdvertising(); - } boolean isEnabled() { return mAdapter.isEnabled(); } @@ -129,14 +126,6 @@ public final class LocalBluetoothAdapter { mAdapter.setDiscoverableTimeout(timeout); } - boolean startAdvertising() { - return mAdapter.startAdvertising(); - } - - boolean stopAdvertising() { - return mAdapter.stopAdvertisting(); - } - void setName(String name) { mAdapter.setName(name); } diff --git a/src/com/android/settings/bluetooth/LocalBluetoothManager.java b/src/com/android/settings/bluetooth/LocalBluetoothManager.java index 265cabb76..ae8dec2ae 100644 --- a/src/com/android/settings/bluetooth/LocalBluetoothManager.java +++ b/src/com/android/settings/bluetooth/LocalBluetoothManager.java @@ -37,7 +37,6 @@ public final class LocalBluetoothManager { private Context mForegroundActivity; private BluetoothDiscoverableEnabler mDiscoverableEnabler; - private BluetoothAdvertisingEnabler mAdvertisingEnabler; private final LocalBluetoothAdapter mLocalAdapter; @@ -71,14 +70,6 @@ public final class LocalBluetoothManager { return mDiscoverableEnabler; } - public void setBluetoothAdvertisingEnabler(BluetoothAdvertisingEnabler advertisingEnabler) { - this.mAdvertisingEnabler = advertisingEnabler; - } - - public BluetoothAdvertisingEnabler getAdvertisingEnabler() { - return mAdvertisingEnabler; - } - private LocalBluetoothManager(LocalBluetoothAdapter adapter, Context context) { mContext = context; mLocalAdapter = adapter; diff --git a/src/com/android/settings/bluetooth/LocalBluetoothPreferences.java b/src/com/android/settings/bluetooth/LocalBluetoothPreferences.java index f862f726d..f00b80178 100644 --- a/src/com/android/settings/bluetooth/LocalBluetoothPreferences.java +++ b/src/com/android/settings/bluetooth/LocalBluetoothPreferences.java @@ -17,10 +17,8 @@ package com.android.settings.bluetooth; import android.app.QueuedWork; -import android.bluetooth.BluetoothAdapter; import android.content.Context; import android.content.SharedPreferences; -import android.content.SharedPreferences.Editor; import android.content.res.Configuration; import android.util.Log; @@ -48,10 +46,6 @@ final class LocalBluetoothPreferences { private static final String KEY_DISCOVERABLE_END_TIMESTAMP = "discoverable_end_timestamp"; - private static final String KEY_ADVERTISEMENT_PREFERENCE = "bt_advertisement_perference"; - - private static final boolean DEFAULT_ADVERTISING_ENABLED = false; - private LocalBluetoothPreferences() { } @@ -64,17 +58,6 @@ final class LocalBluetoothPreferences { KEY_DISCOVERABLE_END_TIMESTAMP, 0); } - static boolean isAdvertisingEnabled(Context context) { - return getSharedPreferences(context).getBoolean( - KEY_ADVERTISEMENT_PREFERENCE, DEFAULT_ADVERTISING_ENABLED); - } - - static void setAdvertisingEnabled(Context context, boolean advertisingEnabled) { - Editor preferenceEditor = getSharedPreferences(context).edit(); - preferenceEditor.putBoolean(KEY_ADVERTISEMENT_PREFERENCE, advertisingEnabled); - preferenceEditor.apply(); - } - static boolean shouldShowDialogInForeground(Context context, String deviceAddress) { LocalBluetoothManager manager = LocalBluetoothManager.getInstance(context); diff --git a/src/com/android/settings/bluetooth/LocalDeviceProfilesSettings.java b/src/com/android/settings/bluetooth/LocalDeviceProfilesSettings.java deleted file mode 100644 index f01bbf5ec..000000000 --- a/src/com/android/settings/bluetooth/LocalDeviceProfilesSettings.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.bluetooth; - -import android.os.Bundle; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceClickListener; -import android.preference.PreferenceActivity; -import android.preference.PreferenceScreen; - -import com.android.settings.R; -import com.android.settings.SettingsPreferenceFragment; - -/** - * This preference fragment presents the user with the profiles of the local devices and allow them - * to be modified. - */ -public final class LocalDeviceProfilesSettings extends SettingsPreferenceFragment { - private static final String TAG = "LocalDeviceProfilesSettings"; - - private static final String KEY_RENAME_DEVICE = "rename_device"; - private static final String KEY_BROADCASTING = "broadcasting"; - private static final String KEY_VISIBILITY_TIMEOUT = "visibility_timeout"; - - private LocalBluetoothManager mManager; - private BluetoothDiscoverableEnabler mDiscoverableEnabler; - private BluetoothAdvertisingEnabler mAdvertisingEnabler; - - private Preference mDeviceNamePref; - private Preference mVisibilityPref; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.bluetooth_local_device_profile); - getPreferenceScreen().setOrderingAsAdded(true); - mDeviceNamePref = findPreference(KEY_RENAME_DEVICE); - mDeviceNamePref.setOnPreferenceClickListener(new OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - new BluetoothNameDialogFragment().show(getFragmentManager(), "rename device"); - return true; - } - }); - - mVisibilityPref = findPreference(KEY_VISIBILITY_TIMEOUT); - mVisibilityPref.setOnPreferenceClickListener(new OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - new BluetoothVisibilityTimeoutFragment().show( - getFragmentManager(), "visibility timeout"); - return true; - } - }); - - mManager = LocalBluetoothManager.getInstance(getActivity()); - mDiscoverableEnabler = mManager.getDiscoverableEnabler(); - // Set the visibility timeout preference to the enabler so the visibility timeout - // preference can be updated when the timeout changes. - mDiscoverableEnabler.setVisibilityPreference(mVisibilityPref); - - PreferenceActivity mActivity = (PreferenceActivity)getActivity(); - mActivity.showBreadCrumbs(mManager.getBluetoothAdapter().getName(), ""); - PreferenceScreen bluetoothBroadcast = (PreferenceScreen)findPreference(KEY_BROADCASTING); - mAdvertisingEnabler = new BluetoothAdvertisingEnabler(getActivity(), bluetoothBroadcast); - mManager.setBluetoothAdvertisingEnabler(mAdvertisingEnabler); - } - - @Override - public void onResume() { - super.onResume(); - mManager.setForegroundActivity(getActivity()); - mAdvertisingEnabler.resume(); - mDiscoverableEnabler.resume(); - } - - @Override - public void onPause() { - super.onPause(); - mManager.setForegroundActivity(null); - if (mDiscoverableEnabler != null) { - mDiscoverableEnabler.pause(); - } - } - -} diff --git a/src/com/android/settings/bluetooth/RequestPermissionActivity.java b/src/com/android/settings/bluetooth/RequestPermissionActivity.java index 1b105deeb..9f266a55c 100644 --- a/src/com/android/settings/bluetooth/RequestPermissionActivity.java +++ b/src/com/android/settings/bluetooth/RequestPermissionActivity.java @@ -16,6 +16,8 @@ package com.android.settings.bluetooth; +import com.android.settings.R; + import android.app.Activity; import android.app.AlertDialog; import android.bluetooth.BluetoothAdapter; @@ -26,22 +28,17 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; -import android.os.UserHandle; import android.util.Log; -import com.android.settings.R; - /** - * RequestPermissionActivity asks the user whether to enable bluetooth, discovery or advertisement. - * This is usually started by an application wanted to start bluetooth, discovery or advertisement. + * RequestPermissionActivity asks the user whether to enable discovery. This is + * usually started by an application wanted to start bluetooth and or discovery */ public class RequestPermissionActivity extends Activity implements DialogInterface.OnClickListener { // Command line to test this // adb shell am start -a android.bluetooth.adapter.action.REQUEST_ENABLE // adb shell am start -a android.bluetooth.adapter.action.REQUEST_DISCOVERABLE - // adb shell am start -a android.bluetooth.adapter.action.START_ADVERTISING - // adb shell am start -a android.bluetooth.adapter.action.STOP_ADVERTISING private static final String TAG = "RequestPermissionActivity"; @@ -53,11 +50,6 @@ public class RequestPermissionActivity extends Activity implements private static final int REQUEST_CODE_START_BT = 1; - private static final int EXTRA_INTENT_NONE = 0; - private static final int EXTRA_INTENT_DISCOVERY = 1; - private static final int EXTRA_INTENT_START_ADVERTISING = 2; - private static final int EXTRA_INTENT_STOP_ADVERTISING = 3; - private LocalBluetoothAdapter mLocalAdapter; private int mTimeout = BluetoothDiscoverableEnabler.DEFAULT_DISCOVERABLE_TIMEOUT; @@ -75,14 +67,10 @@ public class RequestPermissionActivity extends Activity implements // False if requesting BT to be turned on + discoverable mode private boolean mEnableOnly; - private int mExtraIntent = EXTRA_INTENT_NONE; - private boolean mUserConfirmed; private AlertDialog mDialog; - private Context mContext; - private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override @@ -106,8 +94,6 @@ public class RequestPermissionActivity extends Activity implements protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mContext = getApplicationContext(); - // Note: initializes mLocalAdapter and returns true on error if (parseIntent()) { finish(); @@ -139,27 +125,21 @@ public class RequestPermissionActivity extends Activity implements intent.setClass(this, RequestPermissionHelperActivity.class); if (mEnableOnly) { intent.setAction(RequestPermissionHelperActivity.ACTION_INTERNAL_REQUEST_BT_ON); - } else if (mExtraIntent == EXTRA_INTENT_DISCOVERY) { + } else { intent.setAction(RequestPermissionHelperActivity. ACTION_INTERNAL_REQUEST_BT_ON_AND_DISCOVERABLE); intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, mTimeout); - } else if (mExtraIntent == EXTRA_INTENT_START_ADVERTISING) { - intent.setAction(RequestPermissionHelperActivity. - ACTION_INTERNAL_REQUEST_BT_ON_AND_START_ADVERTISE); - } else if (mExtraIntent == EXTRA_INTENT_STOP_ADVERTISING) { - // Nothing to do. Advertising cannot be in process with bluetooth disabled. } - startActivityForResult(intent, REQUEST_CODE_START_BT); mNeededToEnableBluetooth = true; break; case BluetoothAdapter.STATE_ON: - if (needAskUserPermission()) { - // Ask the user for permissions of bluetooth operations. - createDialog(); - } else { - // No need to ask for permission, just proceed. + if (mEnableOnly) { + // Nothing to do. Already enabled. proceedAndFinish(); + } else { + // Ask the user about enabling discovery mode + createDialog(); } break; default: @@ -167,19 +147,6 @@ public class RequestPermissionActivity extends Activity implements } } - private boolean needAskUserPermission() { - if (mEnableOnly) { - return false; - } - if (mExtraIntent == EXTRA_INTENT_STOP_ADVERTISING) { - return false; - } - if (mExtraIntent == EXTRA_INTENT_START_ADVERTISING) { - return !LocalBluetoothPreferences.isAdvertisingEnabled(mContext); - } - return true; - } - private void createDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(this); @@ -188,7 +155,7 @@ public class RequestPermissionActivity extends Activity implements // to turn on BT builder.setMessage(getString(R.string.bluetooth_turning_on)); builder.setCancelable(false); - } else if (mExtraIntent == EXTRA_INTENT_DISCOVERY) { + } else { // Ask the user whether to turn on discovery mode or not // For lasting discoverable mode there is a different message if (mTimeout == BluetoothDiscoverableEnabler.DISCOVERABLE_TIMEOUT_NEVER) { @@ -200,11 +167,6 @@ public class RequestPermissionActivity extends Activity implements } builder.setPositiveButton(getString(R.string.allow), this); builder.setNegativeButton(getString(R.string.deny), this); - } else if (mExtraIntent == EXTRA_INTENT_START_ADVERTISING) { - builder.setMessage(getString(R.string.bluetooth_ask_start_broadcast, - Utils.getCallingApp(this))); - builder.setPositiveButton(getString(R.string.allow), this); - builder.setNegativeButton(getString(R.string.deny), this); } mDialog = builder.create(); @@ -261,8 +223,7 @@ public class RequestPermissionActivity extends Activity implements if (mEnableOnly) { // BT enabled. Done returnCode = RESULT_OK; - } else if (mExtraIntent == EXTRA_INTENT_DISCOVERY - && mLocalAdapter.setScanMode( + } else if (mLocalAdapter.setScanMode( BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, mTimeout)) { // If already in discoverable mode, this will extend the timeout. long endTime = System.currentTimeMillis() + (long) mTimeout * 1000; @@ -276,22 +237,6 @@ public class RequestPermissionActivity extends Activity implements if (returnCode < RESULT_FIRST_USER) { returnCode = RESULT_FIRST_USER; } - } else if (mExtraIntent == EXTRA_INTENT_START_ADVERTISING) { - // Advertise allowed as user said yes. - LocalBluetoothPreferences.setAdvertisingEnabled(mContext, true); - if (mLocalAdapter.startAdvertising()) { - returnCode = RESULT_OK; - Intent intent = new Intent(BluetoothAdapter.ACTION_BLUETOOTH_ADVERTISING_STARTED); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); - } else { - returnCode = RESULT_CANCELED; - } - } else if (mExtraIntent == EXTRA_INTENT_STOP_ADVERTISING - && mLocalAdapter.isAdvertising() - && mLocalAdapter.stopAdvertising()) { - Intent intent = new Intent(BluetoothAdapter.ACTION_BLUETOOTH_ADVERTISING_STOPPED); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); - returnCode = RESULT_OK; } else { returnCode = RESULT_CANCELED; } @@ -314,7 +259,6 @@ public class RequestPermissionActivity extends Activity implements mEnableOnly = true; } else if (intent != null && intent.getAction().equals(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE)) { - mExtraIntent = EXTRA_INTENT_DISCOVERY; mTimeout = intent.getIntExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, BluetoothDiscoverableEnabler.DEFAULT_DISCOVERABLE_TIMEOUT); @@ -323,12 +267,6 @@ public class RequestPermissionActivity extends Activity implements if (mTimeout < 0 || mTimeout > MAX_DISCOVERABLE_TIMEOUT) { mTimeout = BluetoothDiscoverableEnabler.DEFAULT_DISCOVERABLE_TIMEOUT; } - } else if (intent != null - && intent.getAction().equals(BluetoothAdapter.ACTION_START_ADVERTISING)) { - mExtraIntent = EXTRA_INTENT_START_ADVERTISING; - } else if (intent != null - && intent.getAction().equals(BluetoothAdapter.ACTION_STOP_ADVERTISING)) { - mExtraIntent = EXTRA_INTENT_STOP_ADVERTISING; } else { Log.e(TAG, "Error: this activity may be started only with intent " + BluetoothAdapter.ACTION_REQUEST_ENABLE + " or " diff --git a/src/com/android/settings/bluetooth/RequestPermissionHelperActivity.java b/src/com/android/settings/bluetooth/RequestPermissionHelperActivity.java index 34874b30c..f10851359 100644 --- a/src/com/android/settings/bluetooth/RequestPermissionHelperActivity.java +++ b/src/com/android/settings/bluetooth/RequestPermissionHelperActivity.java @@ -30,7 +30,7 @@ import android.view.View; import android.widget.TextView; /** - * RequestPermissionHelperActivity asks the user whether to enable discovery or advertisement. + * RequestPermissionHelperActivity asks the user whether to enable discovery. * This is usually started by RequestPermissionActivity. */ public class RequestPermissionHelperActivity extends AlertActivity implements @@ -43,9 +43,6 @@ public class RequestPermissionHelperActivity extends AlertActivity implements public static final String ACTION_INTERNAL_REQUEST_BT_ON_AND_DISCOVERABLE = "com.android.settings.bluetooth.ACTION_INTERNAL_REQUEST_BT_ON_AND_DISCOVERABLE"; - public static final String ACTION_INTERNAL_REQUEST_BT_ON_AND_START_ADVERTISE = - "com.android.settings.bluetooth.ACTION_INTERNAL_REQUEST_BT_ON_AND_ADVERTISE"; - private LocalBluetoothAdapter mLocalAdapter; private int mTimeout; @@ -53,7 +50,6 @@ public class RequestPermissionHelperActivity extends AlertActivity implements // True if requesting BT to be turned on // False if requesting BT to be turned on + discoverable mode private boolean mEnableOnly; - private boolean mDiscovery; @Override protected void onCreate(Bundle savedInstanceState) { @@ -79,15 +75,12 @@ public class RequestPermissionHelperActivity extends AlertActivity implements if (mEnableOnly) { p.mMessage = getString(R.string.bluetooth_ask_enablement); - } else if (mDiscovery) { + } else { if (mTimeout == BluetoothDiscoverableEnabler.DISCOVERABLE_TIMEOUT_NEVER) { p.mMessage = getString(R.string.bluetooth_ask_enablement_and_lasting_discovery); } else { p.mMessage = getString(R.string.bluetooth_ask_enablement_and_discovery, mTimeout); } - } else { - p.mMessage = getString(R.string.bluetooth_ask_enablement_and_start_broadcast, - Utils.getCallingApp(this)); } p.mPositiveButtonText = getString(R.string.allow); @@ -145,14 +138,9 @@ public class RequestPermissionHelperActivity extends AlertActivity implements } else if (intent != null && intent.getAction().equals(ACTION_INTERNAL_REQUEST_BT_ON_AND_DISCOVERABLE)) { mEnableOnly = false; - mDiscovery = true; // Value used for display purposes. Not range checking. mTimeout = intent.getIntExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, BluetoothDiscoverableEnabler.DEFAULT_DISCOVERABLE_TIMEOUT); - } else if (intent != null - && intent.getAction().equals(ACTION_INTERNAL_REQUEST_BT_ON_AND_START_ADVERTISE)) { - mEnableOnly = false; - mDiscovery = false; } else { setResult(RESULT_CANCELED); return true; diff --git a/src/com/android/settings/bluetooth/Utils.java b/src/com/android/settings/bluetooth/Utils.java index 6590b066a..fb44d5a38 100755 --- a/src/com/android/settings/bluetooth/Utils.java +++ b/src/com/android/settings/bluetooth/Utils.java @@ -16,15 +16,11 @@ package com.android.settings.bluetooth; -import android.app.Activity; import android.app.AlertDialog; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.DialogInterface; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; import android.widget.Toast; import com.android.settings.R; @@ -106,18 +102,4 @@ final class Utils { Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); } } - - /** - * Get application name of the calling activity. Returns empty string on errors. - */ - static String getCallingApp(Activity activity) { - final PackageManager pm = activity.getApplicationContext().getPackageManager(); - ApplicationInfo applicationInfo; - try { - applicationInfo = pm.getApplicationInfo(activity.getCallingPackage(), 0); - } catch (final NameNotFoundException e) { - applicationInfo = null; - } - return (applicationInfo == null) ? "" : pm.getApplicationLabel(applicationInfo).toString(); - } } diff --git a/src/com/android/settings/dashboard/DashboardSummary.java b/src/com/android/settings/dashboard/DashboardSummary.java new file mode 100644 index 000000000..0e034ffe3 --- /dev/null +++ b/src/com/android/settings/dashboard/DashboardSummary.java @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2014 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.dashboard; + +import android.app.Fragment; +import android.content.Context; +import android.database.Cursor; +import android.os.AsyncTask; +import android.os.Bundle; +import android.text.Editable; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.InputMethodManager; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.TextView; +import com.android.settings.R; +import com.android.settings.SettingsActivity; +import com.android.settings.indexer.Index; + +public class DashboardSummary extends Fragment { + + private EditText mEditText; + private ListView mListView; + + private SearchResultsAdapter mAdapter; + private Index mIndex; + private UpdateSearchResultsTask mUpdateSearchResultsTask; + + /** + * A basic AsyncTask for updating the query results cursor + */ + private class UpdateSearchResultsTask extends AsyncTask<String, Void, Cursor> { + @Override + protected Cursor doInBackground(String... params) { + return mIndex.search(params[0]); + } + + @Override + protected void onPostExecute(Cursor cursor) { + if (!isCancelled()) { + setCursor(cursor); + } else if (cursor != null) { + cursor.close(); + } + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mIndex = Index.getInstance(getActivity()); + mAdapter = new SearchResultsAdapter(getActivity()); + } + + @Override + public void onStop() { + super.onStop(); + + clearResults(); + } + + @Override + public void onStart() { + super.onStart(); + + updateSearchResults(); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + final View view = inflater.inflate(R.layout.dashboard, container, false); + + mEditText = (EditText)view.findViewById(R.id.edittext_query); + mEditText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + updateSearchResults(); + } + + @Override + public void afterTextChanged(Editable s) { + } + }); + mEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (!hasFocus) { + closeSoftKeyboard(); + } + } + }); + + mListView = (ListView) view.findViewById(R.id.list_results); + mListView.setAdapter(mAdapter); + mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + closeSoftKeyboard(); + final Cursor cursor = mAdapter.mCursor; + cursor.moveToPosition(position); + final String fragmentName = cursor.getString(Index.COLUMN_INDEX_FRAGMENT_NAME); + final String fragmentTitle = cursor.getString(Index.COLUMN_INDEX_FRAGMENT_TITLE); + + ((SettingsActivity) getActivity()).startPreferencePanel(fragmentName, null, 0, + fragmentTitle, null, 0); + } + }); + + return view; + } + + private void closeSoftKeyboard() { + InputMethodManager imm = InputMethodManager.peekInstance(); + if (imm != null && imm.isActive(mEditText)) { + imm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0); + } + } + + private void clearResults() { + if (mUpdateSearchResultsTask != null) { + mUpdateSearchResultsTask.cancel(false); + mUpdateSearchResultsTask = null; + } + setCursor(null); + } + + private void setCursor(Cursor cursor) { + Cursor oldCursor = mAdapter.swapCursor(cursor); + if (oldCursor != null) { + oldCursor.close(); + } + } + + private String getFilteredQueryString() { + final CharSequence query = mEditText.getText().toString(); + final StringBuilder filtered = new StringBuilder(); + for (int n = 0; n < query.length(); n++) { + char c = query.charAt(n); + if (!Character.isLetterOrDigit(c) && !Character.isSpaceChar(c)) { + continue; + } + filtered.append(c); + } + return filtered.toString(); + } + + private void updateSearchResults() { + if (mUpdateSearchResultsTask != null) { + mUpdateSearchResultsTask.cancel(false); + mUpdateSearchResultsTask = null; + } + final String query = getFilteredQueryString(); + if (TextUtils.isEmpty(query)) { + setCursor(null); + } else { + mUpdateSearchResultsTask = new UpdateSearchResultsTask(); + mUpdateSearchResultsTask.execute(query); + } + } + + private static class SearchResult { + public String title; + public String summary; + public int iconResId; + + public SearchResult(String title, String summary, int iconResId) { + this.title = title; + this.summary = summary; + this.iconResId = iconResId; + } + } + + private static class SearchResultsAdapter extends BaseAdapter { + + private Cursor mCursor; + private LayoutInflater mInflater; + private boolean mDataValid; + + public SearchResultsAdapter(Context context) { + mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mDataValid = false; + } + + public Cursor swapCursor(Cursor newCursor) { + if (newCursor == mCursor) { + return null; + } + Cursor oldCursor = mCursor; + mCursor = newCursor; + if (newCursor != null) { + mDataValid = true; + notifyDataSetChanged(); + } else { + mDataValid = false; + notifyDataSetInvalidated(); + } + return oldCursor; + } + + @Override + public int getCount() { + if (!mDataValid || mCursor == null || mCursor.isClosed()) return 0; + return mCursor.getCount(); + } + + @Override + public Object getItem(int position) { + if (mDataValid && mCursor.moveToPosition(position)) { + final String title = mCursor.getString(Index.COLUMN_INDEX_TITLE); + final String summary = mCursor.getString(Index.COLUMN_INDEX_SUMMARY); + final String iconResStr = mCursor.getString(Index.COLUMN_INDEX_ICON); + final int iconResId = + TextUtils.isEmpty(iconResStr) ? 0 : Integer.parseInt(iconResStr); + return new SearchResult(title, summary, iconResId); + } + return null; + } + + @Override + public long getItemId(int position) { + return 0; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (!mDataValid && convertView == null) { + throw new IllegalStateException( + "this should only be called when the cursor is valid"); + } + if (!mCursor.moveToPosition(position)) { + throw new IllegalStateException("couldn't move cursor to position " + position); + } + + View view; + TextView textTitle; + TextView textSummary; + ImageView imageView; + + if (convertView == null) { + view = mInflater.inflate(R.layout.search_result, parent, false); + } else { + view = convertView; + } + textTitle = (TextView) view.findViewById(R.id.title); + textSummary = (TextView) view.findViewById(R.id.summary); + imageView = (ImageView) view.findViewById(R.id.icon); + + SearchResult result = (SearchResult) getItem(position); + + textTitle.setText(result.title); + textSummary.setText(result.summary); + if (result.iconResId != R.drawable.empty_icon) { + imageView.setImageResource(result.iconResId); + imageView.setBackgroundResource(R.color.background_search_result_icon); + } else { + imageView.setImageDrawable(null); + imageView.setBackgroundResource(R.drawable.empty_icon); + } + + return view; + } + } +} diff --git a/src/com/android/settings/deviceinfo/Memory.java b/src/com/android/settings/deviceinfo/Memory.java index 999611d12..4923070b7 100644 --- a/src/com/android/settings/deviceinfo/Memory.java +++ b/src/com/android/settings/deviceinfo/Memory.java @@ -40,7 +40,6 @@ import android.os.storage.StorageEventListener; import android.os.storage.StorageManager; import android.os.storage.StorageVolume; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceScreen; import android.util.Log; import android.view.Menu; @@ -49,6 +48,7 @@ import android.view.MenuItem; import android.widget.Toast; import com.android.settings.R; +import com.android.settings.SettingsActivity; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.Utils; import com.google.android.collect.Lists; @@ -186,8 +186,8 @@ public class Memory extends SettingsPreferenceFragment { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.storage_usb: - if (getActivity() instanceof PreferenceActivity) { - ((PreferenceActivity) getActivity()).startPreferencePanel( + if (getActivity() instanceof SettingsActivity) { + ((SettingsActivity) getActivity()).startPreferencePanel( UsbSettings.class.getCanonicalName(), null, R.string.storage_title_usb, null, diff --git a/src/com/android/settings/deviceinfo/Status.java b/src/com/android/settings/deviceinfo/Status.java index b27b241af..ef1f5c5dc 100644 --- a/src/com/android/settings/deviceinfo/Status.java +++ b/src/com/android/settings/deviceinfo/Status.java @@ -18,6 +18,7 @@ package com.android.settings.deviceinfo; import android.bluetooth.BluetoothAdapter; import android.content.BroadcastReceiver; +import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -35,13 +36,16 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.preference.Preference; import android.preference.PreferenceActivity; -import android.preference.PreferenceScreen; import android.telephony.CellBroadcastMessage; import android.telephony.PhoneNumberUtils; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.TelephonyManager; import android.text.TextUtils; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ListAdapter; +import android.widget.Toast; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneConstants; @@ -364,6 +368,27 @@ public class Status extends PreferenceActivity { } else { removePreferenceFromScreen(KEY_SERIAL_NUMBER); } + + // Make every pref on this screen copy its data to the clipboard on longpress. + // Super convenient for capturing the IMEI, MAC addr, serial, etc. + getListView().setOnItemLongClickListener( + new AdapterView.OnItemLongClickListener() { + @Override + public boolean onItemLongClick(AdapterView<?> parent, View view, + int position, long id) { + ListAdapter listAdapter = (ListAdapter) parent.getAdapter(); + Preference pref = (Preference) listAdapter.getItem(position); + + ClipboardManager cm = (ClipboardManager) + getSystemService(Context.CLIPBOARD_SERVICE); + cm.setText(pref.getSummary()); + Toast.makeText( + Status.this, + com.android.internal.R.string.text_copied, + Toast.LENGTH_SHORT).show(); + return true; + } + }); } @Override diff --git a/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java b/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java index d1ea09489..a98f8d96d 100644 --- a/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java +++ b/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java @@ -38,6 +38,7 @@ import android.provider.MediaStore; import android.text.format.Formatter; import com.android.settings.R; +import com.android.settings.Settings; import com.android.settings.deviceinfo.StorageMeasurement.MeasurementDetails; import com.android.settings.deviceinfo.StorageMeasurement.MeasurementReceiver; import com.google.android.collect.Lists; @@ -428,8 +429,7 @@ public class StorageVolumePreferenceCategory extends PreferenceCategory { intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, mVolume); } else if (pref == mItemApps) { intent = new Intent(Intent.ACTION_MANAGE_PACKAGE_STORAGE); - intent.setClass(getContext(), - com.android.settings.Settings.ManageApplicationsActivity.class); + intent.setClass(getContext(), Settings.ManageApplicationsActivity.class); } else if (pref == mItemDownloads) { intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS).putExtra( DownloadManager.INTENT_EXTRAS_SORT_BY_SIZE, true); diff --git a/src/com/android/settings/fuelgauge/BatteryEntry.java b/src/com/android/settings/fuelgauge/BatteryEntry.java new file mode 100644 index 000000000..c2bf127ea --- /dev/null +++ b/src/com/android/settings/fuelgauge/BatteryEntry.java @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2014 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.fuelgauge; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.UserInfo; +import android.graphics.drawable.Drawable; +import android.os.BatteryStats; +import android.os.Handler; +import android.os.UserManager; +import com.android.internal.os.BatterySipper; +import com.android.settings.R; +import com.android.settings.users.UserUtils; + +import java.util.ArrayList; +import java.util.HashMap; + +/** + * Wraps the power usage data of a BatterySipper with information about package name + * and icon image. + */ +public class BatteryEntry { + public static final int MSG_UPDATE_NAME_ICON = 1; + public static final int MSG_REPORT_FULLY_DRAWN = 2; + + static final HashMap<String,UidToDetail> sUidCache = new HashMap<String,UidToDetail>(); + + static final ArrayList<BatteryEntry> mRequestQueue = new ArrayList<BatteryEntry>(); + static Handler sHandler; + + static private class NameAndIconLoader extends Thread { + private boolean mAbort = false; + + public NameAndIconLoader() { + super("BatteryUsage Icon Loader"); + } + + public void abort() { + mAbort = true; + } + + @Override + public void run() { + while (true) { + BatteryEntry be; + synchronized (mRequestQueue) { + if (mRequestQueue.isEmpty() || mAbort) { + if (sHandler != null) { + sHandler.sendEmptyMessage(MSG_REPORT_FULLY_DRAWN); + } + mRequestQueue.clear(); + return; + } + be = mRequestQueue.remove(0); + } + be.loadNameAndIcon(); + } + } + } + + private static NameAndIconLoader mRequestThread; + + public static void startRequestQueue() { + if (sHandler != null) { + synchronized (mRequestQueue) { + if (!mRequestQueue.isEmpty()) { + if (mRequestThread != null) { + mRequestThread.abort(); + } + mRequestThread = new NameAndIconLoader(); + mRequestThread.setPriority(Thread.MIN_PRIORITY); + mRequestThread.start(); + mRequestQueue.notify(); + } + } + } + } + + public static void stopRequestQueue() { + synchronized (mRequestQueue) { + if (mRequestThread != null) { + mRequestThread.abort(); + mRequestThread = null; + sHandler = null; + } + } + } + + public static void clearUidCache() { + sUidCache.clear(); + } + + public final Context context; + public final BatterySipper sipper; + + public String name; + public Drawable icon; + public int iconId; // For passing to the detail screen. + public String defaultPackageName; + + static class UidToDetail { + String name; + String packageName; + Drawable icon; + } + + public BatteryEntry(Context context, Handler handler, UserManager um, BatterySipper sipper) { + sHandler = handler; + this.context = context; + this.sipper = sipper; + switch (sipper.drainType) { + case IDLE: + name = context.getResources().getString(R.string.power_idle); + iconId = R.drawable.ic_settings_phone_idle; + break; + case CELL: + name = context.getResources().getString(R.string.power_cell); + iconId = R.drawable.ic_settings_cell_standby; + break; + case PHONE: + name = context.getResources().getString(R.string.power_phone); + iconId = R.drawable.ic_settings_voice_calls; + break; + case WIFI: + name = context.getResources().getString(R.string.power_wifi); + iconId = R.drawable.ic_settings_wifi; + break; + case BLUETOOTH: + name = context.getResources().getString(R.string.power_bluetooth); + iconId = R.drawable.ic_settings_bluetooth; + break; + case SCREEN: + name = context.getResources().getString(R.string.power_screen); + iconId = R.drawable.ic_settings_display; + break; + case APP: + name = sipper.packageWithHighestDrain; + break; + case USER: { + UserInfo info = um.getUserInfo(sipper.userId); + if (info != null) { + icon = UserUtils.getUserIcon(context, um, info, context.getResources()); + name = info != null ? info.name : null; + if (name == null) { + name = Integer.toString(info.id); + } + name = context.getResources().getString( + R.string.running_process_item_user_label, name); + } else { + icon = null; + name = context.getResources().getString( + R.string.running_process_item_removed_user_label); + } + } break; + case UNACCOUNTED: + name = context.getResources().getString(R.string.power_unaccounted); + iconId = R.drawable.ic_power_system; + break; + case OVERCOUNTED: + name = context.getResources().getString(R.string.power_overcounted); + iconId = R.drawable.ic_power_system; + break; + } + if (iconId > 0) { + icon = context.getResources().getDrawable(iconId); + } + if ((name == null || iconId == 0) && this.sipper.uidObj != null) { + getQuickNameIconForUid(this.sipper.uidObj); + } + } + + public Drawable getIcon() { + return icon; + } + + /** + * Gets the application name + */ + public String getLabel() { + return name; + } + + void getQuickNameIconForUid(BatteryStats.Uid uidObj) { + final int uid = uidObj.getUid(); + final String uidString = Integer.toString(uid); + if (sUidCache.containsKey(uidString)) { + UidToDetail utd = sUidCache.get(uidString); + defaultPackageName = utd.packageName; + name = utd.name; + icon = utd.icon; + return; + } + PackageManager pm = context.getPackageManager(); + String[] packages = pm.getPackagesForUid(uid); + icon = pm.getDefaultActivityIcon(); + if (packages == null) { + //name = Integer.toString(uid); + if (uid == 0) { + name = context.getResources().getString(R.string.process_kernel_label); + } else if ("mediaserver".equals(name)) { + name = context.getResources().getString(R.string.process_mediaserver_label); + } + iconId = R.drawable.ic_power_system; + icon = context.getResources().getDrawable(iconId); + return; + } else { + //name = packages[0]; + } + if (sHandler != null) { + synchronized (mRequestQueue) { + mRequestQueue.add(this); + } + } + } + + /** + * Loads the app label and icon image and stores into the cache. + */ + public void loadNameAndIcon() { + // Bail out if the current sipper is not an App sipper. + if (sipper.uidObj == null) { + return; + } + PackageManager pm = context.getPackageManager(); + final int uid = sipper.uidObj.getUid(); + final Drawable defaultActivityIcon = pm.getDefaultActivityIcon(); + sipper.mPackages = pm.getPackagesForUid(uid); + if (sipper.mPackages == null) { + name = Integer.toString(uid); + return; + } + + String[] packageLabels = new String[sipper.mPackages.length]; + System.arraycopy(sipper.mPackages, 0, packageLabels, 0, sipper.mPackages.length); + + // Convert package names to user-facing labels where possible + for (int i = 0; i < packageLabels.length; i++) { + try { + ApplicationInfo ai = pm.getApplicationInfo(packageLabels[i], 0); + CharSequence label = ai.loadLabel(pm); + if (label != null) { + packageLabels[i] = label.toString(); + } + if (ai.icon != 0) { + defaultPackageName = sipper.mPackages[i]; + icon = ai.loadIcon(pm); + break; + } + } catch (PackageManager.NameNotFoundException e) { + } + } + if (icon == null) icon = defaultActivityIcon; + + if (packageLabels.length == 1) { + name = packageLabels[0]; + } else { + // Look for an official name for this UID. + for (String pkgName : sipper.mPackages) { + try { + final PackageInfo pi = pm.getPackageInfo(pkgName, 0); + if (pi.sharedUserLabel != 0) { + final CharSequence nm = pm.getText(pkgName, + pi.sharedUserLabel, pi.applicationInfo); + if (nm != null) { + name = nm.toString(); + if (pi.applicationInfo.icon != 0) { + defaultPackageName = pkgName; + icon = pi.applicationInfo.loadIcon(pm); + } + break; + } + } + } catch (PackageManager.NameNotFoundException e) { + } + } + } + final String uidString = Integer.toString(sipper.uidObj.getUid()); + UidToDetail utd = new UidToDetail(); + utd.name = name; + utd.icon = icon; + utd.packageName = defaultPackageName; + sUidCache.put(uidString, utd); + if (sHandler != null) { + sHandler.sendMessage(sHandler.obtainMessage(MSG_UPDATE_NAME_ICON, this)); + } + } +} diff --git a/src/com/android/settings/fuelgauge/BatteryHistoryChart.java b/src/com/android/settings/fuelgauge/BatteryHistoryChart.java index 55a045747..25435ef61 100644 --- a/src/com/android/settings/fuelgauge/BatteryHistoryChart.java +++ b/src/com/android/settings/fuelgauge/BatteryHistoryChart.java @@ -193,8 +193,8 @@ public class BatteryHistoryChart extends View { mWifiRunningPaint.setStyle(Paint.Style.STROKE); mWakeLockPaint.setStyle(Paint.Style.STROKE); mPhoneSignalChart.setColors(new int[] { - 0x00000000, 0xffa00000, 0xffa0a000, 0xff808020, - 0xff808040, 0xff808060, 0xff008000 + 0x00000000, 0xffa00000, 0xffa07000, 0xffa0a000, + 0xff80a000, 0xff409000, 0xff008000 }); mTextPaint.density = getResources().getDisplayMetrics().density; @@ -361,7 +361,7 @@ public class BatteryHistoryChart extends View { final HistoryItem rec = new HistoryItem(); while (stats.getNextHistoryLocked(rec)) { pos++; - if (rec.cmd == HistoryItem.CMD_UPDATE) { + if (rec.isDeltaData()) { if (first) { first = false; mHistStart = rec.time; @@ -523,7 +523,7 @@ public class BatteryHistoryChart extends View { if (mStats.startIteratingHistoryLocked()) { final HistoryItem rec = new HistoryItem(); while (mStats.getNextHistoryLocked(rec) && i < N) { - if (rec.cmd == BatteryStats.HistoryItem.CMD_UPDATE) { + if (rec.isDeltaData()) { x = (int)(((rec.time-timeStart)*w)/timeChange); y = mLevelTop + levelh - ((rec.batteryLevel-batLow)*(levelh-1))/batChange; diff --git a/src/com/android/settings/fuelgauge/BatterySipper.java b/src/com/android/settings/fuelgauge/BatterySipper.java deleted file mode 100644 index fcc8f6970..000000000 --- a/src/com/android/settings/fuelgauge/BatterySipper.java +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright (C) 2009 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.fuelgauge; - -import com.android.settings.R; -import com.android.settings.fuelgauge.PowerUsageDetail.DrainType; - -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.graphics.drawable.Drawable; -import android.os.Handler; -import android.os.BatteryStats.Uid; - -import java.util.ArrayList; -import java.util.HashMap; - -/** - * Contains information about package name, icon image, power usage about an - * application or a system service. - */ -public class BatterySipper implements Comparable<BatterySipper> { - final Context mContext; - /* Cache cleared when PowerUsageSummary is destroyed */ - static final HashMap<String,UidToDetail> sUidCache = new HashMap<String,UidToDetail>(); - final ArrayList<BatterySipper> mRequestQueue; - final Handler mHandler; - String name; - Drawable icon; - int iconId; // For passing to the detail screen. - Uid uidObj; - double value; - double[] values; - DrainType drainType; - long usageTime; - long cpuTime; - long gpsTime; - long wifiRunningTime; - long cpuFgTime; - long wakeLockTime; - long mobileRxBytes; - long mobileTxBytes; - long wifiRxBytes; - long wifiTxBytes; - double percent; - double noCoveragePercent; - String defaultPackageName; - String[] mPackages; - - static class UidToDetail { - String name; - String packageName; - Drawable icon; - } - - BatterySipper(Context context, ArrayList<BatterySipper> requestQueue, - Handler handler, String label, DrainType drainType, - int iconId, Uid uid, double[] values) { - mContext = context; - mRequestQueue = requestQueue; - mHandler = handler; - this.values = values; - name = label; - this.drainType = drainType; - if (iconId > 0) { - icon = mContext.getResources().getDrawable(iconId); - } - if (values != null) value = values[0]; - if ((label == null || iconId == 0) && uid != null) { - getQuickNameIconForUid(uid); - } - uidObj = uid; - } - - double getSortValue() { - return value; - } - - double[] getValues() { - return values; - } - - public Drawable getIcon() { - return icon; - } - - /** - * Gets the application name - */ - public String getLabel() { - return name; - } - - @Override - public int compareTo(BatterySipper other) { - // Return the flipped value because we want the items in descending order - return Double.compare(other.getSortValue(), getSortValue()); - } - - /** - * Gets a list of packages associated with the current user - */ - public String[] getPackages() { - return mPackages; - } - - public int getUid() { - // Bail out if the current sipper is not an App sipper. - if (uidObj == null) { - return 0; - } - return uidObj.getUid(); - } - - void getQuickNameIconForUid(Uid uidObj) { - final int uid = uidObj.getUid(); - final String uidString = Integer.toString(uid); - if (sUidCache.containsKey(uidString)) { - UidToDetail utd = sUidCache.get(uidString); - defaultPackageName = utd.packageName; - name = utd.name; - icon = utd.icon; - return; - } - PackageManager pm = mContext.getPackageManager(); - String[] packages = pm.getPackagesForUid(uid); - icon = pm.getDefaultActivityIcon(); - if (packages == null) { - //name = Integer.toString(uid); - if (uid == 0) { - name = mContext.getResources().getString(R.string.process_kernel_label); - } else if ("mediaserver".equals(name)) { - name = mContext.getResources().getString(R.string.process_mediaserver_label); - } - iconId = R.drawable.ic_power_system; - icon = mContext.getResources().getDrawable(iconId); - return; - } else { - //name = packages[0]; - } - if (mHandler != null) { - synchronized (mRequestQueue) { - mRequestQueue.add(this); - } - } - } - - public static void clearUidCache() { - sUidCache.clear(); - } - - /** - * Loads the app label and icon image and stores into the cache. - */ - public void loadNameAndIcon() { - // Bail out if the current sipper is not an App sipper. - if (uidObj == null) { - return; - } - PackageManager pm = mContext.getPackageManager(); - final int uid = uidObj.getUid(); - final Drawable defaultActivityIcon = pm.getDefaultActivityIcon(); - mPackages = pm.getPackagesForUid(uid); - if (mPackages == null) { - name = Integer.toString(uid); - return; - } - - String[] packageLabels = new String[mPackages.length]; - System.arraycopy(mPackages, 0, packageLabels, 0, mPackages.length); - - int preferredIndex = -1; - // Convert package names to user-facing labels where possible - for (int i = 0; i < packageLabels.length; i++) { - // Check if package matches preferred package - if (packageLabels[i].equals(name)) preferredIndex = i; - try { - ApplicationInfo ai = pm.getApplicationInfo(packageLabels[i], 0); - CharSequence label = ai.loadLabel(pm); - if (label != null) { - packageLabels[i] = label.toString(); - } - if (ai.icon != 0) { - defaultPackageName = mPackages[i]; - icon = ai.loadIcon(pm); - break; - } - } catch (NameNotFoundException e) { - } - } - if (icon == null) icon = defaultActivityIcon; - - if (packageLabels.length == 1) { - name = packageLabels[0]; - } else { - // Look for an official name for this UID. - for (String pkgName : mPackages) { - try { - final PackageInfo pi = pm.getPackageInfo(pkgName, 0); - if (pi.sharedUserLabel != 0) { - final CharSequence nm = pm.getText(pkgName, - pi.sharedUserLabel, pi.applicationInfo); - if (nm != null) { - name = nm.toString(); - if (pi.applicationInfo.icon != 0) { - defaultPackageName = pkgName; - icon = pi.applicationInfo.loadIcon(pm); - } - break; - } - } - } catch (PackageManager.NameNotFoundException e) { - } - } - } - final String uidString = Integer.toString(uidObj.getUid()); - UidToDetail utd = new UidToDetail(); - utd.name = name; - utd.icon = icon; - utd.packageName = defaultPackageName; - sUidCache.put(uidString, utd); - if (mHandler != null) { - mHandler.sendMessage( - mHandler.obtainMessage(BatteryStatsHelper.MSG_UPDATE_NAME_ICON, this)); - } - } -} diff --git a/src/com/android/settings/fuelgauge/BatteryStatsHelper.java b/src/com/android/settings/fuelgauge/BatteryStatsHelper.java deleted file mode 100644 index 0191692a1..000000000 --- a/src/com/android/settings/fuelgauge/BatteryStatsHelper.java +++ /dev/null @@ -1,836 +0,0 @@ -/* - * Copyright (C) 2009 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.fuelgauge; - -import static android.os.BatteryStats.NETWORK_MOBILE_RX_BYTES; -import static android.os.BatteryStats.NETWORK_MOBILE_TX_BYTES; -import static android.os.BatteryStats.NETWORK_WIFI_RX_BYTES; -import static android.os.BatteryStats.NETWORK_WIFI_TX_BYTES; - -import android.app.Activity; -import android.content.Context; -import android.content.pm.UserInfo; -import android.graphics.drawable.Drawable; -import android.hardware.Sensor; -import android.hardware.SensorManager; -import android.os.BatteryStats; -import android.os.BatteryStats.Uid; -import android.os.Bundle; -import android.os.Handler; -import android.os.Parcel; -import android.os.Process; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.SystemClock; -import android.os.UserHandle; -import android.os.UserManager; -import android.preference.PreferenceActivity; -import android.telephony.SignalStrength; -import android.util.Log; -import android.util.SparseArray; - -import com.android.internal.app.IBatteryStats; -import com.android.internal.os.BatteryStatsImpl; -import com.android.internal.os.PowerProfile; -import com.android.internal.util.FastPrintWriter; -import com.android.settings.R; -import com.android.settings.fuelgauge.PowerUsageDetail.DrainType; -import com.android.settings.users.UserUtils; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.io.Writer; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -/** - * A helper class for retrieving the power usage information for all applications and services. - * - * The caller must initialize this class as soon as activity object is ready to use (for example, in - * onAttach() for Fragment), call create() in onCreate() and call destroy() in onDestroy(). - */ -public class BatteryStatsHelper { - - private static final boolean DEBUG = false; - - private static final String TAG = BatteryStatsHelper.class.getSimpleName(); - - private static BatteryStatsImpl sStatsXfer; - private IBatteryStats mBatteryInfo; - private UserManager mUm; - private BatteryStatsImpl mStats; - private PowerProfile mPowerProfile; - - private final List<BatterySipper> mUsageList = new ArrayList<BatterySipper>(); - private final List<BatterySipper> mWifiSippers = new ArrayList<BatterySipper>(); - private final List<BatterySipper> mBluetoothSippers = new ArrayList<BatterySipper>(); - private final SparseArray<List<BatterySipper>> mUserSippers - = new SparseArray<List<BatterySipper>>(); - private final SparseArray<Double> mUserPower = new SparseArray<Double>(); - - private int mStatsType = BatteryStats.STATS_SINCE_CHARGED; - - private long mStatsPeriod = 0; - private double mMaxPower = 1; - private double mTotalPower; - private double mWifiPower; - private double mBluetoothPower; - - // How much the apps together have left WIFI running. - private long mAppWifiRunning; - - /** Queue for fetching name and icon for an application */ - private ArrayList<BatterySipper> mRequestQueue = new ArrayList<BatterySipper>(); - - private Activity mActivity; - private Handler mHandler; - - private class NameAndIconLoader extends Thread { - private boolean mAbort = false; - - public NameAndIconLoader() { - super("BatteryUsage Icon Loader"); - } - - public void abort() { - mAbort = true; - } - - @Override - public void run() { - while (true) { - BatterySipper bs; - synchronized (mRequestQueue) { - if (mRequestQueue.isEmpty() || mAbort) { - mHandler.sendEmptyMessage(MSG_REPORT_FULLY_DRAWN); - return; - } - bs = mRequestQueue.remove(0); - } - bs.loadNameAndIcon(); - } - } - } - - private NameAndIconLoader mRequestThread; - - public BatteryStatsHelper(Activity activity, Handler handler) { - mActivity = activity; - mHandler = handler; - } - - /** Clears the current stats and forces recreating for future use. */ - public void clearStats() { - mStats = null; - } - - public BatteryStatsImpl getStats() { - if (mStats == null) { - load(); - } - return mStats; - } - - public PowerProfile getPowerProfile() { - return mPowerProfile; - } - - public void create(Bundle icicle) { - if (icicle != null) { - mStats = sStatsXfer; - } - mBatteryInfo = IBatteryStats.Stub.asInterface( - ServiceManager.getService(BatteryStats.SERVICE_NAME)); - mUm = (UserManager) mActivity.getSystemService(Context.USER_SERVICE); - mPowerProfile = new PowerProfile(mActivity); - } - - public void pause() { - if (mRequestThread != null) { - mRequestThread.abort(); - } - } - - public void destroy() { - if (mActivity.isChangingConfigurations()) { - sStatsXfer = mStats; - } else { - BatterySipper.sUidCache.clear(); - } - } - - public void startBatteryDetailPage( - PreferenceActivity caller, BatterySipper sipper, boolean showLocationButton) { - // Initialize mStats if necessary. - getStats(); - - Bundle args = new Bundle(); - args.putString(PowerUsageDetail.EXTRA_TITLE, sipper.name); - args.putInt(PowerUsageDetail.EXTRA_PERCENT, (int) - Math.ceil(sipper.getSortValue() * 100 / mTotalPower)); - args.putInt(PowerUsageDetail.EXTRA_GAUGE, (int) - Math.ceil(sipper.getSortValue() * 100 / mMaxPower)); - args.putLong(PowerUsageDetail.EXTRA_USAGE_DURATION, mStatsPeriod); - args.putString(PowerUsageDetail.EXTRA_ICON_PACKAGE, sipper.defaultPackageName); - args.putInt(PowerUsageDetail.EXTRA_ICON_ID, sipper.iconId); - args.putDouble(PowerUsageDetail.EXTRA_NO_COVERAGE, sipper.noCoveragePercent); - if (sipper.uidObj != null) { - args.putInt(PowerUsageDetail.EXTRA_UID, sipper.uidObj.getUid()); - } - args.putSerializable(PowerUsageDetail.EXTRA_DRAIN_TYPE, sipper.drainType); - args.putBoolean(PowerUsageDetail.EXTRA_SHOW_LOCATION_BUTTON, showLocationButton); - - int[] types; - double[] values; - switch (sipper.drainType) { - case APP: - case USER: - { - Uid uid = sipper.uidObj; - types = new int[] { - R.string.usage_type_cpu, - R.string.usage_type_cpu_foreground, - R.string.usage_type_wake_lock, - R.string.usage_type_gps, - R.string.usage_type_wifi_running, - R.string.usage_type_data_recv, - R.string.usage_type_data_send, - R.string.usage_type_data_wifi_recv, - R.string.usage_type_data_wifi_send, - R.string.usage_type_audio, - R.string.usage_type_video, - }; - values = new double[] { - sipper.cpuTime, - sipper.cpuFgTime, - sipper.wakeLockTime, - sipper.gpsTime, - sipper.wifiRunningTime, - sipper.mobileRxBytes, - sipper.mobileTxBytes, - sipper.wifiRxBytes, - sipper.wifiTxBytes, - 0, - 0 - }; - - if (sipper.drainType == DrainType.APP) { - Writer result = new StringWriter(); - PrintWriter printWriter = new FastPrintWriter(result, false, 1024); - mStats.dumpLocked(printWriter, "", mStatsType, uid.getUid()); - printWriter.flush(); - args.putString(PowerUsageDetail.EXTRA_REPORT_DETAILS, result.toString()); - - result = new StringWriter(); - printWriter = new FastPrintWriter(result, false, 1024); - mStats.dumpCheckinLocked(printWriter, mStatsType, uid.getUid()); - printWriter.flush(); - args.putString(PowerUsageDetail.EXTRA_REPORT_CHECKIN_DETAILS, - result.toString()); - } - } - break; - case CELL: - { - types = new int[] { - R.string.usage_type_on_time, - R.string.usage_type_no_coverage - }; - values = new double[] { - sipper.usageTime, - sipper.noCoveragePercent - }; - } - break; - case WIFI: - { - types = new int[] { - R.string.usage_type_wifi_running, - R.string.usage_type_cpu, - R.string.usage_type_cpu_foreground, - R.string.usage_type_wake_lock, - R.string.usage_type_data_recv, - R.string.usage_type_data_send, - R.string.usage_type_data_wifi_recv, - R.string.usage_type_data_wifi_send, - }; - values = new double[] { - sipper.usageTime, - sipper.cpuTime, - sipper.cpuFgTime, - sipper.wakeLockTime, - sipper.mobileRxBytes, - sipper.mobileTxBytes, - sipper.wifiRxBytes, - sipper.wifiTxBytes, - }; - } break; - case BLUETOOTH: - { - types = new int[] { - R.string.usage_type_on_time, - R.string.usage_type_cpu, - R.string.usage_type_cpu_foreground, - R.string.usage_type_wake_lock, - R.string.usage_type_data_recv, - R.string.usage_type_data_send, - R.string.usage_type_data_wifi_recv, - R.string.usage_type_data_wifi_send, - }; - values = new double[] { - sipper.usageTime, - sipper.cpuTime, - sipper.cpuFgTime, - sipper.wakeLockTime, - sipper.mobileRxBytes, - sipper.mobileTxBytes, - sipper.wifiRxBytes, - sipper.wifiTxBytes, - }; - } break; - default: - { - types = new int[] { - R.string.usage_type_on_time - }; - values = new double[] { - sipper.usageTime - }; - } - } - args.putIntArray(PowerUsageDetail.EXTRA_DETAIL_TYPES, types); - args.putDoubleArray(PowerUsageDetail.EXTRA_DETAIL_VALUES, values); - caller.startPreferencePanel(PowerUsageDetail.class.getName(), args, - R.string.details_title, null, null, 0); - } - - /** - * Refreshes the power usage list. - * @param includeZeroConsumption whether includes those applications which have consumed very - * little power up till now. - */ - public void refreshStats(boolean includeZeroConsumption) { - // Initialize mStats if necessary. - getStats(); - - mMaxPower = 0; - mTotalPower = 0; - mWifiPower = 0; - mBluetoothPower = 0; - mAppWifiRunning = 0; - - mUsageList.clear(); - mWifiSippers.clear(); - mBluetoothSippers.clear(); - mUserSippers.clear(); - mUserPower.clear(); - - processAppUsage(includeZeroConsumption); - processMiscUsage(); - - Collections.sort(mUsageList); - - if (mHandler != null) { - synchronized (mRequestQueue) { - if (!mRequestQueue.isEmpty()) { - if (mRequestThread != null) { - mRequestThread.abort(); - } - mRequestThread = new NameAndIconLoader(); - mRequestThread.setPriority(Thread.MIN_PRIORITY); - mRequestThread.start(); - mRequestQueue.notify(); - } - } - } - } - - private void processAppUsage(boolean includeZeroConsumption) { - SensorManager sensorManager = (SensorManager) mActivity.getSystemService( - Context.SENSOR_SERVICE); - final int which = mStatsType; - final int speedSteps = mPowerProfile.getNumSpeedSteps(); - final double[] powerCpuNormal = new double[speedSteps]; - final long[] cpuSpeedStepTimes = new long[speedSteps]; - for (int p = 0; p < speedSteps; p++) { - powerCpuNormal[p] = mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE, p); - } - final double mobilePowerPerByte = getMobilePowerPerByte(); - final double wifiPowerPerByte = getWifiPowerPerByte(); - long uSecTime = mStats.computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which); - long appWakelockTime = 0; - BatterySipper osApp = null; - mStatsPeriod = uSecTime; - SparseArray<? extends Uid> uidStats = mStats.getUidStats(); - final int NU = uidStats.size(); - for (int iu = 0; iu < NU; iu++) { - Uid u = uidStats.valueAt(iu); - double p; // in mAs - double power = 0; // in mAs - double highestDrain = 0; - String packageWithHighestDrain = null; - //mUsageList.add(new AppUsage(u.getUid(), new double[] {power})); - Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats(); - long cpuTime = 0; - long cpuFgTime = 0; - long wakelockTime = 0; - long gpsTime = 0; - if (DEBUG) Log.i(TAG, "UID " + u.getUid()); - if (processStats.size() > 0) { - // Process CPU time - for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent - : processStats.entrySet()) { - Uid.Proc ps = ent.getValue(); - final long userTime = ps.getUserTime(which); - final long systemTime = ps.getSystemTime(which); - final long foregroundTime = ps.getForegroundTime(which); - cpuFgTime += foregroundTime * 10; // convert to millis - final long tmpCpuTime = (userTime + systemTime) * 10; // convert to millis - int totalTimeAtSpeeds = 0; - // Get the total first - for (int step = 0; step < speedSteps; step++) { - cpuSpeedStepTimes[step] = ps.getTimeAtCpuSpeedStep(step, which); - totalTimeAtSpeeds += cpuSpeedStepTimes[step]; - } - if (totalTimeAtSpeeds == 0) totalTimeAtSpeeds = 1; - // Then compute the ratio of time spent at each speed - double processPower = 0; - for (int step = 0; step < speedSteps; step++) { - double ratio = (double) cpuSpeedStepTimes[step] / totalTimeAtSpeeds; - processPower += ratio * tmpCpuTime * powerCpuNormal[step]; - } - cpuTime += tmpCpuTime; - if (DEBUG && processPower != 0) { - Log.i(TAG, String.format("process %s, cpu power=%.2f", - ent.getKey(), processPower / 1000)); - } - power += processPower; - if (packageWithHighestDrain == null - || packageWithHighestDrain.startsWith("*")) { - highestDrain = processPower; - packageWithHighestDrain = ent.getKey(); - } else if (highestDrain < processPower - && !ent.getKey().startsWith("*")) { - highestDrain = processPower; - packageWithHighestDrain = ent.getKey(); - } - } - } - if (cpuFgTime > cpuTime) { - if (DEBUG && cpuFgTime > cpuTime + 10000) { - Log.i(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time"); - } - cpuTime = cpuFgTime; // Statistics may not have been gathered yet. - } - power /= 1000; - if (DEBUG && power != 0) Log.i(TAG, String.format("total cpu power=%.2f", power)); - - // Process wake lock usage - Map<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats = u.getWakelockStats(); - for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> wakelockEntry - : wakelockStats.entrySet()) { - Uid.Wakelock wakelock = wakelockEntry.getValue(); - // Only care about partial wake locks since full wake locks - // are canceled when the user turns the screen off. - BatteryStats.Timer timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL); - if (timer != null) { - wakelockTime += timer.getTotalTimeLocked(uSecTime, which); - } - } - wakelockTime /= 1000; // convert to millis - appWakelockTime += wakelockTime; - - // Add cost of holding a wake lock - p = (wakelockTime - * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE)) / 1000; - power += p; - if (DEBUG && p != 0) Log.i(TAG, String.format("wakelock power=%.2f", p)); - - // Add cost of mobile traffic - final long mobileRx = u.getNetworkActivityCount(NETWORK_MOBILE_RX_BYTES, mStatsType); - final long mobileTx = u.getNetworkActivityCount(NETWORK_MOBILE_TX_BYTES, mStatsType); - p = (mobileRx + mobileTx) * mobilePowerPerByte; - power += p; - if (DEBUG && p != 0) Log.i(TAG, String.format("mobile power=%.2f", p)); - - // Add cost of wifi traffic - final long wifiRx = u.getNetworkActivityCount(NETWORK_WIFI_RX_BYTES, mStatsType); - final long wifiTx = u.getNetworkActivityCount(NETWORK_WIFI_TX_BYTES, mStatsType); - p = (wifiRx + wifiTx) * wifiPowerPerByte; - power += p; - if (DEBUG && p != 0) Log.i(TAG, String.format("wifi power=%.2f", p)); - - // Add cost of keeping WIFI running. - long wifiRunningTimeMs = u.getWifiRunningTime(uSecTime, which) / 1000; - mAppWifiRunning += wifiRunningTimeMs; - p = (wifiRunningTimeMs - * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)) / 1000; - power += p; - if (DEBUG && p != 0) Log.i(TAG, String.format("wifi running power=%.2f", p)); - - // Add cost of WIFI scans - long wifiScanTimeMs = u.getWifiScanTime(uSecTime, which) / 1000; - p = (wifiScanTimeMs - * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_SCAN)) / 1000; - power += p; - if (DEBUG && p != 0) Log.i(TAG, String.format("wifi scanning power=%.2f", p)); - for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) { - long batchScanTimeMs = u.getWifiBatchedScanTime(bin, uSecTime, which) / 1000; - p = (batchScanTimeMs - * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN, bin)); - power += p; - if (DEBUG && p != 0) { - Log.i(TAG, String.format("wifi batched scanning lvl %d = %.2f", bin, p)); - } - } - - // Process Sensor usage - Map<Integer, ? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats(); - for (Map.Entry<Integer, ? extends BatteryStats.Uid.Sensor> sensorEntry - : sensorStats.entrySet()) { - Uid.Sensor sensor = sensorEntry.getValue(); - int sensorHandle = sensor.getHandle(); - BatteryStats.Timer timer = sensor.getSensorTime(); - long sensorTime = timer.getTotalTimeLocked(uSecTime, which) / 1000; - double multiplier = 0; - switch (sensorHandle) { - case Uid.Sensor.GPS: - multiplier = mPowerProfile.getAveragePower(PowerProfile.POWER_GPS_ON); - gpsTime = sensorTime; - break; - default: - List<Sensor> sensorList = sensorManager.getSensorList( - android.hardware.Sensor.TYPE_ALL); - for (android.hardware.Sensor s : sensorList) { - if (s.getHandle() == sensorHandle) { - multiplier = s.getPower(); - break; - } - } - } - p = (multiplier * sensorTime) / 1000; - power += p; - if (DEBUG && p != 0) { - Log.i(TAG, String.format("sensor %s power=%.2f", sensor.toString(), p)); - } - } - - if (DEBUG) Log.i(TAG, String.format("UID %d total power=%.2f", u.getUid(), power)); - - // Add the app to the list if it is consuming power - boolean isOtherUser = false; - final int userId = UserHandle.getUserId(u.getUid()); - if (power != 0 || includeZeroConsumption || u.getUid() == 0) { - BatterySipper app = new BatterySipper(mActivity, mRequestQueue, mHandler, - packageWithHighestDrain, DrainType.APP, 0, u, - new double[] {power}); - app.cpuTime = cpuTime; - app.gpsTime = gpsTime; - app.wifiRunningTime = wifiRunningTimeMs; - app.cpuFgTime = cpuFgTime; - app.wakeLockTime = wakelockTime; - app.mobileRxBytes = mobileRx; - app.mobileTxBytes = mobileTx; - app.wifiRxBytes = wifiRx; - app.wifiTxBytes = wifiTx; - if (u.getUid() == Process.WIFI_UID) { - mWifiSippers.add(app); - } else if (u.getUid() == Process.BLUETOOTH_UID) { - mBluetoothSippers.add(app); - } else if (userId != UserHandle.myUserId() - && UserHandle.getAppId(u.getUid()) >= Process.FIRST_APPLICATION_UID) { - isOtherUser = true; - List<BatterySipper> list = mUserSippers.get(userId); - if (list == null) { - list = new ArrayList<BatterySipper>(); - mUserSippers.put(userId, list); - } - list.add(app); - } else { - mUsageList.add(app); - } - if (u.getUid() == 0) { - osApp = app; - } - } - if (power != 0 || includeZeroConsumption) { - if (u.getUid() == Process.WIFI_UID) { - mWifiPower += power; - } else if (u.getUid() == Process.BLUETOOTH_UID) { - mBluetoothPower += power; - } else if (isOtherUser) { - Double userPower = mUserPower.get(userId); - if (userPower == null) { - userPower = power; - } else { - userPower += power; - } - mUserPower.put(userId, userPower); - } else { - if (power > mMaxPower) mMaxPower = power; - mTotalPower += power; - } - } - } - - // The device has probably been awake for longer than the screen on - // time and application wake lock time would account for. Assign - // this remainder to the OS, if possible. - if (osApp != null) { - long wakeTimeMillis = mStats.computeBatteryUptime( - SystemClock.uptimeMillis() * 1000, which) / 1000; - wakeTimeMillis -= appWakelockTime + (mStats.getScreenOnTime( - SystemClock.elapsedRealtime(), which) / 1000); - if (wakeTimeMillis > 0) { - double power = (wakeTimeMillis - * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE)) / 1000; - if (DEBUG) Log.i(TAG, "OS wakeLockTime " + wakeTimeMillis + " power " + power); - osApp.wakeLockTime += wakeTimeMillis; - osApp.value += power; - osApp.values[0] += power; - if (osApp.value > mMaxPower) mMaxPower = osApp.value; - mTotalPower += power; - } - } - } - - private void addPhoneUsage(long uSecNow) { - long phoneOnTimeMs = mStats.getPhoneOnTime(uSecNow, mStatsType) / 1000; - double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE) - * phoneOnTimeMs / 1000; - addEntry(mActivity.getString(R.string.power_phone), DrainType.PHONE, phoneOnTimeMs, - R.drawable.ic_settings_voice_calls, phoneOnPower); - } - - private void addScreenUsage(long uSecNow) { - double power = 0; - long screenOnTimeMs = mStats.getScreenOnTime(uSecNow, mStatsType) / 1000; - power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON); - final double screenFullPower = - mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL); - for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) { - double screenBinPower = screenFullPower * (i + 0.5f) - / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; - long brightnessTime = mStats.getScreenBrightnessTime(i, uSecNow, mStatsType) / 1000; - power += screenBinPower * brightnessTime; - if (DEBUG) { - Log.i(TAG, "Screen bin power = " + (int) screenBinPower + ", time = " - + brightnessTime); - } - } - power /= 1000; // To seconds - addEntry(mActivity.getString(R.string.power_screen), DrainType.SCREEN, screenOnTimeMs, - R.drawable.ic_settings_display, power); - } - - private void addRadioUsage(long uSecNow) { - double power = 0; - final int BINS = SignalStrength.NUM_SIGNAL_STRENGTH_BINS; - long signalTimeMs = 0; - for (int i = 0; i < BINS; i++) { - long strengthTimeMs = mStats.getPhoneSignalStrengthTime(i, uSecNow, mStatsType) / 1000; - power += strengthTimeMs / 1000 - * mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ON, i); - signalTimeMs += strengthTimeMs; - } - long scanningTimeMs = mStats.getPhoneSignalScanningTime(uSecNow, mStatsType) / 1000; - power += scanningTimeMs / 1000 * mPowerProfile.getAveragePower( - PowerProfile.POWER_RADIO_SCANNING); - BatterySipper bs = - addEntry(mActivity.getString(R.string.power_cell), DrainType.CELL, - signalTimeMs, R.drawable.ic_settings_cell_standby, power); - if (signalTimeMs != 0) { - bs.noCoveragePercent = mStats.getPhoneSignalStrengthTime(0, uSecNow, mStatsType) - / 1000 * 100.0 / signalTimeMs; - } - } - - private void aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag) { - for (int i=0; i<from.size(); i++) { - BatterySipper wbs = from.get(i); - if (DEBUG) Log.i(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTime); - bs.cpuTime += wbs.cpuTime; - bs.gpsTime += wbs.gpsTime; - bs.wifiRunningTime += wbs.wifiRunningTime; - bs.cpuFgTime += wbs.cpuFgTime; - bs.wakeLockTime += wbs.wakeLockTime; - bs.mobileRxBytes += wbs.mobileRxBytes; - bs.mobileTxBytes += wbs.mobileTxBytes; - bs.wifiRxBytes += wbs.wifiRxBytes; - bs.wifiTxBytes += wbs.wifiTxBytes; - } - } - - private void addWiFiUsage(long uSecNow) { - long onTimeMs = mStats.getWifiOnTime(uSecNow, mStatsType) / 1000; - long runningTimeMs = mStats.getGlobalWifiRunningTime(uSecNow, mStatsType) / 1000; - if (DEBUG) Log.i(TAG, "WIFI runningTime=" + runningTimeMs - + " app runningTime=" + mAppWifiRunning); - runningTimeMs -= mAppWifiRunning; - if (runningTimeMs < 0) runningTimeMs = 0; - double wifiPower = (onTimeMs * 0 /* TODO */ - * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON) - + runningTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)) / 1000; - if (DEBUG) Log.i(TAG, "WIFI power=" + wifiPower + " from procs=" + mWifiPower); - BatterySipper bs = addEntry(mActivity.getString(R.string.power_wifi), DrainType.WIFI, - runningTimeMs, R.drawable.ic_settings_wifi, wifiPower + mWifiPower); - aggregateSippers(bs, mWifiSippers, "WIFI"); - } - - private void addIdleUsage(long uSecNow) { - long idleTimeMs = (uSecNow - mStats.getScreenOnTime(uSecNow, mStatsType)) / 1000; - double idlePower = (idleTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE)) - / 1000; - addEntry(mActivity.getString(R.string.power_idle), DrainType.IDLE, idleTimeMs, - R.drawable.ic_settings_phone_idle, idlePower); - } - - private void addBluetoothUsage(long uSecNow) { - long btOnTimeMs = mStats.getBluetoothOnTime(uSecNow, mStatsType) / 1000; - double btPower = btOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_ON) - / 1000; - int btPingCount = mStats.getBluetoothPingCount(); - btPower += (btPingCount - * mPowerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_AT_CMD)) / 1000; - BatterySipper bs = addEntry(mActivity.getString(R.string.power_bluetooth), - DrainType.BLUETOOTH, btOnTimeMs, R.drawable.ic_settings_bluetooth, - btPower + mBluetoothPower); - aggregateSippers(bs, mBluetoothSippers, "Bluetooth"); - } - - private void addUserUsage() { - for (int i=0; i<mUserSippers.size(); i++) { - final int userId = mUserSippers.keyAt(i); - final List<BatterySipper> sippers = mUserSippers.valueAt(i); - UserInfo info = mUm.getUserInfo(userId); - Drawable icon; - String name; - if (info != null) { - icon = UserUtils.getUserIcon(mActivity, mUm, info, mActivity.getResources()); - name = info != null ? info.name : null; - if (name == null) { - name = Integer.toString(info.id); - } - name = mActivity.getResources().getString( - R.string.running_process_item_user_label, name); - } else { - icon = null; - name = mActivity.getResources().getString( - R.string.running_process_item_removed_user_label); - } - Double userPower = mUserPower.get(userId); - double power = (userPower != null) ? userPower : 0.0; - BatterySipper bs = addEntry(name, DrainType.USER, 0, 0, power); - bs.icon = icon; - aggregateSippers(bs, sippers, "User"); - } - } - - /** - * Return estimated power (in mAs) of sending a byte with the mobile radio. - */ - private double getMobilePowerPerByte() { - final long MOBILE_BPS = 200000; // TODO: Extract average bit rates from system - final double MOBILE_POWER = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE) - / 3600; - - final long mobileRx = mStats.getNetworkActivityCount(NETWORK_MOBILE_RX_BYTES, mStatsType); - final long mobileTx = mStats.getNetworkActivityCount(NETWORK_MOBILE_TX_BYTES, mStatsType); - final long mobileData = mobileRx + mobileTx; - - final long radioDataUptimeMs = mStats.getRadioDataUptime() / 1000; - final long mobileBps = radioDataUptimeMs != 0 - ? mobileData * 8 * 1000 / radioDataUptimeMs - : MOBILE_BPS; - - return MOBILE_POWER / (mobileBps / 8); - } - - /** - * Return estimated power (in mAs) of sending a byte with the Wi-Fi radio. - */ - private double getWifiPowerPerByte() { - final long WIFI_BPS = 1000000; // TODO: Extract average bit rates from system - final double WIFI_POWER = mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE) - / 3600; - return WIFI_POWER / (WIFI_BPS / 8); - } - - private void processMiscUsage() { - final int which = mStatsType; - long uSecTime = SystemClock.elapsedRealtime() * 1000; - final long uSecNow = mStats.computeBatteryRealtime(uSecTime, which); - final long timeSinceUnplugged = uSecNow; - if (DEBUG) { - Log.i(TAG, "Uptime since last unplugged = " + (timeSinceUnplugged / 1000)); - } - - addUserUsage(); - addPhoneUsage(uSecNow); - addScreenUsage(uSecNow); - addWiFiUsage(uSecNow); - addBluetoothUsage(uSecNow); - addIdleUsage(uSecNow); // Not including cellular idle power - // Don't compute radio usage if it's a wifi-only device - if (!com.android.settings.Utils.isWifiOnly(mActivity)) { - addRadioUsage(uSecNow); - } - } - - private BatterySipper addEntry(String label, DrainType drainType, long time, int iconId, - double power) { - if (power > mMaxPower) mMaxPower = power; - mTotalPower += power; - BatterySipper bs = new BatterySipper(mActivity, mRequestQueue, mHandler, - label, drainType, iconId, null, new double[] {power}); - bs.usageTime = time; - bs.iconId = iconId; - mUsageList.add(bs); - return bs; - } - - public List<BatterySipper> getUsageList() { - return mUsageList; - } - - static final int MSG_UPDATE_NAME_ICON = 1; - static final int MSG_REPORT_FULLY_DRAWN = 2; - - public double getMaxPower() { - return mMaxPower; - } - - public double getTotalPower() { - return mTotalPower; - } - - private void load() { - try { - byte[] data = mBatteryInfo.getStatistics(); - Parcel parcel = Parcel.obtain(); - parcel.unmarshall(data, 0, data.length); - parcel.setDataPosition(0); - mStats = com.android.internal.os.BatteryStatsImpl.CREATOR - .createFromParcel(parcel); - mStats.distributeWorkLocked(BatteryStats.STATS_SINCE_CHARGED); - } catch (RemoteException e) { - Log.e(TAG, "RemoteException:", e); - } - } -} diff --git a/src/com/android/settings/fuelgauge/PowerGaugePreference.java b/src/com/android/settings/fuelgauge/PowerGaugePreference.java index c8bfa217d..a028adf77 100644 --- a/src/com/android/settings/fuelgauge/PowerGaugePreference.java +++ b/src/com/android/settings/fuelgauge/PowerGaugePreference.java @@ -24,6 +24,7 @@ import android.view.View; import android.widget.ProgressBar; import android.widget.TextView; +import com.android.internal.os.BatterySipper; import com.android.settings.R; /** @@ -31,11 +32,11 @@ import com.android.settings.R; * the left for the subsystem/app type. */ public class PowerGaugePreference extends Preference { - private BatterySipper mInfo; + private BatteryEntry mInfo; private int mProgress; private CharSequence mProgressText; - public PowerGaugePreference(Context context, Drawable icon, BatterySipper info) { + public PowerGaugePreference(Context context, Drawable icon, BatteryEntry info) { super(context); setLayoutResource(R.layout.app_percentage_item); setIcon(icon != null ? icon : new ColorDrawable(0)); @@ -45,11 +46,11 @@ public class PowerGaugePreference extends Preference { public void setPercent(double percentOfMax, double percentOfTotal) { mProgress = (int) Math.ceil(percentOfMax); mProgressText = getContext().getResources().getString( - R.string.percentage, (int) Math.ceil(percentOfTotal)); + R.string.percentage, (int) (percentOfTotal+.5)); notifyChanged(); } - BatterySipper getInfo() { + BatteryEntry getInfo() { return mInfo; } diff --git a/src/com/android/settings/fuelgauge/PowerUsageDetail.java b/src/com/android/settings/fuelgauge/PowerUsageDetail.java index 45e451655..518fc0eb2 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageDetail.java +++ b/src/com/android/settings/fuelgauge/PowerUsageDetail.java @@ -34,13 +34,11 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.BatteryStats; import android.os.Bundle; import android.os.Process; import android.os.UserHandle; -import android.preference.PreferenceActivity; -import android.provider.Settings; import android.text.TextUtils; -import android.text.format.Formatter; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -49,26 +47,23 @@ import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; +import com.android.internal.os.BatterySipper; +import com.android.internal.os.BatteryStatsHelper; +import com.android.internal.util.FastPrintWriter; import com.android.settings.DisplaySettings; import com.android.settings.R; +import com.android.settings.SettingsActivity; import com.android.settings.WirelessSettings; import com.android.settings.applications.InstalledAppDetails; import com.android.settings.bluetooth.BluetoothSettings; import com.android.settings.location.LocationSettings; import com.android.settings.wifi.WifiSettings; -public class PowerUsageDetail extends Fragment implements Button.OnClickListener { +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; - enum DrainType { - IDLE, - CELL, - PHONE, - WIFI, - BLUETOOTH, - SCREEN, - APP, - USER - } +public class PowerUsageDetail extends Fragment implements Button.OnClickListener { // Note: Must match the sequence of the DrainType private static int[] sDrainTypeDesciptions = new int[] { @@ -80,8 +75,178 @@ public class PowerUsageDetail extends Fragment implements Button.OnClickListener R.string.battery_desc_display, R.string.battery_desc_apps, R.string.battery_desc_users, + R.string.battery_desc_unaccounted, + R.string.battery_desc_overcounted, }; + public static void startBatteryDetailPage( + SettingsActivity caller, BatteryStatsHelper helper, BatteryEntry entry, + boolean showLocationButton) { + // Initialize mStats if necessary. + helper.getStats(); + + Bundle args = new Bundle(); + args.putString(PowerUsageDetail.EXTRA_TITLE, entry.name); + args.putInt(PowerUsageDetail.EXTRA_PERCENT, (int) + Math.ceil(entry.sipper.value * 100 / helper.getTotalPower())); + args.putInt(PowerUsageDetail.EXTRA_GAUGE, (int) + Math.ceil(entry.sipper.value * 100 / helper.getMaxPower())); + args.putLong(PowerUsageDetail.EXTRA_USAGE_DURATION, helper.getStatsPeriod()); + args.putString(PowerUsageDetail.EXTRA_ICON_PACKAGE, entry.defaultPackageName); + args.putInt(PowerUsageDetail.EXTRA_ICON_ID, entry.iconId); + args.putDouble(PowerUsageDetail.EXTRA_NO_COVERAGE, entry.sipper.noCoveragePercent); + if (entry.sipper.uidObj != null) { + args.putInt(PowerUsageDetail.EXTRA_UID, entry.sipper.uidObj.getUid()); + } + args.putSerializable(PowerUsageDetail.EXTRA_DRAIN_TYPE, entry.sipper.drainType); + args.putBoolean(PowerUsageDetail.EXTRA_SHOW_LOCATION_BUTTON, showLocationButton); + + int[] types; + double[] values; + switch (entry.sipper.drainType) { + case APP: + case USER: + { + BatteryStats.Uid uid = entry.sipper.uidObj; + types = new int[] { + R.string.usage_type_cpu, + R.string.usage_type_cpu_foreground, + R.string.usage_type_wake_lock, + R.string.usage_type_gps, + R.string.usage_type_wifi_running, + R.string.usage_type_data_recv, + R.string.usage_type_data_send, + R.string.usage_type_radio_active, + R.string.usage_type_data_wifi_recv, + R.string.usage_type_data_wifi_send, + R.string.usage_type_audio, + R.string.usage_type_video, + }; + values = new double[] { + entry.sipper.cpuTime, + entry.sipper.cpuFgTime, + entry.sipper.wakeLockTime, + entry.sipper.gpsTime, + entry.sipper.wifiRunningTime, + entry.sipper.mobileRxPackets, + entry.sipper.mobileTxPackets, + entry.sipper.mobileActive, + entry.sipper.wifiRxPackets, + entry.sipper.wifiTxPackets, + 0, + 0 + }; + + if (entry.sipper.drainType == BatterySipper.DrainType.APP) { + Writer result = new StringWriter(); + PrintWriter printWriter = new FastPrintWriter(result, false, 1024); + helper.getStats().dumpLocked(caller, printWriter, "", helper.getStatsType(), + uid.getUid()); + printWriter.flush(); + args.putString(PowerUsageDetail.EXTRA_REPORT_DETAILS, result.toString()); + + result = new StringWriter(); + printWriter = new FastPrintWriter(result, false, 1024); + helper.getStats().dumpCheckinLocked(caller, printWriter, helper.getStatsType(), + uid.getUid()); + printWriter.flush(); + args.putString(PowerUsageDetail.EXTRA_REPORT_CHECKIN_DETAILS, + result.toString()); + } + } + break; + case CELL: + { + types = new int[] { + R.string.usage_type_on_time, + R.string.usage_type_no_coverage, + R.string.usage_type_radio_active, + }; + values = new double[] { + entry.sipper.usageTime, + entry.sipper.noCoveragePercent, + entry.sipper.mobileActive + }; + } + break; + case WIFI: + { + types = new int[] { + R.string.usage_type_wifi_running, + R.string.usage_type_cpu, + R.string.usage_type_cpu_foreground, + R.string.usage_type_wake_lock, + R.string.usage_type_data_recv, + R.string.usage_type_data_send, + R.string.usage_type_data_wifi_recv, + R.string.usage_type_data_wifi_send, + }; + values = new double[] { + entry.sipper.usageTime, + entry.sipper.cpuTime, + entry.sipper.cpuFgTime, + entry.sipper.wakeLockTime, + entry.sipper.mobileRxPackets, + entry.sipper.mobileTxPackets, + entry.sipper.wifiRxPackets, + entry.sipper.wifiTxPackets, + }; + } break; + case BLUETOOTH: + { + types = new int[] { + R.string.usage_type_on_time, + R.string.usage_type_cpu, + R.string.usage_type_cpu_foreground, + R.string.usage_type_wake_lock, + R.string.usage_type_data_recv, + R.string.usage_type_data_send, + R.string.usage_type_data_wifi_recv, + R.string.usage_type_data_wifi_send, + }; + values = new double[] { + entry.sipper.usageTime, + entry.sipper.cpuTime, + entry.sipper.cpuFgTime, + entry.sipper.wakeLockTime, + entry.sipper.mobileRxPackets, + entry.sipper.mobileTxPackets, + entry.sipper.wifiRxPackets, + entry.sipper.wifiTxPackets, + }; + } break; + case UNACCOUNTED: + case OVERCOUNTED: + { + types = new int[] { + R.string.usage_type_total_battery_capacity, + R.string.usage_type_computed_power, + R.string.usage_type_min_actual_power, + R.string.usage_type_max_actual_power, + }; + values = new double[] { + helper.getPowerProfile().getBatteryCapacity(), + helper.getComputedPower(), + helper.getMinDrainedPower(), + helper.getMaxDrainedPower(), + }; + } break; + default: + { + types = new int[] { + R.string.usage_type_on_time + }; + values = new double[] { + entry.sipper.usageTime + }; + } + } + args.putIntArray(PowerUsageDetail.EXTRA_DETAIL_TYPES, types); + args.putDoubleArray(PowerUsageDetail.EXTRA_DETAIL_VALUES, values); + caller.startPreferencePanel(PowerUsageDetail.class.getName(), args, + R.string.details_title, null, null, 0); + } + public static final int ACTION_DISPLAY_SETTINGS = 1; public static final int ACTION_WIFI_SETTINGS = 2; public static final int ACTION_BLUETOOTH_SETTINGS = 3; @@ -125,7 +290,7 @@ public class PowerUsageDetail extends Fragment implements Button.OnClickListener private ViewGroup mDetailsParent; private ViewGroup mControlsParent; private long mStartTime; - private DrainType mDrainType; + private BatterySipper.DrainType mDrainType; private Drawable mAppIcon; private double mNoCoverage; // Percentage of time that there was no coverage @@ -175,7 +340,7 @@ public class PowerUsageDetail extends Fragment implements Button.OnClickListener final int gaugeValue = args.getInt(EXTRA_GAUGE, 1); mUsageSince = args.getInt(EXTRA_USAGE_SINCE, USAGE_SINCE_UNPLUGGED); mUid = args.getInt(EXTRA_UID, 0); - mDrainType = (DrainType) args.getSerializable(EXTRA_DRAIN_TYPE); + mDrainType = (BatterySipper.DrainType) args.getSerializable(EXTRA_DRAIN_TYPE); mNoCoverage = args.getDouble(EXTRA_NO_COVERAGE, 0); String iconPackage = args.getString(EXTRA_ICON_PACKAGE); int iconId = args.getInt(EXTRA_ICON_ID, 0); @@ -238,8 +403,8 @@ public class PowerUsageDetail extends Fragment implements Button.OnClickListener mReportButton.setOnClickListener(this); // check if error reporting is enabled in secure settings - int enabled = Settings.Global.getInt(getActivity().getContentResolver(), - Settings.Global.SEND_ACTION_APP_ERROR, 0); + int enabled = android.provider.Settings.Global.getInt(getActivity().getContentResolver(), + android.provider.Settings.Global.SEND_ACTION_APP_ERROR, 0); if (enabled != 0) { if (mPackages != null && mPackages.length > 0) { try { @@ -269,35 +434,35 @@ public class PowerUsageDetail extends Fragment implements Button.OnClickListener Bundle args = new Bundle(); args.putString(InstalledAppDetails.ARG_PACKAGE_NAME, mPackages[0]); - PreferenceActivity pa = (PreferenceActivity)getActivity(); - pa.startPreferencePanel(InstalledAppDetails.class.getName(), args, + SettingsActivity sa = (SettingsActivity) getActivity(); + sa.startPreferencePanel(InstalledAppDetails.class.getName(), args, R.string.application_info_label, null, null, 0); } private void doAction(int action) { - PreferenceActivity pa = (PreferenceActivity)getActivity(); + SettingsActivity sa = (SettingsActivity)getActivity(); switch (action) { case ACTION_DISPLAY_SETTINGS: - pa.startPreferencePanel(DisplaySettings.class.getName(), null, + sa.startPreferencePanel(DisplaySettings.class.getName(), null, R.string.display_settings_title, null, null, 0); break; case ACTION_WIFI_SETTINGS: - pa.startPreferencePanel(WifiSettings.class.getName(), null, + sa.startPreferencePanel(WifiSettings.class.getName(), null, R.string.wifi_settings, null, null, 0); break; case ACTION_BLUETOOTH_SETTINGS: - pa.startPreferencePanel(BluetoothSettings.class.getName(), null, + sa.startPreferencePanel(BluetoothSettings.class.getName(), null, R.string.bluetooth_settings, null, null, 0); break; case ACTION_WIRELESS_SETTINGS: - pa.startPreferencePanel(WirelessSettings.class.getName(), null, + sa.startPreferencePanel(WirelessSettings.class.getName(), null, R.string.radio_controls_title, null, null, 0); break; case ACTION_APP_DETAILS: startApplicationDetailsActivity(); break; case ACTION_LOCATION_SETTINGS: - pa.startPreferencePanel(LocationSettings.class.getName(), null, + sa.startPreferencePanel(LocationSettings.class.getName(), null, R.string.location_settings_title, null, null, 0); break; case ACTION_FORCE_STOP: @@ -322,13 +487,19 @@ public class PowerUsageDetail extends Fragment implements Button.OnClickListener case R.string.usage_type_data_send: case R.string.usage_type_data_wifi_recv: case R.string.usage_type_data_wifi_send: - final long bytes = (long) (mValues[i]); - value = Formatter.formatFileSize(getActivity(), bytes); + final long packets = (long) (mValues[i]); + value = Long.toString(packets); break; case R.string.usage_type_no_coverage: final int percentage = (int) Math.floor(mValues[i]); value = getActivity().getString(R.string.percentage, percentage); break; + case R.string.usage_type_total_battery_capacity: + case R.string.usage_type_computed_power: + case R.string.usage_type_min_actual_power: + case R.string.usage_type_max_actual_power: + value = getActivity().getString(R.string.mah, (long)(mValues[i])); + break; case R.string.usage_type_gps: mUsesGps = true; // Fall through diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java index dc86b8de2..8a7299413 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java +++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java @@ -26,8 +26,9 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Parcel; +import android.os.UserHandle; +import android.os.UserManager; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceFragment; import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; @@ -36,9 +37,12 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import com.android.internal.os.BatterySipper; +import com.android.internal.os.BatteryStatsHelper; import com.android.internal.os.PowerProfile; import com.android.settings.HelpUtils; import com.android.settings.R; +import com.android.settings.SettingsActivity; import java.util.List; @@ -59,7 +63,11 @@ public class PowerUsageSummary extends PreferenceFragment { private static final int MENU_STATS_REFRESH = Menu.FIRST + 1; private static final int MENU_HELP = Menu.FIRST + 2; + private UserManager mUm; + private PreferenceGroup mAppListGroup; + private String mBatteryLevel; + private String mBatteryStatus; private Preference mBatteryStatusPref; private int mStatsType = BatteryStats.STATS_SINCE_CHARGED; @@ -74,13 +82,8 @@ public class PowerUsageSummary extends PreferenceFragment { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - if (Intent.ACTION_BATTERY_CHANGED.equals(action)) { - String batteryLevel = com.android.settings.Utils.getBatteryPercentage(intent); - String batteryStatus = com.android.settings.Utils.getBatteryStatus(getResources(), - intent); - String batterySummary = context.getResources().getString( - R.string.power_usage_level_and_status, batteryLevel, batteryStatus); - mBatteryStatusPref.setTitle(batterySummary); + if (Intent.ACTION_BATTERY_CHANGED.equals(action) + && updateBatteryStatus(intent)) { mStatsHelper.clearStats(); refreshStats(); } @@ -90,7 +93,8 @@ public class PowerUsageSummary extends PreferenceFragment { @Override public void onAttach(Activity activity) { super.onAttach(activity); - mStatsHelper = new BatteryStatsHelper(activity, mHandler); + mUm = (UserManager) activity.getSystemService(Context.USER_SERVICE); + mStatsHelper = new BatteryStatsHelper(activity); } @Override @@ -107,15 +111,15 @@ public class PowerUsageSummary extends PreferenceFragment { @Override public void onResume() { super.onResume(); - getActivity().registerReceiver(mBatteryInfoReceiver, - new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + updateBatteryStatus(getActivity().registerReceiver(mBatteryInfoReceiver, + new IntentFilter(Intent.ACTION_BATTERY_CHANGED))); refreshStats(); } @Override public void onPause() { - mStatsHelper.pause(); - mHandler.removeMessages(BatteryStatsHelper.MSG_UPDATE_NAME_ICON); + BatteryEntry.stopRequestQueue(); + mHandler.removeMessages(BatteryEntry.MSG_UPDATE_NAME_ICON); getActivity().unregisterReceiver(mBatteryInfoReceiver); super.onPause(); } @@ -123,7 +127,10 @@ public class PowerUsageSummary extends PreferenceFragment { @Override public void onDestroy() { super.onDestroy(); - mStatsHelper.destroy(); + if (getActivity().isChangingConfigurations()) { + mStatsHelper.storeState(); + BatteryEntry.clearUidCache(); + } } @Override @@ -134,8 +141,8 @@ public class PowerUsageSummary extends PreferenceFragment { byte[] histData = hist.marshall(); Bundle args = new Bundle(); args.putByteArray(BatteryHistoryDetail.EXTRA_STATS, histData); - PreferenceActivity pa = (PreferenceActivity)getActivity(); - pa.startPreferencePanel(BatteryHistoryDetail.class.getName(), args, + SettingsActivity sa = (SettingsActivity) getActivity(); + sa.startPreferencePanel(BatteryHistoryDetail.class.getName(), args, R.string.history_details_title, null, null, 0); return super.onPreferenceTreeClick(preferenceScreen, preference); } @@ -143,8 +150,9 @@ public class PowerUsageSummary extends PreferenceFragment { return false; } PowerGaugePreference pgp = (PowerGaugePreference) preference; - BatterySipper sipper = pgp.getInfo(); - mStatsHelper.startBatteryDetailPage((PreferenceActivity) getActivity(), sipper, true); + BatteryEntry entry = pgp.getInfo(); + PowerUsageDetail.startBatteryDetailPage((SettingsActivity) getActivity(), mStatsHelper, + entry, true); return super.onPreferenceTreeClick(preferenceScreen, preference); } @@ -194,12 +202,32 @@ public class PowerUsageSummary extends PreferenceFragment { mAppListGroup.addPreference(notAvailable); } + private boolean updateBatteryStatus(Intent intent) { + if (intent != null) { + String batteryLevel = com.android.settings.Utils.getBatteryPercentage(intent); + String batteryStatus = com.android.settings.Utils.getBatteryStatus(getResources(), + intent); + if (!batteryLevel.equals(mBatteryLevel) || !batteryStatus.equals(mBatteryStatus)) { + mBatteryLevel = batteryLevel; + mBatteryStatus = batteryStatus; + return true; + } + } + return false; + } + private void refreshStats() { mAppListGroup.removeAll(); mAppListGroup.setOrderingAsAdded(false); mBatteryStatusPref.setOrder(-2); + if (mBatteryLevel != null && mBatteryStatus != null) { + String batterySummary = getActivity().getResources().getString( + R.string.power_usage_level_and_status, mBatteryLevel, mBatteryStatus); + mBatteryStatusPref.setTitle(batterySummary); + } mAppListGroup.addPreference(mBatteryStatusPref); + BatteryHistoryPreference hist = new BatteryHistoryPreference( getActivity(), mStatsHelper.getStats()); hist.setOrder(-1); @@ -210,20 +238,29 @@ public class PowerUsageSummary extends PreferenceFragment { addNotAvailableMessage(); return; } - mStatsHelper.refreshStats(false); + int dischargeAmount = mStatsType == BatteryStats.STATS_SINCE_CHARGED + ? mStatsHelper.getStats().getHighDischargeAmountSinceCharge() + : (mStatsHelper.getStats().getDischargeStartLevel() + - mStatsHelper.getStats().getDischargeCurrentLevel()); + if (dischargeAmount < 0) { + dischargeAmount = 0; + } + mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.myUserId()); List<BatterySipper> usageList = mStatsHelper.getUsageList(); - for (BatterySipper sipper : usageList) { - if (sipper.getSortValue() < MIN_POWER_THRESHOLD) continue; + for (int i=0; i<usageList.size(); i++) { + BatterySipper sipper = usageList.get(i); + if ((sipper.value*60*60) < MIN_POWER_THRESHOLD) continue; final double percentOfTotal = - ((sipper.getSortValue() / mStatsHelper.getTotalPower()) * 100); - if (percentOfTotal < 1) continue; + ((sipper.value / mStatsHelper.getTotalPower()) * dischargeAmount); + if (((int)(percentOfTotal+.5)) < 1) continue; + BatteryEntry entry = new BatteryEntry(getActivity(), mHandler, mUm, sipper); PowerGaugePreference pref = - new PowerGaugePreference(getActivity(), sipper.getIcon(), sipper); + new PowerGaugePreference(getActivity(), entry.getIcon(), entry); final double percentOfMax = - (sipper.getSortValue() * 100) / mStatsHelper.getMaxPower(); + (sipper.value * 100) / mStatsHelper.getMaxPower(); sipper.percent = percentOfTotal; - pref.setTitle(sipper.name); - pref.setOrder(Integer.MAX_VALUE - (int) sipper.getSortValue()); // Invert the order + pref.setTitle(entry.getLabel()); + pref.setOrder(i+1); pref.setPercent(percentOfMax, percentOfTotal); if (sipper.uidObj != null) { pref.setKey(Integer.toString(sipper.uidObj.getUid())); @@ -231,6 +268,8 @@ public class PowerUsageSummary extends PreferenceFragment { mAppListGroup.addPreference(pref); if (mAppListGroup.getPreferenceCount() > (MAX_ITEMS_TO_LIST+1)) break; } + + BatteryEntry.startRequestQueue(); } Handler mHandler = new Handler() { @@ -238,17 +277,17 @@ public class PowerUsageSummary extends PreferenceFragment { @Override public void handleMessage(Message msg) { switch (msg.what) { - case BatteryStatsHelper.MSG_UPDATE_NAME_ICON: - BatterySipper bs = (BatterySipper) msg.obj; + case BatteryEntry.MSG_UPDATE_NAME_ICON: + BatteryEntry entry = (BatteryEntry) msg.obj; PowerGaugePreference pgp = (PowerGaugePreference) findPreference( - Integer.toString(bs.uidObj.getUid())); + Integer.toString(entry.sipper.uidObj.getUid())); if (pgp != null) { - pgp.setIcon(bs.icon); - pgp.setTitle(bs.name); + pgp.setIcon(entry.icon); + pgp.setTitle(entry.name); } break; - case BatteryStatsHelper.MSG_REPORT_FULLY_DRAWN: + case BatteryEntry.MSG_REPORT_FULLY_DRAWN: Activity activity = getActivity(); if (activity != null) { activity.reportFullyDrawn(); diff --git a/src/com/android/settings/indexer/Index.java b/src/com/android/settings/indexer/Index.java new file mode 100644 index 000000000..19b545f9b --- /dev/null +++ b/src/com/android/settings/indexer/Index.java @@ -0,0 +1,524 @@ +/* + * Copyright (C) 2014 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.indexer; + +import android.content.ContentValues; +import android.content.Context; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.database.sqlite.SQLiteDatabase; +import android.os.AsyncTask; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.util.Log; +import android.util.TypedValue; +import android.util.Xml; +import com.android.settings.R; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicBoolean; + +import static com.android.settings.indexer.IndexDatabaseHelper.Tables; +import static com.android.settings.indexer.IndexDatabaseHelper.IndexColumns; + +public class Index { + + private static final String LOG_TAG = "Index"; + + // Those indices should match the indices of SELECT_COLUMNS ! + public static final int COLUMN_INDEX_TITLE = 1; + public static final int COLUMN_INDEX_SUMMARY = 2; + public static final int COLUMN_INDEX_FRAGMENT_NAME = 4; + public static final int COLUMN_INDEX_FRAGMENT_TITLE = 5; + public static final int COLUMN_INDEX_ICON = 7; + + // If you change the order of columns here, you SHOULD change the COLUMN_INDEX_XXX values + private static final String[] SELECT_COLUMNS = new String[] { + IndexColumns.DATA_RANK, + IndexColumns.DATA_TITLE, + IndexColumns.DATA_SUMMARY, + IndexColumns.DATA_KEYWORDS, + IndexColumns.FRAGMENT_NAME, + IndexColumns.FRAGMENT_TITLE, + IndexColumns.INTENT, + IndexColumns.ICON + }; + + private static final String[] MATCH_COLUMNS = { + IndexColumns.DATA_TITLE, + IndexColumns.DATA_TITLE_NORMALIZED, + IndexColumns.DATA_SUMMARY, + IndexColumns.DATA_SUMMARY_NORMALIZED, + IndexColumns.DATA_KEYWORDS + }; + + private static final String EMPTY = ""; + private static final String NON_BREAKING_HYPHEN = "\u2011"; + private static final String HYPHEN = "-"; + + private static Index sInstance; + + private final AtomicBoolean mIsAvailable = new AtomicBoolean(false); + + private final UpdateData mUpdateData = new UpdateData(); + + private final Context mContext; + + /** + * A basic singleton + */ + public static Index getInstance(Context context) { + if (sInstance == null) { + sInstance = new Index(context); + } + return sInstance; + } + + public Index(Context context) { + mContext = context; + } + + public boolean isAvailable() { + return mIsAvailable.get(); + } + + public void addIndexableData(IndexableRef[] array) { + synchronized (mUpdateData) { + final int count = array.length; + for (int n = 0; n < count; n++) { + mUpdateData.dataToAdd.add(array[n]); + } + } + } + + public void deleteIndexableData(String[] array) { + synchronized (mUpdateData) { + final int count = array.length; + for (int n = 0; n < count; n++) { + mUpdateData.dataToDelete.add(array[n]); + } + } + } + + public boolean update() { + synchronized (mUpdateData) { + final UpdateIndexTask task = new UpdateIndexTask(); + task.execute(mUpdateData); + try { + final boolean result = task.get(); + mUpdateData.clear(); + return result; + } catch (InterruptedException e) { + Log.e(LOG_TAG, "Cannot update index: " + e.getMessage()); + return false; + } catch (ExecutionException e) { + Log.e(LOG_TAG, "Cannot update index: " + e.getMessage()); + return false; + } + } + } + + public Cursor search(String query) { + final String sql = buildSQL(query); + Log.d(LOG_TAG, "Query: " + sql); + return getReadableDatabase().rawQuery(sql, null); + } + + private String buildSQL(String query) { + StringBuilder sb = new StringBuilder(); + sb.append(buildSQLForColumn(query, MATCH_COLUMNS)); + sb.append(" ORDER BY "); + sb.append(IndexColumns.DATA_RANK); + return sb.toString(); + } + + private String buildSQLForColumn(String query, String[] columnNames) { + StringBuilder sb = new StringBuilder(); + sb.append("SELECT "); + for (int n = 0; n < SELECT_COLUMNS.length; n++) { + sb.append(SELECT_COLUMNS[n]); + if (n < SELECT_COLUMNS.length - 1) { + sb.append(", "); + } + } + sb.append(" FROM "); + sb.append(Tables.TABLE_PREFS_INDEX); + sb.append(" WHERE "); + sb.append(buildWhereStringForColumns(query, columnNames)); + + return sb.toString(); + } + + private String buildWhereStringForColumns(String query, String[] columnNames) { + final StringBuilder sb = new StringBuilder(Tables.TABLE_PREFS_INDEX); + sb.append(" MATCH "); + DatabaseUtils.appendEscapedSQLString(sb, buildMatchStringForColumns(query, columnNames)); + sb.append(" AND "); + sb.append(IndexColumns.LOCALE); + sb.append(" = "); + DatabaseUtils.appendEscapedSQLString(sb, Locale.getDefault().toString()); + return sb.toString(); + } + + private String buildMatchStringForColumns(String query, String[] columnNames) { + final String value = query + "*"; + StringBuilder sb = new StringBuilder(); + final int count = columnNames.length; + for (int n = 0; n < count; n++) { + sb.append(columnNames[n]); + sb.append(":"); + sb.append(value); + if (n < count - 1) { + sb.append(" OR "); + } + } + return sb.toString(); + } + + private SQLiteDatabase getReadableDatabase() { + return IndexDatabaseHelper.getInstance(mContext).getReadableDatabase(); + } + + private SQLiteDatabase getWritableDatabase() { + return IndexDatabaseHelper.getInstance(mContext).getWritableDatabase(); + } + + /** + * A private class to describe the update data for the Index database + */ + private class UpdateData { + public List<IndexableRef> dataToAdd; + public List<String> dataToDelete; + + public UpdateData() { + dataToAdd = new ArrayList<IndexableRef>(); + dataToDelete = new ArrayList<String>(); + } + + public void clear() { + dataToAdd.clear(); + dataToDelete.clear(); + } + } + + /** + * A private class for updating the Index database + */ + private class UpdateIndexTask extends AsyncTask<UpdateData, Integer, Boolean> { + + @Override + protected void onPreExecute() { + super.onPreExecute(); + mIsAvailable.set(false); + } + + @Override + protected void onPostExecute(Boolean aBoolean) { + super.onPostExecute(aBoolean); + mIsAvailable.set(true); + } + + @Override + protected Boolean doInBackground(UpdateData... params) { + boolean result = false; + + final List<IndexableRef> dataToAdd = params[0].dataToAdd; + final List<String> dataToDelete = params[0].dataToDelete; + final SQLiteDatabase database = getWritableDatabase(); + final String localeStr = Locale.getDefault().toString(); + + try { + database.beginTransaction(); + if (dataToAdd.size() > 0) { + processDataToAdd(database, localeStr, dataToAdd); + } + if (dataToDelete.size() > 0) { + processDataToDelete(database, localeStr, dataToDelete); + } + database.setTransactionSuccessful(); + result = true; + } finally { + database.endTransaction(); + } + return result; + } + + private boolean processDataToDelete(SQLiteDatabase database, String localeStr, + List<String> dataToDelete) { + + boolean result = false; + final long current = System.currentTimeMillis(); + + final int count = dataToDelete.size(); + for (int n = 0; n < count; n++) { + final String data = dataToDelete.get(n); + delete(database, data); + } + + final long now = System.currentTimeMillis(); + Log.d(LOG_TAG, "Deleting data for locale '" + localeStr + "' took " + + (now - current) + " millis"); + return result; + } + + private boolean processDataToAdd(SQLiteDatabase database, String localeStr, + List<IndexableRef> dataToAdd) { + if (isLocaleAlreadyIndexed(database, localeStr)) { + Log.d(LOG_TAG, "Locale '" + localeStr + "' is already indexed"); + return true; + } + + boolean result = false; + final long current = System.currentTimeMillis(); + + final int count = dataToAdd.size(); + for (int n = 0; n < count; n++) { + final IndexableRef ref = dataToAdd.get(n); + indexOneRef(database, localeStr, ref); + } + + final long now = System.currentTimeMillis(); + Log.d(LOG_TAG, "Indexing locale '" + localeStr + "' took " + + (now - current) + " millis"); + return result; + } + + private void indexOneRef(SQLiteDatabase database, String localeStr, IndexableRef ref) { + if (ref.xmlResId > 0) { + indexFromResource(database, localeStr, ref.xmlResId, ref.fragmentName, + ref.iconResId, ref.rank); + } else if (!TextUtils.isEmpty(ref.fragmentName)) { + indexRawData(database, localeStr, ref); + } + } + + private void indexRawData(SQLiteDatabase database, String localeStr, IndexableRef ref) { + try { + final Class<?> clazz = Class.forName(ref.fragmentName); + if (Indexable.class.isAssignableFrom(clazz)) { + final Field f = clazz.getField("INDEX_DATA_PROVIDER"); + final Indexable.IndexDataProvider provider = + (Indexable.IndexDataProvider) f.get(null); + + final List<IndexableData> data = provider.getRawDataToIndex(mContext); + + final int size = data.size(); + for (int i = 0; i < size; i++) { + IndexableData raw = data.get(i); + + // Should be the same locale as the one we are processing + if (!raw.locale.toString().equalsIgnoreCase(localeStr)) { + continue; + } + + inserOneRowWithFilteredData(database, localeStr, + raw.title, + raw.summary, + ref.fragmentName, + raw.fragmentTitle, + ref.iconResId, + ref.rank, + raw.keywords); + } + } + } catch (ClassNotFoundException e) { + Log.e(LOG_TAG, "Cannot find class: " + ref.fragmentName, e); + } catch (NoSuchFieldException e) { + Log.e(LOG_TAG, "Cannot find field 'INDEX_DATA_PROVIDER'", e); + } catch (IllegalAccessException e) { + Log.e(LOG_TAG, "Illegal access to field 'INDEX_DATA_PROVIDER'", e); + } + } + + private boolean isLocaleAlreadyIndexed(SQLiteDatabase database, String locale) { + Cursor cursor = null; + boolean result = false; + final StringBuilder sb = new StringBuilder(IndexColumns.LOCALE); + sb.append(" = "); + DatabaseUtils.appendEscapedSQLString(sb, locale); + try { + // We care only for 1 row + cursor = database.query(Tables.TABLE_PREFS_INDEX, null, + sb.toString(), null, null, null, null, "1"); + final int count = cursor.getCount(); + result = (count >= 1); + } finally { + if (cursor != null) { + cursor.close(); + } + } + return result; + } + + private void indexFromResource(SQLiteDatabase database, String localeStr, int xmlResId, + String fragmentName, int iconResId, int rank) { + XmlResourceParser parser = null; + try { + parser = mContext.getResources().getXml(xmlResId); + + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && type != XmlPullParser.START_TAG) { + // Parse next until start tag is found + } + + String nodeName = parser.getName(); + if (!"PreferenceScreen".equals(nodeName)) { + throw new RuntimeException( + "XML document must start with <PreferenceScreen> tag; found" + + nodeName + " at " + parser.getPositionDescription()); + } + + final int outerDepth = parser.getDepth(); + final AttributeSet attrs = Xml.asAttributeSet(parser); + final String fragmentTitle = getDataTitle(attrs); + + String title = getDataTitle(attrs); + String summary = getDataSummary(attrs); + String keywords = getDataKeywords(attrs); + + // Insert rows for the main PreferenceScreen node. Rewrite the data for removing + // hyphens. + inserOneRowWithFilteredData(database, localeStr, title, summary, fragmentName, + fragmentTitle, iconResId, rank, keywords); + + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + title = getDataTitle(attrs); + summary = getDataSummary(attrs); + keywords = getDataKeywords(attrs); + + // Insert rows for the child nodes of PreferenceScreen + inserOneRowWithFilteredData(database, localeStr, title, summary, fragmentName, + fragmentTitle, iconResId, rank, keywords); + } + + } catch (XmlPullParserException e) { + throw new RuntimeException("Error parsing PreferenceScreen", e); + } catch (IOException e) { + throw new RuntimeException("Error parsing PreferenceScreen", e); + } finally { + if (parser != null) parser.close(); + } + } + + private void inserOneRowWithFilteredData(SQLiteDatabase database, String locale, + String title, String summary, String fragmentName, String fragmentTitle, + int iconResId, int rank, String keywords) { + + String updatedTitle; + if (title != null) { + updatedTitle = title.replaceAll(NON_BREAKING_HYPHEN, HYPHEN); + } + else { + updatedTitle = EMPTY; + } + + String updatedSummary; + if (summary != null) { + updatedSummary = summary.replaceAll(NON_BREAKING_HYPHEN, HYPHEN); + } else { + updatedSummary = EMPTY; + } + + String normalizedTitle = updatedTitle.replaceAll(HYPHEN, EMPTY); + String normalizedSummary = updatedSummary.replaceAll(HYPHEN, EMPTY); + + insertOneRow(database, locale, + updatedTitle, normalizedTitle, updatedSummary, normalizedSummary, + fragmentName, fragmentTitle, iconResId, rank, keywords); + } + + private void insertOneRow(SQLiteDatabase database, String locale, + String updatedTitle, String normalizedTitle, + String updatedSummary, String normalizedSummary, + String fragmentName, String fragmentTitle, + int iconResId, int rank, String keywords) { + + if (TextUtils.isEmpty(updatedTitle)) { + return; + } + ContentValues values = new ContentValues(); + values.put(IndexColumns.LOCALE, locale); + values.put(IndexColumns.DATA_RANK, rank); + values.put(IndexColumns.DATA_TITLE, updatedTitle); + values.put(IndexColumns.DATA_TITLE_NORMALIZED, normalizedTitle); + values.put(IndexColumns.DATA_SUMMARY, updatedSummary); + values.put(IndexColumns.DATA_SUMMARY_NORMALIZED, normalizedSummary); + values.put(IndexColumns.DATA_KEYWORDS, keywords); + values.put(IndexColumns.FRAGMENT_NAME, fragmentName); + values.put(IndexColumns.FRAGMENT_TITLE, fragmentTitle); + values.put(IndexColumns.INTENT, ""); + values.put(IndexColumns.ICON, iconResId); + + database.insertOrThrow(Tables.TABLE_PREFS_INDEX, null, values); + } + + private int delete(SQLiteDatabase database, String title) { + final String whereClause = IndexColumns.DATA_TITLE + "=?"; + final String[] whereArgs = new String[] { title }; + + return database.delete(Tables.TABLE_PREFS_INDEX, whereClause, whereArgs); + } + + private String getDataTitle(AttributeSet attrs) { + return getData(attrs, + com.android.internal.R.styleable.Preference, + com.android.internal.R.styleable.Preference_title); + } + + private String getDataSummary(AttributeSet attrs) { + return getData(attrs, + com.android.internal.R.styleable.Preference, + com.android.internal.R.styleable.Preference_summary); + } + + private String getDataKeywords(AttributeSet attrs) { + return getData(attrs, + R.styleable.Preference, + R.styleable.Preference_keywords); + } + + private String getData(AttributeSet set, int[] attrs, int resId) { + final TypedArray sa = mContext.obtainStyledAttributes(set, attrs); + final TypedValue tv = sa.peekValue(resId); + + CharSequence data = null; + if (tv != null && tv.type == TypedValue.TYPE_STRING) { + if (tv.resourceId != 0) { + data = mContext.getText(tv.resourceId); + } else { + data = tv.string; + } + } + return (data != null) ? data.toString() : null; + } + } +} diff --git a/src/com/android/settings/indexer/IndexDatabaseHelper.java b/src/com/android/settings/indexer/IndexDatabaseHelper.java new file mode 100644 index 000000000..3e6396e88 --- /dev/null +++ b/src/com/android/settings/indexer/IndexDatabaseHelper.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2014 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.indexer; + +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.os.Build; +import android.util.Log; + +public class IndexDatabaseHelper extends SQLiteOpenHelper { + + private static final String TAG = "IndexDatabaseHelper"; + + private static final String DATABASE_NAME = "search_index.db"; + private static final int DATABASE_VERSION = 101; + + public interface Tables { + public static final String TABLE_PREFS_INDEX = "prefs_index"; + public static final String TABLE_META_INDEX = "meta_index"; + } + + public interface IndexColumns { + public static final String LOCALE = "locale"; + public static final String DATA_RANK = "data_rank"; + public static final String DATA_TITLE = "data_title"; + public static final String DATA_TITLE_NORMALIZED = "data_title_normalized"; + public static final String DATA_SUMMARY = "data_summary"; + public static final String DATA_SUMMARY_NORMALIZED = "data_summary_normalized"; + public static final String DATA_KEYWORDS = "data_keywords"; + public static final String FRAGMENT_NAME = "fragment_name"; + public static final String FRAGMENT_TITLE = "fragment_title"; + public static final String INTENT = "intent"; + public static final String ICON = "icon"; + } + + public interface MetaColumns { + public static final String BUILD = "build"; + } + + private static final String CREATE_INDEX_TABLE = + "CREATE VIRTUAL TABLE " + Tables.TABLE_PREFS_INDEX + " USING fts4" + + "(" + + IndexColumns.LOCALE + + ", " + + IndexColumns.DATA_RANK + + ", " + + IndexColumns.DATA_TITLE + + ", " + + IndexColumns.DATA_TITLE_NORMALIZED + + ", " + + IndexColumns.DATA_SUMMARY + + ", " + + IndexColumns.DATA_SUMMARY_NORMALIZED + + ", " + + IndexColumns.DATA_KEYWORDS + + ", " + + IndexColumns.FRAGMENT_NAME + + ", " + + IndexColumns.FRAGMENT_TITLE + + ", " + + IndexColumns.INTENT + + ", " + + IndexColumns.ICON + + ");"; + + private static final String CREATE_META_TABLE = + "CREATE TABLE " + Tables.TABLE_META_INDEX + + "(" + + MetaColumns.BUILD + " VARCHAR(32) NOT NULL" + + ")"; + + private static final String INSERT_BUILD_VERSION = + "INSERT INTO " + Tables.TABLE_META_INDEX + + " VALUES ('" + Build.VERSION.INCREMENTAL + "');"; + + private static final String SELECT_BUILD_VERSION = + "SELECT " + MetaColumns.BUILD + " FROM " + Tables.TABLE_META_INDEX + " LIMIT 1;"; + + private static IndexDatabaseHelper sSingleton; + + public static synchronized IndexDatabaseHelper getInstance(Context context) { + if (sSingleton == null) { + sSingleton = new IndexDatabaseHelper(context); + } + return sSingleton; + } + + public IndexDatabaseHelper(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + bootstrapDB(db); + } + + private void bootstrapDB(SQLiteDatabase db) { + db.execSQL(CREATE_INDEX_TABLE); + db.execSQL(CREATE_META_TABLE); + db.execSQL(INSERT_BUILD_VERSION); + Log.i(TAG, "Bootstrapped database"); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + if (newVersion > 100) { + Log.w(TAG, "Detected schema version 100. " + + "Index needs to be rebuilt for schema version 101"); + // We need to drop the tables and recreate them + dropTables(db); + bootstrapDB(db); + } + } + + private String getBuildVersion(SQLiteDatabase db) { + String version = null; + Cursor cursor = null; + try { + cursor = db.rawQuery(SELECT_BUILD_VERSION, null); + if (cursor.moveToFirst()) { + version = cursor.getString(0); + } + } + catch (Exception e) { + Log.e(TAG, "Cannot get build version from Index metadata"); + } + finally { + if (cursor != null) { + cursor.close(); + } + } + return version; + } + + private void dropTables(SQLiteDatabase db) { + db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_META_INDEX); + db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_PREFS_INDEX); + } + + @Override + public void onOpen(SQLiteDatabase db) { + super.onOpen(db); + + Log.i(TAG, "Using schema version: " + db.getVersion()); + + if (!Build.VERSION.INCREMENTAL.equals(getBuildVersion(db))) { + Log.w(TAG, "Index needs to be rebuilt as build-version is not the same"); + // We need to drop the tables and recreate them + dropTables(db); + bootstrapDB(db); + } else { + Log.i(TAG, "Index is fine"); + } + } +} diff --git a/src/com/android/settings/indexer/Indexable.java b/src/com/android/settings/indexer/Indexable.java new file mode 100644 index 000000000..1b29b75e3 --- /dev/null +++ b/src/com/android/settings/indexer/Indexable.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2014 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.indexer; + +import android.content.Context; + +import java.util.List; + +/** + * Interface for classes whose instances can provide data for indexing. + * + * Classes implementing the Indexable interface must have a static field called + * <code>INDEX_DATA_PROVIDER</code>, which is an object implementing the + * {@link Indexable.IndexDataProvider Indexable.IndexDataProvider} interface. + * + * See {@link IndexableRef} and {@link IndexableData}. + * + */ +public interface Indexable { + + public interface IndexDataProvider { + /** + * Return a list of references for indexing. See {@link IndexableRef} + * + * @param context the context + * @return a list of {@link IndexableRef} references. Can be null. + */ + List<IndexableRef> getRefsToIndex(Context context); + + /** + * Return a list of raw data for indexing. See {@link IndexableData} + * + * @param context the context + * @return a list of {@link IndexableData} references. Can be null. + */ + List<IndexableData> getRawDataToIndex(Context context); + } +} diff --git a/src/com/android/settings/indexer/IndexableData.java b/src/com/android/settings/indexer/IndexableData.java new file mode 100644 index 000000000..ca388dede --- /dev/null +++ b/src/com/android/settings/indexer/IndexableData.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2014 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.indexer; + +import java.util.Locale; + +/** + * Indexable Data. + * + * This is the raw data used by the Indexer and should match its data model. + * + * See {@link Indexable} and {@link IndexableRef}. + */ +public class IndexableData { + + public Locale locale; + + public String title; + public String summary; + public String keywords; + + public String intentAction; + public String intentTargetPackage; + public String intentTargetClass; + + public String fragmentTitle; + + public IndexableData() { + locale = Locale.getDefault(); + } +} diff --git a/src/com/android/settings/indexer/IndexableRef.java b/src/com/android/settings/indexer/IndexableRef.java new file mode 100644 index 000000000..c1ebcca63 --- /dev/null +++ b/src/com/android/settings/indexer/IndexableRef.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2014 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.indexer; + +/** + * Indexable Reference. + * + * This class wraps a set of information representing data that can be indexed for a high + * level (see {@link android.preference.PreferenceScreen}). + * + * rank: is the rank of the data (basically its order in the list of Settings) + * xmlResId: is the resource Id of a PreferenceScreen xml file + * fragmentName: is the fragment class name associated with the data + * iconRedId: is the resource Id of an icon that represents the data + * + * See {@link Indexable} and {@link IndexableData}. + * + */ +public class IndexableRef { + + public int rank; + public int xmlResId; + public String fragmentName; + public int iconResId; + + public IndexableRef(int rank, int dataResId, String name, int iconResId) { + this.rank = rank; + this.xmlResId = dataResId; + this.fragmentName = name; + this.iconResId = iconResId; + } +}
\ No newline at end of file diff --git a/src/com/android/settings/inputmethod/InputMethodAndSubtypeEnablerActivity.java b/src/com/android/settings/inputmethod/InputMethodAndSubtypeEnablerActivity.java index 5693e209e..67e32ab56 100644 --- a/src/com/android/settings/inputmethod/InputMethodAndSubtypeEnablerActivity.java +++ b/src/com/android/settings/inputmethod/InputMethodAndSubtypeEnablerActivity.java @@ -15,13 +15,11 @@ */ package com.android.settings.inputmethod; -import android.app.Fragment; import android.content.Intent; -import android.preference.PreferenceActivity; -import com.android.settings.ChooseLockPassword.ChooseLockPasswordFragment; +import com.android.settings.SettingsActivity; -public class InputMethodAndSubtypeEnablerActivity extends PreferenceActivity { +public class InputMethodAndSubtypeEnablerActivity extends SettingsActivity { @Override public Intent getIntent() { final Intent modIntent = new Intent(super.getIntent()); diff --git a/src/com/android/settings/inputmethod/InputMethodPreference.java b/src/com/android/settings/inputmethod/InputMethodPreference.java index aa6430fc9..1d920de78 100644 --- a/src/com/android/settings/inputmethod/InputMethodPreference.java +++ b/src/com/android/settings/inputmethod/InputMethodPreference.java @@ -18,6 +18,7 @@ package com.android.settings.inputmethod; import com.android.internal.inputmethod.InputMethodUtils; import com.android.settings.R; +import com.android.settings.SettingsActivity; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.Utils; @@ -31,8 +32,6 @@ import android.content.res.Configuration; import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.Preference; -import android.preference.PreferenceActivity; -import android.provider.Settings; import android.text.TextUtils; import android.util.Log; import android.view.View; @@ -115,7 +114,7 @@ public class InputMethodPreference extends CheckBoxPreference { @Override public boolean onLongClick(View arg0) { final Bundle bundle = new Bundle(); - bundle.putString(Settings.EXTRA_INPUT_METHOD_ID, imiId); + bundle.putString(android.provider.Settings.EXTRA_INPUT_METHOD_ID, imiId); startFragment(mFragment, InputMethodAndSubtypeEnabler.class.getName(), 0, bundle); return true; @@ -147,7 +146,7 @@ public class InputMethodPreference extends CheckBoxPreference { @Override public boolean onLongClick(View arg0) { final Bundle bundle = new Bundle(); - bundle.putString(Settings.EXTRA_INPUT_METHOD_ID, imiId); + bundle.putString(android.provider.Settings.EXTRA_INPUT_METHOD_ID, imiId); startFragment(mFragment, InputMethodAndSubtypeEnabler.class.getName(), 0, bundle); return true; @@ -206,13 +205,12 @@ public class InputMethodPreference extends CheckBoxPreference { public static boolean startFragment( Fragment fragment, String fragmentClass, int requestCode, Bundle extras) { - if (fragment.getActivity() instanceof PreferenceActivity) { - PreferenceActivity preferenceActivity = (PreferenceActivity)fragment.getActivity(); - preferenceActivity.startPreferencePanel(fragmentClass, extras, 0, null, fragment, - requestCode); + if (fragment.getActivity() instanceof SettingsActivity) { + SettingsActivity sa = (SettingsActivity) fragment.getActivity(); + sa.startPreferencePanel(fragmentClass, extras, 0, null, fragment, requestCode); return true; } else { - Log.w(TAG, "Parent isn't PreferenceActivity, thus there's no way to launch the " + Log.w(TAG, "Parent isn't Settings, thus there's no way to launch the " + "given Fragment (name: " + fragmentClass + ", requestCode: " + requestCode + ")"); return false; diff --git a/src/com/android/settings/inputmethod/KeyboardLayoutDialogFragment.java b/src/com/android/settings/inputmethod/KeyboardLayoutDialogFragment.java index 451b36ea6..c77b2c9f6 100644 --- a/src/com/android/settings/inputmethod/KeyboardLayoutDialogFragment.java +++ b/src/com/android/settings/inputmethod/KeyboardLayoutDialogFragment.java @@ -17,7 +17,6 @@ package com.android.settings.inputmethod; import com.android.settings.R; -import com.android.settings.Settings.KeyboardLayoutPickerActivity; import android.app.AlertDialog; import android.app.Activity; @@ -29,7 +28,6 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.Loader; -import android.content.res.Resources; import android.hardware.input.InputDeviceIdentifier; import android.hardware.input.InputManager; import android.hardware.input.KeyboardLayout; diff --git a/src/com/android/settings/inputmethod/UserDictionaryAddWordFragment.java b/src/com/android/settings/inputmethod/UserDictionaryAddWordFragment.java index 86c3e79d8..4f231cbbf 100644 --- a/src/com/android/settings/inputmethod/UserDictionaryAddWordFragment.java +++ b/src/com/android/settings/inputmethod/UserDictionaryAddWordFragment.java @@ -18,7 +18,6 @@ package com.android.settings.inputmethod; import android.app.Fragment; import android.os.Bundle; -import android.preference.PreferenceActivity; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -29,6 +28,7 @@ import android.widget.AdapterView; import android.widget.ArrayAdapter; import com.android.settings.R; +import com.android.settings.SettingsActivity; import com.android.settings.inputmethod.UserDictionaryAddWordContents.LocaleRenderer; import java.util.ArrayList; @@ -137,8 +137,8 @@ public class UserDictionaryAddWordFragment extends Fragment final long id) { final LocaleRenderer locale = (LocaleRenderer)parent.getItemAtPosition(pos); if (locale.isMoreLanguages()) { - PreferenceActivity preferenceActivity = (PreferenceActivity)getActivity(); - preferenceActivity.startPreferenceFragment(new UserDictionaryLocalePicker(this), true); + SettingsActivity sa = (SettingsActivity)getActivity(); + sa.startPreferenceFragment(new UserDictionaryLocalePicker(this), true); } else { mContents.updateLocale(locale.getLocaleString()); } diff --git a/src/com/android/settings/location/LocationSettings.java b/src/com/android/settings/location/LocationSettings.java index 06a665056..b159e9913 100644 --- a/src/com/android/settings/location/LocationSettings.java +++ b/src/com/android/settings/location/LocationSettings.java @@ -22,21 +22,19 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.location.LocationManager; import android.location.SettingInjectorService; import android.os.Bundle; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceCategory; import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; -import android.provider.Settings; import android.util.Log; import android.view.Gravity; import android.widget.CompoundButton; import android.widget.Switch; import com.android.settings.R; +import com.android.settings.SettingsActivity; import java.util.Collections; import java.util.Comparator; @@ -63,18 +61,57 @@ public class LocationSettings extends LocationSettingsBase private PreferenceCategory mCategoryRecentLocationRequests; /** Receives UPDATE_INTENT */ private BroadcastReceiver mReceiver; + private SettingsInjector injector; public LocationSettings() { mValidListener = false; } @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + final Activity activity = getActivity(); + + mSwitch = new Switch(activity); + final int padding = activity.getResources().getDimensionPixelSize( + R.dimen.action_bar_switch_padding); + mSwitch.setPaddingRelative(0, 0, padding, 0); + } + + @Override + public void onStart() { + super.onStart(); + + final SettingsActivity activity = (SettingsActivity) getActivity(); + + // Only show the master switch when we're not being used as Setup Wizard. + if (!activity.onIsHidingHeaders()) { + activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, + ActionBar.DISPLAY_SHOW_CUSTOM); + activity.getActionBar().setCustomView(mSwitch, new ActionBar.LayoutParams( + ActionBar.LayoutParams.WRAP_CONTENT, + ActionBar.LayoutParams.WRAP_CONTENT, + Gravity.CENTER_VERTICAL | Gravity.END)); + } + } + + @Override + public void onStop() { + super.onStop(); + final SettingsActivity activity = (SettingsActivity) getActivity(); + if (!activity.onIsHidingHeaders()) { + activity.getActionBar().setDisplayOptions(0, ActionBar.DISPLAY_SHOW_CUSTOM); + activity.getActionBar().setCustomView(null); + } + } + + @Override public void onResume() { super.onResume(); - mSwitch = new Switch(getActivity()); - mSwitch.setOnCheckedChangeListener(this); mValidListener = true; createPreferenceHierarchy(); + mSwitch.setOnCheckedChangeListener(this); } @Override @@ -103,7 +140,7 @@ public class LocationSettings extends LocationSettingsBase } private PreferenceScreen createPreferenceHierarchy() { - final PreferenceActivity activity = (PreferenceActivity) getActivity(); + final SettingsActivity activity = (SettingsActivity) getActivity(); PreferenceScreen root = getPreferenceScreen(); if (root != null) { root.removeAll(); @@ -141,22 +178,6 @@ public class LocationSettings extends LocationSettingsBase addLocationServices(activity, root); - // Only show the master switch when we're not in multi-pane mode, and not being used as - // Setup Wizard. - if (activity.onIsHidingHeaders() || !activity.onIsMultiPane()) { - final int padding = activity.getResources().getDimensionPixelSize( - R.dimen.action_bar_switch_padding); - mSwitch.setPaddingRelative(0, 0, padding, 0); - activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, - ActionBar.DISPLAY_SHOW_CUSTOM); - activity.getActionBar().setCustomView(mSwitch, new ActionBar.LayoutParams( - ActionBar.LayoutParams.WRAP_CONTENT, - ActionBar.LayoutParams.WRAP_CONTENT, - Gravity.CENTER_VERTICAL | Gravity.END)); - } - - setHasOptionsMenu(true); - refreshLocationMode(); return root; } @@ -166,15 +187,12 @@ public class LocationSettings extends LocationSettingsBase * category if there are no injected settings. * * Reloads the settings whenever receives - * {@link SettingInjectorService#ACTION_INJECTED_SETTING_CHANGED}. As a safety measure, - * also reloads on {@link LocationManager#MODE_CHANGED_ACTION} to ensure the settings are - * up-to-date after mode changes even if an affected app doesn't send the setting changed - * broadcast. + * {@link SettingInjectorService#ACTION_INJECTED_SETTING_CHANGED}. */ private void addLocationServices(Context context, PreferenceScreen root) { PreferenceCategory categoryLocationServices = (PreferenceCategory) root.findPreference(KEY_LOCATION_SERVICES); - final SettingsInjector injector = new SettingsInjector(context); + injector = new SettingsInjector(context); List<Preference> locationServices = injector.getInjectedSettings(); mReceiver = new BroadcastReceiver() { @@ -189,7 +207,6 @@ public class LocationSettings extends LocationSettingsBase IntentFilter filter = new IntentFilter(); filter.addAction(SettingInjectorService.ACTION_INJECTED_SETTING_CHANGED); - filter.addAction(LocationManager.MODE_CHANGED_ACTION); context.registerReceiver(mReceiver, filter); if (locationServices.size() > 0) { @@ -208,16 +225,16 @@ public class LocationSettings extends LocationSettingsBase @Override public void onModeChanged(int mode, boolean restricted) { switch (mode) { - case Settings.Secure.LOCATION_MODE_OFF: + case android.provider.Settings.Secure.LOCATION_MODE_OFF: mLocationMode.setSummary(R.string.location_mode_location_off_title); break; - case Settings.Secure.LOCATION_MODE_SENSORS_ONLY: + case android.provider.Settings.Secure.LOCATION_MODE_SENSORS_ONLY: mLocationMode.setSummary(R.string.location_mode_sensors_only_title); break; - case Settings.Secure.LOCATION_MODE_BATTERY_SAVING: + case android.provider.Settings.Secure.LOCATION_MODE_BATTERY_SAVING: mLocationMode.setSummary(R.string.location_mode_battery_saving_title); break; - case Settings.Secure.LOCATION_MODE_HIGH_ACCURACY: + case android.provider.Settings.Secure.LOCATION_MODE_HIGH_ACCURACY: mLocationMode.setSummary(R.string.location_mode_high_accuracy_title); break; default: @@ -227,7 +244,7 @@ public class LocationSettings extends LocationSettingsBase // Restricted user can't change the location mode, so disable the master switch. But in some // corner cases, the location might still be enabled. In such case the master switch should // be disabled but checked. - boolean enabled = (mode != Settings.Secure.LOCATION_MODE_OFF); + boolean enabled = (mode != android.provider.Settings.Secure.LOCATION_MODE_OFF); mSwitch.setEnabled(!restricted); mLocationMode.setEnabled(enabled && !restricted); mCategoryRecentLocationRequests.setEnabled(enabled); @@ -242,6 +259,9 @@ public class LocationSettings extends LocationSettingsBase mSwitch.setOnCheckedChangeListener(this); } } + // As a safety measure, also reloads on location mode change to ensure the settings are + // up-to-date even if an affected app doesn't send the setting changed broadcast. + injector.reloadStatusMessages(); } /** @@ -250,9 +270,9 @@ public class LocationSettings extends LocationSettingsBase @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { - setLocationMode(Settings.Secure.LOCATION_MODE_HIGH_ACCURACY); + setLocationMode(android.provider.Settings.Secure.LOCATION_MODE_HIGH_ACCURACY); } else { - setLocationMode(Settings.Secure.LOCATION_MODE_OFF); + setLocationMode(android.provider.Settings.Secure.LOCATION_MODE_OFF); } } } diff --git a/src/com/android/settings/location/LocationSettingsBase.java b/src/com/android/settings/location/LocationSettingsBase.java index 86c2ee5e8..69fbd5c85 100644 --- a/src/com/android/settings/location/LocationSettingsBase.java +++ b/src/com/android/settings/location/LocationSettingsBase.java @@ -16,12 +16,11 @@ package com.android.settings.location; -import android.app.LoaderManager.LoaderCallbacks; +import android.content.BroadcastReceiver; import android.content.Context; -import android.content.CursorLoader; import android.content.Intent; -import android.content.Loader; -import android.database.Cursor; +import android.content.IntentFilter; +import android.location.LocationManager; import android.os.Bundle; import android.os.UserManager; import android.provider.Settings; @@ -33,8 +32,7 @@ import com.android.settings.SettingsPreferenceFragment; * A base class that listens to location settings change and modifies location * settings. */ -public abstract class LocationSettingsBase extends SettingsPreferenceFragment - implements LoaderCallbacks<Cursor> { +public abstract class LocationSettingsBase extends SettingsPreferenceFragment { private static final String TAG = "LocationSettingsBase"; /** Broadcast intent action when the location mode is about to change. */ private static final String MODE_CHANGING_ACTION = @@ -42,8 +40,8 @@ public abstract class LocationSettingsBase extends SettingsPreferenceFragment private static final String CURRENT_MODE_KEY = "CURRENT_MODE"; private static final String NEW_MODE_KEY = "NEW_MODE"; - private static final int LOADER_ID_LOCATION_MODE = 1; private int mCurrentMode; + private BroadcastReceiver mReceiver; /** * Whether the fragment is actively running. @@ -53,17 +51,33 @@ public abstract class LocationSettingsBase extends SettingsPreferenceFragment @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); - getLoaderManager().initLoader(LOADER_ID_LOCATION_MODE, null, this); + mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "Received location mode change intent: " + intent); + } + refreshLocationMode(); + } + }; } @Override public void onResume() { super.onResume(); mActive = true; + IntentFilter filter = new IntentFilter(); + filter.addAction(LocationManager.MODE_CHANGED_ACTION); + getActivity().registerReceiver(mReceiver, filter); } @Override public void onPause() { + try { + getActivity().unregisterReceiver(mReceiver); + } catch (RuntimeException e) { + // Ignore exceptions caused by race condition + } super.onPause(); mActive = false; } @@ -103,29 +117,10 @@ public abstract class LocationSettingsBase extends SettingsPreferenceFragment int mode = Settings.Secure.getInt(getContentResolver(), Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF); mCurrentMode = mode; + if (Log.isLoggable(TAG, Log.INFO)) { + Log.i(TAG, "Location mode has been changed"); + } onModeChanged(mode, isRestricted()); } } - - @Override - public Loader<Cursor> onCreateLoader(int id, Bundle args) { - switch (id) { - case LOADER_ID_LOCATION_MODE: - return new CursorLoader(getActivity(), Settings.Secure.CONTENT_URI, null, - "(" + Settings.System.NAME + "=?)", - new String[] { Settings.Secure.LOCATION_MODE }, null); - default: - return null; - } - } - - @Override - public void onLoadFinished(Loader<Cursor> loader, Cursor data) { - refreshLocationMode(); - } - - @Override - public void onLoaderReset(Loader<Cursor> loader) { - // Nothing to do here. - } } diff --git a/src/com/android/settings/location/RecentLocationApps.java b/src/com/android/settings/location/RecentLocationApps.java index 57084348c..164f4e71e 100644 --- a/src/com/android/settings/location/RecentLocationApps.java +++ b/src/com/android/settings/location/RecentLocationApps.java @@ -26,10 +26,10 @@ import android.os.Bundle; import android.os.Process; import android.os.UserHandle; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.util.Log; import com.android.settings.R; +import com.android.settings.SettingsActivity; import com.android.settings.applications.InstalledAppDetails; import java.util.ArrayList; @@ -44,10 +44,10 @@ public class RecentLocationApps { private static final int RECENT_TIME_INTERVAL_MILLIS = 15 * 60 * 1000; - private final PreferenceActivity mActivity; + private final SettingsActivity mActivity; private final PackageManager mPackageManager; - public RecentLocationApps(PreferenceActivity activity) { + public RecentLocationApps(SettingsActivity activity) { mActivity = activity; mPackageManager = activity.getPackageManager(); } diff --git a/src/com/android/settings/location/SettingsInjector.java b/src/com/android/settings/location/SettingsInjector.java index b9190802e..76b3103db 100644 --- a/src/com/android/settings/location/SettingsInjector.java +++ b/src/com/android/settings/location/SettingsInjector.java @@ -244,12 +244,12 @@ class SettingsInjector { } /** - * Adds an injected setting to the root with status "Loading...". + * Adds an injected setting to the root. */ private Preference addServiceSetting(List<Preference> prefs, InjectedSetting info) { Preference pref = new Preference(mContext); pref.setTitle(info.title); - pref.setSummary(R.string.location_loading_injected_setting); + pref.setSummary(null); PackageManager pm = mContext.getPackageManager(); Drawable icon = pm.getDrawable(info.packageName, info.iconId, null); pref.setIcon(icon); @@ -425,12 +425,11 @@ class SettingsInjector { @Override public void handleMessage(Message msg) { Bundle bundle = msg.getData(); - String summary = bundle.getString(SettingInjectorService.SUMMARY_KEY); boolean enabled = bundle.getBoolean(SettingInjectorService.ENABLED_KEY, true); if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, setting + ": received " + msg + ", bundle: " + bundle); } - preference.setSummary(summary); + preference.setSummary(null); preference.setEnabled(enabled); mHandler.sendMessage( mHandler.obtainMessage(WHAT_RECEIVED_STATUS, Setting.this)); diff --git a/src/com/android/settings/nfc/AndroidBeam.java b/src/com/android/settings/nfc/AndroidBeam.java index 158ca78af..b3cd9c24e 100644 --- a/src/com/android/settings/nfc/AndroidBeam.java +++ b/src/com/android/settings/nfc/AndroidBeam.java @@ -21,16 +21,14 @@ import android.app.Activity; import android.app.Fragment; import android.nfc.NfcAdapter; import android.os.Bundle; -import android.os.Handler; -import android.preference.PreferenceActivity; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.CompoundButton; -import android.widget.ImageView; import android.widget.Switch; import com.android.settings.R; +import com.android.settings.SettingsActivity; public class AndroidBeam extends Fragment implements CompoundButton.OnCheckedChangeListener { @@ -46,7 +44,7 @@ public class AndroidBeam extends Fragment mActionBarSwitch = new Switch(activity); - if (activity instanceof PreferenceActivity) { + if (activity instanceof SettingsActivity) { final int padding = activity.getResources().getDimensionPixelSize( R.dimen.action_bar_switch_padding); mActionBarSwitch.setPaddingRelative(0, 0, padding, 0); diff --git a/src/com/android/settings/print/PrintJobSettingsFragment.java b/src/com/android/settings/print/PrintJobSettingsFragment.java index f420a82a2..cbe70ae6f 100644 --- a/src/com/android/settings/print/PrintJobSettingsFragment.java +++ b/src/com/android/settings/print/PrintJobSettingsFragment.java @@ -80,7 +80,7 @@ public class PrintJobSettingsFragment extends SettingsPreferenceFragment { Context.PRINT_SERVICE)).getGlobalPrintManagerForUser( ActivityManager.getCurrentUser()); - getActivity().setTitle(R.string.print_print_job); + getActivity().getActionBar().setTitle(R.string.print_print_job); processArguments(); diff --git a/src/com/android/settings/print/PrintServiceSettingsFragment.java b/src/com/android/settings/print/PrintServiceSettingsFragment.java index 326dd78c3..d5bfce20e 100644 --- a/src/com/android/settings/print/PrintServiceSettingsFragment.java +++ b/src/com/android/settings/print/PrintServiceSettingsFragment.java @@ -38,13 +38,11 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.Handler; -import android.preference.PreferenceActivity; import android.print.PrintManager; import android.print.PrinterDiscoverySession; import android.print.PrinterDiscoverySession.OnPrintersChangeListener; import android.print.PrinterId; import android.print.PrinterInfo; -import android.provider.Settings; import android.text.TextUtils; import android.util.Log; import android.view.Gravity; @@ -56,7 +54,6 @@ import android.view.ViewGroup; import android.view.accessibility.AccessibilityManager; import android.widget.BaseAdapter; import android.widget.CompoundButton; -import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.Filter; import android.widget.Filterable; import android.widget.ImageView; @@ -65,6 +62,7 @@ import android.widget.SearchView; import android.widget.TextView; import com.android.settings.R; +import com.android.settings.SettingsActivity; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.print.PrintSettingsFragment.ToggleSwitch; import com.android.settings.print.PrintSettingsFragment.ToggleSwitch.OnBeforeCheckedChangeListener; @@ -73,6 +71,8 @@ import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; + +import android.widget.CompoundButton.OnCheckedChangeListener; /** * Fragment with print service settings. */ @@ -130,12 +130,14 @@ public class PrintServiceSettingsFragment extends SettingsPreferenceFragment private PrintersAdapter mPrintersAdapter; + // TODO: Showing sub-sub fragment does not handle the activity title + // so we do it but this is wrong. Do a real fix when there is time. + private CharSequence mOldActivityTitle; + private int mLastUnfilteredItemCount; private boolean mServiceEnabled; - private AnnounceFilterResult mAnnounceFilterResult; - @Override public void onResume() { super.onResume(); @@ -147,9 +149,6 @@ public class PrintServiceSettingsFragment extends SettingsPreferenceFragment @Override public void onPause() { mSettingsContentObserver.unregister(getContentResolver()); - if (mAnnounceFilterResult != null) { - mAnnounceFilterResult.remove(); - } super.onPause(); } @@ -163,6 +162,9 @@ public class PrintServiceSettingsFragment extends SettingsPreferenceFragment @Override public void onDestroyView() { getActivity().getActionBar().setCustomView(null); + if (mOldActivityTitle != null) { + getActivity().getActionBar().setTitle(mOldActivityTitle); + } mToggleSwitch.setOnBeforeCheckedChangeListener(null); super.onDestroyView(); } @@ -233,7 +235,8 @@ public class PrintServiceSettingsFragment extends SettingsPreferenceFragment if (emptyView == null) { emptyView = getActivity().getLayoutInflater().inflate( R.layout.empty_print_state, contentRoot, false); - emptyView.setContentDescription(getString(R.string.print_service_disabled)); + ImageView iconView = (ImageView) emptyView.findViewById(R.id.icon); + iconView.setContentDescription(getString(R.string.print_service_disabled)); TextView textView = (TextView) emptyView.findViewById(R.id.message); textView.setText(R.string.print_service_disabled); contentRoot.addView(emptyView); @@ -259,7 +262,8 @@ public class PrintServiceSettingsFragment extends SettingsPreferenceFragment if (emptyView == null) { emptyView = getActivity().getLayoutInflater().inflate( R.layout.empty_print_state, contentRoot, false); - emptyView.setContentDescription(getString(R.string.print_no_printers_found)); + ImageView iconView = (ImageView) emptyView.findViewById(R.id.icon); + iconView.setContentDescription(getString(R.string.print_no_printers_found)); TextView textView = (TextView) emptyView.findViewById(R.id.message); textView.setText(R.string.print_no_printers_found); contentRoot.addView(emptyView); @@ -324,16 +328,6 @@ public class PrintServiceSettingsFragment extends SettingsPreferenceFragment final boolean enabled = arguments.getBoolean(PrintSettingsFragment.EXTRA_CHECKED); mToggleSwitch.setCheckedInternal(enabled); - // Title. - PreferenceActivity activity = (PreferenceActivity) getActivity(); - if (!activity.onIsMultiPane() || activity.onIsHidingHeaders()) { - // PreferenceActivity allows passing as an extra only title by its - // resource id but we do not have the resource id for the print - // service label. Therefore, we do it ourselves. - String title = arguments.getString(PrintSettingsFragment.EXTRA_TITLE); - getActivity().setTitle(title); - } - // Settings title and intent. String settingsTitle = arguments.getString(PrintSettingsFragment.EXTRA_SETTINGS_TITLE); String settingsComponentName = arguments.getString( @@ -467,8 +461,8 @@ public class PrintServiceSettingsFragment extends SettingsPreferenceFragment } public void register(ContentResolver contentResolver) { - contentResolver.registerContentObserver(Settings.Secure.getUriFor( - Settings.Secure.ENABLED_PRINT_SERVICES), false, this); + contentResolver.registerContentObserver(android.provider.Settings.Secure.getUriFor( + android.provider.Settings.Secure.ENABLED_PRINT_SERVICES), false, this); } public void unregister(ContentResolver contentResolver) { @@ -479,39 +473,6 @@ public class PrintServiceSettingsFragment extends SettingsPreferenceFragment public abstract void onChange(boolean selfChange, Uri uri); } - private final class AnnounceFilterResult implements Runnable { - private static final int SEARCH_RESULT_ANNOUNCEMENT_DELAY = 1000; // 1 sec - - public void post() { - remove(); - getListView().postDelayed(this, SEARCH_RESULT_ANNOUNCEMENT_DELAY); - } - - public void remove() { - getListView().removeCallbacks(this); - } - - @Override - public void run() { - final int count = getListView().getAdapter().getCount(); - final String text; - if (count <= 0) { - text = getString(R.string.print_no_printers_found); - } else { - text = getActivity().getResources().getQuantityString( - R.plurals.print_search_result_count_utterance, count, count); - } - getListView().announceForAccessibility(text); - } - } - - private void announceSearchResult() { - if (mAnnounceFilterResult == null) { - mAnnounceFilterResult = new AnnounceFilterResult(); - } - mAnnounceFilterResult.post(); - } - private final class PrintersAdapter extends BaseAdapter implements LoaderManager.LoaderCallbacks<List<PrinterInfo>>, Filterable { private final Object mLock = new Object(); @@ -563,9 +524,7 @@ public class PrintServiceSettingsFragment extends SettingsPreferenceFragment @Override @SuppressWarnings("unchecked") protected void publishResults(CharSequence constraint, FilterResults results) { - final boolean resultCountChanged; synchronized (mLock) { - final int oldPrinterCount = mFilteredPrinters.size(); mLastSearchString = constraint; mFilteredPrinters.clear(); if (results == null) { @@ -574,10 +533,6 @@ public class PrintServiceSettingsFragment extends SettingsPreferenceFragment List<PrinterInfo> printers = (List<PrinterInfo>) results.values; mFilteredPrinters.addAll(printers); } - resultCountChanged = (oldPrinterCount != mFilteredPrinters.size()); - } - if (resultCountChanged) { - announceSearchResult(); } notifyDataSetChanged(); } diff --git a/src/com/android/settings/quicklaunch/QuickLaunchSettings.java b/src/com/android/settings/quicklaunch/QuickLaunchSettings.java index 5654323c0..0081a1ed3 100644 --- a/src/com/android/settings/quicklaunch/QuickLaunchSettings.java +++ b/src/com/android/settings/quicklaunch/QuickLaunchSettings.java @@ -16,6 +16,7 @@ package com.android.settings.quicklaunch; +import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; @@ -27,7 +28,6 @@ import android.database.Cursor; import android.os.Bundle; import android.os.Handler; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; import android.provider.Settings.Bookmarks; @@ -40,6 +40,7 @@ import android.view.View; import android.widget.AdapterView; import com.android.settings.R; +import com.android.settings.SettingsPreferenceFragment; import java.net.URISyntaxException; @@ -49,7 +50,7 @@ import java.net.URISyntaxException; * Shows a list of possible shortcuts, the current application each is bound to, * and allows choosing a new bookmark for a shortcut. */ -public class QuickLaunchSettings extends PreferenceActivity implements +public class QuickLaunchSettings extends SettingsPreferenceFragment implements AdapterView.OnItemLongClickListener, DialogInterface.OnClickListener { private static final String TAG = "QuickLaunchSettings"; @@ -91,7 +92,7 @@ public class QuickLaunchSettings extends PreferenceActivity implements private static final String CLEAR_DIALOG_SHORTCUT = "CLEAR_DIALOG_SHORTCUT"; @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.quick_launch_settings); @@ -100,27 +101,35 @@ public class QuickLaunchSettings extends PreferenceActivity implements mShortcutToPreference = new SparseArray<ShortcutPreference>(); mBookmarksObserver = new BookmarksObserver(mUiHandler); initShortcutPreferences(); - mBookmarksCursor = managedQuery(Bookmarks.CONTENT_URI, sProjection, null, null); + mBookmarksCursor = getActivity().getContentResolver().query(Bookmarks.CONTENT_URI, + sProjection, null, null, null); getListView().setOnItemLongClickListener(this); } @Override - protected void onResume() { + public void onResume() { super.onResume(); + mBookmarksCursor = getActivity().getContentResolver().query(Bookmarks.CONTENT_URI, + sProjection, null, null, null); getContentResolver().registerContentObserver(Bookmarks.CONTENT_URI, true, mBookmarksObserver); refreshShortcuts(); } @Override - protected void onPause() { + public void onPause() { super.onPause(); getContentResolver().unregisterContentObserver(mBookmarksObserver); } @Override - protected void onRestoreInstanceState(Bundle state) { - super.onRestoreInstanceState(state); + public void onStop() { + mBookmarksCursor.close(); + } + + @Override + public void onActivityCreated(Bundle state) { + super.onActivityCreated(state); // Restore the clear dialog's info mClearDialogBookmarkTitle = state.getString(CLEAR_DIALOG_BOOKMARK_TITLE); @@ -128,7 +137,7 @@ public class QuickLaunchSettings extends PreferenceActivity implements } @Override - protected void onSaveInstanceState(Bundle outState) { + public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); // Save the clear dialog's info @@ -137,12 +146,12 @@ public class QuickLaunchSettings extends PreferenceActivity implements } @Override - protected Dialog onCreateDialog(int id) { + public Dialog onCreateDialog(int id) { switch (id) { case DIALOG_CLEAR_SHORTCUT: { // Create the dialog for clearing a shortcut - return new AlertDialog.Builder(this) + return new AlertDialog.Builder(getActivity()) .setTitle(getString(R.string.quick_launch_clear_dialog_title)) .setIconAttribute(android.R.attr.alertDialogIcon) .setMessage(getString(R.string.quick_launch_clear_dialog_message, @@ -156,18 +165,6 @@ public class QuickLaunchSettings extends PreferenceActivity implements return super.onCreateDialog(id); } - @Override - protected void onPrepareDialog(int id, Dialog dialog) { - switch (id) { - - case DIALOG_CLEAR_SHORTCUT: { - AlertDialog alertDialog = (AlertDialog) dialog; - alertDialog.setMessage(getString(R.string.quick_launch_clear_dialog_message, - mClearDialogShortcut, mClearDialogBookmarkTitle)); - } - } - } - private void showClearDialog(ShortcutPreference pref) { if (!pref.hasBookmark()) return; @@ -197,7 +194,7 @@ public class QuickLaunchSettings extends PreferenceActivity implements // Open the screen to pick a bookmark for this shortcut ShortcutPreference pref = (ShortcutPreference) preference; - Intent intent = new Intent(this, BookmarkPicker.class); + Intent intent = new Intent(getActivity(), BookmarkPicker.class); intent.putExtra(BookmarkPicker.EXTRA_SHORTCUT, pref.getShortcut()); startActivityForResult(intent, REQUEST_PICK_BOOKMARK); @@ -214,8 +211,8 @@ public class QuickLaunchSettings extends PreferenceActivity implements } @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (resultCode != RESULT_OK) { + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode != Activity.RESULT_OK) { return; } @@ -253,7 +250,7 @@ public class QuickLaunchSettings extends PreferenceActivity implements } private ShortcutPreference createPreference(char shortcut) { - ShortcutPreference pref = new ShortcutPreference(QuickLaunchSettings.this, shortcut); + ShortcutPreference pref = new ShortcutPreference(getActivity(), shortcut); mShortcutGroup.addPreference(pref); mShortcutToPreference.put(shortcut, pref); return pref; @@ -303,7 +300,7 @@ public class QuickLaunchSettings extends PreferenceActivity implements if (shortcut == 0) continue; ShortcutPreference pref = getOrCreatePreference(shortcut); - CharSequence title = Bookmarks.getTitle(this, c); + CharSequence title = Bookmarks.getTitle(getActivity(), c); /* * The title retrieved from Bookmarks.getTitle() will be in diff --git a/src/com/android/settings/tts/TextToSpeechSettings.java b/src/com/android/settings/tts/TextToSpeechSettings.java index 3a3aec461..1d4fe7127 100644 --- a/src/com/android/settings/tts/TextToSpeechSettings.java +++ b/src/com/android/settings/tts/TextToSpeechSettings.java @@ -20,6 +20,7 @@ import static android.provider.Settings.Secure.TTS_DEFAULT_RATE; import static android.provider.Settings.Secure.TTS_DEFAULT_SYNTH; import com.android.settings.R; +import com.android.settings.SettingsActivity; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.tts.TtsEnginePreference.RadioButtonGroupState; @@ -30,9 +31,7 @@ import android.content.Intent; import android.os.Bundle; import android.preference.ListPreference; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceCategory; -import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.speech.tts.TextToSpeech; import android.speech.tts.UtteranceProgressListener; @@ -213,7 +212,7 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment implements // Set up the default rate. try { - mDefaultRate = Settings.Secure.getInt(resolver, TTS_DEFAULT_RATE); + mDefaultRate = android.provider.Settings.Secure.getInt(resolver, TTS_DEFAULT_RATE); } catch (SettingNotFoundException e) { // Default rate setting not found, initialize it mDefaultRate = TextToSpeech.Engine.DEFAULT_RATE; @@ -223,12 +222,12 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment implements mCurrentEngine = mTts.getCurrentEngine(); - PreferenceActivity preferenceActivity = null; - if (getActivity() instanceof PreferenceActivity) { - preferenceActivity = (PreferenceActivity) getActivity(); + SettingsActivity activity = null; + if (getActivity() instanceof SettingsActivity) { + activity = (SettingsActivity) getActivity(); } else { throw new IllegalStateException("TextToSpeechSettings used outside a " + - "PreferenceActivity"); + "Settings"); } mEnginePreferenceCategory.removeAll(); @@ -236,7 +235,7 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment implements List<EngineInfo> engines = mEnginesHelper.getEngines(); for (EngineInfo engine : engines) { TtsEnginePreference enginePref = new TtsEnginePreference(getActivity(), engine, - this, preferenceActivity); + this, activity); mEnginePreferenceCategory.addPreference(enginePref); } @@ -434,7 +433,8 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment implements // Default rate mDefaultRate = Integer.parseInt((String) objValue); try { - Settings.Secure.putInt(getContentResolver(), TTS_DEFAULT_RATE, mDefaultRate); + android.provider.Settings.Secure.putInt(getContentResolver(), + TTS_DEFAULT_RATE, mDefaultRate); if (mTts != null) { mTts.setSpeechRate(mDefaultRate / 100.0f); } @@ -577,7 +577,7 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment implements return; } - Settings.Secure.putString(getContentResolver(), TTS_DEFAULT_SYNTH, engine); + android.provider.Settings.Secure.putString(getContentResolver(), TTS_DEFAULT_SYNTH, engine); mAvailableStrLocals = data.getStringArrayListExtra( TextToSpeech.Engine.EXTRA_AVAILABLE_VOICES); diff --git a/src/com/android/settings/tts/TtsEnginePreference.java b/src/com/android/settings/tts/TtsEnginePreference.java index 486fdf8f1..7e50c4739 100644 --- a/src/com/android/settings/tts/TtsEnginePreference.java +++ b/src/com/android/settings/tts/TtsEnginePreference.java @@ -22,7 +22,6 @@ import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.speech.tts.TextToSpeech.EngineInfo; import android.util.Log; import android.view.View; @@ -33,6 +32,7 @@ import android.widget.RadioButton; import com.android.settings.R; +import com.android.settings.SettingsActivity; import com.android.settings.Utils; @@ -63,7 +63,7 @@ public class TtsEnginePreference extends Preference { * The preference activity that owns this preference. Required * for instantiating the engine specific settings screen. */ - private final PreferenceActivity mPreferenceActivity; + private final SettingsActivity mSettingsActivity; /** * The engine information for the engine this preference represents. @@ -95,12 +95,12 @@ public class TtsEnginePreference extends Preference { }; public TtsEnginePreference(Context context, EngineInfo info, RadioButtonGroupState state, - PreferenceActivity prefActivity) { + SettingsActivity prefActivity) { super(context); setLayoutResource(R.layout.preference_tts_engine); mSharedState = state; - mPreferenceActivity = prefActivity; + mSettingsActivity = prefActivity; mEngineInfo = info; mPreventRadioButtonCallbacks = false; @@ -156,10 +156,10 @@ public class TtsEnginePreference extends Preference { } // Note that we use this instead of the (easier to use) - // PreferenceActivity.startPreferenceFragment because the + // SettingsActivity.startPreferenceFragment because the // title will not be updated correctly in the fragment // breadcrumb since it isn't inflated from the XML layout. - mPreferenceActivity.startPreferencePanel( + mSettingsActivity.startPreferencePanel( TtsEngineSettingsFragment.class.getName(), args, 0, mEngineInfo.label, null, 0); } diff --git a/src/com/android/settings/tts/TtsEngineSettingsFragment.java b/src/com/android/settings/tts/TtsEngineSettingsFragment.java index bb5ac7ae4..25ec1fdca 100644 --- a/src/com/android/settings/tts/TtsEngineSettingsFragment.java +++ b/src/com/android/settings/tts/TtsEngineSettingsFragment.java @@ -158,12 +158,22 @@ public class TtsEngineSettingsFragment extends SettingsPreferenceFragment implem @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == VOICE_DATA_INTEGRITY_CHECK) { - mVoiceDataDetails = data; - updateVoiceDetails(); + if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) { + updateVoiceDetails(data); + } else { + Log.e(TAG, "CheckVoiceData activity failed"); + } } } - private void updateVoiceDetails() { + private void updateVoiceDetails(Intent data) { + if (data == null){ + Log.e(TAG, "Engine failed voice data integrity check (null return)" + + mTts.getCurrentEngine()); + return; + } + mVoiceDataDetails = data; + if (DBG) Log.d(TAG, "Parsing voice data details, data: " + mVoiceDataDetails.toUri(0)); final ArrayList<String> available = mVoiceDataDetails.getStringArrayListExtra( diff --git a/src/com/android/settings/users/RestrictionUtils.java b/src/com/android/settings/users/RestrictionUtils.java index 3ee6d510f..e8d46e9f6 100644 --- a/src/com/android/settings/users/RestrictionUtils.java +++ b/src/com/android/settings/users/RestrictionUtils.java @@ -84,8 +84,8 @@ public class RestrictionUtils { userRestrictions.putBoolean(entry.getKey(), !entry.getSelectedState()); if (entry.getKey().equals(UserManager.DISALLOW_SHARE_LOCATION) && !entry.getSelectedState()) { - Secure.putStringForUser(context.getContentResolver(), - Secure.LOCATION_PROVIDERS_ALLOWED, "", user.getIdentifier()); + Secure.putIntForUser(context.getContentResolver(), + Secure.LOCATION_MODE, Secure.LOCATION_MODE_OFF, user.getIdentifier()); } } um.setUserRestrictions(userRestrictions, user); diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java index bbae37d03..1a60e782b 100644 --- a/src/com/android/settings/users/UserSettings.java +++ b/src/com/android/settings/users/UserSettings.java @@ -47,7 +47,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.preference.Preference; import android.preference.Preference.OnPreferenceClickListener; -import android.preference.PreferenceActivity; import android.preference.PreferenceGroup; import android.provider.ContactsContract; import android.provider.ContactsContract.Contacts; @@ -67,6 +66,7 @@ import com.android.settings.OwnerInfoSettings; import com.android.settings.R; import com.android.settings.RestrictedSettingsFragment; import com.android.settings.SelectableEditTextPreference; +import com.android.settings.SettingsActivity; import com.android.settings.Utils; public class UserSettings extends RestrictedSettingsFragment @@ -345,8 +345,8 @@ public class UserSettings extends RestrictedSettingsFragment UserHandle user = new UserHandle(userId); mUserManager.setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user); mUserManager.setUserRestriction(UserManager.DISALLOW_SHARE_LOCATION, true, user); - Secure.putStringForUser(getContentResolver(), - Secure.LOCATION_PROVIDERS_ALLOWED, "", userId); + Secure.putIntForUser(getContentResolver(), + Secure.LOCATION_MODE, Secure.LOCATION_MODE_OFF, userId); Bitmap bitmap = BitmapFactory.decodeResource(getResources(), UserSettings.USER_DRAWABLES[ userId % UserSettings.USER_DRAWABLES.length]); @@ -377,7 +377,7 @@ public class UserSettings extends RestrictedSettingsFragment Bundle extras = new Bundle(); extras.putInt(RestrictedProfileSettings.EXTRA_USER_ID, userId); extras.putBoolean(RestrictedProfileSettings.EXTRA_NEW_USER, newUser); - ((PreferenceActivity) getActivity()).startPreferencePanel( + ((SettingsActivity) getActivity()).startPreferencePanel( RestrictedProfileSettings.class.getName(), extras, R.string.user_restrictions_title, null, null, 0); @@ -390,7 +390,7 @@ public class UserSettings extends RestrictedSettingsFragment int titleResId = info.id == UserHandle.USER_OWNER ? R.string.owner_info_settings_title : (info.isRestricted() ? R.string.profile_info_settings_title : R.string.user_info_settings_title); - ((PreferenceActivity) getActivity()).startPreferencePanel( + ((SettingsActivity) getActivity()).startPreferencePanel( OwnerInfoSettings.class.getName(), extras, titleResId, null, null, 0); } diff --git a/src/com/android/settings/wfd/WifiDisplaySettings.java b/src/com/android/settings/wfd/WifiDisplaySettings.java index d7f4708ac..c3f22a79a 100755 --- a/src/com/android/settings/wfd/WifiDisplaySettings.java +++ b/src/com/android/settings/wfd/WifiDisplaySettings.java @@ -42,15 +42,12 @@ import android.os.Looper; import android.preference.CheckBoxPreference; import android.preference.ListPreference; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceCategory; import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; import android.provider.Settings; -import android.text.Html; import android.util.Slog; import android.util.TypedValue; -import android.view.Gravity; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -59,14 +56,11 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.Button; -import android.widget.CompoundButton; import android.widget.EditText; import android.widget.ImageView; -import android.widget.Switch; import android.widget.TextView; import com.android.internal.app.MediaRouteDialogPresenter; -import com.android.settings.ProgressCategory; import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; diff --git a/src/com/android/settings/wifi/AccessPoint.java b/src/com/android/settings/wifi/AccessPoint.java index c4d1f7c65..81706c909 100644 --- a/src/com/android/settings/wifi/AccessPoint.java +++ b/src/com/android/settings/wifi/AccessPoint.java @@ -60,7 +60,7 @@ class AccessPoint extends Preference { String ssid; String bssid; int security; - int networkId; + int networkId = -1; boolean wpsAvailable = false; PskType pskType = PskType.UNKNOWN; @@ -68,7 +68,7 @@ class AccessPoint extends Preference { private WifiConfiguration mConfig; /* package */ScanResult mScanResult; - private int mRssi; + private int mRssi = Integer.MAX_VALUE; private WifiInfo mInfo; private DetailedState mState; @@ -187,7 +187,6 @@ class AccessPoint extends Preference { bssid = config.BSSID; security = getSecurity(config); networkId = config.networkId; - mRssi = Integer.MAX_VALUE; mConfig = config; } @@ -198,7 +197,6 @@ class AccessPoint extends Preference { wpsAvailable = security != SECURITY_EAP && result.capabilities.contains("WPS"); if (security == SECURITY_PSK) pskType = getPskType(result); - networkId = -1; mRssi = result.level; mScanResult = result; } @@ -276,6 +274,7 @@ class AccessPoint extends Preference { if (security == SECURITY_PSK) { pskType = getPskType(result); } + mScanResult = result; refresh(); return true; } diff --git a/src/com/android/settings/wifi/AdvancedWifiSettings.java b/src/com/android/settings/wifi/AdvancedWifiSettings.java index bbcd50d5a..4accb040b 100644 --- a/src/com/android/settings/wifi/AdvancedWifiSettings.java +++ b/src/com/android/settings/wifi/AdvancedWifiSettings.java @@ -22,7 +22,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; -import android.net.wifi.WifiWatchdogStateMachine; import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.ListPreference; @@ -112,7 +111,7 @@ public class AdvancedWifiSettings extends SettingsPreferenceFragment } else { poorNetworkDetection.setChecked(Global.getInt(getContentResolver(), Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED, - WifiWatchdogStateMachine.DEFAULT_POOR_NETWORK_AVOIDANCE_ENABLED ? + WifiManager.DEFAULT_POOR_NETWORK_AVOIDANCE_ENABLED ? 1 : 0) == 1); } } diff --git a/src/com/android/settings/wifi/WifiPickerActivity.java b/src/com/android/settings/wifi/WifiPickerActivity.java index e1e7c51c1..deef8fa1a 100644 --- a/src/com/android/settings/wifi/WifiPickerActivity.java +++ b/src/com/android/settings/wifi/WifiPickerActivity.java @@ -16,7 +16,6 @@ package com.android.settings.wifi; import com.android.settings.ButtonBarHandler; -import com.android.settings.ChooseLockGeneric.ChooseLockGenericFragment; import com.android.settings.wifi.p2p.WifiP2pSettings; import android.app.Fragment; @@ -54,8 +53,8 @@ public class WifiPickerActivity extends PreferenceActivity implements ButtonBarH /** * Almost dead copy of - * {@link PreferenceActivity#startWithFragment(String, Bundle, Fragment, int)}, except - * this has additional codes for button bar handling. + * {@link PreferenceActivity#startWithFragment(String, Bundle, Fragment, int)}, except this has + * additional codes for button bar handling. */ @Override public void startWithFragment(String fragmentName, Bundle args, diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java index 1caf58bcd..d6e68a320 100644 --- a/src/com/android/settings/wifi/WifiSettings.java +++ b/src/com/android/settings/wifi/WifiSettings.java @@ -19,8 +19,13 @@ package com.android.settings.wifi; import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID; import static android.os.UserManager.DISALLOW_CONFIG_WIFI; +import android.preference.PreferenceActivity; import com.android.settings.R; import com.android.settings.RestrictedSettingsFragment; +import com.android.settings.SettingsActivity; +import com.android.settings.indexer.Indexable; +import com.android.settings.indexer.IndexableData; +import com.android.settings.indexer.IndexableRef; import com.android.settings.wifi.p2p.WifiP2pSettings; import android.app.ActionBar; @@ -49,9 +54,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceScreen; -import android.provider.Settings; import android.util.AttributeSet; import android.util.Log; import android.view.ContextMenu; @@ -90,7 +93,7 @@ import java.util.concurrent.atomic.AtomicBoolean; * and menus. */ public class WifiSettings extends RestrictedSettingsFragment - implements DialogInterface.OnClickListener { + implements DialogInterface.OnClickListener, Indexable { private static final String TAG = "WifiSettings"; private static final int MENU_ID_WPS_PBC = Menu.FIRST; private static final int MENU_ID_WPS_PIN = Menu.FIRST + 1; @@ -172,6 +175,8 @@ public class WifiSettings extends RestrictedSettingsFragment // the action bar uses a different set of controls for Setup Wizard private boolean mSetupWizardMode; + private Switch mSwitch; + /* End of "used in Wifi Setup context" */ public WifiSettings() { @@ -383,35 +388,12 @@ public class WifiSettings extends RestrictedSettingsFragment if (mSetupWizardMode) { getView().setSystemUiVisibility( -// View.STATUS_BAR_DISABLE_BACK | View.STATUS_BAR_DISABLE_HOME | View.STATUS_BAR_DISABLE_RECENT | View.STATUS_BAR_DISABLE_NOTIFICATION_ALERTS | View.STATUS_BAR_DISABLE_CLOCK); } - // On/off switch is hidden for Setup Wizard - if (!mSetupWizardMode) { - Switch actionBarSwitch = new Switch(activity); - - if (activity instanceof PreferenceActivity) { - PreferenceActivity preferenceActivity = (PreferenceActivity) activity; - if (preferenceActivity.onIsHidingHeaders() || !preferenceActivity.onIsMultiPane()) { - final int padding = activity.getResources().getDimensionPixelSize( - R.dimen.action_bar_switch_padding); - actionBarSwitch.setPaddingRelative(0, 0, padding, 0); - activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, - ActionBar.DISPLAY_SHOW_CUSTOM); - activity.getActionBar().setCustomView(actionBarSwitch, new ActionBar.LayoutParams( - ActionBar.LayoutParams.WRAP_CONTENT, - ActionBar.LayoutParams.WRAP_CONTENT, - Gravity.CENTER_VERTICAL | Gravity.END)); - } - } - - mWifiEnabler = new WifiEnabler(activity, actionBarSwitch); - } - mEmptyView = (TextView) getView().findViewById(android.R.id.empty); getListView().setEmptyView(mEmptyView); @@ -422,6 +404,59 @@ public class WifiSettings extends RestrictedSettingsFragment } @Override + public void onStart() { + super.onStart(); + + // On/off switch is hidden for Setup Wizard + if (!mSetupWizardMode) { + final Activity activity = getActivity(); + + mSwitch = new Switch(activity); + boolean addSwitch = false; + + if (activity instanceof SettingsActivity) { + SettingsActivity sa = (SettingsActivity) activity; + addSwitch = !sa.onIsHidingHeaders(); + } else if (activity instanceof WifiPickerActivity) { + PreferenceActivity pa = (PreferenceActivity) activity; + addSwitch = pa.onIsHidingHeaders(); + } + + if (addSwitch) { + final int padding = activity.getResources().getDimensionPixelSize( + R.dimen.action_bar_switch_padding); + mSwitch.setPaddingRelative(0, 0, padding, 0); + activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, + ActionBar.DISPLAY_SHOW_CUSTOM); + activity.getActionBar().setCustomView(mSwitch, new ActionBar.LayoutParams( + ActionBar.LayoutParams.WRAP_CONTENT, + ActionBar.LayoutParams.WRAP_CONTENT, + Gravity.CENTER_VERTICAL | Gravity.END)); + } + + mWifiEnabler = new WifiEnabler(activity, mSwitch); + } + } + + @Override + public void onStop() { + super.onStop(); + Activity activity = getActivity(); + boolean onIsHidingHeaders = true; + if (activity instanceof SettingsActivity){ + SettingsActivity sa = (SettingsActivity) activity; + onIsHidingHeaders = sa.onIsHidingHeaders(); + } else if (activity instanceof PreferenceActivity) { + PreferenceActivity pa = (PreferenceActivity) activity; + onIsHidingHeaders = pa.onIsHidingHeaders(); + } + if (!onIsHidingHeaders) { + activity.getActionBar().setDisplayOptions(0, ActionBar.DISPLAY_SHOW_CUSTOM); + activity.getActionBar().setCustomView(null); + } + } + + @Override public void onResume() { super.onResume(); if (mWifiEnabler != null) { @@ -512,8 +547,8 @@ public class WifiSettings extends RestrictedSettingsFragment showDialog(WPS_PBC_DIALOG_ID); return true; case MENU_ID_P2P: - if (getActivity() instanceof PreferenceActivity) { - ((PreferenceActivity) getActivity()).startPreferencePanel( + if (getActivity() instanceof SettingsActivity) { + ((SettingsActivity) getActivity()).startPreferencePanel( WifiP2pSettings.class.getCanonicalName(), null, R.string.wifi_p2p_settings_title, null, @@ -536,8 +571,8 @@ public class WifiSettings extends RestrictedSettingsFragment } return true; case MENU_ID_ADVANCED: - if (getActivity() instanceof PreferenceActivity) { - ((PreferenceActivity) getActivity()).startPreferencePanel( + if (getActivity() instanceof SettingsActivity) { + ((SettingsActivity) getActivity()).startPreferencePanel( AdvancedWifiSettings.class.getCanonicalName(), null, R.string.wifi_advanced_titlebar, null, @@ -716,7 +751,8 @@ public class WifiSettings extends RestrictedSettingsFragment switch (wifiState) { case WifiManager.WIFI_STATE_ENABLED: // AccessPoints are automatically sorted with TreeSet. - final Collection<AccessPoint> accessPoints = constructAccessPoints(); + final Collection<AccessPoint> accessPoints = + constructAccessPoints(getActivity(), mWifiManager, mLastInfo, mLastState); getPreferenceScreen().removeAll(); if(accessPoints.size() == 0) { addMessagePreference(R.string.wifi_empty_list_wifi_on); @@ -743,12 +779,12 @@ public class WifiSettings extends RestrictedSettingsFragment private void setOffMessage() { if (mEmptyView != null) { mEmptyView.setText(R.string.wifi_empty_list_wifi_off); - if (Settings.Global.getInt(getActivity().getContentResolver(), - Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1) { + if (android.provider.Settings.Global.getInt(getActivity().getContentResolver(), + android.provider.Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1) { mEmptyView.append("\n\n"); int resId; - if (Settings.Secure.isLocationProviderEnabled(getActivity().getContentResolver(), - LocationManager.NETWORK_PROVIDER)) { + if (android.provider.Settings.Secure.isLocationProviderEnabled( + getActivity().getContentResolver(), LocationManager.NETWORK_PROVIDER)) { resId = R.string.wifi_scan_notify_text_location_on; } else { resId = R.string.wifi_scan_notify_text_location_off; @@ -766,23 +802,26 @@ public class WifiSettings extends RestrictedSettingsFragment } /** Returns sorted list of access points */ - private List<AccessPoint> constructAccessPoints() { + private static List<AccessPoint> constructAccessPoints(Context context, + WifiManager wifiManager, WifiInfo lastInfo, DetailedState lastState) { ArrayList<AccessPoint> accessPoints = new ArrayList<AccessPoint>(); /** Lookup table to more quickly update AccessPoints by only considering objects with the * correct SSID. Maps SSID -> List of AccessPoints with the given SSID. */ Multimap<String, AccessPoint> apMap = new Multimap<String, AccessPoint>(); - final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks(); + final List<WifiConfiguration> configs = wifiManager.getConfiguredNetworks(); if (configs != null) { for (WifiConfiguration config : configs) { - AccessPoint accessPoint = new AccessPoint(getActivity(), config); - accessPoint.update(mLastInfo, mLastState); + AccessPoint accessPoint = new AccessPoint(context, config); + if (lastInfo != null && lastState != null) { + accessPoint.update(lastInfo, lastState); + } accessPoints.add(accessPoint); apMap.put(accessPoint.ssid, accessPoint); } } - final List<ScanResult> results = mWifiManager.getScanResults(); + final List<ScanResult> results = wifiManager.getScanResults(); if (results != null) { for (ScanResult result : results) { // Ignore hidden and ad-hoc networks. @@ -797,7 +836,7 @@ public class WifiSettings extends RestrictedSettingsFragment found = true; } if (!found) { - AccessPoint accessPoint = new AccessPoint(getActivity(), result); + AccessPoint accessPoint = new AccessPoint(context, result); accessPoints.add(accessPoint); apMap.put(accessPoint.ssid, accessPoint); } @@ -810,7 +849,7 @@ public class WifiSettings extends RestrictedSettingsFragment } /** A restricted multimap for use in constructAccessPoints */ - private class Multimap<K,V> { + private static class Multimap<K,V> { private final HashMap<K,List<V>> store = new HashMap<K,List<V>>(); /** retrieve a non-null list of values with key K */ List<V> getAll(K key) { @@ -1109,10 +1148,10 @@ public class WifiSettings extends RestrictedSettingsFragment protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int parentWidth = MeasureSpec.getSize(widthMeasureSpec); int parentHeight = MeasureSpec.getSize(heightMeasureSpec); - final Resources resources = getContext().getResources(); - float titleHeight = resources.getFraction(R.dimen.setup_title_height, 1, 1); - float sideMargin = resources.getFraction(R.dimen.setup_border_width, 1, 1); - int bottom = resources.getDimensionPixelSize(R.dimen.setup_margin_bottom); + final Resources res = getContext().getResources(); + float titleHeight = res.getFraction(R.dimen.setup_title_height, 1, 1); + float sideMargin = res.getFraction(R.dimen.setup_border_width, 1, 1); + int bottom = res.getDimensionPixelSize(R.dimen.setup_margin_bottom); setPaddingRelative( (int) (parentWidth * sideMargin), 0, @@ -1126,4 +1165,39 @@ public class WifiSettings extends RestrictedSettingsFragment } } + public static final Indexable.IndexDataProvider INDEX_DATA_PROVIDER = + new Indexable.IndexDataProvider() { + @Override + public List<IndexableRef> getRefsToIndex(Context context) { + return null; + } + + @Override + public List<IndexableData> getRawDataToIndex(Context context) { + final List<IndexableData> result = new ArrayList<IndexableData>(); + final Resources res = context.getResources(); + + // Add fragment title + IndexableData data = new IndexableData(); + data.title = res.getString(R.string.wifi_settings); + data.fragmentTitle = res.getString(R.string.wifi_settings); + result.add(data); + + // Add available Wi-Fi access points + WifiManager wifiManager = + (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + final Collection<AccessPoint> accessPoints = + constructAccessPoints(context, wifiManager, null, null); + for (AccessPoint accessPoint : accessPoints) { + // We are indexing only the saved Wi-Fi networks. + if (accessPoint.getConfig() == null) continue; + data = new IndexableData(); + data.title = accessPoint.getTitle().toString(); + data.fragmentTitle = res.getString(R.string.wifi_settings); + result.add(data); + } + + return result; + } + }; } diff --git a/src/com/android/settings/wifi/p2p/WifiP2pSettings.java b/src/com/android/settings/wifi/p2p/WifiP2pSettings.java index cd1d72171..879ebf835 100644 --- a/src/com/android/settings/wifi/p2p/WifiP2pSettings.java +++ b/src/com/android/settings/wifi/p2p/WifiP2pSettings.java @@ -16,7 +16,6 @@ package com.android.settings.wifi.p2p; -import android.app.ActionBar; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; @@ -34,40 +33,32 @@ import android.net.wifi.p2p.WifiP2pDeviceList; import android.net.wifi.p2p.WifiP2pGroup; import android.net.wifi.p2p.WifiP2pGroupList; import android.net.wifi.p2p.WifiP2pManager; -import android.net.wifi.p2p.WifiP2pManager.GroupInfoListener; +import android.net.wifi.p2p.WifiP2pManager.PeerListListener; import android.net.wifi.p2p.WifiP2pManager.PersistentGroupInfoListener; import android.net.wifi.WpsInfo; import android.os.Bundle; -import android.os.Handler; import android.os.SystemProperties; import android.preference.Preference; -import android.preference.PreferenceActivity; import android.preference.PreferenceCategory; import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; import android.text.InputFilter; import android.text.TextUtils; import android.util.Log; -import android.view.Gravity; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.widget.EditText; -import android.widget.Switch; import android.widget.Toast; import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; -import java.util.Arrays; -import java.util.List; -import java.util.Collection; - /* * Displays Wi-fi p2p settings UI */ public class WifiP2pSettings extends SettingsPreferenceFragment - implements PersistentGroupInfoListener, GroupInfoListener { + implements PersistentGroupInfoListener, PeerListListener { private static final String TAG = "WifiP2pSettings"; private static final boolean DBG = false; @@ -89,7 +80,6 @@ public class WifiP2pSettings extends SettingsPreferenceFragment private boolean mWifiP2pEnabled; private boolean mWifiP2pSearching; private int mConnectedDevices; - private WifiP2pGroup mConnectedGroup; private boolean mLastGroupFormed = false; private PreferenceGroup mPeersGroup; @@ -129,9 +119,6 @@ public class WifiP2pSettings extends SettingsPreferenceFragment WifiP2pManager.EXTRA_NETWORK_INFO); WifiP2pInfo wifip2pinfo = (WifiP2pInfo) intent.getParcelableExtra( WifiP2pManager.EXTRA_WIFI_P2P_INFO); - if (mWifiP2pManager != null) { - mWifiP2pManager.requestGroupInfo(mChannel, WifiP2pSettings.this); - } if (networkInfo.isConnected()) { if (DBG) Log.d(TAG, "Connected"); } else if (mLastGroupFormed != true) { @@ -298,16 +285,20 @@ public class WifiP2pSettings extends SettingsPreferenceFragment final PreferenceScreen preferenceScreen = getPreferenceScreen(); preferenceScreen.removeAll(); - preferenceScreen.setOrderingAsAdded(true); + mThisDevicePref = new Preference(getActivity()); + mThisDevicePref.setPersistent(false); + mThisDevicePref.setSelectable(false); preferenceScreen.addPreference(mThisDevicePref); mPeersGroup = new PreferenceCategory(getActivity()); mPeersGroup.setTitle(R.string.wifi_p2p_peer_devices); + preferenceScreen.addPreference(mPeersGroup); mPersistentGroup = new PreferenceCategory(getActivity()); mPersistentGroup.setTitle(R.string.wifi_p2p_remembered_groups); + preferenceScreen.addPreference(mPersistentGroup); super.onActivityCreated(savedInstanceState); } @@ -316,12 +307,17 @@ public class WifiP2pSettings extends SettingsPreferenceFragment public void onResume() { super.onResume(); getActivity().registerReceiver(mReceiver, mIntentFilter); + if (mWifiP2pManager != null) { + mWifiP2pManager.requestPeers(mChannel, WifiP2pSettings.this); + } } @Override public void onPause() { super.onPause(); - mWifiP2pManager.stopPeerDiscovery(mChannel, null); + if (mWifiP2pManager != null) { + mWifiP2pManager.stopPeerDiscovery(mChannel, null); + } getActivity().unregisterReceiver(mReceiver); } @@ -506,6 +502,7 @@ public class WifiP2pSettings extends SettingsPreferenceFragment if (DBG) Log.d(TAG, " mConnectedDevices " + mConnectedDevices); } + @Override public void onPersistentGroupInfoAvailable(WifiP2pGroupList groups) { mPersistentGroup.removeAll(); @@ -528,27 +525,18 @@ public class WifiP2pSettings extends SettingsPreferenceFragment } } - public void onGroupInfoAvailable(WifiP2pGroup group) { - if (DBG) Log.d(TAG, " group " + group); - mConnectedGroup = group; - updateDevicePref(); + @Override + public void onPeersAvailable(WifiP2pDeviceList peers) { + if (DBG) Log.d(TAG, "Requested peers are available"); + mPeers = peers; + handlePeersChanged(); } private void handleP2pStateChanged() { updateSearchMenu(false); - if (mWifiP2pEnabled) { - final PreferenceScreen preferenceScreen = getPreferenceScreen(); - preferenceScreen.removeAll(); - - preferenceScreen.setOrderingAsAdded(true); - preferenceScreen.addPreference(mThisDevicePref); - - mPeersGroup.setEnabled(true); - preferenceScreen.addPreference(mPeersGroup); - - mPersistentGroup.setEnabled(true); - preferenceScreen.addPreference(mPersistentGroup); - } + mThisDevicePref.setEnabled(mWifiP2pEnabled); + mPeersGroup.setEnabled(mWifiP2pEnabled); + mPersistentGroup.setEnabled(mWifiP2pEnabled); } private void updateSearchMenu(boolean searching) { @@ -576,10 +564,6 @@ public class WifiP2pSettings extends SettingsPreferenceFragment } else { mThisDevicePref.setTitle(mThisDevice.deviceName); } - - mThisDevicePref.setPersistent(false); - mThisDevicePref.setEnabled(true); - mThisDevicePref.setSelectable(false); } } } |