summaryrefslogtreecommitdiffstats
path: root/src/com/android
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android')
-rw-r--r--src/com/android/settings/AccessibilitySettings.java219
-rw-r--r--src/com/android/settings/AccessibilityTutorialActivity.java698
-rw-r--r--src/com/android/settings/AccountPreference.java56
-rw-r--r--src/com/android/settings/AllowBindAppWidgetActivity.java130
-rw-r--r--src/com/android/settings/AppPicker.java144
-rw-r--r--src/com/android/settings/BiometricWeakLiveliness.java136
-rw-r--r--src/com/android/settings/BrightnessPreference.java126
-rw-r--r--src/com/android/settings/ChooseLockGeneric.java54
-rw-r--r--src/com/android/settings/CredentialStorage.java36
-rw-r--r--src/com/android/settings/CryptKeeper.java327
-rw-r--r--src/com/android/settings/CryptKeeperSettings.java9
-rw-r--r--src/com/android/settings/DataUsageSummary.java655
-rw-r--r--src/com/android/settings/DevelopmentSettings.java615
-rw-r--r--src/com/android/settings/DisplaySettings.java64
-rw-r--r--src/com/android/settings/DreamComponentPreference.java236
-rw-r--r--src/com/android/settings/DreamSettings.java159
-rw-r--r--src/com/android/settings/DreamTesterPreference.java87
-rw-r--r--src/com/android/settings/EditPinPreference.java10
-rw-r--r--src/com/android/settings/NsdEnabler.java87
-rw-r--r--src/com/android/settings/PointerSpeedPreference.java53
-rw-r--r--src/com/android/settings/PrivacySettings.java5
-rw-r--r--src/com/android/settings/ProgressCategory.java3
-rw-r--r--src/com/android/settings/RingerVolumePreference.java15
-rw-r--r--src/com/android/settings/SecuritySettings.java68
-rw-r--r--src/com/android/settings/Settings.java218
-rw-r--r--src/com/android/settings/SettingsPreferenceFragment.java44
-rw-r--r--src/com/android/settings/SoundSettings.java113
-rw-r--r--src/com/android/settings/SubSettings.java6
-rw-r--r--src/com/android/settings/TetherSettings.java68
-rw-r--r--src/com/android/settings/UserDictionarySettings.java207
-rw-r--r--src/com/android/settings/Utils.java2
-rw-r--r--src/com/android/settings/WirelessSettings.java39
-rw-r--r--src/com/android/settings/accounts/AccountPreferenceBase.java97
-rw-r--r--src/com/android/settings/accounts/AccountSyncSettings.java68
-rw-r--r--src/com/android/settings/accounts/AuthenticatorHelper.java137
-rw-r--r--src/com/android/settings/accounts/ManageAccountsSettings.java196
-rw-r--r--src/com/android/settings/accounts/SyncSettings.java174
-rw-r--r--src/com/android/settings/accounts/SyncSettingsActivity.java34
-rw-r--r--src/com/android/settings/applications/AppViewHolder.java64
-rw-r--r--src/com/android/settings/applications/ApplicationsState.java389
-rw-r--r--src/com/android/settings/applications/InstalledAppDetails.java169
-rw-r--r--src/com/android/settings/applications/ManageApplications.java1054
-rw-r--r--src/com/android/settings/applications/RunningServiceDetails.java1
-rw-r--r--src/com/android/settings/bluetooth/BluetoothEventManager.java2
-rw-r--r--src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java30
-rwxr-xr-xsrc/com/android/settings/bluetooth/BluetoothSettings.java6
-rw-r--r--src/com/android/settings/bluetooth/RequestPermissionActivity.java5
-rw-r--r--src/com/android/settings/bluetooth/RequestPermissionHelperActivity.java6
-rw-r--r--src/com/android/settings/deviceinfo/Memory.java32
-rw-r--r--src/com/android/settings/deviceinfo/Status.java3
-rw-r--r--src/com/android/settings/deviceinfo/StorageMeasurement.java28
-rw-r--r--src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java2
-rw-r--r--src/com/android/settings/fuelgauge/PowerUsageSummary.java13
-rw-r--r--src/com/android/settings/inputmethod/CheckBoxAndSettingsPreference.java31
-rw-r--r--src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java246
-rw-r--r--src/com/android/settings/inputmethod/InputMethodAndSubtypeUtil.java54
-rw-r--r--src/com/android/settings/inputmethod/InputMethodPreference.java40
-rw-r--r--src/com/android/settings/inputmethod/KeyboardLayoutDialogFragment.java354
-rw-r--r--src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment.java154
-rw-r--r--src/com/android/settings/inputmethod/SingleSpellCheckerPreference.java10
-rw-r--r--src/com/android/settings/inputmethod/UserDictionaryAddWordActivity.java78
-rw-r--r--src/com/android/settings/inputmethod/UserDictionaryAddWordContents.java231
-rw-r--r--src/com/android/settings/inputmethod/UserDictionaryAddWordFragment.java148
-rw-r--r--src/com/android/settings/inputmethod/UserDictionaryList.java9
-rw-r--r--src/com/android/settings/inputmethod/UserDictionaryLocalePicker.java26
-rw-r--r--src/com/android/settings/net/ChartDataLoader.java52
-rw-r--r--src/com/android/settings/net/DataUsageMeteredSettings.java144
-rw-r--r--src/com/android/settings/net/NetworkPolicyEditor.java127
-rw-r--r--src/com/android/settings/net/SummaryForAllUidLoader.java11
-rw-r--r--src/com/android/settings/net/UidDetailProvider.java38
-rw-r--r--src/com/android/settings/tts/TextToSpeechSettings.java18
-rw-r--r--src/com/android/settings/tts/TtsEngineSettingsFragment.java44
-rw-r--r--src/com/android/settings/users/UserDetailsSettings.java285
-rw-r--r--src/com/android/settings/users/UserSettings.java109
-rw-r--r--src/com/android/settings/vpn2/VpnDialog.java8
-rw-r--r--src/com/android/settings/vpn2/VpnSettings.java20
-rw-r--r--src/com/android/settings/widget/ChartDataUsageView.java144
-rw-r--r--src/com/android/settings/widget/ChartGridView.java7
-rw-r--r--src/com/android/settings/widget/ChartNetworkSeriesView.java50
-rw-r--r--src/com/android/settings/widget/InvertedChartAxis.java14
-rw-r--r--src/com/android/settings/widget/SettingsAppWidgetProvider.java11
-rw-r--r--src/com/android/settings/wifi/AdvancedWifiSettings.java23
-rw-r--r--src/com/android/settings/wifi/WifiConfigController.java213
-rw-r--r--src/com/android/settings/wifi/WifiDialog.java3
-rw-r--r--src/com/android/settings/wifi/WifiPickerActivity.java10
-rw-r--r--src/com/android/settings/wifi/WifiSettings.java520
-rw-r--r--src/com/android/settings/wifi/WifiSettingsForSetupWizardXL.java38
-rw-r--r--src/com/android/settings/wifi/WifiSetupActivity.java23
-rw-r--r--src/com/android/settings/wifi/WpsDialog.java250
-rw-r--r--src/com/android/settings/wifi/p2p/WifiP2pDialog.java132
-rw-r--r--src/com/android/settings/wifi/p2p/WifiP2pEnabler.java117
-rw-r--r--src/com/android/settings/wifi/p2p/WifiP2pSettings.java349
92 files changed, 7853 insertions, 3487 deletions
diff --git a/src/com/android/settings/AccessibilitySettings.java b/src/com/android/settings/AccessibilitySettings.java
index 827af13..149e336 100644
--- a/src/com/android/settings/AccessibilitySettings.java
+++ b/src/com/android/settings/AccessibilitySettings.java
@@ -30,12 +30,12 @@ import android.content.SharedPreferences;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
+import android.database.ContentObserver;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemProperties;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
@@ -46,15 +46,12 @@ import android.preference.PreferenceScreen;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
-import android.util.Log;
import android.view.Gravity;
-import android.view.IWindowManager;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
-import android.view.Surface;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -63,6 +60,7 @@ import android.widget.Switch;
import android.widget.TextView;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.view.RotationPolicy;
import com.android.settings.AccessibilitySettings.ToggleSwitch.OnBeforeCheckedChangeListener;
import java.util.HashMap;
@@ -76,8 +74,6 @@ import java.util.Set;
*/
public class AccessibilitySettings extends SettingsPreferenceFragment implements DialogCreatable,
Preference.OnPreferenceChangeListener {
- private static final String TAG = "AccessibilitySettings";
-
private static final String DEFAULT_SCREENREADER_MARKET_LINK =
"market://search?q=pname:com.google.android.marvin.talkback";
@@ -92,9 +88,6 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
private static final char ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR = ':';
- private static final String KEY_ACCESSIBILITY_TUTORIAL_LAUNCHED_ONCE =
- "key_accessibility_tutorial_launched_once";
-
private static final String KEY_INSTALL_ACCESSIBILITY_SERVICE_OFFERED_ONCE =
"key_install_accessibility_service_offered_once";
@@ -106,12 +99,10 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
private static final String TOGGLE_LARGE_TEXT_PREFERENCE = "toggle_large_text_preference";
private static final String TOGGLE_POWER_BUTTON_ENDS_CALL_PREFERENCE =
"toggle_power_button_ends_call_preference";
- private static final String TOGGLE_AUTO_ROTATE_SCREEN_PREFERENCE =
- "toggle_auto_rotate_screen_preference";
+ private static final String TOGGLE_LOCK_SCREEN_ROTATION_PREFERENCE =
+ "toggle_lock_screen_rotation_preference";
private static final String TOGGLE_SPEAK_PASSWORD_PREFERENCE =
"toggle_speak_password_preference";
- private static final String TOGGLE_TOUCH_EXPLORATION_PREFERENCE =
- "toggle_touch_exploration_preference";
private static final String SELECT_LONG_PRESS_TIMEOUT_PREFERENCE =
"select_long_press_timeout_preference";
private static final String TOGGLE_SCRIPT_INJECTION_PREFERENCE =
@@ -154,15 +145,22 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
}
};
+ private final RotationPolicy.RotationPolicyListener mRotationPolicyListener =
+ new RotationPolicy.RotationPolicyListener() {
+ @Override
+ public void onChange() {
+ updateLockScreenRotationCheckbox();
+ }
+ };
+
// Preference controls.
private PreferenceCategory mServicesCategory;
private PreferenceCategory mSystemsCategory;
private CheckBoxPreference mToggleLargeTextPreference;
private CheckBoxPreference mTogglePowerButtonEndsCallPreference;
- private CheckBoxPreference mToggleAutoRotateScreenPreference;
+ private CheckBoxPreference mToggleLockScreenRotationPreference;
private CheckBoxPreference mToggleSpeakPasswordPreference;
- private Preference mToggleTouchExplorationPreference;
private ListPreference mSelectLongPressTimeoutPreference;
private AccessibilityEnableScriptInjectionPreference mToggleScriptInjectionPreference;
private Preference mNoServicesMessagePreference;
@@ -184,12 +182,16 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
if (mServicesCategory.getPreference(0) == mNoServicesMessagePreference) {
offerInstallAccessibilitySerivceOnce();
}
- mSettingsPackageMonitor.register(getActivity(), false);
+ mSettingsPackageMonitor.register(getActivity(), getActivity().getMainLooper(), false);
+ RotationPolicy.registerRotationPolicyListener(getActivity(),
+ mRotationPolicyListener);
}
@Override
public void onPause() {
mSettingsPackageMonitor.unregister();
+ RotationPolicy.unregisterRotationPolicyListener(getActivity(),
+ mRotationPolicyListener);
super.onPause();
}
@@ -213,8 +215,8 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
} else if (mTogglePowerButtonEndsCallPreference == preference) {
handleTogglePowerButtonEndsCallPreferenceClick();
return true;
- } else if (mToggleAutoRotateScreenPreference == preference) {
- handleToggleAutoRotateScreenPreferenceClick();
+ } else if (mToggleLockScreenRotationPreference == preference) {
+ handleLockScreenRotationPreferenceClick();
return true;
} else if (mToggleSpeakPasswordPreference == preference) {
handleToggleSpeakPasswordPreferenceClick();
@@ -239,18 +241,9 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
: Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_SCREEN_OFF));
}
- private void handleToggleAutoRotateScreenPreferenceClick() {
- try {
- IWindowManager wm = IWindowManager.Stub.asInterface(
- ServiceManager.getService(Context.WINDOW_SERVICE));
- if (mToggleAutoRotateScreenPreference.isChecked()) {
- wm.thawRotation();
- } else {
- wm.freezeRotation(Surface.ROTATION_0);
- }
- } catch (RemoteException exc) {
- Log.w(TAG, "Unable to save auto-rotate setting");
- }
+ private void handleLockScreenRotationPreferenceClick() {
+ RotationPolicy.setRotationLockForAccessibility(getActivity(),
+ !mToggleLockScreenRotationPreference.isChecked());
}
private void handleToggleSpeakPasswordPreferenceClick() {
@@ -275,17 +268,14 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
mSystemsCategory.removePreference(mTogglePowerButtonEndsCallPreference);
}
- // Auto-rotate screen
- mToggleAutoRotateScreenPreference =
- (CheckBoxPreference) findPreference(TOGGLE_AUTO_ROTATE_SCREEN_PREFERENCE);
+ // Lock screen rotation.
+ mToggleLockScreenRotationPreference =
+ (CheckBoxPreference) findPreference(TOGGLE_LOCK_SCREEN_ROTATION_PREFERENCE);
// Speak passwords.
mToggleSpeakPasswordPreference =
(CheckBoxPreference) findPreference(TOGGLE_SPEAK_PASSWORD_PREFERENCE);
- // Touch exploration enabled.
- mToggleTouchExplorationPreference = findPreference(TOGGLE_TOUCH_EXPLORATION_PREFERENCE);
-
// Long press timeout.
mSelectLongPressTimeoutPreference =
(ListPreference) findPreference(SELECT_LONG_PRESS_TIMEOUT_PREFERENCE);
@@ -352,7 +342,7 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
}
preference.setOrder(i);
- preference.setFragment(ToggleAccessibilityServiceFragment.class.getName());
+ preference.setFragment(ToggleAccessibilityServicePreferenceFragment.class.getName());
preference.setPersistent(true);
Bundle extras = preference.getExtras();
@@ -360,7 +350,7 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
extras.putBoolean(EXTRA_CHECKED, serviceEnabled);
extras.putString(EXTRA_TITLE, title);
- String description = info.getDescription();
+ String description = info.loadDescription(getPackageManager());
if (TextUtils.isEmpty(description)) {
description = getString(R.string.accessibility_service_default_description);
}
@@ -438,34 +428,13 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
}
// Auto-rotate screen
- final boolean autoRotationEnabled = Settings.System.getInt(getContentResolver(),
- Settings.System.ACCELEROMETER_ROTATION, 0) != 0;
- mToggleAutoRotateScreenPreference.setChecked(autoRotationEnabled);
+ updateLockScreenRotationCheckbox();
// Speak passwords.
final boolean speakPasswordEnabled = Settings.Secure.getInt(getContentResolver(),
Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0) != 0;
mToggleSpeakPasswordPreference.setChecked(speakPasswordEnabled);
- // Touch exploration enabled.
- if (AccessibilityManager.getInstance(getActivity()).isEnabled()) {
- mSystemsCategory.addPreference(mToggleTouchExplorationPreference);
- final boolean touchExplorationEnabled = (Settings.Secure.getInt(getContentResolver(),
- Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1);
- if (touchExplorationEnabled) {
- mToggleTouchExplorationPreference.setSummary(
- getString(R.string.accessibility_service_state_on));
- mToggleTouchExplorationPreference.getExtras().putBoolean(EXTRA_CHECKED, true);
- } else {
- mToggleTouchExplorationPreference.setSummary(
- getString(R.string.accessibility_service_state_off));
- mToggleTouchExplorationPreference.getExtras().putBoolean(EXTRA_CHECKED, false);
- }
-
- } else {
- mSystemsCategory.removePreference(mToggleTouchExplorationPreference);
- }
-
// Long press timeout.
final int longPressTimeout = Settings.Secure.getInt(getContentResolver(),
Settings.Secure.LONG_PRESS_TIMEOUT, mLongPressTimeoutDefault);
@@ -479,6 +448,11 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
mToggleScriptInjectionPreference.setInjectionAllowed(scriptInjectionAllowed);
}
+ private void updateLockScreenRotationCheckbox() {
+ mToggleLockScreenRotationPreference.setChecked(
+ !RotationPolicy.isRotationLocked(getActivity()));
+ }
+
private void offerInstallAccessibilitySerivceOnce() {
// There is always one preference - if no services it is just a message.
if (mServicesCategory.getPreference(0) != mNoServicesMessagePreference) {
@@ -632,79 +606,8 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
}
}
- public static class ToggleAccessibilityServiceFragment extends TogglePreferenceFragment {
- @Override
- public void onPreferenceToggled(String preferenceKey, boolean enabled) {
- // Parse the enabled services.
- Set<ComponentName> enabledServices = getEnabledServicesFromSettings(getActivity());
-
- // Determine enabled services and accessibility state.
- ComponentName toggledService = ComponentName.unflattenFromString(preferenceKey);
- final boolean accessibilityEnabled;
- if (enabled) {
- // Enabling at least one service enables accessibility.
- accessibilityEnabled = true;
- enabledServices.add(toggledService);
- } else {
- // Check how many enabled and installed services are present.
- int enabledAndInstalledServiceCount = 0;
- Set<ComponentName> installedServices = sInstalledServices;
- for (ComponentName enabledService : enabledServices) {
- if (installedServices.contains(enabledService)) {
- enabledAndInstalledServiceCount++;
- }
- }
- // Disabling the last service disables accessibility.
- accessibilityEnabled = enabledAndInstalledServiceCount > 1
- || (enabledAndInstalledServiceCount == 1
- && !installedServices.contains(toggledService));
- enabledServices.remove(toggledService);
- }
-
- // Update the enabled services setting.
- StringBuilder enabledServicesBuilder = new StringBuilder();
- // Keep the enabled services even if they are not installed since we have
- // no way to know whether the application restore process has completed.
- // In general the system should be responsible for the clean up not settings.
- for (ComponentName enabledService : enabledServices) {
- enabledServicesBuilder.append(enabledService.flattenToString());
- enabledServicesBuilder.append(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR);
- }
- final int enabledServicesBuilderLength = enabledServicesBuilder.length();
- if (enabledServicesBuilderLength > 0) {
- enabledServicesBuilder.deleteCharAt(enabledServicesBuilderLength - 1);
- }
- Settings.Secure.putString(getContentResolver(),
- Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
- enabledServicesBuilder.toString());
-
- // Update accessibility enabled.
- Settings.Secure.putInt(getContentResolver(),
- Settings.Secure.ACCESSIBILITY_ENABLED, accessibilityEnabled ? 1 : 0);
- }
- }
-
- public static class ToggleTouchExplorationFragment extends TogglePreferenceFragment {
- @Override
- public void onPreferenceToggled(String preferenceKey, boolean enabled) {
- Settings.Secure.putInt(getContentResolver(),
- Settings.Secure.TOUCH_EXPLORATION_ENABLED, enabled ? 1 : 0);
- if (enabled) {
- SharedPreferences preferences = getActivity().getPreferences(Context.MODE_PRIVATE);
- final boolean launchAccessibilityTutorial = !preferences.getBoolean(
- KEY_ACCESSIBILITY_TUTORIAL_LAUNCHED_ONCE, false);
- if (launchAccessibilityTutorial) {
- preferences.edit().putBoolean(KEY_ACCESSIBILITY_TUTORIAL_LAUNCHED_ONCE,
- true).commit();
- Intent intent = new Intent(AccessibilityTutorialActivity.ACTION);
- getActivity().startActivity(intent);
- }
- }
- }
- }
-
- private abstract static class TogglePreferenceFragment extends SettingsPreferenceFragment
- implements DialogInterface.OnClickListener {
+ public static class ToggleAccessibilityServicePreferenceFragment
+ extends SettingsPreferenceFragment implements DialogInterface.OnClickListener {
private static final int DIALOG_ID_ENABLE_WARNING = 1;
private static final int DIALOG_ID_DISABLE_WARNING = 2;
@@ -717,6 +620,7 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
private CharSequence mEnableWarningMessage;
private CharSequence mDisableWarningTitle;
private CharSequence mDisableWarningMessage;
+
private Preference mSummaryPreference;
private CharSequence mSettingsTitle;
@@ -782,7 +686,54 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
super.onDestroyView();
}
- public abstract void onPreferenceToggled(String preferenceKey, boolean value);
+ public void onPreferenceToggled(String preferenceKey, boolean enabled) {
+ // Parse the enabled services.
+ Set<ComponentName> enabledServices = getEnabledServicesFromSettings(getActivity());
+
+ // Determine enabled services and accessibility state.
+ ComponentName toggledService = ComponentName.unflattenFromString(preferenceKey);
+ final boolean accessibilityEnabled;
+ if (enabled) {
+ // Enabling at least one service enables accessibility.
+ accessibilityEnabled = true;
+ enabledServices.add(toggledService);
+ } else {
+ // Check how many enabled and installed services are present.
+ int enabledAndInstalledServiceCount = 0;
+ Set<ComponentName> installedServices = sInstalledServices;
+ for (ComponentName enabledService : enabledServices) {
+ if (installedServices.contains(enabledService)) {
+ enabledAndInstalledServiceCount++;
+ }
+ }
+ // Disabling the last service disables accessibility.
+ accessibilityEnabled = enabledAndInstalledServiceCount > 1
+ || (enabledAndInstalledServiceCount == 1
+ && !installedServices.contains(toggledService));
+ enabledServices.remove(toggledService);
+ }
+
+ // Update the enabled services setting.
+ StringBuilder enabledServicesBuilder = new StringBuilder();
+ // Keep the enabled services even if they are not installed since we have
+ // no way to know whether the application restore process has completed.
+ // In general the system should be responsible for the clean up not settings.
+ for (ComponentName enabledService : enabledServices) {
+ enabledServicesBuilder.append(enabledService.flattenToString());
+ enabledServicesBuilder.append(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR);
+ }
+ final int enabledServicesBuilderLength = enabledServicesBuilder.length();
+ if (enabledServicesBuilderLength > 0) {
+ enabledServicesBuilder.deleteCharAt(enabledServicesBuilderLength - 1);
+ }
+ Settings.Secure.putString(getContentResolver(),
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ enabledServicesBuilder.toString());
+
+ // Update accessibility enabled.
+ Settings.Secure.putInt(getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_ENABLED, accessibilityEnabled ? 1 : 0);
+ }
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
diff --git a/src/com/android/settings/AccessibilityTutorialActivity.java b/src/com/android/settings/AccessibilityTutorialActivity.java
deleted file mode 100644
index 206aa94..0000000
--- a/src/com/android/settings/AccessibilityTutorialActivity.java
+++ /dev/null
@@ -1,698 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc.
- *
- * 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.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.provider.Settings;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.animation.Animation;
-import android.view.animation.Animation.AnimationListener;
-import android.view.animation.AnimationUtils;
-import android.widget.AbsListView;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.FrameLayout;
-import android.widget.GridView;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.ViewAnimator;
-
-import com.android.settings.R;
-
-import java.util.List;
-
-/**
- * This class provides a short tutorial that introduces the user to the features
- * available in Touch Exploration.
- */
-public class AccessibilityTutorialActivity extends Activity {
-
- /** Intent action for launching this activity. */
- public static final String ACTION = "android.settings.ACCESSIBILITY_TUTORIAL";
-
- /** Instance state saving constant for the active module. */
- private static final String KEY_ACTIVE_MODULE = "active_module";
-
- /** The index of the module to show when first opening the tutorial. */
- private static final int DEFAULT_MODULE = 0;
-
- /** View animator for switching between modules. */
- private ViewAnimator mViewAnimator;
-
- private AccessibilityManager mAccessibilityManager;
-
- /** Should touch exploration be disabled when this activity is paused? */
- private boolean mDisableOnPause;
-
- private final AnimationListener mInAnimationListener = new AnimationListener() {
- @Override
- public void onAnimationEnd(Animation animation) {
- final int index = mViewAnimator.getDisplayedChild();
- final TutorialModule module = (TutorialModule) mViewAnimator.getChildAt(index);
-
- activateModule(module);
- }
-
- @Override
- public void onAnimationRepeat(Animation animation) {
- // Do nothing.
- }
-
- @Override
- public void onAnimationStart(Animation animation) {
- // Do nothing.
- }
- };
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- final Animation inAnimation = AnimationUtils.loadAnimation(this,
- android.R.anim.slide_in_left);
- inAnimation.setAnimationListener(mInAnimationListener);
-
- final Animation outAnimation = AnimationUtils.loadAnimation(this,
- android.R.anim.slide_in_left);
-
- mViewAnimator = new ViewAnimator(this);
- mViewAnimator.setInAnimation(inAnimation);
- mViewAnimator.setOutAnimation(outAnimation);
- mViewAnimator.addView(new TouchTutorialModule1(this, this));
- mViewAnimator.addView(new TouchTutorialModule2(this, this));
-
- setContentView(mViewAnimator);
-
- mAccessibilityManager = (AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE);
-
- if (savedInstanceState != null) {
- show(savedInstanceState.getInt(KEY_ACTIVE_MODULE, DEFAULT_MODULE));
- } else {
- show(DEFAULT_MODULE);
- }
- }
-
- @Override
- protected void onResume() {
- super.onResume();
-
- final ContentResolver cr = getContentResolver();
-
- if (Settings.Secure.getInt(cr, Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 0) {
- Settings.Secure.putInt(cr, Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1);
- mDisableOnPause = true;
- } else {
- mDisableOnPause = false;
- }
- }
-
- @Override
- protected void onPause() {
- super.onPause();
-
- if (mDisableOnPause) {
- final ContentResolver cr = getContentResolver();
- Settings.Secure.putInt(cr, Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0);
- }
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
-
- outState.putInt(KEY_ACTIVE_MODULE, mViewAnimator.getDisplayedChild());
- }
-
- private void activateModule(TutorialModule module) {
- module.activate();
- }
-
- private void deactivateModule(TutorialModule module) {
- mAccessibilityManager.interrupt();
- mViewAnimator.setOnKeyListener(null);
- module.deactivate();
- }
-
- private void interrupt() {
- mAccessibilityManager.interrupt();
- }
-
- private void next() {
- show(mViewAnimator.getDisplayedChild() + 1);
- }
-
- private void previous() {
- show(mViewAnimator.getDisplayedChild() - 1);
- }
-
- private void show(int which) {
- if ((which < 0) || (which >= mViewAnimator.getChildCount())) {
- return;
- }
-
- mAccessibilityManager.interrupt();
-
- final int displayedIndex = mViewAnimator.getDisplayedChild();
- final TutorialModule displayedView = (TutorialModule) mViewAnimator.getChildAt(
- displayedIndex);
- deactivateModule(displayedView);
-
- mViewAnimator.setDisplayedChild(which);
- }
-
- /**
- * Loads application labels and icons.
- */
- private static class AppsAdapter extends ArrayAdapter<ResolveInfo> {
- protected final int mTextViewResourceId;
-
- private final int mIconSize;
- private final View.OnHoverListener mDefaultHoverListener;
-
- private View.OnHoverListener mHoverListener;
-
- public AppsAdapter(Context context, int resource, int textViewResourceId) {
- super(context, resource, textViewResourceId);
-
- mIconSize = context.getResources().getDimensionPixelSize(R.dimen.app_icon_size);
- mTextViewResourceId = textViewResourceId;
- mDefaultHoverListener = new View.OnHoverListener() {
- @Override
- public boolean onHover(View v, MotionEvent event) {
- if (mHoverListener != null) {
- return mHoverListener.onHover(v, event);
- } else {
- return false;
- }
- }
- };
-
- loadAllApps();
- }
-
- public CharSequence getLabel(int position) {
- final PackageManager packageManager = getContext().getPackageManager();
- final ResolveInfo appInfo = getItem(position);
- return appInfo.loadLabel(packageManager);
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- final PackageManager packageManager = getContext().getPackageManager();
- final View view = super.getView(position, convertView, parent);
- view.setOnHoverListener(mDefaultHoverListener);
- view.setTag(position);
-
- final ResolveInfo appInfo = getItem(position);
- final CharSequence label = appInfo.loadLabel(packageManager);
- final Drawable icon = appInfo.loadIcon(packageManager);
- final TextView text = (TextView) view.findViewById(mTextViewResourceId);
-
- icon.setBounds(0, 0, mIconSize, mIconSize);
-
- populateView(text, label, icon);
-
- return view;
- }
-
- private void loadAllApps() {
- final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
- mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-
- final PackageManager pm = getContext().getPackageManager();
- final List<ResolveInfo> apps = pm.queryIntentActivities(mainIntent, 0);
-
- addAll(apps);
- }
-
- protected void populateView(TextView text, CharSequence label, Drawable icon) {
- text.setText(label);
- text.setCompoundDrawables(null, icon, null, null);
- }
-
- public void setOnHoverListener(View.OnHoverListener hoverListener) {
- mHoverListener = hoverListener;
- }
- }
-
- /**
- * Introduces using a finger to explore and interact with on-screen content.
- */
- private static class TouchTutorialModule1 extends TutorialModule implements
- View.OnHoverListener, AdapterView.OnItemClickListener {
- /**
- * Handles the case where the user overshoots the target area.
- */
- private class HoverTargetHandler extends Handler {
- private static final int MSG_ENTERED_TARGET = 1;
- private static final int DELAY_ENTERED_TARGET = 500;
-
- private boolean mInsideTarget = false;
-
- public void enteredTarget() {
- mInsideTarget = true;
- mHandler.sendEmptyMessageDelayed(MSG_ENTERED_TARGET, DELAY_ENTERED_TARGET);
- }
-
- public void exitedTarget() {
- mInsideTarget = false;
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_ENTERED_TARGET:
- if (mInsideTarget) {
- addInstruction(R.string.accessibility_tutorial_lesson_1_text_4,
- mTargetName);
- } else {
- addInstruction(R.string.accessibility_tutorial_lesson_1_text_4_exited,
- mTargetName);
- setFlag(FLAG_TOUCHED_TARGET, false);
- }
- break;
- }
- }
- }
-
- private static final int FLAG_TOUCH_ITEMS = 0x1;
- private static final int FLAG_TOUCHED_ITEMS = 0x2;
- private static final int FLAG_TOUCHED_TARGET = 0x4;
- private static final int FLAG_TAPPED_TARGET = 0x8;
-
- private static final int MORE_EXPLORED_COUNT = 1;
- private static final int DONE_EXPLORED_COUNT = 2;
-
- private final HoverTargetHandler mHandler;
- private final AppsAdapter mAppsAdapter;
- private final GridView mAllApps;
-
- private int mTouched = 0;
-
- private int mTargetPosition;
- private CharSequence mTargetName;
-
- public TouchTutorialModule1(Context context, AccessibilityTutorialActivity controller) {
- super(context, controller, R.layout.accessibility_tutorial_1,
- R.string.accessibility_tutorial_lesson_1_title);
-
- mHandler = new HoverTargetHandler();
-
- mAppsAdapter = new AppsAdapter(context, R.layout.accessibility_tutorial_app_icon,
- R.id.app_icon);
- mAppsAdapter.setOnHoverListener(this);
-
- mAllApps = (GridView) findViewById(R.id.all_apps);
- mAllApps.setAdapter(mAppsAdapter);
- mAllApps.setOnItemClickListener(this);
-
- findViewById(R.id.next_button).setOnHoverListener(this);
-
- setSkipVisible(true);
- }
-
- @Override
- public boolean onHover(View v, MotionEvent event) {
- switch (v.getId()) {
- case R.id.app_icon:
- if (hasFlag(FLAG_TOUCH_ITEMS) && !hasFlag(FLAG_TOUCHED_ITEMS) && v.isEnabled()
- && (event.getAction() == MotionEvent.ACTION_HOVER_ENTER)) {
- mTouched++;
-
- if (mTouched >= DONE_EXPLORED_COUNT) {
- setFlag(FLAG_TOUCHED_ITEMS, true);
- addInstruction(R.string.accessibility_tutorial_lesson_1_text_3,
- mTargetName);
- } else if (mTouched == MORE_EXPLORED_COUNT) {
- addInstruction(R.string.accessibility_tutorial_lesson_1_text_2_more);
- }
-
- v.setEnabled(false);
- } else if (hasFlag(FLAG_TOUCHED_ITEMS)
- && ((Integer) v.getTag() == mTargetPosition)) {
- if (!hasFlag(FLAG_TOUCHED_TARGET)
- && (event.getAction() == MotionEvent.ACTION_HOVER_ENTER)) {
- mHandler.enteredTarget();
- setFlag(FLAG_TOUCHED_TARGET, true);
- } else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
- mHandler.exitedTarget();
- }
- }
- break;
- }
-
- return false;
- }
-
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- if (hasFlag(FLAG_TOUCHED_TARGET) && !hasFlag(FLAG_TAPPED_TARGET)
- && (position == mTargetPosition)) {
- setFlag(FLAG_TAPPED_TARGET, true);
- final CharSequence nextText = getContext().getText(
- R.string.accessibility_tutorial_next);
- addInstruction(R.string.accessibility_tutorial_lesson_1_text_5, nextText);
- setNextVisible(true);
- }
- }
-
- @Override
- public void onShown() {
- final int first = mAllApps.getFirstVisiblePosition();
- final int last = mAllApps.getLastVisiblePosition();
-
- mTargetPosition = 0;
- mTargetName = mAppsAdapter.getLabel(mTargetPosition);
-
- addInstruction(R.string.accessibility_tutorial_lesson_1_text_1);
- setFlag(FLAG_TOUCH_ITEMS, true);
- }
- }
-
- /**
- * Introduces using two fingers to scroll through a list.
- */
- private static class TouchTutorialModule2 extends TutorialModule implements
- AbsListView.OnScrollListener, View.OnHoverListener {
- private static final int FLAG_EXPLORE_LIST = 0x1;
- private static final int FLAG_SCROLL_LIST = 0x2;
- private static final int FLAG_COMPLETED_TUTORIAL = 0x4;
-
- private static final int MORE_EXPLORE_COUNT = 1;
- private static final int DONE_EXPLORE_COUNT = 2;
- private static final int MORE_SCROLL_COUNT = 2;
- private static final int DONE_SCROLL_COUNT = 4;
-
- private final AppsAdapter mAppsAdapter;
-
- private int mExploreCount = 0;
- private int mInitialVisibleItem = -1;
- private int mScrollCount = 0;
-
- public TouchTutorialModule2(Context context, AccessibilityTutorialActivity controller) {
- super(context, controller, R.layout.accessibility_tutorial_2,
- R.string.accessibility_tutorial_lesson_2_title);
-
- mAppsAdapter = new AppsAdapter(context, android.R.layout.simple_list_item_1,
- android.R.id.text1) {
- @Override
- protected void populateView(TextView text, CharSequence label, Drawable icon) {
- text.setText(label);
- text.setCompoundDrawables(icon, null, null, null);
- }
- };
- mAppsAdapter.setOnHoverListener(this);
-
- ((ListView) findViewById(R.id.list_view)).setAdapter(mAppsAdapter);
- ((ListView) findViewById(R.id.list_view)).setOnScrollListener(this);
-
- setBackVisible(true);
- }
-
- @Override
- public boolean onHover(View v, MotionEvent e) {
- if (e.getAction() != MotionEvent.ACTION_HOVER_ENTER) {
- return false;
- }
-
- switch (v.getId()) {
- case android.R.id.text1:
- if (hasFlag(FLAG_EXPLORE_LIST) && !hasFlag(FLAG_SCROLL_LIST)) {
- mExploreCount++;
-
- if (mExploreCount >= DONE_EXPLORE_COUNT) {
- addInstruction(R.string.accessibility_tutorial_lesson_2_text_3);
- setFlag(FLAG_SCROLL_LIST, true);
- } else if (mExploreCount == MORE_EXPLORE_COUNT) {
- addInstruction(R.string.accessibility_tutorial_lesson_2_text_2_more);
- }
- }
- break;
- }
-
- return false;
- }
-
- @Override
- public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
- int totalItemCount) {
- if (hasFlag(FLAG_SCROLL_LIST) && !hasFlag(FLAG_COMPLETED_TUTORIAL)) {
- if (mInitialVisibleItem < 0) {
- mInitialVisibleItem = firstVisibleItem;
- }
-
- final int scrollCount = Math.abs(mInitialVisibleItem - firstVisibleItem);
-
- if ((mScrollCount == scrollCount) || (scrollCount <= 0)) {
- return;
- } else {
- mScrollCount = scrollCount;
- }
-
- if (mScrollCount >= DONE_SCROLL_COUNT) {
- final CharSequence finishText = getContext().getText(
- R.string.accessibility_tutorial_finish);
- addInstruction(R.string.accessibility_tutorial_lesson_2_text_4, finishText);
- setFlag(FLAG_COMPLETED_TUTORIAL, true);
- setFinishVisible(true);
- } else if (mScrollCount == MORE_SCROLL_COUNT) {
- addInstruction(R.string.accessibility_tutorial_lesson_2_text_3_more);
- }
- }
- }
-
- @Override
- public void onScrollStateChanged(AbsListView view, int scrollState) {
- // Do nothing.
- }
-
- @Override
- public void onShown() {
- addInstruction(R.string.accessibility_tutorial_lesson_2_text_1);
- setFlag(FLAG_EXPLORE_LIST, true);
- }
- }
-
- /**
- * Abstract class that represents a single module within a tutorial.
- */
- private static abstract class TutorialModule extends FrameLayout implements OnClickListener {
- private final AccessibilityTutorialActivity mController;
- private final TextView mInstructions;
- private final Button mSkip;
- private final Button mBack;
- private final Button mNext;
- private final Button mFinish;
- private final int mTitleResId;
-
- /** Which bit flags have been set. */
- private long mFlags;
-
- /** Whether this module is currently focused. */
- private boolean mIsVisible;
-
- /** Handler for sending accessibility events after the current UI action. */
- private InstructionHandler mHandler = new InstructionHandler();
-
- /**
- * Constructs a new tutorial module for the given context and controller
- * with the specified layout.
- *
- * @param context The parent context.
- * @param controller The parent tutorial controller.
- * @param layoutResId The layout to use for this module.
- */
- public TutorialModule(Context context, AccessibilityTutorialActivity controller,
- int layoutResId, int titleResId) {
- super(context);
-
- mController = controller;
- mTitleResId = titleResId;
-
- final View container = LayoutInflater.from(context).inflate(
- R.layout.accessibility_tutorial_container, this, true);
-
- mInstructions = (TextView) container.findViewById(R.id.instructions);
- mSkip = (Button) container.findViewById(R.id.skip_button);
- mSkip.setOnClickListener(this);
- mBack = (Button) container.findViewById(R.id.back_button);
- mBack.setOnClickListener(this);
- mNext = (Button) container.findViewById(R.id.next_button);
- mNext.setOnClickListener(this);
- mFinish = (Button) container.findViewById(R.id.finish_button);
- mFinish.setOnClickListener(this);
-
- final TextView title = (TextView) container.findViewById(R.id.title);
-
- if (title != null) {
- title.setText(titleResId);
- }
-
- final ViewGroup contentHolder = (ViewGroup) container.findViewById(R.id.content);
- LayoutInflater.from(context).inflate(layoutResId, contentHolder, true);
- }
-
- /**
- * Called when this tutorial gains focus.
- */
- public final void activate() {
- mIsVisible = true;
-
- mFlags = 0;
- mInstructions.setVisibility(View.GONE);
- mController.setTitle(mTitleResId);
-
- onShown();
- }
-
- /**
- * Formats an instruction string and adds it to the speaking queue.
- *
- * @param resId The resource id of the instruction string.
- * @param formatArgs Optional formatting arguments.
- * @see String#format(String, Object...)
- */
- protected void addInstruction(final int resId, Object... formatArgs) {
- if (!mIsVisible) {
- return;
- }
-
- final String text = mContext.getString(resId, formatArgs);
- mHandler.addInstruction(text);
- }
-
- private void addInstructionSync(CharSequence text) {
- mInstructions.setVisibility(View.VISIBLE);
- mInstructions.setText(text);
- mInstructions.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
- }
-
- /**
- * Called when this tutorial loses focus.
- */
- public void deactivate() {
- mIsVisible = false;
-
- mController.interrupt();
- }
-
- /**
- * Returns {@code true} if the flag with the specified id has been set.
- *
- * @param flagId The id of the flag to check for.
- * @return {@code true} if the flag with the specified id has been set.
- */
- protected boolean hasFlag(int flagId) {
- return (mFlags & flagId) == flagId;
- }
-
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.skip_button:
- mController.finish();
- break;
- case R.id.back_button:
- mController.previous();
- break;
- case R.id.next_button:
- mController.next();
- break;
- case R.id.finish_button:
- mController.finish();
- break;
- }
- }
-
- public abstract void onShown();
-
- /**
- * Sets or removes the flag with the specified id.
- *
- * @param flagId The id of the flag to modify.
- * @param value {@code true} to set the flag, {@code false} to remove
- * it.
- */
- protected void setFlag(int flagId, boolean value) {
- if (value) {
- mFlags |= flagId;
- } else {
- mFlags = ~(~mFlags | flagId);
- }
- }
-
- protected void setSkipVisible(boolean visible) {
- mSkip.setVisibility(visible ? VISIBLE : GONE);
- }
-
- protected void setBackVisible(boolean visible) {
- mBack.setVisibility(visible ? VISIBLE : GONE);
- }
-
- protected void setNextVisible(boolean visible) {
- mNext.setVisibility(visible ? VISIBLE : GONE);
- }
-
- protected void setFinishVisible(boolean visible) {
- mFinish.setVisibility(visible ? VISIBLE : GONE);
- }
-
- private class InstructionHandler extends Handler {
- private static final int ADD_INSTRUCTION = 1;
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case ADD_INSTRUCTION:
- final String text = (String) msg.obj;
- addInstructionSync(text);
- break;
- }
- }
-
- public void addInstruction(String text) {
- obtainMessage(ADD_INSTRUCTION, text).sendToTarget();
- }
- }
- }
-
- /**
- * Provides a tutorial-specific class name for fired accessibility events.
- */
- public static class TutorialTextView extends TextView {
- public TutorialTextView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
- }
-}
diff --git a/src/com/android/settings/AccountPreference.java b/src/com/android/settings/AccountPreference.java
index 824420d..2cc013c 100644
--- a/src/com/android/settings/AccountPreference.java
+++ b/src/com/android/settings/AccountPreference.java
@@ -20,7 +20,6 @@ import java.util.ArrayList;
import android.accounts.Account;
import android.content.Context;
-import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.preference.Preference;
import android.util.Log;
@@ -36,25 +35,28 @@ public class AccountPreference extends Preference {
public static final int SYNC_ENABLED = 0; // all know sync adapters are enabled and OK
public static final int SYNC_DISABLED = 1; // no sync adapters are enabled
public static final int SYNC_ERROR = 2; // one or more sync adapters have a problem
+ public static final int SYNC_IN_PROGRESS = 3; // currently syncing
private int mStatus;
private Account mAccount;
private ArrayList<String> mAuthorities;
- private Drawable mProviderIcon;
private ImageView mSyncStatusIcon;
- private ImageView mProviderIconView;
+ private boolean mShowTypeIcon;
public AccountPreference(Context context, Account account, Drawable icon,
- ArrayList<String> authorities) {
+ ArrayList<String> authorities, boolean showTypeIcon) {
super(context);
mAccount = account;
mAuthorities = authorities;
- mProviderIcon = icon;
- setWidgetLayoutResource(R.layout.account_preference);
+ mShowTypeIcon = showTypeIcon;
+ if (showTypeIcon) {
+ setIcon(icon);
+ } else {
+ setIcon(getSyncStatusIcon(SYNC_DISABLED));
+ }
setTitle(mAccount.name);
setSummary("");
setPersistent(false);
- setSyncStatus(SYNC_DISABLED);
- setIcon(mProviderIcon);
+ setSyncStatus(SYNC_DISABLED, false);
}
public Account getAccount() {
@@ -68,25 +70,22 @@ public class AccountPreference extends Preference {
@Override
protected void onBindView(View view) {
super.onBindView(view);
- setSummary(getSyncStatusMessage(mStatus));
- mSyncStatusIcon = (ImageView) view.findViewById(R.id.syncStatusIcon);
- mSyncStatusIcon.setImageResource(getSyncStatusIcon(mStatus));
- mSyncStatusIcon.setContentDescription(getSyncContentDescription(mStatus));
- }
-
- public void setProviderIcon(Drawable icon) {
- mProviderIcon = icon;
- if (mProviderIconView != null) {
- mProviderIconView.setImageDrawable(icon);
+ if (!mShowTypeIcon) {
+ mSyncStatusIcon = (ImageView) view.findViewById(android.R.id.icon);
+ mSyncStatusIcon.setImageResource(getSyncStatusIcon(mStatus));
+ mSyncStatusIcon.setContentDescription(getSyncContentDescription(mStatus));
}
}
- public void setSyncStatus(int status) {
+ public void setSyncStatus(int status, boolean updateSummary) {
mStatus = status;
- if (mSyncStatusIcon != null) {
+ if (!mShowTypeIcon && mSyncStatusIcon != null) {
mSyncStatusIcon.setImageResource(getSyncStatusIcon(status));
+ mSyncStatusIcon.setContentDescription(getSyncContentDescription(mStatus));
+ }
+ if (updateSummary) {
+ setSummary(getSyncStatusMessage(status));
}
- setSummary(getSyncStatusMessage(status));
}
private int getSyncStatusMessage(int status) {
@@ -101,6 +100,9 @@ public class AccountPreference extends Preference {
case SYNC_ERROR:
res = R.string.sync_error;
break;
+ case SYNC_IN_PROGRESS:
+ res = R.string.sync_in_progress;
+ break;
default:
res = R.string.sync_error;
Log.e(TAG, "Unknown sync status: " + status);
@@ -120,6 +122,9 @@ public class AccountPreference extends Preference {
case SYNC_ERROR:
res = R.drawable.ic_sync_red_holo;
break;
+ case SYNC_IN_PROGRESS:
+ res = R.drawable.ic_sync_green_holo;
+ break;
default:
res = R.drawable.ic_sync_red_holo;
Log.e(TAG, "Unknown sync status: " + status);
@@ -140,13 +145,4 @@ public class AccountPreference extends Preference {
return getContext().getString(R.string.accessibility_sync_error);
}
}
-
- @Override
- public int compareTo(Preference other) {
- if (!(other instanceof AccountPreference)) {
- // Put other preference types above us
- return 1;
- }
- return mAccount.name.compareTo(((AccountPreference) other).mAccount.name);
- }
}
diff --git a/src/com/android/settings/AllowBindAppWidgetActivity.java b/src/com/android/settings/AllowBindAppWidgetActivity.java
new file mode 100644
index 0000000..2f54f8e
--- /dev/null
+++ b/src/com/android/settings/AllowBindAppWidgetActivity.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2012 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.appwidget.AppWidgetManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.widget.CheckBox;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+
+/**
+ * This activity is displayed when an app launches the BIND_APPWIDGET intent. This allows apps
+ * that don't have the BIND_APPWIDGET permission to bind specific widgets.
+ */
+public class AllowBindAppWidgetActivity extends AlertActivity implements
+ DialogInterface.OnClickListener {
+
+ private CheckBox mAlwaysUse;
+ private int mAppWidgetId;
+ private ComponentName mComponentName;
+ private String mCallingPackage;
+ private AppWidgetManager mAppWidgetManager;
+
+ // Indicates whether this activity was closed because of a click
+ private boolean mClicked;
+
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == AlertDialog.BUTTON_POSITIVE) {
+ // By default, set the result to cancelled
+ setResult(RESULT_CANCELED);
+ if (mAppWidgetId != -1 && mComponentName != null && mCallingPackage != null) {
+ try {
+ mAppWidgetManager.bindAppWidgetId(mAppWidgetId, mComponentName);
+ Intent result = new Intent();
+ result.putExtra("EXTRA_APPWIDGET_ID", mAppWidgetId);
+ setResult(RESULT_OK);
+ } catch (Exception e) {
+ Log.v("BIND_APPWIDGET", "Error binding widget with id "
+ + mAppWidgetId + " and component " + mComponentName);
+ }
+ }
+ boolean alwaysAllowBind = mAlwaysUse.isChecked();
+ if (alwaysAllowBind != mAppWidgetManager.hasBindAppWidgetPermission(mCallingPackage)) {
+ mAppWidgetManager.setBindAppWidgetPermission(mCallingPackage, alwaysAllowBind);
+ }
+ }
+ finish();
+ }
+
+ protected void onDestroy() {
+ if (!mClicked) {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ super.onDestroy();
+ }
+
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Intent intent = getIntent();
+ CharSequence label = "";
+ if (intent != null) {
+ try {
+ mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
+ mComponentName = (ComponentName)
+ intent.getParcelableExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER);
+ mCallingPackage = getCallingPackage();
+ PackageManager pm = getPackageManager();
+ ApplicationInfo ai = pm.getApplicationInfo(mCallingPackage, 0);
+ label = pm.getApplicationLabel(ai);
+ } catch (Exception e) {
+ mAppWidgetId = -1;
+ mComponentName = null;
+ mCallingPackage = null;
+ Log.v("BIND_APPWIDGET", "Error getting parameters");
+ setResult(RESULT_CANCELED);
+ finish();
+ return;
+ }
+ }
+ AlertController.AlertParams ap = mAlertParams;
+ ap.mTitle = getString(R.string.allow_bind_app_widget_activity_allow_bind_title);
+ ap.mMessage = getString(R.string.allow_bind_app_widget_activity_allow_bind, label);
+ ap.mPositiveButtonText = getString(R.string.create);
+ ap.mNegativeButtonText = getString(android.R.string.cancel);
+ ap.mPositiveButtonListener = this;
+ ap.mNegativeButtonListener = this;
+ LayoutInflater inflater =
+ (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
+ mAlwaysUse = (CheckBox) ap.mView.findViewById(com.android.internal.R.id.alwaysUse);
+ mAlwaysUse.setText(getString(R.string.allow_bind_app_widget_activity_always_allow_bind, label));
+
+ mAlwaysUse.setPadding(mAlwaysUse.getPaddingLeft(),
+ mAlwaysUse.getPaddingTop(),
+ mAlwaysUse.getPaddingRight(),
+ (int) (mAlwaysUse.getPaddingBottom() +
+ getResources().getDimension(R.dimen.bind_app_widget_dialog_checkbox_bottom_padding)));
+
+ mAppWidgetManager = AppWidgetManager.getInstance(this);
+ mAlwaysUse.setChecked(mAppWidgetManager.hasBindAppWidgetPermission(mCallingPackage));
+
+ setupAlert();
+ }
+}
diff --git a/src/com/android/settings/AppPicker.java b/src/com/android/settings/AppPicker.java
new file mode 100644
index 0000000..e58b835
--- /dev/null
+++ b/src/com/android/settings/AppPicker.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings;
+
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import com.android.settings.applications.AppViewHolder;
+
+import android.app.ActivityManagerNative;
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+public class AppPicker extends ListActivity {
+ private AppListAdapter mAdapter;
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ mAdapter = new AppListAdapter(this);
+ if (mAdapter.getCount() <= 0) {
+ finish();
+ } else {
+ setListAdapter(mAdapter);
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ }
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ MyApplicationInfo app = mAdapter.getItem(position);
+ Intent intent = new Intent();
+ if (app.info != null) intent.setAction(app.info.packageName);
+ setResult(RESULT_OK, intent);
+ finish();
+ }
+
+ class MyApplicationInfo {
+ ApplicationInfo info;
+ CharSequence label;
+ }
+
+ public class AppListAdapter extends ArrayAdapter<MyApplicationInfo> {
+ private final List<MyApplicationInfo> mPackageInfoList = new ArrayList<MyApplicationInfo>();
+ private final LayoutInflater mInflater;
+
+ public AppListAdapter(Context context) {
+ super(context, 0);
+ mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ List<ApplicationInfo> pkgs = context.getPackageManager().getInstalledApplications(0);
+ for (int i=0; i<pkgs.size(); i++) {
+ ApplicationInfo ai = pkgs.get(i);
+ if (ai.uid == Process.SYSTEM_UID) {
+ continue;
+ }
+ // On a user build, we only allow debugging of apps that
+ // are marked as debuggable. Otherwise (for platform development)
+ // we allow all apps.
+ if ((ai.flags&ApplicationInfo.FLAG_DEBUGGABLE) == 0
+ && "user".equals(Build.TYPE)) {
+ continue;
+ }
+ MyApplicationInfo info = new MyApplicationInfo();
+ info.info = ai;
+ info.label = info.info.loadLabel(getPackageManager()).toString();
+ mPackageInfoList.add(info);
+ }
+ Collections.sort(mPackageInfoList, sDisplayNameComparator);
+ MyApplicationInfo info = new MyApplicationInfo();
+ info.label = context.getText(R.string.no_application);
+ mPackageInfoList.add(0, info);
+ addAll(mPackageInfoList);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ // A ViewHolder keeps references to children views to avoid unnecessary calls
+ // to findViewById() on each row.
+ AppViewHolder holder = AppViewHolder.createOrRecycle(mInflater, convertView);
+ convertView = holder.rootView;
+ MyApplicationInfo info = getItem(position);
+ holder.appName.setText(info.label);
+ if (info.info != null) {
+ holder.appIcon.setImageDrawable(info.info.loadIcon(getPackageManager()));
+ holder.appSize.setText(info.info.packageName);
+ } else {
+ holder.appIcon.setImageDrawable(null);
+ holder.appSize.setText("");
+ }
+ holder.disabled.setVisibility(View.GONE);
+ holder.checkBox.setVisibility(View.GONE);
+ return convertView;
+ }
+ }
+
+ private final static Comparator<MyApplicationInfo> sDisplayNameComparator
+ = new Comparator<MyApplicationInfo>() {
+ public final int
+ compare(MyApplicationInfo a, MyApplicationInfo b) {
+ return collator.compare(a.label, b.label);
+ }
+
+ private final Collator collator = Collator.getInstance();
+ };
+}
diff --git a/src/com/android/settings/BiometricWeakLiveliness.java b/src/com/android/settings/BiometricWeakLiveliness.java
new file mode 100644
index 0000000..6bba77f
--- /dev/null
+++ b/src/com/android/settings/BiometricWeakLiveliness.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2012 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.ActionBar;
+import android.app.Activity;
+import android.app.Fragment;
+import android.os.Bundle;
+import android.os.Handler;
+import android.content.Intent;
+import android.preference.PreferenceActivity;
+import android.util.Log;
+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;
+
+import com.android.internal.widget.LockPatternUtils;
+
+public class BiometricWeakLiveliness extends Fragment
+ implements CompoundButton.OnCheckedChangeListener {
+ private static final int CONFIRM_EXISTING_FOR_BIOMETRIC_WEAK_LIVELINESS_OFF = 125;
+
+ private View mView;
+ private ChooseLockSettingsHelper mChooseLockSettingsHelper;
+ private LockPatternUtils mLockPatternUtils;
+ private Switch mActionBarSwitch;
+ private boolean mSuppressCheckChanged;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Activity activity = getActivity();
+
+ mActionBarSwitch = new Switch(activity);
+ mSuppressCheckChanged = false;
+
+ 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);
+ mActionBarSwitch.setPadding(0, 0, padding, 0);
+ activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
+ ActionBar.DISPLAY_SHOW_CUSTOM);
+ activity.getActionBar().setCustomView(mActionBarSwitch, new ActionBar.LayoutParams(
+ ActionBar.LayoutParams.WRAP_CONTENT,
+ ActionBar.LayoutParams.WRAP_CONTENT,
+ Gravity.CENTER_VERTICAL | Gravity.RIGHT));
+ activity.getActionBar().setTitle(R.string.biometric_weak_liveliness_title);
+ }
+ }
+
+ mActionBarSwitch.setOnCheckedChangeListener(this);
+
+ mLockPatternUtils = new LockPatternUtils(getActivity());
+ mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity());
+ mActionBarSwitch.setChecked(mLockPatternUtils.isBiometricWeakLivelinessEnabled());
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ mView = inflater.inflate(R.layout.biometric_weak_liveliness, container, false);
+ initView(mView);
+ return mView;
+ }
+
+ private void initView(View view) {
+ mActionBarSwitch.setOnCheckedChangeListener(this);
+ mActionBarSwitch.setChecked(mLockPatternUtils.isBiometricWeakLivelinessEnabled());
+ }
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean desiredState) {
+ if (mSuppressCheckChanged) {
+ return;
+ }
+ mActionBarSwitch.setEnabled(false);
+ if (desiredState) {
+ mLockPatternUtils.setBiometricWeakLivelinessEnabled(true);
+ mActionBarSwitch.setChecked(true);
+ } else {
+ // In this case the user has just turned it off, but this action requires them
+ // to confirm their password. We need to turn the switch back on until
+ // they've confirmed their password
+ mActionBarSwitch.setChecked(true);
+ mActionBarSwitch.requestLayout();
+ ChooseLockSettingsHelper helper =
+ new ChooseLockSettingsHelper(this.getActivity(), this);
+ if (!helper.launchConfirmationActivity(
+ CONFIRM_EXISTING_FOR_BIOMETRIC_WEAK_LIVELINESS_OFF, null, null)) {
+ // If this returns false, it means no password confirmation is required, so
+ // go ahead and turn it off here.
+ // Note: currently a backup is required for biometric_weak so this code path
+ // can't be reached, but is here in case things change in the future
+ mLockPatternUtils.setBiometricWeakLivelinessEnabled(false);
+ mActionBarSwitch.setChecked(false);
+ mActionBarSwitch.requestLayout();
+ }
+ }
+ mActionBarSwitch.setEnabled(true);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == CONFIRM_EXISTING_FOR_BIOMETRIC_WEAK_LIVELINESS_OFF &&
+ resultCode == Activity.RESULT_OK) {
+ final LockPatternUtils lockPatternUtils = mChooseLockSettingsHelper.utils();
+ lockPatternUtils.setBiometricWeakLivelinessEnabled(false);
+ mSuppressCheckChanged = true;
+ mActionBarSwitch.setChecked(false);
+ mSuppressCheckChanged = false;
+ return;
+ }
+ }
+
+}
diff --git a/src/com/android/settings/BrightnessPreference.java b/src/com/android/settings/BrightnessPreference.java
index c34d212..eff5c50 100644
--- a/src/com/android/settings/BrightnessPreference.java
+++ b/src/com/android/settings/BrightnessPreference.java
@@ -45,6 +45,9 @@ public class BrightnessPreference extends SeekBarDialogPreference implements
private int mOldAutomatic;
private boolean mAutomaticAvailable;
+ private boolean mAutomaticMode;
+
+ private int mCurBrightness = -1;
private boolean mRestoredOldState;
@@ -52,11 +55,14 @@ public class BrightnessPreference extends SeekBarDialogPreference implements
// doesn't set the backlight to 0 and get stuck
private int mScreenBrightnessDim =
getContext().getResources().getInteger(com.android.internal.R.integer.config_screenBrightnessDim);
- private static final int MAXIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_ON;
+ private static final int MAXIMUM_BACKLIGHT = android.os.PowerManager.BRIGHTNESS_ON;
+
+ private static final int SEEK_BAR_RANGE = 10000;
private ContentObserver mBrightnessObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
+ mCurBrightness = -1;
onBrightnessChanged();
}
};
@@ -97,24 +103,26 @@ public class BrightnessPreference extends SeekBarDialogPreference implements
super.onBindDialogView(view);
mSeekBar = getSeekBar(view);
- mSeekBar.setMax(MAXIMUM_BACKLIGHT - mScreenBrightnessDim);
- mOldBrightness = getBrightness(0);
- mSeekBar.setProgress(mOldBrightness - mScreenBrightnessDim);
+ mSeekBar.setMax(SEEK_BAR_RANGE);
+ mOldBrightness = getBrightness();
+ mSeekBar.setProgress(mOldBrightness);
mCheckBox = (CheckBox)view.findViewById(R.id.automatic_mode);
if (mAutomaticAvailable) {
mCheckBox.setOnCheckedChangeListener(this);
mOldAutomatic = getBrightnessMode(0);
- mCheckBox.setChecked(mOldAutomatic != 0);
+ mAutomaticMode = mOldAutomatic == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
+ mCheckBox.setChecked(mAutomaticMode);
+ mSeekBar.setEnabled(!mAutomaticMode);
} else {
- mCheckBox.setVisibility(View.GONE);
+ mSeekBar.setEnabled(true);
}
mSeekBar.setOnSeekBarChangeListener(this);
}
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromTouch) {
- setBrightness(progress + mScreenBrightnessDim);
+ setBrightness(progress, false);
}
public void onStartTrackingTouch(SeekBar seekBar) {
@@ -128,19 +136,29 @@ public class BrightnessPreference extends SeekBarDialogPreference implements
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
setMode(isChecked ? Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC
: Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
- if (!isChecked) {
- setBrightness(mSeekBar.getProgress() + mScreenBrightnessDim);
- }
+ mSeekBar.setProgress(getBrightness());
+ mSeekBar.setEnabled(!mAutomaticMode);
+ setBrightness(mSeekBar.getProgress(), false);
}
- private int getBrightness(int defaultValue) {
- int brightness = defaultValue;
- try {
- brightness = Settings.System.getInt(getContext().getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS);
- } catch (SettingNotFoundException snfe) {
+ private int getBrightness() {
+ int mode = getBrightnessMode(0);
+ float brightness = 0;
+ if (false && mode == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC) {
+ brightness = Settings.System.getFloat(getContext().getContentResolver(),
+ Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0);
+ brightness = (brightness+1)/2;
+ } else {
+ if (mCurBrightness < 0) {
+ brightness = Settings.System.getInt(getContext().getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS, 100);
+ } else {
+ brightness = mCurBrightness;
+ }
+ brightness = (brightness - mScreenBrightnessDim)
+ / (MAXIMUM_BACKLIGHT - mScreenBrightnessDim);
}
- return brightness;
+ return (int)(brightness*SEEK_BAR_RANGE);
}
private int getBrightnessMode(int defaultValue) {
@@ -154,13 +172,15 @@ public class BrightnessPreference extends SeekBarDialogPreference implements
}
private void onBrightnessChanged() {
- int brightness = getBrightness(MAXIMUM_BACKLIGHT);
- mSeekBar.setProgress(brightness - mScreenBrightnessDim);
+ mSeekBar.setProgress(getBrightness());
}
private void onBrightnessModeChanged() {
- boolean checked = getBrightnessMode(0) != 0;
+ boolean checked = getBrightnessMode(0)
+ == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
mCheckBox.setChecked(checked);
+ mSeekBar.setProgress(getBrightness());
+ mSeekBar.setEnabled(!checked);
}
@Override
@@ -170,9 +190,7 @@ public class BrightnessPreference extends SeekBarDialogPreference implements
final ContentResolver resolver = getContext().getContentResolver();
if (positiveResult) {
- Settings.System.putInt(resolver,
- Settings.System.SCREEN_BRIGHTNESS,
- mSeekBar.getProgress() + mScreenBrightnessDim);
+ setBrightness(mSeekBar.getProgress(), true);
} else {
restoreOldState();
}
@@ -187,30 +205,53 @@ public class BrightnessPreference extends SeekBarDialogPreference implements
if (mAutomaticAvailable) {
setMode(mOldAutomatic);
}
- if (!mAutomaticAvailable || mOldAutomatic == 0) {
- setBrightness(mOldBrightness);
- }
+ setBrightness(mOldBrightness, false);
mRestoredOldState = true;
+ mCurBrightness = -1;
}
- private void setBrightness(int brightness) {
- try {
- IPowerManager power = IPowerManager.Stub.asInterface(
- ServiceManager.getService("power"));
- if (power != null) {
- power.setBacklightBrightness(brightness);
+ private void setBrightness(int brightness, boolean write) {
+ if (mAutomaticMode) {
+ if (false) {
+ float valf = (((float)brightness*2)/SEEK_BAR_RANGE) - 1.0f;
+ try {
+ IPowerManager power = IPowerManager.Stub.asInterface(
+ ServiceManager.getService("power"));
+ if (power != null) {
+ power.setAutoBrightnessAdjustment(valf);
+ }
+ if (write) {
+ final ContentResolver resolver = getContext().getContentResolver();
+ Settings.System.putFloat(resolver,
+ Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, valf);
+ }
+ } catch (RemoteException doe) {
+ }
+ }
+ } else {
+ int range = (MAXIMUM_BACKLIGHT - mScreenBrightnessDim);
+ brightness = (brightness*range)/SEEK_BAR_RANGE + mScreenBrightnessDim;
+ try {
+ IPowerManager power = IPowerManager.Stub.asInterface(
+ ServiceManager.getService("power"));
+ if (power != null) {
+ power.setBacklightBrightness(brightness);
+ }
+ if (write) {
+ mCurBrightness = -1;
+ final ContentResolver resolver = getContext().getContentResolver();
+ Settings.System.putInt(resolver,
+ Settings.System.SCREEN_BRIGHTNESS, brightness);
+ } else {
+ mCurBrightness = brightness;
+ }
+ } catch (RemoteException doe) {
}
- } catch (RemoteException doe) {
-
}
}
private void setMode(int mode) {
- if (mode == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC) {
- mSeekBar.setVisibility(View.GONE);
- } else {
- mSeekBar.setVisibility(View.VISIBLE);
- }
+ mAutomaticMode = mode == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
Settings.System.putInt(getContext().getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS_MODE, mode);
}
@@ -226,6 +267,7 @@ public class BrightnessPreference extends SeekBarDialogPreference implements
myState.progress = mSeekBar.getProgress();
myState.oldAutomatic = mOldAutomatic == 1;
myState.oldProgress = mOldBrightness;
+ myState.curBrightness = mCurBrightness;
// Restore the old state when the activity or dialog is being paused
restoreOldState();
@@ -245,7 +287,8 @@ public class BrightnessPreference extends SeekBarDialogPreference implements
mOldBrightness = myState.oldProgress;
mOldAutomatic = myState.oldAutomatic ? 1 : 0;
setMode(myState.automatic ? 1 : 0);
- setBrightness(myState.progress + mScreenBrightnessDim);
+ setBrightness(myState.progress, false);
+ mCurBrightness = myState.curBrightness;
}
private static class SavedState extends BaseSavedState {
@@ -254,6 +297,7 @@ public class BrightnessPreference extends SeekBarDialogPreference implements
boolean oldAutomatic;
int progress;
int oldProgress;
+ int curBrightness;
public SavedState(Parcel source) {
super(source);
@@ -261,6 +305,7 @@ public class BrightnessPreference extends SeekBarDialogPreference implements
progress = source.readInt();
oldAutomatic = source.readInt() == 1;
oldProgress = source.readInt();
+ curBrightness = source.readInt();
}
@Override
@@ -270,6 +315,7 @@ public class BrightnessPreference extends SeekBarDialogPreference implements
dest.writeInt(progress);
dest.writeInt(oldAutomatic ? 1 : 0);
dest.writeInt(oldProgress);
+ dest.writeInt(curBrightness);
}
public SavedState(Parcelable superState) {
diff --git a/src/com/android/settings/ChooseLockGeneric.java b/src/com/android/settings/ChooseLockGeneric.java
index df421a2..0f9fcee 100644
--- a/src/com/android/settings/ChooseLockGeneric.java
+++ b/src/com/android/settings/ChooseLockGeneric.java
@@ -33,6 +33,8 @@ import android.widget.ListView;
import com.android.internal.widget.LockPatternUtils;
+import libcore.util.MutableBoolean;
+
public class ChooseLockGeneric extends PreferenceActivity {
@Override
@@ -56,6 +58,7 @@ public class ChooseLockGeneric extends PreferenceActivity {
private static final int FALLBACK_REQUEST = 101;
private static final String PASSWORD_CONFIRMED = "password_confirmed";
private static final String CONFIRM_CREDENTIALS = "confirm_credentials";
+ private static final String WAITING_FOR_CONFIRMATION = "waiting_for_confirmation";
public static final String MINIMUM_QUALITY_KEY = "minimum_quality";
private static final boolean ALWAY_SHOW_TUTORIAL = true;
@@ -64,6 +67,7 @@ public class ChooseLockGeneric extends PreferenceActivity {
private DevicePolicyManager mDPM;
private KeyStore mKeyStore;
private boolean mPasswordConfirmed = false;
+ private boolean mWaitingForConfirmation = false;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -80,20 +84,25 @@ public class ChooseLockGeneric extends PreferenceActivity {
if (savedInstanceState != null) {
mPasswordConfirmed = savedInstanceState.getBoolean(PASSWORD_CONFIRMED);
+ mWaitingForConfirmation = savedInstanceState.getBoolean(WAITING_FOR_CONFIRMATION);
}
- if (!mPasswordConfirmed) {
+ if (mPasswordConfirmed) {
+ updatePreferencesOrFinish();
+ } else if (!mWaitingForConfirmation) {
ChooseLockSettingsHelper helper =
new ChooseLockSettingsHelper(this.getActivity(), this);
if (!helper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST, null, null)) {
mPasswordConfirmed = true; // no password set, so no need to confirm
updatePreferencesOrFinish();
+ } else {
+ mWaitingForConfirmation = true;
}
- } else {
- updatePreferencesOrFinish();
}
}
+
+
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
Preference preference) {
@@ -141,6 +150,7 @@ public class ChooseLockGeneric extends PreferenceActivity {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
+ mWaitingForConfirmation = false;
if (requestCode == CONFIRM_EXISTING_REQUEST && resultCode == Activity.RESULT_OK) {
mPasswordConfirmed = true;
updatePreferencesOrFinish();
@@ -159,6 +169,7 @@ public class ChooseLockGeneric extends PreferenceActivity {
super.onSaveInstanceState(outState);
// Saved so we don't force user to re-enter their password if configuration changes
outState.putBoolean(PASSWORD_CONFIRMED, mPasswordConfirmed);
+ outState.putBoolean(WAITING_FOR_CONFIRMATION, mWaitingForConfirmation);
}
private void updatePreferencesOrFinish() {
@@ -167,23 +178,37 @@ public class ChooseLockGeneric extends PreferenceActivity {
if (quality == -1) {
// If caller didn't specify password quality, show UI and allow the user to choose.
quality = intent.getIntExtra(MINIMUM_QUALITY_KEY, -1);
- quality = upgradeQuality(quality);
+ MutableBoolean allowBiometric = new MutableBoolean(false);
+ quality = upgradeQuality(quality, allowBiometric);
final PreferenceScreen prefScreen = getPreferenceScreen();
if (prefScreen != null) {
prefScreen.removeAll();
}
addPreferencesFromResource(R.xml.security_settings_picker);
- disableUnusablePreferences(quality);
+ disableUnusablePreferences(quality, allowBiometric);
} else {
updateUnlockMethodAndFinish(quality, false);
}
}
- private int upgradeQuality(int quality) {
+ /** increases the quality if necessary, and returns whether biometric is allowed */
+ private int upgradeQuality(int quality, MutableBoolean allowBiometric) {
quality = upgradeQualityForDPM(quality);
- quality = upgradeQualityForEncryption(quality);
quality = upgradeQualityForKeyStore(quality);
- return 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;
}
private int upgradeQualityForDPM(int quality) {
@@ -228,7 +253,7 @@ public class ChooseLockGeneric extends PreferenceActivity {
*
* @param quality the requested quality.
*/
- private void disableUnusablePreferences(final int quality) {
+ private void disableUnusablePreferences(final int quality, MutableBoolean allowBiometric) {
final PreferenceScreen entries = getPreferenceScreen();
final boolean onlyShowFallback = getActivity().getIntent()
.getBooleanExtra(LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, false);
@@ -245,7 +270,8 @@ public class ChooseLockGeneric extends PreferenceActivity {
} else if (KEY_UNLOCK_SET_NONE.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
} else if (KEY_UNLOCK_SET_BIOMETRIC_WEAK.equals(key)) {
- enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK;
+ enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK ||
+ allowBiometric.value;
visible = weakBiometricAvailable; // If not available, then don't show it.
} else if (KEY_UNLOCK_SET_PATTERN.equals(key)) {
enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
@@ -311,7 +337,7 @@ public class ChooseLockGeneric extends PreferenceActivity {
final boolean isFallback = getActivity().getIntent()
.getBooleanExtra(LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, false);
- quality = upgradeQuality(quality);
+ quality = upgradeQuality(quality, null);
if (quality >= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) {
int minLength = mDPM.getPasswordMinimumLength(null);
@@ -360,5 +386,11 @@ public class ChooseLockGeneric extends PreferenceActivity {
}
finish();
}
+
+ @Override
+ protected int getHelpResource() {
+ return R.string.help_url_choose_lockscreen;
+ }
+
}
}
diff --git a/src/com/android/settings/CredentialStorage.java b/src/com/android/settings/CredentialStorage.java
index e246fce..c12d06c 100644
--- a/src/com/android/settings/CredentialStorage.java
+++ b/src/com/android/settings/CredentialStorage.java
@@ -25,6 +25,7 @@ import android.content.res.Resources;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.RemoteException;
+import android.security.Credentials;
import android.security.KeyChain.KeyChainConnection;
import android.security.KeyChain;
import android.security.KeyStore;
@@ -187,13 +188,38 @@ public final class CredentialStorage extends Activity {
if (mInstallBundle != null && !mInstallBundle.isEmpty()) {
Bundle bundle = mInstallBundle;
mInstallBundle = null;
- for (String key : bundle.keySet()) {
- byte[] value = bundle.getByteArray(key);
- if (value != null && !mKeyStore.put(key, value)) {
+
+ if (bundle.containsKey(Credentials.EXTRA_USER_PRIVATE_KEY_NAME)) {
+ String key = bundle.getString(Credentials.EXTRA_USER_PRIVATE_KEY_NAME);
+ byte[] value = bundle.getByteArray(Credentials.EXTRA_USER_PRIVATE_KEY_DATA);
+
+ if (!mKeyStore.importKey(key, value)) {
Log.e(TAG, "Failed to install " + key);
return;
}
}
+
+ if (bundle.containsKey(Credentials.EXTRA_USER_CERTIFICATE_NAME)) {
+ String certName = bundle.getString(Credentials.EXTRA_USER_CERTIFICATE_NAME);
+ byte[] certData = bundle.getByteArray(Credentials.EXTRA_USER_CERTIFICATE_DATA);
+
+ if (!mKeyStore.put(certName, certData)) {
+ Log.e(TAG, "Failed to install " + certName);
+ return;
+ }
+ }
+
+ if (bundle.containsKey(Credentials.EXTRA_CA_CERTIFICATES_NAME)) {
+ String caListName = bundle.getString(Credentials.EXTRA_CA_CERTIFICATES_NAME);
+ byte[] caListData = bundle.getByteArray(Credentials.EXTRA_CA_CERTIFICATES_DATA);
+
+ if (!mKeyStore.put(caListName, caListData)) {
+ Log.e(TAG, "Failed to install " + caListName);
+ return;
+ }
+
+ }
+
setResult(RESULT_OK);
}
}
@@ -312,8 +338,8 @@ public final class CredentialStorage extends Activity {
Resources res = getResources();
boolean launched = new ChooseLockSettingsHelper(this)
.launchConfirmationActivity(CONFIRM_KEY_GUARD_REQUEST,
- res.getText(R.string.master_clear_gesture_prompt),
- res.getText(R.string.master_clear_gesture_explanation));
+ res.getText(R.string.credentials_install_gesture_prompt),
+ res.getText(R.string.credentials_install_gesture_explanation));
return launched;
}
diff --git a/src/com/android/settings/CryptKeeper.java b/src/com/android/settings/CryptKeeper.java
index 655d8ad..5fb72ed 100644
--- a/src/com/android/settings/CryptKeeper.java
+++ b/src/com/android/settings/CryptKeeper.java
@@ -22,6 +22,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.media.AudioManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
@@ -32,12 +33,18 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.storage.IMountService;
+import android.provider.Settings;
import android.telephony.TelephonyManager;
+import android.text.Editable;
import android.text.TextUtils;
+import android.text.TextWatcher;
import android.util.Log;
import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
+import android.view.View.OnKeyListener;
+import android.view.View.OnTouchListener;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
@@ -48,6 +55,7 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.Phone;
import java.util.List;
@@ -64,14 +72,19 @@ import java.util.List;
* -n com.android.settings/.CryptKeeper
* </pre>
*/
-public class CryptKeeper extends Activity implements TextView.OnEditorActionListener {
+public class CryptKeeper extends Activity implements TextView.OnEditorActionListener,
+ OnKeyListener, OnTouchListener, TextWatcher {
private static final String TAG = "CryptKeeper";
private static final String DECRYPT_STATE = "trigger_restart_framework";
-
- private static final int UPDATE_PROGRESS = 1;
- private static final int COOLDOWN = 2;
-
+ /** Message sent to us to indicate encryption update progress. */
+ private static final int MESSAGE_UPDATE_PROGRESS = 1;
+ /** Message sent to us to cool-down (waste user's time between password attempts) */
+ private static final int MESSAGE_COOLDOWN = 2;
+ /** Message sent to us to indicate alerting the user that we are waiting for password entry */
+ private static final int MESSAGE_NOTIFY = 3;
+
+ // Constants used to control policy.
private static final int MAX_FAILED_ATTEMPTS = 30;
private static final int COOL_DOWN_ATTEMPTS = 10;
private static final int COOL_DOWN_INTERVAL = 30; // 30 seconds
@@ -83,18 +96,21 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
private static final String EXTRA_FORCE_VIEW =
"com.android.settings.CryptKeeper.DEBUG_FORCE_VIEW";
private static final String FORCE_VIEW_PROGRESS = "progress";
- private static final String FORCE_VIEW_ENTRY = "entry";
private static final String FORCE_VIEW_ERROR = "error";
+ private static final String FORCE_VIEW_PASSWORD = "password";
- /** When encryption is detected, this flag indivates whether or not we've checked for erros. */
+ /** When encryption is detected, this flag indicates whether or not we've checked for errors. */
private boolean mValidationComplete;
private boolean mValidationRequested;
/** A flag to indicate that the volume is in a bad state (e.g. partially encrypted). */
private boolean mEncryptionGoneBad;
-
+ /** A flag to indicate when the back event should be ignored */
+ private boolean mIgnoreBack = false;
private int mCooldown;
PowerManager.WakeLock mWakeLock;
private EditText mPasswordEntry;
+ /** Number of calls to {@link #notifyUser()} to ignore before notifying. */
+ private int mNotificationCountdown = 0;
/**
* Used to propagate state through configuration changes (e.g. screen rotation)
@@ -107,19 +123,26 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
}
}
- // This activity is used to fade the screen to black after the password is entered.
- public static class Blank extends Activity {
+ /**
+ * Activity used to fade the screen to black after the password is entered.
+ */
+ public static class FadeToBlack extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.crypt_keeper_blank);
}
+ /** Ignore all back events. */
+ @Override
+ public void onBackPressed() {
+ return;
+ }
}
private class DecryptTask extends AsyncTask<String, Void, Integer> {
@Override
protected Integer doInBackground(String... params) {
- IMountService service = getMountService();
+ final IMountService service = getMountService();
try {
return service.decryptStorage(params[0]);
} catch (Exception e) {
@@ -135,7 +158,7 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
// so this activity animates to black before the devices starts. Note
// It has 1 second to complete the animation or it will be frozen
// until the boot animation comes back up.
- Intent intent = new Intent(CryptKeeper.this, Blank.class);
+ Intent intent = new Intent(CryptKeeper.this, FadeToBlack.class);
finish();
startActivity(intent);
} else if (failedAttempts == MAX_FAILED_ATTEMPTS) {
@@ -145,10 +168,8 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
mCooldown = COOL_DOWN_INTERVAL;
cooldown();
} else {
- TextView tv = (TextView) findViewById(R.id.status);
- tv.setText(R.string.try_again);
- tv.setVisibility(View.VISIBLE);
-
+ final TextView status = (TextView) findViewById(R.id.status);
+ status.setText(R.string.try_again);
// Reenable the password entry
mPasswordEntry.setEnabled(true);
}
@@ -158,7 +179,7 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
private class ValidationTask extends AsyncTask<Void, Void, Boolean> {
@Override
protected Boolean doInBackground(Void... params) {
- IMountService service = getMountService();
+ final IMountService service = getMountService();
try {
Log.d(TAG, "Validating encryption state.");
int state = service.getEncryptionState();
@@ -190,17 +211,23 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case UPDATE_PROGRESS:
+ case MESSAGE_UPDATE_PROGRESS:
updateProgress();
break;
- case COOLDOWN:
+ case MESSAGE_COOLDOWN:
cooldown();
break;
+
+ case MESSAGE_NOTIFY:
+ notifyUser();
+ break;
}
}
};
+ private AudioManager mAudioManager;
+
/** @return whether or not this Activity was started for debugging the UI only. */
private boolean isDebugView() {
return getIntent().hasExtra(EXTRA_FORCE_VIEW);
@@ -211,12 +238,48 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
return viewType.equals(getIntent().getStringExtra(EXTRA_FORCE_VIEW));
}
+ /**
+ * Notify the user that we are awaiting input. Currently this sends an audio alert.
+ */
+ private void notifyUser() {
+ if (mNotificationCountdown > 0) {
+ Log.d(TAG, "Counting down to notify user..." + mNotificationCountdown);
+ --mNotificationCountdown;
+ } else if (mAudioManager != null) {
+ Log.d(TAG, "Notifying user that we are waiting for input...");
+ try {
+ // Play the standard keypress sound at full volume. This should be available on
+ // every device. We cannot play a ringtone here because media services aren't
+ // available yet. A DTMF-style tone is too soft to be noticed, and might not exist
+ // on tablet devices. The idea is to alert the user that something is needed: this
+ // does not have to be pleasing.
+ mAudioManager.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD, 100);
+ } catch (Exception e) {
+ Log.w(TAG, "notifyUser: Exception while playing sound: " + e);
+ }
+ }
+ // Notify the user again in 5 seconds.
+ mHandler.removeMessages(MESSAGE_NOTIFY);
+ mHandler.sendEmptyMessageDelayed(MESSAGE_NOTIFY, 5 * 1000);
+ }
+
+ /**
+ * Ignore back events after the user has entered the decrypt screen and while the device is
+ * encrypting.
+ */
+ @Override
+ public void onBackPressed() {
+ if (mIgnoreBack)
+ return;
+ super.onBackPressed();
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// If we are not encrypted or encrypting, get out quickly.
- String state = SystemProperties.get("vold.decrypt");
+ final String state = SystemProperties.get("vold.decrypt");
if (!isDebugView() && ("".equals(state) || DECRYPT_STATE.equals(state))) {
// Disable the crypt keeper.
PackageManager pm = getPackageManager();
@@ -234,18 +297,20 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
return;
}
- // Disable the status bar
+ // Disable the status bar, but do NOT disable back because the user needs a way to go
+ // from keyboard settings and back to the password screen.
StatusBarManager sbm = (StatusBarManager) getSystemService(Context.STATUS_BAR_SERVICE);
sbm.disable(StatusBarManager.DISABLE_EXPAND
| StatusBarManager.DISABLE_NOTIFICATION_ICONS
| StatusBarManager.DISABLE_NOTIFICATION_ALERTS
| StatusBarManager.DISABLE_SYSTEM_INFO
| StatusBarManager.DISABLE_HOME
- | StatusBarManager.DISABLE_RECENT
- | StatusBarManager.DISABLE_BACK);
+ | StatusBarManager.DISABLE_RECENT);
+ setAirplaneModeIfNecessary();
+ mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
// Check for (and recover) retained instance data
- Object lastInstance = getLastNonConfigurationInstance();
+ final Object lastInstance = getLastNonConfigurationInstance();
if (lastInstance instanceof NonConfigurationInstanceState) {
NonConfigurationInstanceState retained = (NonConfigurationInstanceState) lastInstance;
mWakeLock = retained.wakelock;
@@ -261,7 +326,6 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
@Override
public void onStart() {
super.onStart();
-
setupUi();
}
@@ -276,11 +340,11 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
return;
}
- String progress = SystemProperties.get("vold.encrypt_progress");
+ final String progress = SystemProperties.get("vold.encrypt_progress");
if (!"".equals(progress) || isDebugView(FORCE_VIEW_PROGRESS)) {
setContentView(R.layout.crypt_keeper_progress);
encryptionProgressInit();
- } else if (mValidationComplete) {
+ } else if (mValidationComplete || isDebugView(FORCE_VIEW_PASSWORD)) {
setContentView(R.layout.crypt_keeper_password_entry);
passwordEntryInit();
} else if (!mValidationRequested) {
@@ -293,9 +357,9 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
@Override
public void onStop() {
super.onStop();
-
- mHandler.removeMessages(COOLDOWN);
- mHandler.removeMessages(UPDATE_PROGRESS);
+ mHandler.removeMessages(MESSAGE_COOLDOWN);
+ mHandler.removeMessages(MESSAGE_UPDATE_PROGRESS);
+ mHandler.removeMessages(MESSAGE_NOTIFY);
}
/**
@@ -322,11 +386,13 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
}
}
+ /**
+ * Start encrypting the device.
+ */
private void encryptionProgressInit() {
// Accquire a partial wakelock to prevent the device from sleeping. Note
// we never release this wakelock as we will be restarted after the device
// is encrypted.
-
Log.d(TAG, "Encryption progress screen initializing.");
if (mWakeLock == null) {
Log.d(TAG, "Acquiring wakelock.");
@@ -335,9 +401,11 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
mWakeLock.acquire();
}
- ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress_bar);
- progressBar.setIndeterminate(true);
-
+ ((ProgressBar) findViewById(R.id.progress_bar)).setIndeterminate(true);
+ // Ignore all back presses from now, both hard and soft keys.
+ mIgnoreBack = true;
+ // Start the first run of progress manually. This method sets up messages to occur at
+ // repeated intervals.
updateProgress();
}
@@ -346,29 +414,29 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
findViewById(R.id.encroid).setVisibility(View.GONE);
// Show the reset button, failure text, and a divider
- Button button = (Button) findViewById(R.id.factory_reset);
+ final Button button = (Button) findViewById(R.id.factory_reset);
button.setVisibility(View.VISIBLE);
button.setOnClickListener(new OnClickListener() {
+ @Override
public void onClick(View v) {
// Factory reset the device.
sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
}
});
- TextView tv = (TextView) findViewById(R.id.title);
- tv.setText(R.string.crypt_keeper_failed_title);
-
- tv = (TextView) findViewById(R.id.status);
- tv.setText(R.string.crypt_keeper_failed_summary);
+ // Alert the user of the failure.
+ ((TextView) findViewById(R.id.title)).setText(R.string.crypt_keeper_failed_title);
+ ((TextView) findViewById(R.id.status)).setText(R.string.crypt_keeper_failed_summary);
- View view = findViewById(R.id.bottom_divider);
+ final View view = findViewById(R.id.bottom_divider);
+ // TODO(viki): Why would the bottom divider be missing in certain layouts? Investigate.
if (view != null) {
view.setVisibility(View.VISIBLE);
}
}
private void updateProgress() {
- String state = SystemProperties.get("vold.encrypt_progress");
+ final String state = SystemProperties.get("vold.encrypt_progress");
if ("error_partially_encrypted".equals(state)) {
showFactoryReset();
@@ -383,33 +451,33 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
Log.w(TAG, "Error parsing progress: " + e.toString());
}
- CharSequence status = getText(R.string.crypt_keeper_setup_description);
+ final CharSequence status = getText(R.string.crypt_keeper_setup_description);
Log.v(TAG, "Encryption progress: " + progress);
- TextView tv = (TextView) findViewById(R.id.status);
- tv.setText(TextUtils.expandTemplate(status, Integer.toString(progress)));
-
+ final TextView tv = (TextView) findViewById(R.id.status);
+ if (tv != null) {
+ tv.setText(TextUtils.expandTemplate(status, Integer.toString(progress)));
+ }
// Check the progress every 5 seconds
- mHandler.removeMessages(UPDATE_PROGRESS);
- mHandler.sendEmptyMessageDelayed(UPDATE_PROGRESS, 5000);
+ mHandler.removeMessages(MESSAGE_UPDATE_PROGRESS);
+ mHandler.sendEmptyMessageDelayed(MESSAGE_UPDATE_PROGRESS, 5000);
}
+ /** Disable password input for a while to force the user to waste time between retries */
private void cooldown() {
- TextView tv = (TextView) findViewById(R.id.status);
+ final TextView status = (TextView) findViewById(R.id.status);
if (mCooldown <= 0) {
- // Re-enable the password entry
+ // Re-enable the password entry and back presses.
mPasswordEntry.setEnabled(true);
-
- tv.setVisibility(View.GONE);
+ mIgnoreBack = false;
+ status.setText(R.string.enter_password);
} else {
CharSequence template = getText(R.string.crypt_keeper_cooldown);
- tv.setText(TextUtils.expandTemplate(template, Integer.toString(mCooldown)));
-
- tv.setVisibility(View.VISIBLE);
+ status.setText(TextUtils.expandTemplate(template, Integer.toString(mCooldown)));
mCooldown--;
- mHandler.removeMessages(COOLDOWN);
- mHandler.sendEmptyMessageDelayed(COOLDOWN, 1000); // Tick every second
+ mHandler.removeMessages(MESSAGE_COOLDOWN);
+ mHandler.sendEmptyMessageDelayed(MESSAGE_COOLDOWN, 1000); // Tick every second
}
}
@@ -417,19 +485,45 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
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);
+
+ // Disable the Emergency call button if the device has no voice telephone capability
+ final TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
+ if (!tm.isVoiceCapable()) {
+ final View emergencyCall = findViewById(R.id.emergencyCallButton);
+ if (emergencyCall != null) {
+ Log.d(TAG, "Removing the emergency Call button");
+ emergencyCall.setVisibility(View.GONE);
+ }
+ }
- View imeSwitcher = findViewById(R.id.switch_ime_button);
+ final View imeSwitcher = findViewById(R.id.switch_ime_button);
final InputMethodManager imm = (InputMethodManager) getSystemService(
Context.INPUT_METHOD_SERVICE);
if (imeSwitcher != null && hasMultipleEnabledIMEsOrSubtypes(imm, false)) {
imeSwitcher.setVisibility(View.VISIBLE);
imeSwitcher.setOnClickListener(new OnClickListener() {
+ @Override
public void onClick(View v) {
imm.showInputMethodPicker();
}
});
}
+ // We want to keep the screen on while waiting for input. In minimal boot mode, the device
+ // is completely non-functional, and we want the user to notice the device and enter a
+ // password.
+ if (mWakeLock == null) {
+ Log.d(TAG, "Acquiring wakelock.");
+ final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+ if (pm != null) {
+ mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
+ mWakeLock.acquire();
+ }
+ }
// Asynchronously throw up the IME, since there are issues with requesting it to be shown
// immediately.
mHandler.postDelayed(new Runnable() {
@@ -439,6 +533,9 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
}, 0);
updateEmergencyCallButtonState();
+ // Notify the user in 120 seconds that we are waiting for him to enter the password.
+ mHandler.removeMessages(MESSAGE_NOTIFY);
+ mHandler.sendEmptyMessageDelayed(MESSAGE_NOTIFY, 120 * 1000);
}
/**
@@ -490,7 +587,7 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
}
private IMountService getMountService() {
- IBinder service = ServiceManager.getService("mount");
+ final IBinder service = ServiceManager.getService("mount");
if (service != null) {
return IMountService.Stub.asInterface(service);
}
@@ -501,7 +598,7 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE) {
// Get the password
- String password = v.getText().toString();
+ final String password = v.getText().toString();
if (TextUtils.isEmpty(password)) {
return true;
@@ -510,10 +607,10 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
// Now that we have the password clear the password field.
v.setText(null);
- // Disable the password entry while checking the password. This
- // we either be reenabled if the password was wrong or after the
- // cooldown period.
+ // Disable the password entry and back keypress while checking the password. These
+ // we either be re-enabled if the password was wrong or after the cooldown period.
mPasswordEntry.setEnabled(false);
+ mIgnoreBack = true;
Log.d(TAG, "Attempting to send command to decrypt");
new DecryptTask().execute(password);
@@ -523,43 +620,72 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
return false;
}
- //
- // Code to update the state of, and handle clicks from, the "Emergency call" button.
- //
- // This code is mostly duplicated from the corresponding code in
- // LockPatternUtils and LockPatternKeyguardView under frameworks/base.
- //
+ /**
+ * Set airplane mode on the device if it isn't an LTE device.
+ * Full story: In minimal boot mode, we cannot save any state. In particular, we cannot save
+ * any incoming SMS's. So SMSs that are received here will be silently dropped to the floor.
+ * That is bad. Also, we cannot receive any telephone calls in this state. So to avoid
+ * both these problems, we turn the radio off. However, on certain networks turning on and
+ * off the radio takes a long time. In such cases, we are better off leaving the radio
+ * running so the latency of an E911 call is short.
+ * The behavior after this is:
+ * 1. Emergency dialing: the emergency dialer has logic to force the device out of
+ * airplane mode and restart the radio.
+ * 2. Full boot: we read the persistent settings from the previous boot and restore the
+ * radio to whatever it was before it restarted. This also happens when rebooting a
+ * phone that has no encryption.
+ */
+ private final void setAirplaneModeIfNecessary() {
+ final boolean isLteDevice =
+ TelephonyManager.getDefault().getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE;
+ if (!isLteDevice) {
+ Log.d(TAG, "Going into airplane mode.");
+ Settings.System.putInt(getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 1);
+ final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ intent.putExtra("state", true);
+ sendBroadcast(intent);
+ }
+ }
+ /**
+ * Code to update the state of, and handle clicks from, the "Emergency call" button.
+ *
+ * This code is mostly duplicated from the corresponding code in
+ * LockPatternUtils and LockPatternKeyguardView under frameworks/base.
+ */
private void updateEmergencyCallButtonState() {
- Button button = (Button) findViewById(R.id.emergencyCallButton);
+ final Button emergencyCall = (Button) findViewById(R.id.emergencyCallButton);
// The button isn't present at all in some configurations.
- if (button == null) return;
+ if (emergencyCall == null)
+ return;
if (isEmergencyCallCapable()) {
- button.setVisibility(View.VISIBLE);
- button.setOnClickListener(new View.OnClickListener() {
+ emergencyCall.setVisibility(View.VISIBLE);
+ emergencyCall.setOnClickListener(new View.OnClickListener() {
+ @Override
+
public void onClick(View v) {
takeEmergencyCallAction();
}
});
} else {
- button.setVisibility(View.GONE);
+ emergencyCall.setVisibility(View.GONE);
return;
}
- int newState = TelephonyManager.getDefault().getCallState();
+ final int newState = TelephonyManager.getDefault().getCallState();
int textId;
if (newState == TelephonyManager.CALL_STATE_OFFHOOK) {
- // show "return to call" text and show phone icon
+ // Show "return to call" text and show phone icon
textId = R.string.cryptkeeper_return_to_call;
- int phoneCallIcon = R.drawable.stat_sys_phone_call;
- button.setCompoundDrawablesWithIntrinsicBounds(phoneCallIcon, 0, 0, 0);
+ final int phoneCallIcon = R.drawable.stat_sys_phone_call;
+ emergencyCall.setCompoundDrawablesWithIntrinsicBounds(phoneCallIcon, 0, 0, 0);
} else {
textId = R.string.cryptkeeper_emergency_call;
- int emergencyIcon = R.drawable.ic_emergency;
- button.setCompoundDrawablesWithIntrinsicBounds(emergencyIcon, 0, 0, 0);
+ final int emergencyIcon = R.drawable.ic_emergency;
+ emergencyCall.setCompoundDrawablesWithIntrinsicBounds(emergencyIcon, 0, 0, 0);
}
- button.setText(textId);
+ emergencyCall.setText(textId);
}
private boolean isEmergencyCallCapable() {
@@ -575,7 +701,7 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
}
private void resumeCall() {
- ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
+ final ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
if (phone != null) {
try {
phone.showCallScreen();
@@ -586,9 +712,44 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
}
private void launchEmergencyDialer() {
- Intent intent = new Intent(ACTION_EMERGENCY_DIAL);
+ final Intent intent = new Intent(ACTION_EMERGENCY_DIAL);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
startActivity(intent);
}
+
+ /**
+ * Listen to key events so we can disable sounds when we get a keyinput in EditText.
+ */
+ private void delayAudioNotification() {
+ Log.d(TAG, "User entering password: delay audio notification");
+ mNotificationCountdown = 20;
+ }
+
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ delayAudioNotification();
+ return false;
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ delayAudioNotification();
+ return false;
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ return;
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ delayAudioNotification();
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ return;
+ }
}
diff --git a/src/com/android/settings/CryptKeeperSettings.java b/src/com/android/settings/CryptKeeperSettings.java
index 41a4be5..ce3ad9d 100644
--- a/src/com/android/settings/CryptKeeperSettings.java
+++ b/src/com/android/settings/CryptKeeperSettings.java
@@ -159,7 +159,14 @@ public class CryptKeeperSettings extends Fragment {
*/
private boolean runKeyguardConfirmation(int request) {
// 1. Confirm that we have a sufficient PIN/Password to continue
- int quality = new LockPatternUtils(getActivity()).getActivePasswordQuality();
+ LockPatternUtils lockPatternUtils = new LockPatternUtils(getActivity());
+ int quality = lockPatternUtils.getActivePasswordQuality();
+ if (quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK
+ && lockPatternUtils.isLockPasswordEnabled()) {
+ // Use the alternate as the quality. We expect this to be
+ // PASSWORD_QUALITY_SOMETHING(pattern) or PASSWORD_QUALITY_NUMERIC(PIN).
+ quality = lockPatternUtils.getKeyguardStoredPasswordQuality();
+ }
if (quality < MIN_PASSWORD_QUALITY) {
return false;
}
diff --git a/src/com/android/settings/DataUsageSummary.java b/src/com/android/settings/DataUsageSummary.java
index a2f0c3f..13512d4 100644
--- a/src/com/android/settings/DataUsageSummary.java
+++ b/src/com/android/settings/DataUsageSummary.java
@@ -35,12 +35,14 @@ import static android.net.NetworkTemplate.buildTemplateEthernet;
import static android.net.NetworkTemplate.buildTemplateMobile3gLower;
import static android.net.NetworkTemplate.buildTemplateMobile4g;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
-import static android.net.NetworkTemplate.buildTemplateWifi;
+import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
+import static android.net.TrafficStats.GB_IN_BYTES;
+import static android.net.TrafficStats.MB_IN_BYTES;
import static android.net.TrafficStats.UID_REMOVED;
import static android.net.TrafficStats.UID_TETHERING;
+import static android.telephony.TelephonyManager.SIM_STATE_READY;
import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
-import static android.text.format.Time.TIMEZONE_UTC;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.settings.Utils.prepareCustomPreferencesList;
@@ -67,25 +69,34 @@ import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
+import android.net.INetworkStatsSession;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
+import android.net.TrafficStats;
+import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.INetworkManagementService;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
+import android.os.UserId;
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;
import android.text.format.Formatter;
+import android.text.format.Time;
import android.util.Log;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -93,7 +104,6 @@ import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
@@ -121,6 +131,7 @@ import com.android.internal.telephony.Phone;
import com.android.settings.drawable.InsetBoundsDrawable;
import com.android.settings.net.ChartData;
import com.android.settings.net.ChartDataLoader;
+import com.android.settings.net.DataUsageMeteredSettings;
import com.android.settings.net.NetworkPolicyEditor;
import com.android.settings.net.SummaryForAllUidLoader;
import com.android.settings.net.UidDetail;
@@ -131,7 +142,6 @@ import com.android.settings.widget.PieChartView;
import com.google.android.collect.Lists;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
@@ -139,8 +149,8 @@ import java.util.Locale;
import libcore.util.Objects;
/**
- * Panel show data usage history across various networks, including options to
- * inspect based on usage cycle and control through {@link NetworkPolicy}.
+ * Panel showing data usage history across various networks, including options
+ * to inspect based on usage cycle and control through {@link NetworkPolicy}.
*/
public class DataUsageSummary extends Fragment {
private static final String TAG = "DataUsage";
@@ -149,7 +159,9 @@ public class DataUsageSummary extends Fragment {
// TODO: remove this testing code
private static final boolean TEST_ANIM = false;
private static final boolean TEST_RADIOS = false;
+
private static final String TEST_RADIOS_PROP = "test.radios";
+ private static final String TEST_SUBSCRIBER_PROP = "test.subscriberid";
private static final String TAB_3G = "3g";
private static final String TAB_4G = "4g";
@@ -166,20 +178,19 @@ public class DataUsageSummary extends Fragment {
private static final String TAG_CONFIRM_RESTRICT = "confirmRestrict";
private static final String TAG_DENIED_RESTRICT = "deniedRestrict";
private static final String TAG_CONFIRM_APP_RESTRICT = "confirmAppRestrict";
+ private static final String TAG_CONFIRM_AUTO_SYNC_CHANGE = "confirmAutoSyncChange";
private static final String TAG_APP_DETAILS = "appDetails";
private static final int LOADER_CHART_DATA = 2;
private static final int LOADER_SUMMARY = 3;
- private static final long KB_IN_BYTES = 1024;
- private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
- private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
-
private INetworkManagementService mNetworkService;
private INetworkStatsService mStatsService;
- private INetworkPolicyManager mPolicyService;
+ private NetworkPolicyManager mPolicyManager;
private ConnectivityManager mConnService;
+ private INetworkStatsSession mStatsSession;
+
private static final String PREF_FILE = "data_usage";
private static final String PREF_SHOW_WIFI = "show_wifi";
private static final String PREF_SHOW_ETHERNET = "show_ethernet";
@@ -230,7 +241,7 @@ public class DataUsageSummary extends Fragment {
private NetworkTemplate mTemplate;
private ChartData mChartData;
- private int[] mAppDetailUids = null;
+ private AppItem mCurrentApp = null;
private Intent mAppSettingsIntent;
@@ -241,6 +252,7 @@ public class DataUsageSummary extends Fragment {
private MenuItem mMenuDataRoaming;
private MenuItem mMenuRestrictBackground;
+ private MenuItem mMenuAutoSync;
/** Flag used to ignore listeners during binding. */
private boolean mBinding;
@@ -250,24 +262,29 @@ public class DataUsageSummary extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ final Context context = getActivity();
mNetworkService = INetworkManagementService.Stub.asInterface(
ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
mStatsService = INetworkStatsService.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
- mPolicyService = INetworkPolicyManager.Stub.asInterface(
- ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
- mConnService = (ConnectivityManager) getActivity().getSystemService(
- Context.CONNECTIVITY_SERVICE);
+ mPolicyManager = NetworkPolicyManager.from(context);
+ mConnService = ConnectivityManager.from(context);
mPrefs = getActivity().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
- mPolicyEditor = new NetworkPolicyEditor(mPolicyService);
+ mPolicyEditor = new NetworkPolicyEditor(mPolicyManager);
mPolicyEditor.read();
mShowWifi = mPrefs.getBoolean(PREF_SHOW_WIFI, false);
mShowEthernet = mPrefs.getBoolean(PREF_SHOW_ETHERNET, false);
+ // override preferences when no mobile radio
+ if (!hasReadyMobileRadio(context)) {
+ mShowWifi = hasWifiRadio(context);
+ mShowEthernet = hasEthernet(context);
+ }
+
setHasOptionsMenu(true);
}
@@ -280,6 +297,12 @@ public class DataUsageSummary extends Fragment {
mUidDetailProvider = new UidDetailProvider(context);
+ try {
+ mStatsSession = mStatsService.openSession();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+
mTabHost = (TabHost) view.findViewById(android.R.id.tabhost);
mTabsContainer = (ViewGroup) view.findViewById(R.id.tabs_container);
mTabWidget = (TabWidget) view.findViewById(android.R.id.tabs);
@@ -372,9 +395,6 @@ public class DataUsageSummary extends Fragment {
mUsageSummary = (TextView) mHeader.findViewById(R.id.usage_summary);
mEmpty = (TextView) mHeader.findViewById(android.R.id.empty);
- // only assign layout transitions once first layout is finished
- mListView.getViewTreeObserver().addOnGlobalLayoutListener(mFirstLayoutListener);
-
mAdapter = new DataUsageAdapter(mUidDetailProvider, mInsetSide);
mListView.setOnItemClickListener(mListListener);
mListView.setAdapter(mAdapter);
@@ -428,33 +448,53 @@ public class DataUsageSummary extends Fragment {
final boolean appDetailMode = isAppDetailMode();
mMenuDataRoaming = menu.findItem(R.id.data_usage_menu_roaming);
- mMenuDataRoaming.setVisible(hasMobileRadio(context) && !appDetailMode);
+ mMenuDataRoaming.setVisible(hasReadyMobileRadio(context) && !appDetailMode);
mMenuDataRoaming.setChecked(getDataRoaming());
mMenuRestrictBackground = menu.findItem(R.id.data_usage_menu_restrict_background);
- mMenuRestrictBackground.setVisible(hasMobileRadio(context) && !appDetailMode);
- mMenuRestrictBackground.setChecked(getRestrictBackground());
+ mMenuRestrictBackground.setVisible(hasReadyMobileRadio(context) && !appDetailMode);
+ mMenuRestrictBackground.setChecked(mPolicyManager.getRestrictBackground());
+
+ mMenuAutoSync = menu.findItem(R.id.data_usage_menu_auto_sync);
+ mMenuAutoSync.setChecked(ContentResolver.getMasterSyncAutomatically());
final MenuItem split4g = menu.findItem(R.id.data_usage_menu_split_4g);
- split4g.setVisible(hasMobile4gRadio(context) && !appDetailMode);
+ split4g.setVisible(hasReadyMobile4gRadio(context) && !appDetailMode);
split4g.setChecked(isMobilePolicySplit());
final MenuItem showWifi = menu.findItem(R.id.data_usage_menu_show_wifi);
- if (hasWifiRadio(context) && hasMobileRadio(context)) {
+ if (hasWifiRadio(context) && hasReadyMobileRadio(context)) {
showWifi.setVisible(!appDetailMode);
showWifi.setChecked(mShowWifi);
} else {
showWifi.setVisible(false);
- mShowWifi = true;
}
final MenuItem showEthernet = menu.findItem(R.id.data_usage_menu_show_ethernet);
- if (hasEthernet(context) && hasMobileRadio(context)) {
+ if (hasEthernet(context) && hasReadyMobileRadio(context)) {
showEthernet.setVisible(!appDetailMode);
showEthernet.setChecked(mShowEthernet);
} else {
showEthernet.setVisible(false);
- mShowEthernet = true;
+ }
+
+ final MenuItem metered = menu.findItem(R.id.data_usage_menu_metered);
+ if (hasReadyMobileRadio(context) || hasWifiRadio(context)) {
+ metered.setVisible(!appDetailMode);
+ } else {
+ metered.setVisible(false);
+ }
+
+ final MenuItem help = menu.findItem(R.id.data_usage_menu_help);
+ String helpUrl;
+ if (!TextUtils.isEmpty(helpUrl = getResources().getString(R.string.help_url_data_usage))) {
+ Intent helpIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(helpUrl));
+ helpIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ help.setIntent(helpIntent);
+ help.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+ } else {
+ help.setVisible(false);
}
}
@@ -474,11 +514,7 @@ public class DataUsageSummary extends Fragment {
case R.id.data_usage_menu_restrict_background: {
final boolean restrictBackground = !item.isChecked();
if (restrictBackground) {
- if (hasLimitedNetworks()) {
- ConfirmRestrictFragment.show(this);
- } else {
- DeniedRestrictFragment.show(this);
- }
+ ConfirmRestrictFragment.show(this);
} else {
// no confirmation to drop restriction
setRestrictBackground(false);
@@ -506,50 +542,55 @@ public class DataUsageSummary extends Fragment {
updateTabs();
return true;
}
+ case R.id.data_usage_menu_metered: {
+ final PreferenceActivity activity = (PreferenceActivity) getActivity();
+ activity.startPreferencePanel(DataUsageMeteredSettings.class.getCanonicalName(), null,
+ R.string.data_usage_metered_title, null, this, 0);
+ return true;
+ }
+ case R.id.data_usage_menu_auto_sync: {
+ ConfirmAutoSyncChangeFragment.show(this, !item.isChecked());
+ return true;
+ }
}
return false;
}
@Override
- public void onDestroyView() {
- super.onDestroyView();
-
+ public void onDestroy() {
mDataEnabledView = null;
mDisableAtLimitView = null;
mUidDetailProvider.clearCache();
mUidDetailProvider = null;
- }
- @Override
- public void onDestroy() {
+ TrafficStats.closeQuietly(mStatsSession);
+
if (this.isRemoving()) {
getFragmentManager()
.popBackStack(TAG_APP_DETAILS, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
+
super.onDestroy();
}
/**
- * Listener to setup {@link LayoutTransition} after first layout pass.
+ * Build and assign {@link LayoutTransition} to various containers. Should
+ * only be assigned after initial layout is complete.
*/
- private OnGlobalLayoutListener mFirstLayoutListener = new OnGlobalLayoutListener() {
- /** {@inheritDoc} */
- public void onGlobalLayout() {
- mListView.getViewTreeObserver().removeGlobalOnLayoutListener(mFirstLayoutListener);
-
- mTabsContainer.setLayoutTransition(buildLayoutTransition());
- mHeader.setLayoutTransition(buildLayoutTransition());
- mNetworkSwitchesContainer.setLayoutTransition(buildLayoutTransition());
-
- final LayoutTransition chartTransition = buildLayoutTransition();
- chartTransition.setStartDelay(LayoutTransition.APPEARING, 0);
- chartTransition.setStartDelay(LayoutTransition.DISAPPEARING, 0);
- chartTransition.setAnimator(LayoutTransition.APPEARING, null);
- chartTransition.setAnimator(LayoutTransition.DISAPPEARING, null);
- mChart.setLayoutTransition(chartTransition);
- }
- };
+ private void ensureLayoutTransitions() {
+ // skip when already setup
+ if (mChart.getLayoutTransition() != null) return;
+
+ mTabsContainer.setLayoutTransition(buildLayoutTransition());
+ mHeader.setLayoutTransition(buildLayoutTransition());
+ mNetworkSwitchesContainer.setLayoutTransition(buildLayoutTransition());
+
+ final LayoutTransition chartTransition = buildLayoutTransition();
+ chartTransition.disableTransitionType(LayoutTransition.APPEARING);
+ chartTransition.disableTransitionType(LayoutTransition.DISAPPEARING);
+ mChart.setLayoutTransition(chartTransition);
+ }
private static LayoutTransition buildLayoutTransition() {
final LayoutTransition transition = new LayoutTransition();
@@ -570,10 +611,10 @@ public class DataUsageSummary extends Fragment {
mTabHost.clearAllTabs();
final boolean mobileSplit = isMobilePolicySplit();
- if (mobileSplit && hasMobile4gRadio(context)) {
+ if (mobileSplit && hasReadyMobile4gRadio(context)) {
mTabHost.addTab(buildTabSpec(TAB_3G, R.string.data_usage_tab_3g));
mTabHost.addTab(buildTabSpec(TAB_4G, R.string.data_usage_tab_4g));
- } else if (hasMobileRadio(context)) {
+ } else if (hasReadyMobileRadio(context)) {
mTabHost.addTab(buildTabSpec(TAB_MOBILE, R.string.data_usage_tab_mobile));
}
if (mShowWifi && hasWifiRadio(context)) {
@@ -583,6 +624,7 @@ public class DataUsageSummary extends Fragment {
mTabHost.addTab(buildTabSpec(TAB_ETHERNET, R.string.data_usage_tab_ethernet));
}
+ final boolean noTabs = mTabWidget.getTabCount() == 0;
final boolean multipleTabs = mTabWidget.getTabCount() > 1;
mTabWidget.setVisibility(multipleTabs ? View.VISIBLE : View.GONE);
if (mIntentTab != null) {
@@ -593,6 +635,9 @@ public class DataUsageSummary extends Fragment {
mTabHost.setCurrentTabByTag(mIntentTab);
}
mIntentTab = null;
+ } else if (noTabs) {
+ // no usable tabs, so hide body
+ updateBody();
} else {
// already hit updateBody() when added; ignore
}
@@ -602,7 +647,7 @@ public class DataUsageSummary extends Fragment {
* Factory that provide empty {@link View} to make {@link TabHost} happy.
*/
private TabContentFactory mEmptyTabContent = new TabContentFactory() {
- /** {@inheritDoc} */
+ @Override
public View createTabContent(String tag) {
return new View(mTabHost.getContext());
}
@@ -617,7 +662,7 @@ public class DataUsageSummary extends Fragment {
}
private OnTabChangeListener mTabListener = new OnTabChangeListener() {
- /** {@inheritDoc} */
+ @Override
public void onTabChanged(String tabId) {
// user changed tab; update body
updateBody();
@@ -651,6 +696,9 @@ public class DataUsageSummary extends Fragment {
mDataEnabledView.setVisibility(View.VISIBLE);
+ // TODO: remove mobile tabs when SIM isn't ready
+ final TelephonyManager tele = TelephonyManager.from(context);
+
if (TAB_MOBILE.equals(currentTab)) {
setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_mobile);
setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_mobile_limit);
@@ -672,7 +720,7 @@ public class DataUsageSummary extends Fragment {
// wifi doesn't have any controls
mDataEnabledView.setVisibility(View.GONE);
mDisableAtLimitView.setVisibility(View.GONE);
- mTemplate = buildTemplateWifi();
+ mTemplate = buildTemplateWifiWildcard();
} else if (TAB_ETHERNET.equals(currentTab)) {
// ethernet doesn't have any controls
@@ -688,7 +736,7 @@ public class DataUsageSummary extends Fragment {
// TODO: consider chaining two loaders together instead of reloading
// network history when showing app detail.
getLoaderManager().restartLoader(LOADER_CHART_DATA,
- ChartDataLoader.buildArgs(mTemplate, mAppDetailUids), mChartDataCallbacks);
+ ChartDataLoader.buildArgs(mTemplate, mCurrentApp), mChartDataCallbacks);
// detail mode can change visible menus, invalidate
getActivity().invalidateOptionsMenu();
@@ -697,15 +745,11 @@ public class DataUsageSummary extends Fragment {
}
private boolean isAppDetailMode() {
- return mAppDetailUids != null;
- }
-
- private int getAppDetailPrimaryUid() {
- return mAppDetailUids[0];
+ return mCurrentApp != null;
}
/**
- * Update UID details panels to match {@link #mAppDetailUids}, showing or
+ * Update UID details panels to match {@link #mCurrentApp}, showing or
* hiding them depending on {@link #isAppDetailMode()}.
*/
private void updateAppDetail() {
@@ -729,8 +773,8 @@ public class DataUsageSummary extends Fragment {
mChart.bindNetworkPolicy(null);
// show icon and all labels appearing under this app
- final int primaryUid = getAppDetailPrimaryUid();
- final UidDetail detail = mUidDetailProvider.getUidDetail(primaryUid, true);
+ final int appId = mCurrentApp.appId;
+ final UidDetail detail = mUidDetailProvider.getUidDetail(appId, true);
mAppIcon.setImageDrawable(detail.icon);
mAppTitles.removeAllViews();
@@ -744,7 +788,7 @@ public class DataUsageSummary extends Fragment {
// enable settings button when package provides it
// TODO: target torwards entire UID instead of just first package
- final String[] packageNames = pm.getPackagesForUid(primaryUid);
+ final String[] packageNames = pm.getPackagesForUid(appId);
if (packageNames != null && packageNames.length > 0) {
mAppSettingsIntent = new Intent(Intent.ACTION_MANAGE_NETWORK_USAGE);
mAppSettingsIntent.setPackage(packageNames[0]);
@@ -752,25 +796,20 @@ public class DataUsageSummary extends Fragment {
final boolean matchFound = pm.resolveActivity(mAppSettingsIntent, 0) != null;
mAppSettings.setEnabled(matchFound);
+ mAppSettings.setVisibility(View.VISIBLE);
} else {
mAppSettingsIntent = null;
- mAppSettings.setEnabled(false);
+ mAppSettings.setVisibility(View.GONE);
}
updateDetailData();
- if (NetworkPolicyManager.isUidValidForPolicy(context, primaryUid)
- && !getRestrictBackground() && isBandwidthControlEnabled()
- && hasMobileRadio(context)) {
+ if (UserId.isApp(appId) && !mPolicyManager.getRestrictBackground()
+ && isBandwidthControlEnabled() && hasReadyMobileRadio(context)) {
setPreferenceTitle(mAppRestrictView, R.string.data_usage_app_restrict_background);
- if (hasLimitedNetworks()) {
- setPreferenceSummary(mAppRestrictView,
- getString(R.string.data_usage_app_restrict_background_summary));
- } else {
- setPreferenceSummary(mAppRestrictView,
- getString(R.string.data_usage_app_restrict_background_summary_disabled));
- }
+ setPreferenceSummary(mAppRestrictView,
+ getString(R.string.data_usage_app_restrict_background_summary));
mAppRestrictView.setVisibility(View.VISIBLE);
mAppRestrict.setChecked(getAppRestrictBackground());
@@ -840,48 +879,22 @@ public class DataUsageSummary extends Fragment {
mMenuDataRoaming.setChecked(enabled);
}
- private boolean getRestrictBackground() {
- try {
- return mPolicyService.getRestrictBackground();
- } catch (RemoteException e) {
- Log.w(TAG, "problem talking with policy service: " + e);
- return false;
- }
- }
-
- private void setRestrictBackground(boolean restrictBackground) {
- if (LOGD) Log.d(TAG, "setRestrictBackground()");
- try {
- mPolicyService.setRestrictBackground(restrictBackground);
- mMenuRestrictBackground.setChecked(restrictBackground);
- } catch (RemoteException e) {
- Log.w(TAG, "problem talking with policy service: " + e);
- }
+ public void setRestrictBackground(boolean restrictBackground) {
+ mPolicyManager.setRestrictBackground(restrictBackground);
+ mMenuRestrictBackground.setChecked(restrictBackground);
}
private boolean getAppRestrictBackground() {
- final int primaryUid = getAppDetailPrimaryUid();
- final int uidPolicy;
- try {
- uidPolicy = mPolicyService.getUidPolicy(primaryUid);
- } catch (RemoteException e) {
- // since we can't do much without policy, we bail hard.
- throw new RuntimeException("problem reading network policy", e);
- }
-
+ final int appId = mCurrentApp.appId;
+ final int uidPolicy = mPolicyManager.getAppPolicy(appId);
return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
}
private void setAppRestrictBackground(boolean restrictBackground) {
if (LOGD) Log.d(TAG, "setAppRestrictBackground()");
- final int primaryUid = getAppDetailPrimaryUid();
- try {
- mPolicyService.setUidPolicy(primaryUid,
- restrictBackground ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE);
- } catch (RemoteException e) {
- throw new RuntimeException("unable to save policy", e);
- }
-
+ final int appId = mCurrentApp.appId;
+ mPolicyManager.setAppPolicy(appId,
+ restrictBackground ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE);
mAppRestrict.setChecked(restrictBackground);
}
@@ -997,7 +1010,7 @@ public class DataUsageSummary extends Fragment {
}
private OnCheckedChangeListener mDataEnabledListener = new OnCheckedChangeListener() {
- /** {@inheritDoc} */
+ @Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (mBinding) return;
@@ -1018,7 +1031,7 @@ public class DataUsageSummary extends Fragment {
};
private View.OnClickListener mDisableAtLimitListener = new View.OnClickListener() {
- /** {@inheritDoc} */
+ @Override
public void onClick(View v) {
final boolean disableAtLimit = !mDisableAtLimit.isChecked();
if (disableAtLimit) {
@@ -1032,21 +1045,15 @@ public class DataUsageSummary extends Fragment {
};
private View.OnClickListener mAppRestrictListener = new View.OnClickListener() {
- /** {@inheritDoc} */
+ @Override
public void onClick(View v) {
final boolean restrictBackground = !mAppRestrict.isChecked();
if (restrictBackground) {
- if (hasLimitedNetworks()) {
- // enabling restriction; show confirmation dialog which
- // eventually calls setRestrictBackground() once user
- // confirms.
- ConfirmAppRestrictFragment.show(DataUsageSummary.this);
- } else {
- // no limited networks; show dialog to guide user towards
- // setting a network limit. doesn't mutate restrict state.
- DeniedRestrictFragment.show(DataUsageSummary.this);
- }
+ // enabling restriction; show confirmation dialog which
+ // eventually calls setRestrictBackground() once user
+ // confirms.
+ ConfirmAppRestrictFragment.show(DataUsageSummary.this);
} else {
setAppRestrictBackground(false);
}
@@ -1054,25 +1061,31 @@ public class DataUsageSummary extends Fragment {
};
private OnClickListener mAppSettingsListener = new OnClickListener() {
- /** {@inheritDoc} */
+ @Override
public void onClick(View v) {
+ if (!isAdded()) return;
+
// TODO: target torwards entire UID instead of just first package
startActivity(mAppSettingsIntent);
}
};
private OnItemClickListener mListListener = new OnItemClickListener() {
- /** {@inheritDoc} */
+ @Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final Context context = view.getContext();
- final AppUsageItem app = (AppUsageItem) parent.getItemAtPosition(position);
- final UidDetail detail = mUidDetailProvider.getUidDetail(app.uids[0], true);
- AppDetailsFragment.show(DataUsageSummary.this, app.uids, detail.label);
+ final AppItem app = (AppItem) parent.getItemAtPosition(position);
+
+ // TODO: sigh, remove this hack once we understand 6450986
+ if (mUidDetailProvider == null || app == null) return;
+
+ final UidDetail detail = mUidDetailProvider.getUidDetail(app.appId, true);
+ AppDetailsFragment.show(DataUsageSummary.this, app, detail.label);
}
};
private OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
- /** {@inheritDoc} */
+ @Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
final CycleItem cycle = (CycleItem) parent.getItemAtPosition(position);
if (cycle instanceof CycleChangeItem) {
@@ -1097,7 +1110,7 @@ public class DataUsageSummary extends Fragment {
}
}
- /** {@inheritDoc} */
+ @Override
public void onNothingSelected(AdapterView<?> parent) {
// ignored
}
@@ -1154,20 +1167,30 @@ public class DataUsageSummary extends Fragment {
final long totalBytes = entry != null ? entry.rxBytes + entry.txBytes : 0;
final String totalPhrase = Formatter.formatFileSize(context, totalBytes);
- final String rangePhrase = formatDateRange(context, start, end, false);
+ final String rangePhrase = formatDateRange(context, start, end);
- mUsageSummary.setText(
- getString(R.string.data_usage_total_during_range, totalPhrase, rangePhrase));
+ final int summaryRes;
+ if (TAB_MOBILE.equals(mCurrentTab) || TAB_3G.equals(mCurrentApp)
+ || TAB_4G.equals(mCurrentApp)) {
+ summaryRes = R.string.data_usage_total_during_range_mobile;
+ } else {
+ summaryRes = R.string.data_usage_total_during_range;
+ }
+
+ mUsageSummary.setText(getString(summaryRes, totalPhrase, rangePhrase));
+
+ // initial layout is finished above, ensure we have transitions
+ ensureLayoutTransitions();
}
private final LoaderCallbacks<ChartData> mChartDataCallbacks = new LoaderCallbacks<
ChartData>() {
- /** {@inheritDoc} */
+ @Override
public Loader<ChartData> onCreateLoader(int id, Bundle args) {
- return new ChartDataLoader(getActivity(), mStatsService, args);
+ return new ChartDataLoader(getActivity(), mStatsSession, args);
}
- /** {@inheritDoc} */
+ @Override
public void onLoadFinished(Loader<ChartData> loader, ChartData data) {
mChartData = data;
mChart.bindNetworkStats(mChartData.network);
@@ -1183,7 +1206,7 @@ public class DataUsageSummary extends Fragment {
}
}
- /** {@inheritDoc} */
+ @Override
public void onLoaderReset(Loader<ChartData> loader) {
mChartData = null;
mChart.bindNetworkStats(null);
@@ -1193,20 +1216,22 @@ public class DataUsageSummary extends Fragment {
private final LoaderCallbacks<NetworkStats> mSummaryCallbacks = new LoaderCallbacks<
NetworkStats>() {
- /** {@inheritDoc} */
+ @Override
public Loader<NetworkStats> onCreateLoader(int id, Bundle args) {
- return new SummaryForAllUidLoader(getActivity(), mStatsService, args);
+ return new SummaryForAllUidLoader(getActivity(), mStatsSession, args);
}
- /** {@inheritDoc} */
+ @Override
public void onLoadFinished(Loader<NetworkStats> loader, NetworkStats data) {
- mAdapter.bindStats(data);
+ final int[] restrictedAppIds = mPolicyManager.getAppsWithPolicy(
+ POLICY_REJECT_METERED_BACKGROUND);
+ mAdapter.bindStats(data, restrictedAppIds);
updateEmptyVisible();
}
- /** {@inheritDoc} */
+ @Override
public void onLoaderReset(Loader<NetworkStats> loader) {
- mAdapter.bindStats(null);
+ mAdapter.bindStats(null, new int[0]);
updateEmptyVisible();
}
@@ -1216,50 +1241,55 @@ public class DataUsageSummary extends Fragment {
}
};
+ @Deprecated
private boolean isMobilePolicySplit() {
final Context context = getActivity();
- if (hasMobileRadio(context)) {
- final String subscriberId = getActiveSubscriberId(context);
- return mPolicyEditor.isMobilePolicySplit(subscriberId);
+ if (hasReadyMobileRadio(context)) {
+ final TelephonyManager tele = TelephonyManager.from(context);
+ return mPolicyEditor.isMobilePolicySplit(getActiveSubscriberId(context));
} else {
return false;
}
}
+ @Deprecated
private void setMobilePolicySplit(boolean split) {
- final String subscriberId = getActiveSubscriberId(getActivity());
- mPolicyEditor.setMobilePolicySplit(subscriberId, split);
+ final Context context = getActivity();
+ if (hasReadyMobileRadio(context)) {
+ final TelephonyManager tele = TelephonyManager.from(context);
+ mPolicyEditor.setMobilePolicySplit(getActiveSubscriberId(context), split);
+ }
}
private static String getActiveSubscriberId(Context context) {
- final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
- Context.TELEPHONY_SERVICE);
- return telephony.getSubscriberId();
+ final TelephonyManager tele = TelephonyManager.from(context);
+ final String actualSubscriberId = tele.getSubscriberId();
+ return SystemProperties.get(TEST_SUBSCRIBER_PROP, actualSubscriberId);
}
private DataUsageChartListener mChartListener = new DataUsageChartListener() {
- /** {@inheritDoc} */
+ @Override
public void onInspectRangeChanged() {
if (LOGD) Log.d(TAG, "onInspectRangeChanged()");
updateDetailData();
}
- /** {@inheritDoc} */
+ @Override
public void onWarningChanged() {
setPolicyWarningBytes(mChart.getWarningBytes());
}
- /** {@inheritDoc} */
+ @Override
public void onLimitChanged() {
setPolicyLimitBytes(mChart.getLimitBytes());
}
- /** {@inheritDoc} */
+ @Override
public void requestWarningEdit() {
WarningEditorFragment.show(DataUsageSummary.this);
}
- /** {@inheritDoc} */
+ @Override
public void requestLimitEdit() {
LimitEditorFragment.show(DataUsageSummary.this);
}
@@ -1278,7 +1308,7 @@ public class DataUsageSummary extends Fragment {
}
public CycleItem(Context context, long start, long end) {
- this.label = formatDateRange(context, start, end, true);
+ this.label = formatDateRange(context, start, end);
this.start = start;
this.end = end;
}
@@ -1297,7 +1327,7 @@ public class DataUsageSummary extends Fragment {
return false;
}
- /** {@inheritDoc} */
+ @Override
public int compareTo(CycleItem another) {
return Long.compare(start, another.start);
}
@@ -1307,14 +1337,13 @@ public class DataUsageSummary extends Fragment {
private static final java.util.Formatter sFormatter = new java.util.Formatter(
sBuilder, Locale.getDefault());
- public static String formatDateRange(Context context, long start, long end, boolean utcTime) {
+ public static String formatDateRange(Context context, long start, long end) {
final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
- final String timezone = utcTime ? TIMEZONE_UTC : null;
synchronized (sBuilder) {
sBuilder.setLength(0);
- return DateUtils
- .formatDateRange(context, sFormatter, start, end, flags, timezone).toString();
+ return DateUtils.formatDateRange(context, sFormatter, start, end, flags, null)
+ .toString();
}
}
@@ -1377,25 +1406,54 @@ public class DataUsageSummary extends Fragment {
}
}
- private static class AppUsageItem implements Comparable<AppUsageItem> {
- public int[] uids;
+ public static class AppItem implements Comparable<AppItem>, Parcelable {
+ public final int appId;
+ public boolean restricted;
+ public SparseBooleanArray uids = new SparseBooleanArray();
public long total;
- public AppUsageItem(int uid) {
- uids = new int[] { uid };
+ public AppItem(int appId) {
+ this.appId = appId;
+ }
+
+ public AppItem(Parcel parcel) {
+ appId = parcel.readInt();
+ uids = parcel.readSparseBooleanArray();
+ total = parcel.readLong();
}
public void addUid(int uid) {
- if (contains(uids, uid)) return;
- final int length = uids.length;
- uids = Arrays.copyOf(uids, length + 1);
- uids[length] = uid;
+ uids.put(uid, true);
}
- /** {@inheritDoc} */
- public int compareTo(AppUsageItem another) {
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(appId);
+ dest.writeSparseBooleanArray(uids);
+ dest.writeLong(total);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public int compareTo(AppItem another) {
return Long.compare(another.total, total);
}
+
+ public static final Creator<AppItem> CREATOR = new Creator<AppItem>() {
+ @Override
+ public AppItem createFromParcel(Parcel in) {
+ return new AppItem(in);
+ }
+
+ @Override
+ public AppItem[] newArray(int size) {
+ return new AppItem[size];
+ }
+ };
}
/**
@@ -1405,7 +1463,7 @@ public class DataUsageSummary extends Fragment {
private final UidDetailProvider mProvider;
private final int mInsetSide;
- private ArrayList<AppUsageItem> mItems = Lists.newArrayList();
+ private ArrayList<AppItem> mItems = Lists.newArrayList();
private long mLargest;
public DataUsageAdapter(UidDetailProvider provider, int insetSide) {
@@ -1416,33 +1474,43 @@ public class DataUsageSummary extends Fragment {
/**
* Bind the given {@link NetworkStats}, or {@code null} to clear list.
*/
- public void bindStats(NetworkStats stats) {
+ public void bindStats(NetworkStats stats, int[] restrictedAppIds) {
mItems.clear();
- final AppUsageItem systemItem = new AppUsageItem(android.os.Process.SYSTEM_UID);
- final SparseArray<AppUsageItem> knownUids = new SparseArray<AppUsageItem>();
+ final AppItem systemItem = new AppItem(android.os.Process.SYSTEM_UID);
+ final SparseArray<AppItem> knownUids = new SparseArray<AppItem>();
NetworkStats.Entry entry = null;
final int size = stats != null ? stats.size() : 0;
for (int i = 0; i < size; i++) {
entry = stats.getValues(i, entry);
- final int uid = entry.uid;
- final boolean isApp = uid >= android.os.Process.FIRST_APPLICATION_UID
- && uid <= android.os.Process.LAST_APPLICATION_UID;
- if (isApp || uid == UID_REMOVED || uid == UID_TETHERING) {
- AppUsageItem item = knownUids.get(uid);
+ final boolean isApp = UserId.isApp(entry.uid);
+ final int appId = isApp ? UserId.getAppId(entry.uid) : entry.uid;
+ if (isApp || appId == UID_REMOVED || appId == UID_TETHERING) {
+ AppItem item = knownUids.get(appId);
if (item == null) {
- item = new AppUsageItem(uid);
- knownUids.put(uid, item);
+ item = new AppItem(appId);
+ knownUids.put(appId, item);
mItems.add(item);
}
item.total += entry.rxBytes + entry.txBytes;
+ item.addUid(entry.uid);
} else {
systemItem.total += entry.rxBytes + entry.txBytes;
- systemItem.addUid(uid);
+ systemItem.addUid(entry.uid);
+ }
+ }
+
+ for (int appId : restrictedAppIds) {
+ AppItem item = knownUids.get(appId);
+ if (item == null) {
+ item = new AppItem(appId);
+ item.total = -1;
+ mItems.add(item);
}
+ item.restricted = true;
}
if (systemItem.total > 0) {
@@ -1466,7 +1534,7 @@ public class DataUsageSummary extends Fragment {
@Override
public long getItemId(int position) {
- return mItems.get(position).uids[0];
+ return mItems.get(position).appId;
}
@Override
@@ -1487,10 +1555,16 @@ public class DataUsageSummary extends Fragment {
android.R.id.progress);
// kick off async load of app details
- final AppUsageItem item = mItems.get(position);
+ final AppItem item = mItems.get(position);
UidDetailTask.bindView(mProvider, item, convertView);
- text1.setText(Formatter.formatFileSize(context, item.total));
+ if (item.restricted && item.total <= 0) {
+ text1.setText(R.string.data_usage_app_restricted);
+ progress.setVisibility(View.GONE);
+ } else {
+ text1.setText(Formatter.formatFileSize(context, item.total));
+ progress.setVisibility(View.VISIBLE);
+ }
final int percentTotal = mLargest != 0 ? (int) (item.total * 100 / mLargest) : 0;
progress.setProgress(percentTotal);
@@ -1504,13 +1578,13 @@ public class DataUsageSummary extends Fragment {
* {@link DataUsageSummary}.
*/
public static class AppDetailsFragment extends Fragment {
- private static final String EXTRA_UIDS = "uids";
+ private static final String EXTRA_APP = "app";
- public static void show(DataUsageSummary parent, int[] uids, CharSequence label) {
+ public static void show(DataUsageSummary parent, AppItem app, CharSequence label) {
if (!parent.isAdded()) return;
final Bundle args = new Bundle();
- args.putIntArray(EXTRA_UIDS, uids);
+ args.putParcelable(EXTRA_APP, app);
final AppDetailsFragment fragment = new AppDetailsFragment();
fragment.setArguments(args);
@@ -1519,14 +1593,14 @@ public class DataUsageSummary extends Fragment {
ft.add(fragment, TAG_APP_DETAILS);
ft.addToBackStack(TAG_APP_DETAILS);
ft.setBreadCrumbTitle(label);
- ft.commit();
+ ft.commitAllowingStateLoss();
}
@Override
public void onStart() {
super.onStart();
final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
- target.mAppDetailUids = getArguments().getIntArray(EXTRA_UIDS);
+ target.mCurrentApp = getArguments().getParcelable(EXTRA_APP);
target.updateBody();
}
@@ -1534,7 +1608,7 @@ public class DataUsageSummary extends Fragment {
public void onStop() {
super.onStop();
final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
- target.mAppDetailUids = null;
+ target.mCurrentApp = null;
target.updateBody();
}
}
@@ -1552,22 +1626,21 @@ public class DataUsageSummary extends Fragment {
final Resources res = parent.getResources();
final CharSequence message;
+ final long minLimitBytes = (long) (
+ parent.mPolicyEditor.getPolicy(parent.mTemplate).warningBytes * 1.2f);
final long limitBytes;
// TODO: customize default limits based on network template
final String currentTab = parent.mCurrentTab;
if (TAB_3G.equals(currentTab)) {
- message = buildDialogMessage(res, R.string.data_usage_tab_3g);
- limitBytes = 5 * GB_IN_BYTES;
+ message = res.getString(R.string.data_usage_limit_dialog_mobile);
+ limitBytes = Math.max(5 * GB_IN_BYTES, minLimitBytes);
} else if (TAB_4G.equals(currentTab)) {
- message = buildDialogMessage(res, R.string.data_usage_tab_4g);
- limitBytes = 5 * GB_IN_BYTES;
+ message = res.getString(R.string.data_usage_limit_dialog_mobile);
+ limitBytes = Math.max(5 * GB_IN_BYTES, minLimitBytes);
} else if (TAB_MOBILE.equals(currentTab)) {
- message = buildDialogMessage(res, R.string.data_usage_list_mobile);
- limitBytes = 5 * GB_IN_BYTES;
- } else if (TAB_WIFI.equals(currentTab)) {
- message = buildDialogMessage(res, R.string.data_usage_tab_wifi);
- limitBytes = 5 * GB_IN_BYTES;
+ message = res.getString(R.string.data_usage_limit_dialog_mobile);
+ limitBytes = Math.max(5 * GB_IN_BYTES, minLimitBytes);
} else {
throw new IllegalArgumentException("unknown current tab: " + currentTab);
}
@@ -1582,10 +1655,6 @@ public class DataUsageSummary extends Fragment {
dialog.show(parent.getFragmentManager(), TAG_CONFIRM_LIMIT);
}
- private static CharSequence buildDialogMessage(Resources res, int networkResId) {
- return res.getString(R.string.data_usage_limit_dialog, res.getString(networkResId));
- }
-
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Context context = getActivity();
@@ -1598,6 +1667,7 @@ public class DataUsageSummary extends Fragment {
builder.setMessage(message);
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog, int which) {
final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
if (target != null) {
@@ -1653,9 +1723,11 @@ public class DataUsageSummary extends Fragment {
builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog, int which) {
final int cycleDay = cycleDayPicker.getValue();
- editor.setPolicyCycleDay(template, cycleDay);
+ final String cycleTimezone = new Time().timezone;
+ editor.setPolicyCycleDay(template, cycleDay, cycleTimezone);
target.updatePolicy(true);
}
});
@@ -1712,6 +1784,7 @@ public class DataUsageSummary extends Fragment {
builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog, int which) {
// clear focus to finish pending text edits
bytesPicker.clearFocus();
@@ -1774,6 +1847,7 @@ public class DataUsageSummary extends Fragment {
builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog, int which) {
// clear focus to finish pending text edits
bytesPicker.clearFocus();
@@ -1807,6 +1881,7 @@ public class DataUsageSummary extends Fragment {
builder.setMessage(R.string.data_usage_disable_mobile);
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog, int which) {
final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
if (target != null) {
@@ -1823,7 +1898,7 @@ public class DataUsageSummary extends Fragment {
/**
* Dialog to request user confirmation before setting
- * {@link Settings.Secure#DATA_ROAMING}.
+ * {@link android.provider.Settings.Secure#DATA_ROAMING}.
*/
public static class ConfirmDataRoamingFragment extends DialogFragment {
public static void show(DataUsageSummary parent) {
@@ -1843,6 +1918,7 @@ public class DataUsageSummary extends Fragment {
builder.setMessage(R.string.roaming_warning);
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog, int which) {
final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
if (target != null) {
@@ -1878,6 +1954,7 @@ public class DataUsageSummary extends Fragment {
builder.setMessage(getString(R.string.data_usage_restrict_background));
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog, int which) {
final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
if (target != null) {
@@ -1940,6 +2017,7 @@ public class DataUsageSummary extends Fragment {
builder.setMessage(R.string.data_usage_app_restrict_dialog);
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog, int which) {
final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
if (target != null) {
@@ -1954,6 +2032,56 @@ public class DataUsageSummary extends Fragment {
}
/**
+ * Dialog to inform user about changing auto-sync setting
+ */
+ public static class ConfirmAutoSyncChangeFragment extends DialogFragment {
+ private static final String SAVE_ENABLING = "enabling";
+ private boolean mEnabling;
+
+ public static void show(DataUsageSummary parent, boolean enabling) {
+ if (!parent.isAdded()) return;
+
+ final ConfirmAutoSyncChangeFragment dialog = new ConfirmAutoSyncChangeFragment();
+ dialog.mEnabling = enabling;
+ dialog.setTargetFragment(parent, 0);
+ dialog.show(parent.getFragmentManager(), TAG_CONFIRM_AUTO_SYNC_CHANGE);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Context context = getActivity();
+ if (savedInstanceState != null) {
+ mEnabling = savedInstanceState.getBoolean(SAVE_ENABLING);
+ }
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ if (!mEnabling) {
+ builder.setTitle(R.string.data_usage_auto_sync_off_dialog_title);
+ builder.setMessage(R.string.data_usage_auto_sync_off_dialog);
+ } else {
+ builder.setTitle(R.string.data_usage_auto_sync_on_dialog_title);
+ builder.setMessage(R.string.data_usage_auto_sync_on_dialog);
+ }
+
+ builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ ContentResolver.setMasterSyncAutomatically(mEnabling);
+ }
+ });
+ builder.setNegativeButton(android.R.string.cancel, null);
+
+ return builder.create();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(SAVE_ENABLING, mEnabling);
+ }
+ }
+
+ /**
* Compute default tab that should be selected, based on
* {@link NetworkPolicyManager#EXTRA_NETWORK_TEMPLATE} extra.
*/
@@ -1981,23 +2109,23 @@ public class DataUsageSummary extends Fragment {
*/
private static class UidDetailTask extends AsyncTask<Void, Void, UidDetail> {
private final UidDetailProvider mProvider;
- private final AppUsageItem mItem;
+ private final AppItem mItem;
private final View mTarget;
- private UidDetailTask(UidDetailProvider provider, AppUsageItem item, View target) {
+ private UidDetailTask(UidDetailProvider provider, AppItem item, View target) {
mProvider = checkNotNull(provider);
mItem = checkNotNull(item);
mTarget = checkNotNull(target);
}
public static void bindView(
- UidDetailProvider provider, AppUsageItem item, View target) {
+ UidDetailProvider provider, AppItem item, View target) {
final UidDetailTask existing = (UidDetailTask) target.getTag();
if (existing != null) {
existing.cancel(false);
}
- final UidDetail cachedDetail = provider.getUidDetail(item.uids[0], false);
+ final UidDetail cachedDetail = provider.getUidDetail(item.appId, false);
if (cachedDetail != null) {
bindView(cachedDetail, target);
} else {
@@ -2026,7 +2154,7 @@ public class DataUsageSummary extends Fragment {
@Override
protected UidDetail doInBackground(Void... params) {
- return mProvider.getUidDetail(mItem.uids[0], true);
+ return mProvider.getUidDetail(mItem.appId, true);
}
@Override
@@ -2036,22 +2164,24 @@ public class DataUsageSummary extends Fragment {
}
/**
- * Test if device has a mobile data radio.
+ * Test if device has a mobile data radio with SIM in ready state.
*/
- private static boolean hasMobileRadio(Context context) {
+ public static boolean hasReadyMobileRadio(Context context) {
if (TEST_RADIOS) {
return SystemProperties.get(TEST_RADIOS_PROP).contains("mobile");
}
- final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- return conn.isNetworkSupported(TYPE_MOBILE);
+ final ConnectivityManager conn = ConnectivityManager.from(context);
+ final TelephonyManager tele = TelephonyManager.from(context);
+
+ // require both supported network and ready SIM
+ return conn.isNetworkSupported(TYPE_MOBILE) && tele.getSimState() == SIM_STATE_READY;
}
/**
* Test if device has a mobile 4G data radio.
*/
- private static boolean hasMobile4gRadio(Context context) {
+ public static boolean hasReadyMobile4gRadio(Context context) {
if (!NetworkPolicyEditor.ENABLE_SPLIT_POLICIES) {
return false;
}
@@ -2059,40 +2189,53 @@ public class DataUsageSummary extends Fragment {
return SystemProperties.get(TEST_RADIOS_PROP).contains("4g");
}
- final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
- Context.TELEPHONY_SERVICE);
+ final ConnectivityManager conn = ConnectivityManager.from(context);
+ final TelephonyManager tele = TelephonyManager.from(context);
final boolean hasWimax = conn.isNetworkSupported(TYPE_WIMAX);
- final boolean hasLte = telephony.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE;
+ final boolean hasLte = (tele.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE)
+ && hasReadyMobileRadio(context);
return hasWimax || hasLte;
}
/**
* Test if device has a Wi-Fi data radio.
*/
- private static boolean hasWifiRadio(Context context) {
+ public static boolean hasWifiRadio(Context context) {
if (TEST_RADIOS) {
return SystemProperties.get(TEST_RADIOS_PROP).contains("wifi");
}
- final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
- Context.CONNECTIVITY_SERVICE);
+ final ConnectivityManager conn = ConnectivityManager.from(context);
return conn.isNetworkSupported(TYPE_WIFI);
}
/**
* Test if device has an ethernet network connection.
*/
- private static boolean hasEthernet(Context context) {
+ public boolean hasEthernet(Context context) {
if (TEST_RADIOS) {
return SystemProperties.get(TEST_RADIOS_PROP).contains("ethernet");
}
- final ConnectivityManager conn = (ConnectivityManager) context.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- return conn.isNetworkSupported(TYPE_ETHERNET);
+ final ConnectivityManager conn = ConnectivityManager.from(context);
+ final boolean hasEthernet = conn.isNetworkSupported(TYPE_ETHERNET);
+
+ final long ethernetBytes;
+ if (mStatsSession != null) {
+ try {
+ ethernetBytes = mStatsSession.getSummaryForNetwork(
+ NetworkTemplate.buildTemplateEthernet(), Long.MIN_VALUE, Long.MAX_VALUE)
+ .getTotalBytes();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ } else {
+ ethernetBytes = 0;
+ }
+
+ // only show ethernet when both hardware present and traffic has occurred
+ return hasEthernet && ethernetBytes > 0;
}
/**
@@ -2126,6 +2269,7 @@ public class DataUsageSummary extends Fragment {
* Build string describing currently limited networks, which defines when
* background data is restricted.
*/
+ @Deprecated
private CharSequence buildLimitedNetworksString() {
final List<CharSequence> limited = buildLimitedNetworksList();
@@ -2141,22 +2285,28 @@ public class DataUsageSummary extends Fragment {
* Build list of currently limited networks, which defines when background
* data is restricted.
*/
+ @Deprecated
private List<CharSequence> buildLimitedNetworksList() {
final Context context = getActivity();
- final String subscriberId = getActiveSubscriberId(context);
// build combined list of all limited networks
final ArrayList<CharSequence> limited = Lists.newArrayList();
- if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobileAll(subscriberId))) {
- limited.add(getText(R.string.data_usage_list_mobile));
- }
- if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile3gLower(subscriberId))) {
- limited.add(getText(R.string.data_usage_tab_3g));
- }
- if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile4g(subscriberId))) {
- limited.add(getText(R.string.data_usage_tab_4g));
+
+ final TelephonyManager tele = TelephonyManager.from(context);
+ if (tele.getSimState() == SIM_STATE_READY) {
+ final String subscriberId = getActiveSubscriberId(context);
+ if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobileAll(subscriberId))) {
+ limited.add(getText(R.string.data_usage_list_mobile));
+ }
+ if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile3gLower(subscriberId))) {
+ limited.add(getText(R.string.data_usage_tab_3g));
+ }
+ if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile4g(subscriberId))) {
+ limited.add(getText(R.string.data_usage_tab_4g));
+ }
}
- if (mPolicyEditor.hasLimitedPolicy(buildTemplateWifi())) {
+
+ if (mPolicyEditor.hasLimitedPolicy(buildTemplateWifiWildcard())) {
limited.add(getText(R.string.data_usage_tab_wifi));
}
if (mPolicyEditor.hasLimitedPolicy(buildTemplateEthernet())) {
@@ -2202,13 +2352,4 @@ public class DataUsageSummary extends Fragment {
summary.setVisibility(View.VISIBLE);
summary.setText(string);
}
-
- private static boolean contains(int[] haystack, int needle) {
- for (int value : haystack) {
- if (value == needle) {
- return true;
- }
- }
- return false;
- }
}
diff --git a/src/com/android/settings/DevelopmentSettings.java b/src/com/android/settings/DevelopmentSettings.java
index 2ffae19..29c58ab 100644
--- a/src/com/android/settings/DevelopmentSettings.java
+++ b/src/com/android/settings/DevelopmentSettings.java
@@ -16,16 +16,24 @@
package com.android.settings;
+import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+
+import android.app.ActionBar;
+import android.app.Activity;
import android.app.ActivityManagerNative;
+import android.app.ActivityThread;
import android.app.AlertDialog;
import android.app.Dialog;
+import android.app.DialogFragment;
import android.app.backup.IBackupManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.VerifierDeviceIdentity;
+import android.os.AsyncTask;
import android.os.BatteryManager;
import android.os.Build;
import android.os.Bundle;
@@ -35,41 +43,58 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemProperties;
+import android.os.Trace;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
+import android.preference.MultiCheckPreference;
import android.preference.Preference;
+import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
-import android.preference.Preference.OnPreferenceChangeListener;
import android.provider.Settings;
import android.text.TextUtils;
+import android.view.Gravity;
+import android.view.HardwareRenderer;
import android.view.IWindowManager;
+import android.view.View;
+import android.widget.CompoundButton;
+import android.widget.Switch;
+
+import java.util.ArrayList;
/*
* Displays preferences for application developers.
*/
public class DevelopmentSettings extends PreferenceFragment
implements DialogInterface.OnClickListener, DialogInterface.OnDismissListener,
- OnPreferenceChangeListener {
+ OnPreferenceChangeListener, CompoundButton.OnCheckedChangeListener {
private static final String ENABLE_ADB = "enable_adb";
-
- private static final String VERIFIER_DEVICE_IDENTIFIER = "verifier_device_identifier";
private static final String KEEP_SCREEN_ON = "keep_screen_on";
private static final String ALLOW_MOCK_LOCATION = "allow_mock_location";
private static final String HDCP_CHECKING_KEY = "hdcp_checking";
private static final String HDCP_CHECKING_PROPERTY = "persist.sys.hdcp_checking";
+ private static final String ENFORCE_READ_EXTERNAL = "enforce_read_external";
private static final String LOCAL_BACKUP_PASSWORD = "local_backup_password";
private static final String HARDWARE_UI_PROPERTY = "persist.sys.ui.hw";
+ private static final String DEBUG_APP_KEY = "debug_app";
+ private static final String WAIT_FOR_DEBUGGER_KEY = "wait_for_debugger";
private static final String STRICT_MODE_KEY = "strict_mode";
private static final String POINTER_LOCATION_KEY = "pointer_location";
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 SHOW_CPU_USAGE_KEY = "show_cpu_usage";
private static final String FORCE_HARDWARE_UI_KEY = "force_hw_ui";
+ private static final String TRACK_FRAME_TIME_KEY = "track_frame_time";
+ private static final String SHOW_HW_SCREEN_UPDATES_KEY = "show_hw_screen_udpates";
+ private static final String DEBUG_LAYOUT_KEY = "debug_layout";
private static final String WINDOW_ANIMATION_SCALE_KEY = "window_animation_scale";
private static final String TRANSITION_ANIMATION_SCALE_KEY = "transition_animation_scale";
+ private static final String ANIMATOR_DURATION_SCALE_KEY = "animator_duration_scale";
+
+ private static final String ENABLE_TRACES_KEY = "enable_traces";
private static final String IMMEDIATELY_DESTROY_ACTIVITIES_KEY
= "immediately_destroy_activities";
@@ -77,32 +102,56 @@ public class DevelopmentSettings extends PreferenceFragment
private static final String SHOW_ALL_ANRS_KEY = "show_all_anrs";
+ private static final String TAG_CONFIRM_ENFORCE = "confirm_enforce";
+
+ private static final int RESULT_DEBUG_APP = 1000;
+
private IWindowManager mWindowManager;
private IBackupManager mBackupManager;
+ private Switch mEnabledSwitch;
+ private boolean mLastEnabledState;
+ private boolean mHaveDebugSettings;
+ private boolean mDontPokeProperties;
+
private CheckBoxPreference mEnableAdb;
private CheckBoxPreference mKeepScreenOn;
+ private CheckBoxPreference mEnforceReadExternal;
private CheckBoxPreference mAllowMockLocation;
private PreferenceScreen mPassword;
+ private String mDebugApp;
+ private Preference mDebugAppPref;
+ private CheckBoxPreference mWaitForDebugger;
+
private CheckBoxPreference mStrictMode;
private CheckBoxPreference mPointerLocation;
private CheckBoxPreference mShowTouches;
private CheckBoxPreference mShowScreenUpdates;
+ private CheckBoxPreference mDisableOverlays;
private CheckBoxPreference mShowCpuUsage;
private CheckBoxPreference mForceHardwareUi;
+ private CheckBoxPreference mTrackFrameTime;
+ private CheckBoxPreference mShowHwScreenUpdates;
+ private CheckBoxPreference mDebugLayout;
private ListPreference mWindowAnimationScale;
private ListPreference mTransitionAnimationScale;
+ private ListPreference mAnimatorDurationScale;
+ private MultiCheckPreference mEnableTracesPref;
private CheckBoxPreference mImmediatelyDestroyActivities;
private ListPreference mAppProcessLimit;
private CheckBoxPreference mShowAllANRs;
- // To track whether Yes was clicked in the adb warning dialog
- private boolean mOkClicked;
+ private final ArrayList<Preference> mAllPrefs = new ArrayList<Preference>();
+ private final ArrayList<CheckBoxPreference> mResetCbPrefs
+ = new ArrayList<CheckBoxPreference>();
- private Dialog mOkDialog;
+ // To track whether a confirmation dialog was clicked.
+ private boolean mDialogClicked;
+ private Dialog mEnableDialog;
+ private Dialog mAdbDialog;
@Override
public void onCreate(Bundle icicle) {
@@ -114,75 +163,204 @@ public class DevelopmentSettings extends PreferenceFragment
addPreferencesFromResource(R.xml.development_prefs);
- mEnableAdb = (CheckBoxPreference) findPreference(ENABLE_ADB);
- mKeepScreenOn = (CheckBoxPreference) findPreference(KEEP_SCREEN_ON);
- mAllowMockLocation = (CheckBoxPreference) findPreference(ALLOW_MOCK_LOCATION);
+ mEnableAdb = findAndInitCheckboxPref(ENABLE_ADB);
+ mKeepScreenOn = findAndInitCheckboxPref(KEEP_SCREEN_ON);
+ mEnforceReadExternal = findAndInitCheckboxPref(ENFORCE_READ_EXTERNAL);
+ mAllowMockLocation = findAndInitCheckboxPref(ALLOW_MOCK_LOCATION);
mPassword = (PreferenceScreen) findPreference(LOCAL_BACKUP_PASSWORD);
-
- mStrictMode = (CheckBoxPreference) findPreference(STRICT_MODE_KEY);
- mPointerLocation = (CheckBoxPreference) findPreference(POINTER_LOCATION_KEY);
- mShowTouches = (CheckBoxPreference) findPreference(SHOW_TOUCHES_KEY);
- mShowScreenUpdates = (CheckBoxPreference) findPreference(SHOW_SCREEN_UPDATES_KEY);
- mShowCpuUsage = (CheckBoxPreference) findPreference(SHOW_CPU_USAGE_KEY);
- mForceHardwareUi = (CheckBoxPreference) findPreference(FORCE_HARDWARE_UI_KEY);
+ mAllPrefs.add(mPassword);
+
+ mDebugAppPref = findPreference(DEBUG_APP_KEY);
+ mAllPrefs.add(mDebugAppPref);
+ mWaitForDebugger = findAndInitCheckboxPref(WAIT_FOR_DEBUGGER_KEY);
+ mStrictMode = findAndInitCheckboxPref(STRICT_MODE_KEY);
+ mPointerLocation = findAndInitCheckboxPref(POINTER_LOCATION_KEY);
+ mShowTouches = findAndInitCheckboxPref(SHOW_TOUCHES_KEY);
+ mShowScreenUpdates = findAndInitCheckboxPref(SHOW_SCREEN_UPDATES_KEY);
+ mDisableOverlays = findAndInitCheckboxPref(DISABLE_OVERLAYS_KEY);
+ mShowCpuUsage = findAndInitCheckboxPref(SHOW_CPU_USAGE_KEY);
+ mForceHardwareUi = findAndInitCheckboxPref(FORCE_HARDWARE_UI_KEY);
+ mTrackFrameTime = findAndInitCheckboxPref(TRACK_FRAME_TIME_KEY);
+ mShowHwScreenUpdates = findAndInitCheckboxPref(SHOW_HW_SCREEN_UPDATES_KEY);
+ mDebugLayout = findAndInitCheckboxPref(DEBUG_LAYOUT_KEY);
mWindowAnimationScale = (ListPreference) findPreference(WINDOW_ANIMATION_SCALE_KEY);
+ mAllPrefs.add(mWindowAnimationScale);
mWindowAnimationScale.setOnPreferenceChangeListener(this);
mTransitionAnimationScale = (ListPreference) findPreference(TRANSITION_ANIMATION_SCALE_KEY);
+ mAllPrefs.add(mTransitionAnimationScale);
mTransitionAnimationScale.setOnPreferenceChangeListener(this);
+ mAnimatorDurationScale = (ListPreference) findPreference(ANIMATOR_DURATION_SCALE_KEY);
+ mAllPrefs.add(mAnimatorDurationScale);
+ mAnimatorDurationScale.setOnPreferenceChangeListener(this);
+ mEnableTracesPref = (MultiCheckPreference)findPreference(ENABLE_TRACES_KEY);
+ String[] traceValues = new String[Trace.TRACE_TAGS.length];
+ for (int i=Trace.TRACE_FLAGS_START_BIT; i<traceValues.length; i++) {
+ traceValues[i] = Integer.toString(1<<i);
+ }
+ mEnableTracesPref.setEntries(Trace.TRACE_TAGS);
+ mEnableTracesPref.setEntryValues(traceValues);
+ mAllPrefs.add(mEnableTracesPref);
+ mEnableTracesPref.setOnPreferenceChangeListener(this);
mImmediatelyDestroyActivities = (CheckBoxPreference) findPreference(
IMMEDIATELY_DESTROY_ACTIVITIES_KEY);
+ mAllPrefs.add(mImmediatelyDestroyActivities);
+ mResetCbPrefs.add(mImmediatelyDestroyActivities);
mAppProcessLimit = (ListPreference) findPreference(APP_PROCESS_LIMIT_KEY);
+ mAllPrefs.add(mAppProcessLimit);
mAppProcessLimit.setOnPreferenceChangeListener(this);
mShowAllANRs = (CheckBoxPreference) findPreference(
SHOW_ALL_ANRS_KEY);
+ mAllPrefs.add(mShowAllANRs);
+ mResetCbPrefs.add(mShowAllANRs);
- final Preference verifierDeviceIdentifier = findPreference(VERIFIER_DEVICE_IDENTIFIER);
- final PackageManager pm = getActivity().getPackageManager();
- final VerifierDeviceIdentity verifierIndentity = pm.getVerifierDeviceIdentity();
- if (verifierIndentity != null) {
- verifierDeviceIdentifier.setSummary(verifierIndentity.toString());
+ Preference hdcpChecking = findPreference(HDCP_CHECKING_KEY);
+ if (hdcpChecking != null) {
+ mAllPrefs.add(hdcpChecking);
}
-
removeHdcpOptionsForProduction();
}
+ private CheckBoxPreference findAndInitCheckboxPref(String key) {
+ CheckBoxPreference pref = (CheckBoxPreference) findPreference(key);
+ if (pref == null) {
+ throw new IllegalArgumentException("Cannot find preference with key = " + key);
+ }
+ mAllPrefs.add(pref);
+ mResetCbPrefs.add(pref);
+ return pref;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ final Activity activity = getActivity();
+ mEnabledSwitch = new Switch(activity);
+
+ final int padding = activity.getResources().getDimensionPixelSize(
+ R.dimen.action_bar_switch_padding);
+ mEnabledSwitch.setPadding(0, 0, padding, 0);
+ mEnabledSwitch.setOnCheckedChangeListener(this);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ final Activity activity = getActivity();
+ activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
+ ActionBar.DISPLAY_SHOW_CUSTOM);
+ activity.getActionBar().setCustomView(mEnabledSwitch, new ActionBar.LayoutParams(
+ ActionBar.LayoutParams.WRAP_CONTENT,
+ ActionBar.LayoutParams.WRAP_CONTENT,
+ Gravity.CENTER_VERTICAL | Gravity.RIGHT));
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ final Activity activity = getActivity();
+ activity.getActionBar().setDisplayOptions(0, ActionBar.DISPLAY_SHOW_CUSTOM);
+ activity.getActionBar().setCustomView(null);
+ }
+
private void removeHdcpOptionsForProduction() {
if ("user".equals(Build.TYPE)) {
Preference hdcpChecking = findPreference(HDCP_CHECKING_KEY);
if (hdcpChecking != null) {
// Remove the preference
getPreferenceScreen().removePreference(hdcpChecking);
+ mAllPrefs.remove(hdcpChecking);
}
}
}
+ private void setPrefsEnabledState(boolean enabled) {
+ for (int i = 0; i < mAllPrefs.size(); i++) {
+ mAllPrefs.get(i).setEnabled(enabled);
+ }
+ updateAllOptions();
+ }
+
@Override
public void onResume() {
super.onResume();
final ContentResolver cr = getActivity().getContentResolver();
- mEnableAdb.setChecked(Settings.Secure.getInt(cr,
+ mLastEnabledState = Settings.Secure.getInt(cr,
+ Settings.Secure.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
+ mEnabledSwitch.setChecked(mLastEnabledState);
+ setPrefsEnabledState(mLastEnabledState);
+
+ if (mHaveDebugSettings && !mLastEnabledState) {
+ // Overall debugging is disabled, but there are some debug
+ // settings that are enabled. This is an invalid state. Switch
+ // to debug settings being enabled, so the user knows there is
+ // stuff enabled and can turn it all off if they want.
+ Settings.Secure.putInt(getActivity().getContentResolver(),
+ Settings.Secure.DEVELOPMENT_SETTINGS_ENABLED, 1);
+ mLastEnabledState = true;
+ setPrefsEnabledState(mLastEnabledState);
+ }
+ }
+
+ void updateCheckBox(CheckBoxPreference checkBox, boolean value) {
+ checkBox.setChecked(value);
+ mHaveDebugSettings |= value;
+ }
+
+ private void updateAllOptions() {
+ final Context context = getActivity();
+ final ContentResolver cr = context.getContentResolver();
+ mHaveDebugSettings = false;
+ updateCheckBox(mEnableAdb, Settings.Secure.getInt(cr,
Settings.Secure.ADB_ENABLED, 0) != 0);
- mKeepScreenOn.setChecked(Settings.System.getInt(cr,
+ updateCheckBox(mKeepScreenOn, Settings.System.getInt(cr,
Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0) != 0);
- mAllowMockLocation.setChecked(Settings.Secure.getInt(cr,
+ updateCheckBox(mEnforceReadExternal, isPermissionEnforced(context, READ_EXTERNAL_STORAGE));
+ updateCheckBox(mAllowMockLocation, Settings.Secure.getInt(cr,
Settings.Secure.ALLOW_MOCK_LOCATION, 0) != 0);
updateHdcpValues();
updatePasswordSummary();
+ updateDebuggerOptions();
updateStrictModeVisualOptions();
updatePointerLocationOptions();
updateShowTouchesOptions();
updateFlingerOptions();
updateCpuUsageOptions();
updateHardwareUiOptions();
+ updateTrackFrameTimeOptions();
+ updateShowHwScreenUpdatesOptions();
+ updateDebugLayoutOptions();
updateAnimationScaleOptions();
+ updateEnableTracesOptions();
updateImmediatelyDestroyActivitiesOptions();
updateAppProcessLimitOptions();
updateShowAllANRsOptions();
}
+ private void resetDangerousOptions() {
+ mDontPokeProperties = true;
+ for (int i=0; i<mResetCbPrefs.size(); i++) {
+ CheckBoxPreference cb = mResetCbPrefs.get(i);
+ if (cb.isChecked()) {
+ cb.setChecked(false);
+ onPreferenceTreeClick(null, cb);
+ }
+ }
+ resetDebuggerOptions();
+ writeAnimationScaleOption(0, mWindowAnimationScale, null);
+ writeAnimationScaleOption(1, mTransitionAnimationScale, null);
+ writeAnimationScaleOption(2, mAnimatorDurationScale, null);
+ writeEnableTracesOptions(0);
+ writeAppProcessLimitOptions(null);
+ mHaveDebugSettings = false;
+ updateAllOptions();
+ mDontPokeProperties = false;
+ pokeSystemProperties();
+ }
+
private void updateHdcpValues() {
int index = 1; // Defaults to drm-only. Needs to match with R.array.hdcp_checking_values
ListPreference hdcpChecking = (ListPreference) findPreference(HDCP_CHECKING_KEY);
@@ -214,12 +392,52 @@ public class DevelopmentSettings extends PreferenceFragment
}
}
+ private void writeDebuggerOptions() {
+ try {
+ ActivityManagerNative.getDefault().setDebugApp(
+ mDebugApp, mWaitForDebugger.isChecked(), true);
+ } catch (RemoteException ex) {
+ }
+ }
+
+ private static void resetDebuggerOptions() {
+ try {
+ ActivityManagerNative.getDefault().setDebugApp(
+ null, false, true);
+ } catch (RemoteException ex) {
+ }
+ }
+
+ private void updateDebuggerOptions() {
+ mDebugApp = Settings.System.getString(
+ getActivity().getContentResolver(), Settings.System.DEBUG_APP);
+ updateCheckBox(mWaitForDebugger, Settings.System.getInt(
+ getActivity().getContentResolver(), Settings.System.WAIT_FOR_DEBUGGER, 0) != 0);
+ if (mDebugApp != null && mDebugApp.length() > 0) {
+ String label;
+ try {
+ ApplicationInfo ai = getActivity().getPackageManager().getApplicationInfo(mDebugApp,
+ PackageManager.GET_DISABLED_COMPONENTS);
+ CharSequence lab = getActivity().getPackageManager().getApplicationLabel(ai);
+ label = lab != null ? lab.toString() : mDebugApp;
+ } catch (PackageManager.NameNotFoundException e) {
+ label = mDebugApp;
+ }
+ mDebugAppPref.setSummary(getResources().getString(R.string.debug_app_set, label));
+ mWaitForDebugger.setEnabled(true);
+ mHaveDebugSettings = true;
+ } else {
+ mDebugAppPref.setSummary(getResources().getString(R.string.debug_app_not_set));
+ mWaitForDebugger.setEnabled(false);
+ }
+ }
+
// Returns the current state of the system property that controls
// strictmode flashes. One of:
// 0: not explicitly set one way or another
// 1: on
// 2: off
- private int currentStrictModeActiveIndex() {
+ private static int currentStrictModeActiveIndex() {
if (TextUtils.isEmpty(SystemProperties.get(StrictMode.VISUAL_PROPERTY))) {
return 0;
}
@@ -236,7 +454,7 @@ public class DevelopmentSettings extends PreferenceFragment
}
private void updateStrictModeVisualOptions() {
- mStrictMode.setChecked(currentStrictModeActiveIndex() == 1);
+ updateCheckBox(mStrictMode, currentStrictModeActiveIndex() == 1);
}
private void writePointerLocationOptions() {
@@ -245,7 +463,7 @@ public class DevelopmentSettings extends PreferenceFragment
}
private void updatePointerLocationOptions() {
- mPointerLocation.setChecked(Settings.System.getInt(getActivity().getContentResolver(),
+ updateCheckBox(mPointerLocation, Settings.System.getInt(getActivity().getContentResolver(),
Settings.System.POINTER_LOCATION, 0) != 0);
}
@@ -255,7 +473,7 @@ public class DevelopmentSettings extends PreferenceFragment
}
private void updateShowTouchesOptions() {
- mShowTouches.setChecked(Settings.System.getInt(getActivity().getContentResolver(),
+ updateCheckBox(mShowTouches, Settings.System.getInt(getActivity().getContentResolver(),
Settings.System.SHOW_TOUCHES, 0) != 0);
}
@@ -273,9 +491,11 @@ public class DevelopmentSettings extends PreferenceFragment
@SuppressWarnings("unused")
int enableGL = reply.readInt();
int showUpdates = reply.readInt();
- mShowScreenUpdates.setChecked(showUpdates != 0);
+ updateCheckBox(mShowScreenUpdates, showUpdates != 0);
@SuppressWarnings("unused")
int showBackground = reply.readInt();
+ int disableOverlays = reply.readInt();
+ updateCheckBox(mDisableOverlays, disableOverlays != 0);
reply.recycle();
data.recycle();
}
@@ -283,13 +503,14 @@ public class DevelopmentSettings extends PreferenceFragment
}
}
- private void writeFlingerOptions() {
+ private void writeShowUpdatesOption() {
try {
IBinder flinger = ServiceManager.getService("SurfaceFlinger");
if (flinger != null) {
Parcel data = Parcel.obtain();
data.writeInterfaceToken("android.ui.ISurfaceComposer");
- data.writeInt(mShowScreenUpdates.isChecked() ? 1 : 0);
+ final int showUpdates = mShowScreenUpdates.isChecked() ? 1 : 0;
+ data.writeInt(showUpdates);
flinger.transact(1002, data, null, 0);
data.recycle();
@@ -299,16 +520,67 @@ public class DevelopmentSettings extends PreferenceFragment
}
}
+ private void writeDisableOverlaysOption() {
+ try {
+ IBinder flinger = ServiceManager.getService("SurfaceFlinger");
+ if (flinger != null) {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken("android.ui.ISurfaceComposer");
+ final int disableOverlays = mDisableOverlays.isChecked() ? 1 : 0;
+ data.writeInt(disableOverlays);
+ flinger.transact(1008, data, null, 0);
+ data.recycle();
+
+ updateFlingerOptions();
+ }
+ } catch (RemoteException ex) {
+ }
+ }
+
private void updateHardwareUiOptions() {
- mForceHardwareUi.setChecked(SystemProperties.getBoolean(HARDWARE_UI_PROPERTY, false));
+ updateCheckBox(mForceHardwareUi, SystemProperties.getBoolean(HARDWARE_UI_PROPERTY, false));
}
private void writeHardwareUiOptions() {
SystemProperties.set(HARDWARE_UI_PROPERTY, mForceHardwareUi.isChecked() ? "true" : "false");
+ pokeSystemProperties();
+ }
+
+ private void updateTrackFrameTimeOptions() {
+ updateCheckBox(mTrackFrameTime,
+ SystemProperties.getBoolean(HardwareRenderer.PROFILE_PROPERTY, false));
+ }
+
+ private void writeTrackFrameTimeOptions() {
+ SystemProperties.set(HardwareRenderer.PROFILE_PROPERTY,
+ mTrackFrameTime.isChecked() ? "true" : "false");
+ pokeSystemProperties();
+ }
+
+ private void updateShowHwScreenUpdatesOptions() {
+ updateCheckBox(mShowHwScreenUpdates,
+ SystemProperties.getBoolean(HardwareRenderer.DEBUG_DIRTY_REGIONS_PROPERTY, false));
+ }
+
+ private void writeShowHwScreenUpdatesOptions() {
+ SystemProperties.set(HardwareRenderer.DEBUG_DIRTY_REGIONS_PROPERTY,
+ mShowHwScreenUpdates.isChecked() ? "true" : "false");
+ pokeSystemProperties();
+ }
+
+ private void updateDebugLayoutOptions() {
+ updateCheckBox(mDebugLayout,
+ SystemProperties.getBoolean(View.DEBUG_LAYOUT_PROPERTY, false));
+ }
+
+ private void writeDebugLayoutOptions() {
+ SystemProperties.set(View.DEBUG_LAYOUT_PROPERTY,
+ mDebugLayout.isChecked() ? "true" : "false");
+ pokeSystemProperties();
}
private void updateCpuUsageOptions() {
- mShowCpuUsage.setChecked(Settings.System.getInt(getActivity().getContentResolver(),
+ updateCheckBox(mShowCpuUsage, Settings.System.getInt(getActivity().getContentResolver(),
Settings.System.SHOW_PROCESSES, 0) != 0);
}
@@ -334,13 +606,16 @@ public class DevelopmentSettings extends PreferenceFragment
}
private void updateImmediatelyDestroyActivitiesOptions() {
- mImmediatelyDestroyActivities.setChecked(Settings.System.getInt(
+ updateCheckBox(mImmediatelyDestroyActivities, Settings.System.getInt(
getActivity().getContentResolver(), Settings.System.ALWAYS_FINISH_ACTIVITIES, 0) != 0);
}
private void updateAnimationScaleValue(int which, ListPreference pref) {
try {
float scale = mWindowManager.getAnimationScale(which);
+ if (scale != 1) {
+ mHaveDebugSettings = true;
+ }
CharSequence[] values = pref.getEntryValues();
for (int i=0; i<values.length; i++) {
float val = Float.parseFloat(values[i].toString());
@@ -359,11 +634,12 @@ public class DevelopmentSettings extends PreferenceFragment
private void updateAnimationScaleOptions() {
updateAnimationScaleValue(0, mWindowAnimationScale);
updateAnimationScaleValue(1, mTransitionAnimationScale);
+ updateAnimationScaleValue(2, mAnimatorDurationScale);
}
private void writeAnimationScaleOption(int which, ListPreference pref, Object newValue) {
try {
- float scale = Float.parseFloat(newValue.toString());
+ float scale = newValue != null ? Float.parseFloat(newValue.toString()) : 1;
mWindowManager.setAnimationScale(which, scale);
updateAnimationScaleValue(which, pref);
} catch (RemoteException e) {
@@ -377,6 +653,9 @@ public class DevelopmentSettings extends PreferenceFragment
for (int i=0; i<values.length; i++) {
int val = Integer.parseInt(values[i].toString());
if (val >= limit) {
+ if (i != 0) {
+ mHaveDebugSettings = true;
+ }
mAppProcessLimit.setValueIndex(i);
mAppProcessLimit.setSummary(mAppProcessLimit.getEntries()[i]);
return;
@@ -390,7 +669,7 @@ public class DevelopmentSettings extends PreferenceFragment
private void writeAppProcessLimitOptions(Object newValue) {
try {
- int limit = Integer.parseInt(newValue.toString());
+ int limit = newValue != null ? Integer.parseInt(newValue.toString()) : -1;
ActivityManagerNative.getDefault().setProcessLimit(limit);
updateAppProcessLimitOptions();
} catch (RemoteException e) {
@@ -404,10 +683,92 @@ public class DevelopmentSettings extends PreferenceFragment
}
private void updateShowAllANRsOptions() {
- mShowAllANRs.setChecked(Settings.Secure.getInt(
+ updateCheckBox(mShowAllANRs, Settings.Secure.getInt(
getActivity().getContentResolver(), Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0);
}
+ private void updateEnableTracesOptions() {
+ String strValue = SystemProperties.get(Trace.PROPERTY_TRACE_TAG_ENABLEFLAGS);
+ long flags = SystemProperties.getLong(Trace.PROPERTY_TRACE_TAG_ENABLEFLAGS, 0);
+ String[] values = mEnableTracesPref.getEntryValues();
+ int numSet = 0;
+ for (int i=Trace.TRACE_FLAGS_START_BIT; i<values.length; i++) {
+ boolean set = (flags&(1<<i)) != 0;
+ mEnableTracesPref.setValue(i-Trace.TRACE_FLAGS_START_BIT, set);
+ if (set) {
+ numSet++;
+ }
+ }
+ if (numSet == 0) {
+ mEnableTracesPref.setSummary(R.string.enable_traces_summary_none);
+ } else if (numSet == values.length) {
+ mHaveDebugSettings = true;
+ mEnableTracesPref.setSummary(R.string.enable_traces_summary_all);
+ } else {
+ mHaveDebugSettings = true;
+ mEnableTracesPref.setSummary(getString(R.string.enable_traces_summary_num, numSet));
+ }
+ }
+
+ private void writeEnableTracesOptions() {
+ long value = 0;
+ String[] values = mEnableTracesPref.getEntryValues();
+ for (int i=Trace.TRACE_FLAGS_START_BIT; i<values.length; i++) {
+ if (mEnableTracesPref.getValue(i-Trace.TRACE_FLAGS_START_BIT)) {
+ value |= 1<<i;
+ }
+ }
+ writeEnableTracesOptions(value);
+ // Make sure summary is updated.
+ updateEnableTracesOptions();
+ }
+
+ private void writeEnableTracesOptions(long value) {
+ SystemProperties.set(Trace.PROPERTY_TRACE_TAG_ENABLEFLAGS,
+ "0x" + Long.toString(value, 16));
+ pokeSystemProperties();
+ }
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (buttonView == mEnabledSwitch) {
+ if (isChecked != mLastEnabledState) {
+ if (isChecked) {
+ mDialogClicked = false;
+ if (mEnableDialog != null) dismissDialogs();
+ mEnableDialog = new AlertDialog.Builder(getActivity()).setMessage(
+ getActivity().getResources().getString(
+ R.string.dev_settings_warning_message))
+ .setTitle(R.string.dev_settings_warning_title)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setPositiveButton(android.R.string.yes, this)
+ .setNegativeButton(android.R.string.no, this)
+ .show();
+ mEnableDialog.setOnDismissListener(this);
+ } else {
+ resetDangerousOptions();
+ Settings.Secure.putInt(getActivity().getContentResolver(),
+ Settings.Secure.DEVELOPMENT_SETTINGS_ENABLED, 0);
+ mLastEnabledState = isChecked;
+ setPrefsEnabledState(mLastEnabledState);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == RESULT_DEBUG_APP) {
+ if (resultCode == Activity.RESULT_OK) {
+ mDebugApp = data.getAction();
+ writeDebuggerOptions();
+ updateDebuggerOptions();
+ }
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
@@ -417,16 +778,16 @@ public class DevelopmentSettings extends PreferenceFragment
if (preference == mEnableAdb) {
if (mEnableAdb.isChecked()) {
- mOkClicked = false;
- if (mOkDialog != null) dismissDialog();
- mOkDialog = new AlertDialog.Builder(getActivity()).setMessage(
+ mDialogClicked = false;
+ if (mAdbDialog != null) dismissDialogs();
+ mAdbDialog = new AlertDialog.Builder(getActivity()).setMessage(
getActivity().getResources().getString(R.string.adb_warning_message))
.setTitle(R.string.adb_warning_title)
.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(android.R.string.yes, this)
.setNegativeButton(android.R.string.no, this)
.show();
- mOkDialog.setOnDismissListener(this);
+ mAdbDialog.setOnDismissListener(this);
} else {
Settings.Secure.putInt(getActivity().getContentResolver(),
Settings.Secure.ADB_ENABLED, 0);
@@ -436,10 +797,20 @@ public class DevelopmentSettings extends PreferenceFragment
Settings.System.STAY_ON_WHILE_PLUGGED_IN,
mKeepScreenOn.isChecked() ?
(BatteryManager.BATTERY_PLUGGED_AC | BatteryManager.BATTERY_PLUGGED_USB) : 0);
+ } else if (preference == mEnforceReadExternal) {
+ if (mEnforceReadExternal.isChecked()) {
+ ConfirmEnforceFragment.show(this);
+ } else {
+ setPermissionEnforced(getActivity(), READ_EXTERNAL_STORAGE, false);
+ }
} else if (preference == mAllowMockLocation) {
Settings.Secure.putInt(getActivity().getContentResolver(),
Settings.Secure.ALLOW_MOCK_LOCATION,
mAllowMockLocation.isChecked() ? 1 : 0);
+ } else if (preference == mDebugAppPref) {
+ startActivityForResult(new Intent(getActivity(), AppPicker.class), RESULT_DEBUG_APP);
+ } else if (preference == mWaitForDebugger) {
+ writeDebuggerOptions();
} else if (preference == mStrictMode) {
writeStrictModeVisualOptions();
} else if (preference == mPointerLocation) {
@@ -447,7 +818,9 @@ public class DevelopmentSettings extends PreferenceFragment
} else if (preference == mShowTouches) {
writeShowTouchesOptions();
} else if (preference == mShowScreenUpdates) {
- writeFlingerOptions();
+ writeShowUpdatesOption();
+ } else if (preference == mDisableOverlays) {
+ writeDisableOverlaysOption();
} else if (preference == mShowCpuUsage) {
writeCpuUsageOptions();
} else if (preference == mImmediatelyDestroyActivities) {
@@ -456,6 +829,12 @@ public class DevelopmentSettings extends PreferenceFragment
writeShowAllANRsOptions();
} else if (preference == mForceHardwareUi) {
writeHardwareUiOptions();
+ } else if (preference == mTrackFrameTime) {
+ writeTrackFrameTimeOptions();
+ } else if (preference == mShowHwScreenUpdates) {
+ writeShowHwScreenUpdatesOptions();
+ } else if (preference == mDebugLayout) {
+ writeDebugLayoutOptions();
}
return false;
@@ -466,6 +845,7 @@ public class DevelopmentSettings extends PreferenceFragment
if (HDCP_CHECKING_KEY.equals(preference.getKey())) {
SystemProperties.set(HDCP_CHECKING_PROPERTY, newValue.toString());
updateHdcpValues();
+ pokeSystemProperties();
return true;
} else if (preference == mWindowAnimationScale) {
writeAnimationScaleOption(0, mWindowAnimationScale, newValue);
@@ -473,6 +853,12 @@ public class DevelopmentSettings extends PreferenceFragment
} else if (preference == mTransitionAnimationScale) {
writeAnimationScaleOption(1, mTransitionAnimationScale, newValue);
return true;
+ } else if (preference == mAnimatorDurationScale) {
+ writeAnimationScaleOption(2, mAnimatorDurationScale, newValue);
+ return true;
+ } else if (preference == mEnableTracesPref) {
+ writeEnableTracesOptions();
+ return true;
} else if (preference == mAppProcessLimit) {
writeAppProcessLimitOptions(newValue);
return true;
@@ -480,33 +866,144 @@ public class DevelopmentSettings extends PreferenceFragment
return false;
}
- private void dismissDialog() {
- if (mOkDialog == null) return;
- mOkDialog.dismiss();
- mOkDialog = null;
+ private void dismissDialogs() {
+ if (mAdbDialog != null) {
+ mAdbDialog.dismiss();
+ mAdbDialog = null;
+ }
+ if (mEnableDialog != null) {
+ mEnableDialog.dismiss();
+ mEnableDialog = null;
+ }
}
public void onClick(DialogInterface dialog, int which) {
- if (which == DialogInterface.BUTTON_POSITIVE) {
- mOkClicked = true;
- Settings.Secure.putInt(getActivity().getContentResolver(),
- Settings.Secure.ADB_ENABLED, 1);
- } else {
- // Reset the toggle
- mEnableAdb.setChecked(false);
+ if (dialog == mAdbDialog) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ mDialogClicked = true;
+ Settings.Secure.putInt(getActivity().getContentResolver(),
+ Settings.Secure.ADB_ENABLED, 1);
+ } else {
+ // Reset the toggle
+ mEnableAdb.setChecked(false);
+ }
+ } else if (dialog == mEnableDialog) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ mDialogClicked = true;
+ Settings.Secure.putInt(getActivity().getContentResolver(),
+ Settings.Secure.DEVELOPMENT_SETTINGS_ENABLED, 1);
+ mLastEnabledState = true;
+ setPrefsEnabledState(mLastEnabledState);
+ } else {
+ // Reset the toggle
+ mEnabledSwitch.setChecked(false);
+ }
}
}
public void onDismiss(DialogInterface dialog) {
// Assuming that onClick gets called first
- if (!mOkClicked) {
- mEnableAdb.setChecked(false);
+ if (dialog == mAdbDialog) {
+ if (!mDialogClicked) {
+ mEnableAdb.setChecked(false);
+ }
+ mAdbDialog = null;
+ } else if (dialog == mEnableDialog) {
+ if (!mDialogClicked) {
+ mEnabledSwitch.setChecked(false);
+ }
+ mEnableDialog = null;
}
}
@Override
public void onDestroy() {
- dismissDialog();
+ dismissDialogs();
super.onDestroy();
}
+
+ void pokeSystemProperties() {
+ if (!mDontPokeProperties) {
+ (new SystemPropPoker()).execute();
+ }
+ }
+
+ static class SystemPropPoker extends AsyncTask<Void, Void, Void> {
+ @Override
+ protected Void doInBackground(Void... params) {
+ String[] services;
+ try {
+ services = ServiceManager.listServices();
+ } catch (RemoteException e) {
+ return null;
+ }
+ for (String service : services) {
+ IBinder obj = ServiceManager.checkService(service);
+ if (obj != null) {
+ Parcel data = Parcel.obtain();
+ try {
+ obj.transact(IBinder.SYSPROPS_TRANSACTION, data, null, 0);
+ } catch (RemoteException e) {
+ }
+ data.recycle();
+ }
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Dialog to confirm enforcement of {@link #READ_EXTERNAL_STORAGE}.
+ */
+ public static class ConfirmEnforceFragment extends DialogFragment {
+ public static void show(DevelopmentSettings parent) {
+ final ConfirmEnforceFragment dialog = new ConfirmEnforceFragment();
+ dialog.setTargetFragment(parent, 0);
+ dialog.show(parent.getFragmentManager(), TAG_CONFIRM_ENFORCE);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Context context = getActivity();
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.enforce_read_external_confirm_title);
+ builder.setMessage(R.string.enforce_read_external_confirm_message);
+
+ builder.setPositiveButton(android.R.string.ok, new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ setPermissionEnforced(context, READ_EXTERNAL_STORAGE, true);
+ ((DevelopmentSettings) getTargetFragment()).updateAllOptions();
+ }
+ });
+ builder.setNegativeButton(android.R.string.cancel, new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ ((DevelopmentSettings) getTargetFragment()).updateAllOptions();
+ }
+ });
+
+ return builder.create();
+ }
+ }
+
+ private static boolean isPermissionEnforced(Context context, String permission) {
+ try {
+ return ActivityThread.getPackageManager().isPermissionEnforced(READ_EXTERNAL_STORAGE);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Problem talking with PackageManager", e);
+ }
+ }
+
+ private static void setPermissionEnforced(
+ Context context, String permission, boolean enforced) {
+ try {
+ // TODO: offload to background thread
+ ActivityThread.getPackageManager()
+ .setPermissionEnforced(READ_EXTERNAL_STORAGE, enforced);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Problem talking with PackageManager", e);
+ }
+ }
}
diff --git a/src/com/android/settings/DisplaySettings.java b/src/com/android/settings/DisplaySettings.java
index 5887140..fc162e2 100644
--- a/src/com/android/settings/DisplaySettings.java
+++ b/src/com/android/settings/DisplaySettings.java
@@ -24,11 +24,8 @@ 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.os.ServiceManager;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
import android.preference.Preference;
@@ -36,8 +33,9 @@ import android.preference.PreferenceScreen;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.Log;
-import android.view.IWindowManager;
-import android.view.Surface;
+
+import com.android.internal.view.RotationPolicy;
+import com.android.settings.DreamSettings;
import java.util.ArrayList;
@@ -52,6 +50,7 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
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 CheckBoxPreference mAccelerometer;
private ListPreference mFontSizePref;
@@ -60,10 +59,12 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
private final Configuration mCurConfig = new Configuration();
private ListPreference mScreenTimeoutPreference;
+ private Preference mScreenSaverPreference;
- private ContentObserver mAccelerometerRotationObserver = new ContentObserver(new Handler()) {
+ private final RotationPolicy.RotationPolicyListener mRotationPolicyListener =
+ new RotationPolicy.RotationPolicyListener() {
@Override
- public void onChange(boolean selfChange) {
+ public void onChange() {
updateAccelerometerRotationCheckbox();
}
};
@@ -77,7 +78,19 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
mAccelerometer = (CheckBoxPreference) findPreference(KEY_ACCELEROMETER);
mAccelerometer.setPersistent(false);
+ if (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.
+ getPreferenceScreen().removePreference(mAccelerometer);
+ }
+ mScreenSaverPreference = findPreference(KEY_SCREEN_SAVER);
+ if (mScreenSaverPreference != null
+ && getResources().getBoolean(
+ com.android.internal.R.bool.config_enableDreams) == false) {
+ getPreferenceScreen().removePreference(mScreenSaverPreference);
+ }
+
mScreenTimeoutPreference = (ListPreference) findPreference(KEY_SCREEN_TIMEOUT);
final long currentTimeout = Settings.System.getLong(resolver, SCREEN_OFF_TIMEOUT,
FALLBACK_SCREEN_TIMEOUT_VALUE);
@@ -102,6 +115,7 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
Log.e(TAG, Settings.System.NOTIFICATION_LIGHT_PULSE + " not found");
}
}
+
}
private void updateTimeoutPreferenceDescription(long currentTimeout) {
@@ -198,27 +212,36 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
super.onResume();
updateState();
- getContentResolver().registerContentObserver(
- Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION), true,
- mAccelerometerRotationObserver);
+
+ RotationPolicy.registerRotationPolicyListener(getActivity(),
+ mRotationPolicyListener);
}
@Override
public void onPause() {
super.onPause();
- getContentResolver().unregisterContentObserver(mAccelerometerRotationObserver);
+ RotationPolicy.unregisterRotationPolicyListener(getActivity(),
+ mRotationPolicyListener);
}
private void updateState() {
updateAccelerometerRotationCheckbox();
readFontSizePreference(mFontSizePref);
+ updateScreenSaverSummary();
+ }
+
+ private void updateScreenSaverSummary() {
+ mScreenSaverPreference.setSummary(
+ DreamSettings.isScreenSaverEnabled(mScreenSaverPreference.getContext())
+ ? R.string.screensaver_settings_summary_on
+ : R.string.screensaver_settings_summary_off);
}
private void updateAccelerometerRotationCheckbox() {
- mAccelerometer.setChecked(Settings.System.getInt(
- getContentResolver(),
- Settings.System.ACCELEROMETER_ROTATION, 0) != 0);
+ if (getActivity() == null) return;
+
+ mAccelerometer.setChecked(!RotationPolicy.isRotationLocked(getActivity()));
}
public void writeFontSizePreference(Object objValue) {
@@ -233,17 +256,8 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
if (preference == mAccelerometer) {
- try {
- IWindowManager wm = IWindowManager.Stub.asInterface(
- ServiceManager.getService(Context.WINDOW_SERVICE));
- if (mAccelerometer.isChecked()) {
- wm.thawRotation();
- } else {
- wm.freezeRotation(Surface.ROTATION_0);
- }
- } catch (RemoteException exc) {
- Log.w(TAG, "Unable to save auto-rotate setting");
- }
+ RotationPolicy.setRotationLockForAccessibility(
+ getActivity(), !mAccelerometer.isChecked());
} else if (preference == mNotificationPulse) {
boolean value = mNotificationPulse.isChecked();
Settings.System.putInt(getContentResolver(), Settings.System.NOTIFICATION_LIGHT_PULSE,
diff --git a/src/com/android/settings/DreamComponentPreference.java b/src/com/android/settings/DreamComponentPreference.java
new file mode 100644
index 0000000..2114dd1
--- /dev/null
+++ b/src/com/android/settings/DreamComponentPreference.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings;
+
+import static android.provider.Settings.Secure.SCREENSAVER_COMPONENT;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ComponentInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.preference.Preference;
+import android.provider.Settings;
+import android.service.dreams.IDreamManager;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.ListAdapter;
+import android.widget.TextView;
+
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+public class DreamComponentPreference extends Preference {
+ private static final String TAG = "DreamComponentPreference";
+
+ private static final boolean SHOW_DOCK_APPS = false;
+ private static final boolean SHOW_DREAM_SERVICES = true;
+ private static final boolean SHOW_DREAM_ACTIVITIES = false;
+
+ private final PackageManager pm;
+ private final ContentResolver resolver;
+ private final Collator sCollator = Collator.getInstance();
+
+ public DreamComponentPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ pm = getContext().getPackageManager();
+ resolver = getContext().getContentResolver();
+
+ refreshFromSettings();
+ }
+
+ private void refreshFromSettings() {
+ ComponentName cn = null;
+ IDreamManager dm = IDreamManager.Stub.asInterface(
+ ServiceManager.getService("dreams"));
+ try {
+ cn = dm.getDreamComponent();
+ } catch (RemoteException ex) {
+ setSummary("(unknown)");
+ return;
+ }
+
+ try {
+ setSummary(pm.getActivityInfo(cn, 0).loadLabel(pm));
+ } catch (PackageManager.NameNotFoundException ex) {
+ try {
+ setSummary(pm.getServiceInfo(cn, 0).loadLabel(pm));
+ } catch (PackageManager.NameNotFoundException ex2) {
+ setSummary("(unknown)");
+ }
+ }
+ }
+
+ // Group by package, then by name.
+ Comparator<ResolveInfo> sResolveInfoComparator = new Comparator<ResolveInfo>() {
+ @Override
+ public int compare(ResolveInfo a, ResolveInfo b) {
+ CharSequence sa, sb;
+
+ ApplicationInfo aia = a.activityInfo != null ? a.activityInfo.applicationInfo : a.serviceInfo.applicationInfo;
+ ApplicationInfo aib = b.activityInfo != null ? b.activityInfo.applicationInfo : b.serviceInfo.applicationInfo;
+
+ if (!aia.equals(aib)) {
+ sa = pm.getApplicationLabel(aia);
+ sb = pm.getApplicationLabel(aib);
+ } else {
+ sa = a.loadLabel(pm);
+ if (sa == null) {
+ sa = (a.activityInfo != null) ? a.activityInfo.name : a.serviceInfo.name;
+ }
+ sb = b.loadLabel(pm);
+ if (sb == null) {
+ sb = (b.activityInfo != null) ? b.activityInfo.name : b.serviceInfo.name;
+ }
+ }
+ return sCollator.compare(sa.toString(), sb.toString());
+ }
+ };
+
+ public class DreamListAdapter extends BaseAdapter implements ListAdapter {
+ private ArrayList<ResolveInfo> results;
+ private final LayoutInflater inflater;
+
+ public DreamListAdapter(Context context) {
+ Intent choosy = new Intent(Intent.ACTION_MAIN)
+ .addCategory("android.intent.category.DREAM");
+
+ inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ results = new ArrayList<ResolveInfo>();
+
+ if (SHOW_DREAM_ACTIVITIES) {
+ results.addAll(pm.queryIntentActivities(choosy, PackageManager.GET_META_DATA));
+ }
+
+ if (SHOW_DREAM_SERVICES) {
+ results.addAll(pm.queryIntentServices(choosy, PackageManager.GET_META_DATA));
+ }
+
+ // Group by package
+ Collections.sort(results, sResolveInfoComparator);
+
+ if (SHOW_DOCK_APPS) {
+ choosy = new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_DESK_DOCK);
+
+ List<ResolveInfo> dockApps = pm.queryIntentActivities(choosy, 0);
+ for (ResolveInfo app : dockApps) {
+ // do not insert duplicate packages
+ int pos = Collections.binarySearch(results, app, sResolveInfoComparator);
+ if (pos < 0) {
+ results.add(-1-pos, app);
+ }
+ }
+ }
+ }
+
+ @Override
+ public int getCount() {
+ return results.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return results.get(position);
+ }
+
+ @Override
+ public long getItemId (int position) {
+ return (long) position;
+ }
+
+ private CharSequence loadDescription(ResolveInfo ri) {
+ CharSequence desc = null;
+ if (ri != null) {
+ Bundle metaData = (ri.activityInfo != null) ? ri.activityInfo.metaData : ri.serviceInfo.metaData;
+ Log.d(TAG, "loadDescription: ri=" + ri + " metaData=" + metaData);
+ if (metaData != null) {
+ desc = metaData.getCharSequence("android.screensaver.description");
+ Log.d(TAG, "loadDescription: desc=" + desc);
+ if (desc != null) {
+ desc = desc.toString().trim();
+ }
+ }
+ }
+ return desc;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View row = (convertView != null)
+ ? convertView
+ : inflater.inflate(R.layout.dream_picker_row, parent, false);
+ ResolveInfo ri = results.get(position);
+ ((TextView)row.findViewById(R.id.title)).setText(ri.loadLabel(pm));
+ ((ImageView)row.findViewById(R.id.icon)).setImageDrawable(ri.loadIcon(pm));
+ return row;
+ }
+ }
+
+ @Override
+ protected void onClick() {
+ final DreamListAdapter list = new DreamListAdapter(getContext());
+ AlertDialog alert = new AlertDialog.Builder(getContext())
+ .setAdapter(
+ list,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ ResolveInfo ri = (ResolveInfo)list.getItem(which);
+ String pn = (ri.activityInfo != null) ? ri.activityInfo.applicationInfo.packageName
+ : ri.serviceInfo.applicationInfo.packageName;
+ String n = (ri.activityInfo != null) ? ri.activityInfo.name : ri.serviceInfo.name;
+ ComponentName cn = new ComponentName(pn, n);
+
+ setSummary(ri.loadLabel(pm));
+ //getContext().startActivity(intent);
+
+ IDreamManager dm = IDreamManager.Stub.asInterface(
+ ServiceManager.getService("dreams"));
+ try {
+ dm.setDreamComponent(cn);
+ } catch (RemoteException ex) {
+ // too bad, so sad, oh mom, oh dad
+ }
+ }
+ })
+ .create();
+ alert.show();
+ }
+}
diff --git a/src/com/android/settings/DreamSettings.java b/src/com/android/settings/DreamSettings.java
new file mode 100644
index 0000000..d9953aa
--- /dev/null
+++ b/src/com/android/settings/DreamSettings.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings;
+
+import static android.provider.Settings.Secure.SCREENSAVER_ENABLED;
+import static android.provider.Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.ActivityManagerNative;
+import android.app.admin.DevicePolicyManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.database.ContentObserver;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.preference.CheckBoxPreference;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceScreen;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.IWindowManager;
+import android.widget.CompoundButton;
+import android.widget.Switch;
+
+import java.util.ArrayList;
+
+public class DreamSettings extends SettingsPreferenceFragment {
+ private static final String TAG = "DreamSettings";
+
+ private static final String KEY_ACTIVATE_ON_DOCK = "activate_on_dock";
+
+ private CheckBoxPreference mActivateOnDockPreference;
+
+ private Switch mEnableSwitch;
+ private Enabler mEnabler;
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ addPreferencesFromResource(R.xml.dream_settings);
+
+ mActivateOnDockPreference = (CheckBoxPreference) findPreference(KEY_ACTIVATE_ON_DOCK);
+
+ final Activity activity = getActivity();
+
+ mEnableSwitch = new Switch(activity);
+
+ if (activity instanceof PreferenceActivity) {
+ PreferenceActivity preferenceActivity = (PreferenceActivity) activity;
+ // note: we do not check onIsHidingHeaders() or onIsMultiPane() because there's no
+ // switch in the left-hand pane to control this; we need to show the ON/OFF in our
+ // fragment every time
+ final int padding = activity.getResources().getDimensionPixelSize(
+ R.dimen.action_bar_switch_padding);
+ mEnableSwitch.setPadding(0, 0, padding, 0);
+ activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
+ ActionBar.DISPLAY_SHOW_CUSTOM);
+ activity.getActionBar().setCustomView(mEnableSwitch, new ActionBar.LayoutParams(
+ ActionBar.LayoutParams.WRAP_CONTENT,
+ ActionBar.LayoutParams.WRAP_CONTENT,
+ Gravity.CENTER_VERTICAL | Gravity.RIGHT));
+ activity.getActionBar().setTitle(R.string.screensaver_settings_title);
+ }
+
+ mEnabler = new Enabler(activity, mEnableSwitch);
+ }
+
+ public static boolean isScreenSaverEnabled(Context context) {
+ return 0 != Settings.Secure.getInt(
+ context.getContentResolver(), SCREENSAVER_ENABLED, 1);
+ }
+
+ public static void setScreenSaverEnabled(Context context, boolean enabled) {
+ Settings.Secure.putInt(
+ context.getContentResolver(), SCREENSAVER_ENABLED, enabled ? 1 : 0);
+ }
+
+ public static class Enabler implements CompoundButton.OnCheckedChangeListener {
+ private final Context mContext;
+ private Switch mSwitch;
+
+ public Enabler(Context context, Switch switch_) {
+ mContext = context;
+ setSwitch(switch_);
+ }
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ setScreenSaverEnabled(mContext, isChecked);
+ }
+ public void setSwitch(Switch switch_) {
+ if (mSwitch == switch_) return;
+ if (mSwitch != null) mSwitch.setOnCheckedChangeListener(null);
+ mSwitch = switch_;
+ mSwitch.setOnCheckedChangeListener(this);
+
+ final boolean enabled = isScreenSaverEnabled(mContext);
+ mSwitch.setChecked(enabled);
+ }
+ public void pause() {
+ mSwitch.setOnCheckedChangeListener(null);
+ }
+ public void resume() {
+ mSwitch.setOnCheckedChangeListener(this);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ if (mEnabler != null) {
+ mEnabler.resume();
+ }
+
+ final boolean currentActivateOnDock = 0 != Settings.Secure.getInt(getContentResolver(),
+ SCREENSAVER_ACTIVATE_ON_DOCK, 1);
+ mActivateOnDockPreference.setChecked(currentActivateOnDock);
+ super.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ if (mEnabler != null) {
+ mEnabler.pause();
+ }
+
+ super.onPause();
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
+ if (preference == mActivateOnDockPreference) {
+ Settings.Secure.putInt(getContentResolver(),
+ SCREENSAVER_ACTIVATE_ON_DOCK,
+ mActivateOnDockPreference.isChecked() ? 1 : 0);
+ }
+ return super.onPreferenceTreeClick(preferenceScreen, preference);
+ }
+}
diff --git a/src/com/android/settings/DreamTesterPreference.java b/src/com/android/settings/DreamTesterPreference.java
new file mode 100644
index 0000000..87a142c
--- /dev/null
+++ b/src/com/android/settings/DreamTesterPreference.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings;
+
+import static android.provider.Settings.Secure.SCREENSAVER_COMPONENT;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.preference.Preference;
+import android.provider.Settings;
+import android.service.dreams.IDreamManager;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.ListAdapter;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DreamTesterPreference extends Preference {
+ private static final String TAG = "DreamTesterPreference";
+
+ private final PackageManager pm;
+ private final ContentResolver resolver;
+
+ public DreamTesterPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ pm = getContext().getPackageManager();
+ resolver = getContext().getContentResolver();
+ }
+
+ @Override
+ protected void onClick() {
+ String component = Settings.Secure.getString(resolver, SCREENSAVER_COMPONENT);
+ Log.v(TAG, "component=" + component);
+ if (component != null) {
+ ComponentName cn = ComponentName.unflattenFromString(component);
+ Log.v(TAG, "cn=" + cn);
+// Intent intent = new Intent(Intent.ACTION_MAIN)
+// .setComponent(cn)
+// .addFlags(
+// Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+// )
+// .putExtra("android.dreams.TEST", true);
+// getContext().startService(intent);
+ IDreamManager dm = IDreamManager.Stub.asInterface(
+ ServiceManager.getService("dreams"));
+ try {
+ dm.testDream(cn);
+ } catch (RemoteException ex) {
+ // too bad, so sad, oh mom, oh dad
+ }
+ }
+ }
+}
diff --git a/src/com/android/settings/EditPinPreference.java b/src/com/android/settings/EditPinPreference.java
index 362bed1..1877d43 100644
--- a/src/com/android/settings/EditPinPreference.java
+++ b/src/com/android/settings/EditPinPreference.java
@@ -19,8 +19,7 @@ package com.android.settings;
import android.app.Dialog;
import android.content.Context;
import android.preference.EditTextPreference;
-import android.text.method.DigitsKeyListener;
-import android.text.method.PasswordTransformationMethod;
+import android.text.InputType;
import android.util.AttributeSet;
import android.view.View;
import android.widget.EditText;
@@ -52,12 +51,11 @@ class EditPinPreference extends EditTextPreference {
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
- final EditText editText = (EditText) view.findViewById(android.R.id.edit);
+ final EditText editText = getEditText();
if (editText != null) {
- editText.setSingleLine(true);
- editText.setTransformationMethod(PasswordTransformationMethod.getInstance());
- editText.setKeyListener(DigitsKeyListener.getInstance());
+ editText.setInputType(InputType.TYPE_CLASS_NUMBER |
+ InputType.TYPE_NUMBER_VARIATION_PASSWORD);
}
}
diff --git a/src/com/android/settings/NsdEnabler.java b/src/com/android/settings/NsdEnabler.java
new file mode 100644
index 0000000..acdf92e
--- /dev/null
+++ b/src/com/android/settings/NsdEnabler.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2012 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.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.nsd.NsdManager;
+import android.preference.CheckBoxPreference;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+
+import com.android.settings.R;
+
+/**
+ * NsdEnabler is a helper to manage network service discovery on/off checkbox state.
+ */
+public class NsdEnabler implements Preference.OnPreferenceChangeListener {
+ private final Context mContext;
+ private final CheckBoxPreference mCheckbox;
+ private final IntentFilter mIntentFilter;
+ private NsdManager mNsdManager;
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (NsdManager.ACTION_NSD_STATE_CHANGED.equals(action)) {
+ handleNsdStateChanged(intent.getIntExtra(NsdManager.EXTRA_NSD_STATE,
+ NsdManager.NSD_STATE_DISABLED));
+ }
+ }
+ };
+
+ public NsdEnabler(Context context, CheckBoxPreference checkBoxPreference) {
+ mContext = context;
+ mCheckbox = checkBoxPreference;
+ mNsdManager = (NsdManager) mContext.getSystemService(Context.NSD_SERVICE);
+ mIntentFilter = new IntentFilter(NsdManager.ACTION_NSD_STATE_CHANGED);
+ }
+
+ public void resume() {
+ mContext.registerReceiver(mReceiver, mIntentFilter);
+ mCheckbox.setOnPreferenceChangeListener(this);
+ }
+
+ public void pause() {
+ mContext.unregisterReceiver(mReceiver);
+ mCheckbox.setOnPreferenceChangeListener(null);
+ }
+
+ public boolean onPreferenceChange(Preference preference, Object value) {
+
+ final boolean desiredState = (Boolean) value;
+ mCheckbox.setEnabled(false);
+ mNsdManager.setEnabled(desiredState);
+ return false;
+ }
+
+ private void handleNsdStateChanged(int newState) {
+ switch (newState) {
+ case NsdManager.NSD_STATE_DISABLED:
+ mCheckbox.setChecked(false);
+ mCheckbox.setEnabled(true);
+ break;
+ case NsdManager.NSD_STATE_ENABLED:
+ mCheckbox.setChecked(true);
+ mCheckbox.setEnabled(true);
+ break;
+ }
+ }
+}
diff --git a/src/com/android/settings/PointerSpeedPreference.java b/src/com/android/settings/PointerSpeedPreference.java
index 6a6a71f..3f40f9a 100644
--- a/src/com/android/settings/PointerSpeedPreference.java
+++ b/src/com/android/settings/PointerSpeedPreference.java
@@ -19,22 +19,20 @@ package com.android.settings;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
+import android.hardware.input.InputManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.preference.SeekBarDialogPreference;
import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
import android.util.AttributeSet;
-import android.view.IWindowManager;
import android.view.View;
import android.widget.SeekBar;
public class PointerSpeedPreference extends SeekBarDialogPreference implements
SeekBar.OnSeekBarChangeListener {
+ private final InputManager mIm;
private SeekBar mSeekBar;
private int mOldSpeed;
@@ -42,9 +40,6 @@ public class PointerSpeedPreference extends SeekBarDialogPreference implements
private boolean mTouchInProgress;
- private static final int MIN_SPEED = -7;
- private static final int MAX_SPEED = 7;
-
private ContentObserver mSpeedObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
@@ -54,6 +49,7 @@ public class PointerSpeedPreference extends SeekBarDialogPreference implements
public PointerSpeedPreference(Context context, AttributeSet attrs) {
super(context, attrs);
+ mIm = (InputManager)getContext().getSystemService(Context.INPUT_SERVICE);
}
@Override
@@ -72,15 +68,15 @@ public class PointerSpeedPreference extends SeekBarDialogPreference implements
super.onBindDialogView(view);
mSeekBar = getSeekBar(view);
- mSeekBar.setMax(MAX_SPEED - MIN_SPEED);
- mOldSpeed = getSpeed(0);
- mSeekBar.setProgress(mOldSpeed - MIN_SPEED);
+ mSeekBar.setMax(InputManager.MAX_POINTER_SPEED - InputManager.MIN_POINTER_SPEED);
+ mOldSpeed = mIm.getPointerSpeed(getContext());
+ mSeekBar.setProgress(mOldSpeed - InputManager.MIN_POINTER_SPEED);
mSeekBar.setOnSeekBarChangeListener(this);
}
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
if (!mTouchInProgress) {
- setSpeed(progress + MIN_SPEED);
+ mIm.tryPointerSpeed(progress + InputManager.MIN_POINTER_SPEED);
}
}
@@ -90,22 +86,12 @@ public class PointerSpeedPreference extends SeekBarDialogPreference implements
public void onStopTrackingTouch(SeekBar seekBar) {
mTouchInProgress = false;
- setSpeed(seekBar.getProgress() + MIN_SPEED);
- }
-
- private int getSpeed(int defaultValue) {
- int speed = defaultValue;
- try {
- speed = Settings.System.getInt(getContext().getContentResolver(),
- Settings.System.POINTER_SPEED);
- } catch (SettingNotFoundException snfe) {
- }
- return speed;
+ mIm.tryPointerSpeed(seekBar.getProgress() + InputManager.MIN_POINTER_SPEED);
}
private void onSpeedChanged() {
- int speed = getSpeed(0);
- mSeekBar.setProgress(speed - MIN_SPEED);
+ int speed = mIm.getPointerSpeed(getContext());
+ mSeekBar.setProgress(speed - InputManager.MIN_POINTER_SPEED);
}
@Override
@@ -115,8 +101,8 @@ public class PointerSpeedPreference extends SeekBarDialogPreference implements
final ContentResolver resolver = getContext().getContentResolver();
if (positiveResult) {
- Settings.System.putInt(resolver, Settings.System.POINTER_SPEED,
- mSeekBar.getProgress() + MIN_SPEED);
+ mIm.setPointerSpeed(getContext(),
+ mSeekBar.getProgress() + InputManager.MIN_POINTER_SPEED);
} else {
restoreOldState();
}
@@ -127,21 +113,10 @@ public class PointerSpeedPreference extends SeekBarDialogPreference implements
private void restoreOldState() {
if (mRestoredOldState) return;
- setSpeed(mOldSpeed);
+ mIm.tryPointerSpeed(mOldSpeed);
mRestoredOldState = true;
}
- private void setSpeed(int speed) {
- try {
- IWindowManager wm = IWindowManager.Stub.asInterface(
- ServiceManager.getService("window"));
- if (wm != null) {
- wm.setPointerSpeed(speed);
- }
- } catch (RemoteException e) {
- }
- }
-
@Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
@@ -169,7 +144,7 @@ public class PointerSpeedPreference extends SeekBarDialogPreference implements
super.onRestoreInstanceState(myState.getSuperState());
mOldSpeed = myState.oldSpeed;
mSeekBar.setProgress(myState.progress);
- setSpeed(myState.progress + MIN_SPEED);
+ mIm.tryPointerSpeed(myState.progress + InputManager.MIN_POINTER_SPEED);
}
private static class SavedState extends BaseSavedState {
diff --git a/src/com/android/settings/PrivacySettings.java b/src/com/android/settings/PrivacySettings.java
index 3f3b9ad..e2433bd 100644
--- a/src/com/android/settings/PrivacySettings.java
+++ b/src/com/android/settings/PrivacySettings.java
@@ -203,4 +203,9 @@ public class PrivacySettings extends SettingsPreferenceFragment implements
mAutoRestore.setEnabled(enable);
mConfigure.setEnabled(enable);
}
+
+ @Override
+ protected int getHelpResource() {
+ return R.string.help_url_backup_reset;
+ }
}
diff --git a/src/com/android/settings/ProgressCategory.java b/src/com/android/settings/ProgressCategory.java
index c1b25d8..625aa59 100644
--- a/src/com/android/settings/ProgressCategory.java
+++ b/src/com/android/settings/ProgressCategory.java
@@ -36,13 +36,10 @@ public class ProgressCategory extends ProgressCategoryBase {
@Override
public void onBindView(View view) {
super.onBindView(view);
- final TextView scanning = (TextView) view.findViewById(R.id.scanning_text);
final View progressBar = view.findViewById(R.id.scanning_progress);
- scanning.setText(mProgress ? R.string.progress_scanning : R.string.progress_tap_to_pair);
boolean noDeviceFound = (getPreferenceCount() == 0 ||
(getPreferenceCount() == 1 && getPreference(0) == mNoDeviceFoundPreference));
- scanning.setVisibility(noDeviceFound ? View.GONE : View.VISIBLE);
progressBar.setVisibility(mProgress ? View.VISIBLE : View.GONE);
if (mProgress || !noDeviceFound) {
diff --git a/src/com/android/settings/RingerVolumePreference.java b/src/com/android/settings/RingerVolumePreference.java
index 5845c8f..a79f4a5 100644
--- a/src/com/android/settings/RingerVolumePreference.java
+++ b/src/com/android/settings/RingerVolumePreference.java
@@ -116,8 +116,8 @@ public class RingerVolumePreference extends VolumePreference {
boolean muted = mAudioManager.isStreamMute(streamType);
if (mCheckBoxes[i] != null) {
- if (streamType == AudioManager.STREAM_RING && muted
- && mAudioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER)) {
+ if ((streamType == AudioManager.STREAM_RING) &&
+ (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE)) {
mCheckBoxes[i].setImageResource(
com.android.internal.R.drawable.ic_audio_ring_notif_vibrate);
} else {
@@ -126,9 +126,13 @@ public class RingerVolumePreference extends VolumePreference {
}
}
if (mSeekBars[i] != null) {
- final int volume = muted ? mAudioManager.getLastAudibleStreamVolume(streamType)
- : mAudioManager.getStreamVolume(streamType);
+ final int volume = mAudioManager.getStreamVolume(streamType);
mSeekBars[i].setProgress(volume);
+ if (streamType != mAudioManager.getMasterStreamType() && muted) {
+ mSeekBars[i].setEnabled(false);
+ } else {
+ mSeekBars[i].setEnabled(true);
+ }
}
}
}
@@ -169,9 +173,6 @@ public class RingerVolumePreference extends VolumePreference {
}
}
- final int silentableStreams = System.getInt(getContext().getContentResolver(),
- System.MODE_RINGER_STREAMS_AFFECTED,
- ((1 << AudioSystem.STREAM_NOTIFICATION) | (1 << AudioSystem.STREAM_RING)));
// Register callbacks for mute/unmute buttons
for (int i = 0; i < mCheckBoxes.length; i++) {
ImageView checkbox = (ImageView) view.findViewById(CHECKBOX_VIEW_ID[i]);
diff --git a/src/com/android/settings/SecuritySettings.java b/src/com/android/settings/SecuritySettings.java
index adf8c37..6b67730 100644
--- a/src/com/android/settings/SecuritySettings.java
+++ b/src/com/android/settings/SecuritySettings.java
@@ -26,6 +26,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
+import android.os.UserId;
import android.os.Vibrator;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
@@ -53,13 +54,14 @@ public class SecuritySettings extends SettingsPreferenceFragment
private static final String KEY_UNLOCK_SET_OR_CHANGE = "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";
private static final String KEY_LOCK_ENABLED = "lockenabled";
private static final String KEY_VISIBLE_PATTERN = "visiblepattern";
private static final String KEY_TACTILE_FEEDBACK_ENABLED = "unlock_tactile_feedback";
private static final String KEY_SECURITY_CATEGORY = "security_category";
private static final String KEY_LOCK_AFTER_TIMEOUT = "lock_after_timeout";
private static final int SET_OR_CHANGE_LOCK_METHOD_REQUEST = 123;
- private static final int CONFIRM_EXISTING_FOR_BIOMETRIC_IMPROVE_REQUEST = 124;
+ private static final int CONFIRM_EXISTING_FOR_BIOMETRIC_WEAK_IMPROVE_REQUEST = 124;
// Misc Settings
private static final String KEY_SIM_LOCK = "sim_lock";
@@ -74,6 +76,7 @@ public class SecuritySettings extends SettingsPreferenceFragment
private LockPatternUtils mLockPatternUtils;
private ListPreference mLockAfter;
+ private PreferenceScreen mBiometricWeakLiveliness;
private CheckBoxPreference mVisiblePattern;
private CheckBoxPreference mTactileFeedback;
@@ -137,15 +140,17 @@ public class SecuritySettings extends SettingsPreferenceFragment
DevicePolicyManager dpm =
(DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
- switch (dpm.getStorageEncryptionStatus()) {
- case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE:
- // The device is currently encrypted.
- addPreferencesFromResource(R.xml.security_settings_encrypted);
- break;
- case DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE:
- // This device supports encryption but isn't encrypted.
- addPreferencesFromResource(R.xml.security_settings_unencrypted);
- break;
+ if (UserId.myUserId() == 0) {
+ switch (dpm.getStorageEncryptionStatus()) {
+ case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE:
+ // The device is currently encrypted.
+ addPreferencesFromResource(R.xml.security_settings_encrypted);
+ break;
+ case DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE:
+ // This device supports encryption but isn't encrypted.
+ addPreferencesFromResource(R.xml.security_settings_unencrypted);
+ break;
+ }
}
// lock after preference
@@ -155,6 +160,10 @@ public class SecuritySettings extends SettingsPreferenceFragment
updateLockAfterPreferenceSummary();
}
+ // biometric weak liveliness
+ mBiometricWeakLiveliness =
+ (PreferenceScreen) root.findPreference(KEY_BIOMETRIC_WEAK_LIVELINESS);
+
// visible pattern
mVisiblePattern = (CheckBoxPreference) root.findPreference(KEY_VISIBLE_PATTERN);
@@ -183,13 +192,17 @@ public class SecuritySettings extends SettingsPreferenceFragment
}
}
+ if (UserId.myUserId() > 0) {
+ return root;
+ }
+ // Rest are for primary user...
+
// Append the rest of the settings
addPreferencesFromResource(R.xml.security_settings_misc);
- // Do not display SIM lock for CDMA phone
+ // Do not display SIM lock for devices without an Icc card
TelephonyManager tm = TelephonyManager.getDefault();
- if ((TelephonyManager.PHONE_TYPE_CDMA == tm.getCurrentPhoneType()) &&
- (tm.getLteOnCdmaMode() != Phone.LTE_ON_CDMA_TRUE)) {
+ if (!tm.hasIccCard()) {
root.removePreference(root.findPreference(KEY_SIM_LOCK));
} else {
// Disable SIM lock if sim card is missing or unknown
@@ -239,7 +252,9 @@ public class SecuritySettings extends SettingsPreferenceFragment
public void onClick(DialogInterface dialog, int which) {
if (dialog == mWarnInstallApps && which == DialogInterface.BUTTON_POSITIVE) {
setNonMarketAppsAllowed(true);
- mToggleAppInstallation.setChecked(true);
+ if (mToggleAppInstallation != null) {
+ mToggleAppInstallation.setChecked(true);
+ }
}
}
@@ -322,6 +337,11 @@ public class SecuritySettings extends SettingsPreferenceFragment
createPreferenceHierarchy();
final LockPatternUtils lockPatternUtils = mChooseLockSettingsHelper.utils();
+ if (mBiometricWeakLiveliness != null) {
+ mBiometricWeakLiveliness.setSummary(lockPatternUtils.isBiometricWeakLivelinessEnabled()?
+ R.string.biometric_weak_liveliness_on_summary:
+ R.string.biometric_weak_liveliness_off_summary);
+ }
if (mVisiblePattern != null) {
mVisiblePattern.setChecked(lockPatternUtils.isVisiblePatternEnabled());
}
@@ -332,11 +352,15 @@ public class SecuritySettings extends SettingsPreferenceFragment
mPowerButtonInstantlyLocks.setChecked(lockPatternUtils.getPowerButtonInstantlyLocks());
}
- mShowPassword.setChecked(Settings.System.getInt(getContentResolver(),
- Settings.System.TEXT_SHOW_PASSWORD, 1) != 0);
+ if (mShowPassword != null) {
+ mShowPassword.setChecked(Settings.System.getInt(getContentResolver(),
+ Settings.System.TEXT_SHOW_PASSWORD, 1) != 0);
+ }
KeyStore.State state = KeyStore.getInstance().state();
- mResetCredentials.setEnabled(state != KeyStore.State.UNINITIALIZED);
+ if (mResetCredentials != null) {
+ mResetCredentials.setEnabled(state != KeyStore.State.UNINITIALIZED);
+ }
}
@Override
@@ -351,8 +375,12 @@ public class SecuritySettings extends SettingsPreferenceFragment
ChooseLockSettingsHelper helper =
new ChooseLockSettingsHelper(this.getActivity(), this);
if (!helper.launchConfirmationActivity(
- CONFIRM_EXISTING_FOR_BIOMETRIC_IMPROVE_REQUEST, null, null)) {
- startBiometricWeakImprove(); // no password set, so no need to confirm
+ CONFIRM_EXISTING_FOR_BIOMETRIC_WEAK_IMPROVE_REQUEST, null, null)) {
+ // If this returns false, it means no password confirmation is required, so
+ // go ahead and start improve.
+ // Note: currently a backup is required for biometric_weak so this code path
+ // can't be reached, but is here in case things change in the future
+ startBiometricWeakImprove();
}
} else if (KEY_LOCK_ENABLED.equals(key)) {
lockPatternUtils.setLockPatternEnabled(isToggled(preference));
@@ -390,7 +418,7 @@ public class SecuritySettings extends SettingsPreferenceFragment
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
- if (requestCode == CONFIRM_EXISTING_FOR_BIOMETRIC_IMPROVE_REQUEST &&
+ if (requestCode == CONFIRM_EXISTING_FOR_BIOMETRIC_WEAK_IMPROVE_REQUEST &&
resultCode == Activity.RESULT_OK) {
startBiometricWeakImprove();
return;
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index c9f5c73..5826154 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -16,20 +16,34 @@
package com.android.settings;
+import com.android.internal.util.ArrayUtils;
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.bluetooth.BluetoothEnabler;
+import com.android.settings.deviceinfo.Memory;
import com.android.settings.fuelgauge.PowerUsageSummary;
import com.android.settings.wifi.WifiEnabler;
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.OnAccountsUpdateListener;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
+import android.os.INetworkManagementService;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserId;
import android.preference.Preference;
import android.preference.PreferenceActivity;
+import android.preference.PreferenceActivity.Header;
import android.preference.PreferenceFragment;
import android.text.TextUtils;
import android.util.Log;
@@ -45,13 +59,16 @@ import android.widget.Switch;
import android.widget.TextView;
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.
*/
-public class Settings extends PreferenceActivity implements ButtonBarHandler {
+public class Settings extends PreferenceActivity
+ implements ButtonBarHandler, OnAccountsUpdateListener {
private static final String LOG_TAG = "Settings";
private static final String META_DATA_KEY_HEADER_ID =
@@ -75,24 +92,50 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
private Header mParentHeader;
private boolean mInLocalHeaderSwitch;
+ // Show only these settings for restricted users
+ private int[] SETTINGS_FOR_RESTRICTED = {
+ R.id.wifi_settings,
+ R.id.bluetooth_settings,
+ R.id.sound_settings,
+ R.id.display_settings,
+ R.id.security_settings,
+ R.id.account_settings,
+ R.id.about_settings
+ };
+
+ private boolean mEnableUserManagement = false;
+
// TODO: Update Call Settings based on airplane mode state.
protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
private List<Header> mHeaders;
+ private AuthenticatorHelper mAuthenticatorHelper;
+ private Header mLastHeader;
+ private boolean mListeningToAccountUpdates;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
if (getIntent().getBooleanExtra(EXTRA_CLEAR_UI_OPTIONS, false)) {
getWindow().setUiOptions(0);
}
+ if (android.provider.Settings.Secure.getInt(getContentResolver(), "multiuser_enabled", -1)
+ > 0) {
+ mEnableUserManagement = true;
+ }
+
+ mAuthenticatorHelper = new AuthenticatorHelper();
+ mAuthenticatorHelper.updateAuthDescriptions(this);
+ mAuthenticatorHelper.onAccountsUpdated(this, null);
+
getMetaData();
mInLocalHeaderSwitch = true;
super.onCreate(savedInstanceState);
mInLocalHeaderSwitch = false;
if (!onIsHidingHeaders() && onIsMultiPane()) {
- highlightHeader();
+ 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);
@@ -118,9 +161,11 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
});
}
- // TODO Add support for android.R.id.home in all Setting's onOptionsItemSelected
- // getActionBar().setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP,
- // ActionBar.DISPLAY_HOME_AS_UP);
+ // Override up navigation for multi-pane, since we handle it in the fragment breadcrumbs
+ if (onIsMultiPane()) {
+ getActionBar().setDisplayHomeAsUpEnabled(false);
+ getActionBar().setHomeButtonEnabled(false);
+ }
}
@Override
@@ -156,6 +201,14 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
}
}
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (mListeningToAccountUpdates) {
+ AccountManager.get(this).removeOnAccountsUpdatedListener(this);
+ }
+ }
+
private void switchToHeaderLocal(Header header) {
mInLocalHeaderSwitch = true;
switchToHeader(header);
@@ -190,7 +243,7 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
mCurrentHeader = parentHeader;
switchToHeaderLocal(parentHeader);
- highlightHeader();
+ highlightHeader(mTopLevelHeaderId);
mParentHeader = new Header();
mParentHeader.fragment
@@ -213,9 +266,9 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
}
}
- private void highlightHeader() {
- if (mTopLevelHeaderId != 0) {
- Integer index = mHeaderIndexMap.get(mTopLevelHeaderId);
+ private void highlightHeader(int id) {
+ if (id != 0) {
+ Integer index = mHeaderIndexMap.get(id);
if (index != null) {
getListView().setItemChecked(index, true);
getListView().smoothScrollToPosition(index);
@@ -294,7 +347,13 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
if (DataUsageSummary.class.getName().equals(fragmentName) ||
PowerUsageSummary.class.getName().equals(fragmentName) ||
AccountSyncSettings.class.getName().equals(fragmentName) ||
- UserDictionarySettings.class.getName().equals(fragmentName)) {
+ UserDictionarySettings.class.getName().equals(fragmentName) ||
+ Memory.class.getName().equals(fragmentName) ||
+ ManageApplications.class.getName().equals(fragmentName) ||
+ WirelessSettings.class.getName().equals(fragmentName) ||
+ SoundSettings.class.getName().equals(fragmentName) ||
+ PrivacySettings.class.getName().equals(fragmentName) ||
+ ManageAccountsSettings.class.getName().equals(fragmentName)) {
intent.putExtra(EXTRA_CLEAR_UI_OPTIONS, true);
}
@@ -335,6 +394,31 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
target.remove(header);
}
+ } 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(header);
+ }
+ } catch (RemoteException e) {
+ // ignored
+ }
+ } else if (id == R.id.account_settings) {
+ int headerIndex = i + 1;
+ i = insertAccountsHeaders(target, headerIndex);
+ } else if (id == R.id.user_settings) {
+ if (!mEnableUserManagement
+ || !UserId.MU_ENABLED || UserId.myUserId() != 0
+ || !getResources().getBoolean(R.bool.enable_user_management)
+ || Utils.isMonkeyRunning()) {
+ target.remove(header);
+ }
+ }
+ if (UserId.MU_ENABLED && UserId.myUserId() != 0
+ && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
+ target.remove(header);
}
// Increment if the current one wasn't removed by the Utils code.
@@ -350,6 +434,63 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
}
}
+ 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);
+ 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);
+ }
+
+ // 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 needsDockSettings() {
return getResources().getBoolean(R.bool.has_dock_settings);
}
@@ -361,7 +502,7 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
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);
@@ -395,6 +536,7 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
private final WifiEnabler mWifiEnabler;
private final BluetoothEnabler mBluetoothEnabler;
+ private AuthenticatorHelper mAuthHelper;
private static class HeaderViewHolder {
ImageView icon;
@@ -441,10 +583,13 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
return true;
}
- public HeaderAdapter(Context context, List<Header> objects) {
+ public HeaderAdapter(Context context, List<Header> objects,
+ AuthenticatorHelper authenticatorHelper) {
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));
@@ -512,7 +657,20 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
//$FALL-THROUGH$
case HEADER_TYPE_NORMAL:
- holder.icon.setImageResource(header.iconRes);
+ if (header.extras != null
+ && header.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) {
+ String accType = header.extras.getString(
+ ManageAccountsSettings.KEY_ACCOUNT_TYPE);
+ ViewGroup.LayoutParams lp = holder.icon.getLayoutParams();
+ lp.width = getContext().getResources().getDimensionPixelSize(
+ R.dimen.header_icon_width);
+ lp.height = lp.width;
+ holder.icon.setLayoutParams(lp);
+ Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType);
+ holder.icon.setImageDrawable(icon);
+ } else {
+ holder.icon.setImageResource(header.iconRes);
+ }
holder.title.setText(header.getTitle(getContext().getResources()));
CharSequence summary = header.getSummary(getContext().getResources());
if (!TextUtils.isEmpty(summary)) {
@@ -539,16 +697,37 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
}
@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;
}
- startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, null, null, 0);
+ startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
+ null, 0);
return true;
}
+ public boolean shouldUpRecreateTask(Intent targetIntent) {
+ return super.shouldUpRecreateTask(new Intent(this, Settings.class));
+ }
+
@Override
public void setListAdapter(ListAdapter adapter) {
if (mHeaders == null) {
@@ -561,7 +740,13 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
}
// Ignore the adapter provided by PreferenceActivity and substitute ours instead
- super.setListAdapter(new HeaderAdapter(this, mHeaders));
+ super.setListAdapter(new HeaderAdapter(this, mHeaders, mAuthenticatorHelper));
+ }
+
+ @Override
+ public void onAccountsUpdated(Account[] accounts) {
+ mAuthenticatorHelper.onAccountsUpdated(this, accounts);
+ invalidateHeaders();
}
/*
@@ -576,6 +761,7 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler {
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 */ }
diff --git a/src/com/android/settings/SettingsPreferenceFragment.java b/src/com/android/settings/SettingsPreferenceFragment.java
index f7e3b02..368976a 100644
--- a/src/com/android/settings/SettingsPreferenceFragment.java
+++ b/src/com/android/settings/SettingsPreferenceFragment.java
@@ -21,11 +21,17 @@ import android.app.DialogFragment;
import android.app.Fragment;
import android.content.ContentResolver;
import android.content.DialogInterface;
+import android.content.Intent;
import android.content.pm.PackageManager;
+import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;
+import android.text.TextUtils;
import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
import android.widget.Button;
/**
@@ -35,11 +41,49 @@ public class SettingsPreferenceFragment extends PreferenceFragment implements Di
private static final String TAG = "SettingsPreferenceFragment";
+ private static final int MENU_HELP = Menu.FIRST + 100;
+
private SettingsDialogFragment mDialogFragment;
+ private String mHelpUrl;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ // Prepare help url and enable menu if necessary
+ int helpResource = getHelpResource();
+ if (helpResource != 0) {
+ mHelpUrl = getResources().getString(helpResource);
+ }
+ }
+
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
+ if (!TextUtils.isEmpty(mHelpUrl)) {
+ setHasOptionsMenu(true);
+ }
+ }
+
+ /**
+ * Override this if you want to show a help item in the menu, by returning the resource id.
+ * @return the resource id for the help url
+ */
+ protected int getHelpResource() {
+ return 0;
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ if (mHelpUrl != null) {
+ Intent helpIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(mHelpUrl));
+ helpIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ MenuItem helpItem = menu.add(0, MENU_HELP, 0, R.string.help_label);
+ helpItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+ helpItem.setIntent(helpIntent);
+ }
}
/*
diff --git a/src/com/android/settings/SoundSettings.java b/src/com/android/settings/SoundSettings.java
index cf7cd0a..67557b9 100644
--- a/src/com/android/settings/SoundSettings.java
+++ b/src/com/android/settings/SoundSettings.java
@@ -16,17 +16,14 @@
package com.android.settings;
-import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.media.AudioManager;
-import android.media.Ringtone;
import android.media.RingtoneManager;
import android.media.audiofx.AudioEffect;
import android.net.Uri;
@@ -41,8 +38,6 @@ import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.provider.MediaStore;
import android.provider.Settings;
-import android.provider.MediaStore.Images.Media;
-import android.provider.Settings.SettingNotFoundException;
import android.telephony.TelephonyManager;
import android.util.Log;
@@ -55,8 +50,7 @@ public class SoundSettings extends SettingsPreferenceFragment implements
/** If there is no setting in the provider, use this. */
private static final int FALLBACK_EMERGENCY_TONE_VALUE = 0;
- private static final String KEY_SILENT_MODE = "silent_mode";
- private static final String KEY_VIBRATE = "vibrate_on_ring";
+ private static final String KEY_VIBRATE = "vibrate_when_ringing";
private static final String KEY_RING_VOLUME = "ring_volume";
private static final String KEY_MUSICFX = "musicfx";
private static final String KEY_DTMF_TONE = "dtmf_tone";
@@ -67,11 +61,7 @@ public class SoundSettings extends SettingsPreferenceFragment implements
private static final String KEY_LOCK_SOUNDS = "lock_sounds";
private static final String KEY_RINGTONE = "ringtone";
private static final String KEY_NOTIFICATION_SOUND = "notification_sound";
- private static final String KEY_CATEGORY_CALLS = "category_calls";
-
- private static final String SILENT_MODE_OFF = "off";
- private static final String SILENT_MODE_VIBRATE = "vibrate";
- private static final String SILENT_MODE_MUTE = "mute";
+ private static final String KEY_CATEGORY_CALLS = "category_calls_and_notification";
private static final String[] NEED_VOICE_CAPABILITY = {
KEY_RINGTONE, KEY_DTMF_TONE, KEY_CATEGORY_CALLS,
@@ -81,8 +71,7 @@ public class SoundSettings extends SettingsPreferenceFragment implements
private static final int MSG_UPDATE_RINGTONE_SUMMARY = 1;
private static final int MSG_UPDATE_NOTIFICATION_SUMMARY = 2;
- private CheckBoxPreference mVibrateOnRing;
- private ListPreference mSilentMode;
+ private CheckBoxPreference mVibrateWhenRinging;
private CheckBoxPreference mDtmfTone;
private CheckBoxPreference mSoundEffects;
private CheckBoxPreference mHapticFeedback;
@@ -95,15 +84,6 @@ public class SoundSettings extends SettingsPreferenceFragment implements
private AudioManager mAudioManager;
- private BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
- updateState(false);
- }
- }
- };
-
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -134,16 +114,14 @@ public class SoundSettings extends SettingsPreferenceFragment implements
getPreferenceScreen().removePreference(findPreference(KEY_EMERGENCY_TONE));
}
- mSilentMode = (ListPreference) findPreference(KEY_SILENT_MODE);
if (!getResources().getBoolean(R.bool.has_silent_mode)) {
- getPreferenceScreen().removePreference(mSilentMode);
findPreference(KEY_RING_VOLUME).setDependency(null);
- } else {
- mSilentMode.setOnPreferenceChangeListener(this);
}
- mVibrateOnRing = (CheckBoxPreference) findPreference(KEY_VIBRATE);
- mVibrateOnRing.setOnPreferenceChangeListener(this);
+ mVibrateWhenRinging = (CheckBoxPreference) findPreference(KEY_VIBRATE);
+ mVibrateWhenRinging.setPersistent(false);
+ mVibrateWhenRinging.setChecked(Settings.System.getInt(resolver,
+ Settings.System.VIBRATE_WHEN_RINGING, 0) != 0);
mDtmfTone = (CheckBoxPreference) findPreference(KEY_DTMF_TONE);
mDtmfTone.setPersistent(false);
@@ -165,8 +143,9 @@ public class SoundSettings extends SettingsPreferenceFragment implements
mRingtonePreference = findPreference(KEY_RINGTONE);
mNotificationPreference = findPreference(KEY_NOTIFICATION_SOUND);
- if (!((Vibrator) getSystemService(Context.VIBRATOR_SERVICE)).hasVibrator()) {
- getPreferenceScreen().removePreference(mVibrateOnRing);
+ Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
+ if (vibrator == null || !vibrator.hasVibrator()) {
+ getPreferenceScreen().removePreference(mVibrateWhenRinging);
getPreferenceScreen().removePreference(mHapticFeedback);
}
@@ -220,65 +199,7 @@ public class SoundSettings extends SettingsPreferenceFragment implements
public void onResume() {
super.onResume();
- updateState(true);
lookupRingtoneNames();
-
- IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
- getActivity().registerReceiver(mReceiver, filter);
- }
-
- @Override
- public void onPause() {
- super.onPause();
-
- getActivity().unregisterReceiver(mReceiver);
- }
-
- /**
- * Put the audio system into the correct vibrate setting
- */
- private void setPhoneVibrateSettingValue(boolean vibeOnRing) {
- // If vibrate-on-ring is checked, use VIBRATE_SETTING_ON
- // Otherwise vibrate is off when ringer is silent
- int vibrateMode = vibeOnRing ? AudioManager.VIBRATE_SETTING_ON
- : AudioManager.VIBRATE_SETTING_ONLY_SILENT;
- mAudioManager.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER, vibrateMode);
- mAudioManager.setVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION, vibrateMode);
- }
-
- private void setPhoneSilentSettingValue(String value) {
- int ringerMode = AudioManager.RINGER_MODE_NORMAL;
- if (value.equals(SILENT_MODE_MUTE)) {
- ringerMode = AudioManager.RINGER_MODE_SILENT;
- } else if (value.equals(SILENT_MODE_VIBRATE)) {
- ringerMode = AudioManager.RINGER_MODE_VIBRATE;
- }
- mAudioManager.setRingerMode(ringerMode);
- }
-
- private String getPhoneSilentModeSettingValue() {
- switch (mAudioManager.getRingerMode()) {
- case AudioManager.RINGER_MODE_NORMAL:
- return SILENT_MODE_OFF;
- case AudioManager.RINGER_MODE_VIBRATE:
- return SILENT_MODE_VIBRATE;
- case AudioManager.RINGER_MODE_SILENT:
- return SILENT_MODE_MUTE;
- }
- // Shouldn't happen
- return SILENT_MODE_OFF;
- }
-
- // updateState in fact updates the UI to reflect the system state
- private void updateState(boolean force) {
- if (getActivity() == null) return;
-
- final int vibrateMode = mAudioManager.getVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
-
- mVibrateOnRing.setChecked(vibrateMode == AudioManager.VIBRATE_SETTING_ON);
- mSilentMode.setValue(getPhoneSilentModeSettingValue());
-
- mSilentMode.setSummary(mSilentMode.getEntry());
}
private void updateRingtoneName(int type, Preference preference, int msg) {
@@ -314,7 +235,10 @@ public class SoundSettings extends SettingsPreferenceFragment implements
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
- if (preference == mDtmfTone) {
+ if (preference == mVibrateWhenRinging) {
+ Settings.System.putInt(getContentResolver(), Settings.System.VIBRATE_WHEN_RINGING,
+ mVibrateWhenRinging.isChecked() ? 1 : 0);
+ } else if (preference == mDtmfTone) {
Settings.System.putInt(getContentResolver(), Settings.System.DTMF_TONE_WHEN_DIALING,
mDtmfTone.isChecked() ? 1 : 0);
@@ -353,12 +277,13 @@ public class SoundSettings extends SettingsPreferenceFragment implements
} catch (NumberFormatException e) {
Log.e(TAG, "could not persist emergency tone setting", e);
}
- } else if (preference == mVibrateOnRing) {
- setPhoneVibrateSettingValue((Boolean) objValue);
- } else if (preference == mSilentMode) {
- setPhoneSilentSettingValue(objValue.toString());
}
return true;
}
+
+ @Override
+ protected int getHelpResource() {
+ return R.string.help_url_sound;
+ }
}
diff --git a/src/com/android/settings/SubSettings.java b/src/com/android/settings/SubSettings.java
index 9cd3c31..eb275ad 100644
--- a/src/com/android/settings/SubSettings.java
+++ b/src/com/android/settings/SubSettings.java
@@ -21,4 +21,10 @@ package com.android.settings;
* since for our app it is a special singleTask class.
*/
public class SubSettings extends Settings {
+
+ @Override
+ public boolean onNavigateUp() {
+ finish();
+ return true;
+ }
}
diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java
index 867f733..77a72a7 100644
--- a/src/com/android/settings/TetherSettings.java
+++ b/src/com/android/settings/TetherSettings.java
@@ -37,6 +37,7 @@ import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Environment;
+import android.os.SystemProperties;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.PreferenceScreen;
@@ -58,14 +59,8 @@ public class TetherSettings extends SettingsPreferenceFragment
private static final String USB_TETHER_SETTINGS = "usb_tether_settings";
private static final String ENABLE_WIFI_AP = "enable_wifi_ap";
private static final String ENABLE_BLUETOOTH_TETHERING = "enable_bluetooth_tethering";
- private static final String TETHERING_HELP = "tethering_help";
- private static final String USB_HELP_MODIFIER = "usb_";
- private static final String WIFI_HELP_MODIFIER = "wifi_";
- private static final String HELP_URL = "file:///android_asset/html/%y%z/tethering_%xhelp.html";
- private static final String HELP_PATH = "html/%y%z/tethering_help.html";
- private static final int DIALOG_TETHER_HELP = 1;
- private static final int DIALOG_AP_SETTINGS = 2;
+ private static final int DIALOG_AP_SETTINGS = 1;
private WebView mView;
private CheckBoxPreference mUsbTether;
@@ -75,8 +70,6 @@ public class TetherSettings extends SettingsPreferenceFragment
private CheckBoxPreference mBluetoothTether;
- private PreferenceScreen mTetherHelp;
-
private BroadcastReceiver mTetherChangeReceiver;
private String[] mUsbRegexs;
@@ -130,7 +123,6 @@ public class TetherSettings extends SettingsPreferenceFragment
Preference wifiApSettings = findPreference(WIFI_AP_SSID_AND_SECURITY);
mUsbTether = (CheckBoxPreference) findPreference(USB_TETHER_SETTINGS);
mBluetoothTether = (CheckBoxPreference) findPreference(ENABLE_BLUETOOTH_TETHERING);
- mTetherHelp = (PreferenceScreen) findPreference(TETHERING_HELP);
ConnectivityManager cm =
(ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
@@ -204,50 +196,7 @@ public class TetherSettings extends SettingsPreferenceFragment
@Override
public Dialog onCreateDialog(int id) {
- if (id == DIALOG_TETHER_HELP) {
- Locale locale = Locale.getDefault();
-
- // check for the full language + country resource, if not there, try just language
- final AssetManager am = getActivity().getAssets();
- String path = HELP_PATH.replace("%y", locale.getLanguage().toLowerCase());
- path = path.replace("%z", '_'+locale.getCountry().toLowerCase());
- boolean useCountry = true;
- InputStream is = null;
- try {
- is = am.open(path);
- } catch (Exception ignored) {
- useCountry = false;
- } finally {
- if (is != null) {
- try {
- is.close();
- } catch (Exception ignored) {}
- }
- }
- String url = HELP_URL.replace("%y", locale.getLanguage().toLowerCase());
- url = url.replace("%z", useCountry ? '_'+locale.getCountry().toLowerCase() : "");
- if ((mUsbRegexs.length != 0) && (mWifiRegexs.length == 0)) {
- url = url.replace("%x", USB_HELP_MODIFIER);
- } else if ((mWifiRegexs.length != 0) && (mUsbRegexs.length == 0)) {
- url = url.replace("%x", WIFI_HELP_MODIFIER);
- } else {
- // could assert that both wifi and usb have regexs, but the default
- // is to use this anyway so no check is needed
- url = url.replace("%x", "");
- }
-
- mView.loadUrl(url);
- // Detach from old parent first
- ViewParent parent = mView.getParent();
- if (parent != null && parent instanceof ViewGroup) {
- ((ViewGroup) parent).removeView(mView);
- }
- return new AlertDialog.Builder(getActivity())
- .setCancelable(true)
- .setTitle(R.string.tethering_help_button_text)
- .setView(mView)
- .create();
- } else if (id == DIALOG_AP_SETTINGS) {
+ if (id == DIALOG_AP_SETTINGS) {
final Activity activity = getActivity();
mDialog = new WifiApDialog(activity, this, mWifiConfig);
return mDialog;
@@ -476,6 +425,9 @@ public class TetherSettings extends SettingsPreferenceFragment
}
boolean isProvisioningNeeded() {
+ if (SystemProperties.getBoolean("net.tethering.noprovisioning", false)) {
+ return false;
+ }
return mProvisionApp.length == 2;
}
@@ -584,9 +536,6 @@ public class TetherSettings extends SettingsPreferenceFragment
mBluetoothTether.setSummary(R.string.bluetooth_tethering_off_subtext);
}
}
- } else if (preference == mTetherHelp) {
- showDialog(DIALOG_TETHER_HELP);
- return true;
} else if (preference == mCreateNetwork) {
showDialog(DIALOG_AP_SETTINGS);
}
@@ -627,4 +576,9 @@ public class TetherSettings extends SettingsPreferenceFragment
}
}
}
+
+ @Override
+ public int getHelpResource() {
+ return R.string.help_url_tether;
+ }
}
diff --git a/src/com/android/settings/UserDictionarySettings.java b/src/com/android/settings/UserDictionarySettings.java
index 496947b..b561ed4 100644
--- a/src/com/android/settings/UserDictionarySettings.java
+++ b/src/com/android/settings/UserDictionarySettings.java
@@ -20,6 +20,7 @@ import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ListFragment;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -27,6 +28,7 @@ import android.database.Cursor;
import android.os.Bundle;
import android.provider.UserDictionary;
import android.text.InputType;
+import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -44,22 +46,19 @@ import android.widget.SectionIndexer;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;
-import com.android.settings.SettingsPreferenceFragment.SettingsDialogFragment;
+import com.android.settings.inputmethod.UserDictionaryAddWordContents;
import java.util.Locale;
-public class UserDictionarySettings extends ListFragment implements DialogCreatable {
+public class UserDictionarySettings extends ListFragment {
private static final String TAG = "UserDictionarySettings";
- private static final String INSTANCE_KEY_DIALOG_EDITING_WORD = "DIALOG_EDITING_WORD";
- private static final String INSTANCE_KEY_ADDED_WORD = "DIALOG_ADDED_WORD";
-
private static final String[] QUERY_PROJECTION = {
- UserDictionary.Words._ID, UserDictionary.Words.WORD
+ UserDictionary.Words._ID, UserDictionary.Words.WORD, UserDictionary.Words.SHORTCUT
};
- private static final int INDEX_ID = 0;
- private static final int INDEX_WORD = 1;
+ // The index of the shortcut in the above array.
+ private static final int INDEX_SHORTCUT = 2;
// Either the locale is empty (means the word is applicable to all locales)
// or the word equals our current locale
@@ -68,28 +67,18 @@ public class UserDictionarySettings extends ListFragment implements DialogCreata
private static final String QUERY_SELECTION_ALL_LOCALES =
UserDictionary.Words.LOCALE + " is null";
- private static final String DELETE_SELECTION = UserDictionary.Words.WORD + "=?";
-
- private static final String EXTRA_WORD = "word";
+ private static final String DELETE_SELECTION_WITH_SHORTCUT = UserDictionary.Words.WORD
+ + "=? AND " + UserDictionary.Words.SHORTCUT + "=?";
+ private static final String DELETE_SELECTION_WITHOUT_SHORTCUT = UserDictionary.Words.WORD
+ + "=? AND " + UserDictionary.Words.SHORTCUT + " is null OR "
+ + UserDictionary.Words.SHORTCUT + "=''";
private static final int OPTIONS_MENU_ADD = Menu.FIRST;
- private static final int DIALOG_ADD_OR_EDIT = 0;
-
- private static final int FREQUENCY_FOR_USER_DICTIONARY_ADDS = 250;
-
- /** The word being edited in the dialog (null means the user is adding a word). */
- private String mDialogEditingWord;
-
private Cursor mCursor;
protected String mLocale;
- private boolean mAddedWordAlready;
- private boolean mAutoReturn;
-
- private SettingsDialogFragment mDialogFragment;
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -135,31 +124,6 @@ public class UserDictionarySettings extends ListFragment implements DialogCreata
setHasOptionsMenu(true);
- if (savedInstanceState != null) {
- mDialogEditingWord = savedInstanceState.getString(INSTANCE_KEY_DIALOG_EDITING_WORD);
- mAddedWordAlready = savedInstanceState.getBoolean(INSTANCE_KEY_ADDED_WORD, false);
- }
- }
-
- @Override
- public void onResume() {
- super.onResume();
- final Intent intent = getActivity().getIntent();
- if (!mAddedWordAlready
- && intent.getAction().equals("com.android.settings.USER_DICTIONARY_INSERT")) {
- final String word = intent.getStringExtra(EXTRA_WORD);
- mAutoReturn = true;
- if (word != null) {
- showAddOrEditDialog(word);
- }
- }
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putString(INSTANCE_KEY_DIALOG_EDITING_WORD, mDialogEditingWord);
- outState.putBoolean(INSTANCE_KEY_ADDED_WORD, mAddedWordAlready);
}
private Cursor createCursor(final String locale) {
@@ -189,15 +153,16 @@ public class UserDictionarySettings extends ListFragment implements DialogCreata
private ListAdapter createAdapter() {
return new MyAdapter(getActivity(),
R.layout.user_dictionary_item, mCursor,
- new String[] { UserDictionary.Words.WORD, UserDictionary.Words._ID },
- new int[] { android.R.id.text1, R.id.delete_button }, this);
+ new String[] { UserDictionary.Words.WORD, UserDictionary.Words.SHORTCUT },
+ new int[] { android.R.id.text1, android.R.id.text2 }, this);
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
- String word = getWord(position);
+ final String word = getWord(position);
+ final String shortcut = getShortcut(position);
if (word != null) {
- showAddOrEditDialog(word);
+ showAddOrEditDialog(word, shortcut);
}
}
@@ -212,16 +177,34 @@ public class UserDictionarySettings extends ListFragment implements DialogCreata
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- showAddOrEditDialog(null);
- return true;
+ if (item.getItemId() == OPTIONS_MENU_ADD) {
+ showAddOrEditDialog(null, null);
+ return true;
+ }
+ return false;
}
- private void showAddOrEditDialog(String editingWord) {
- mDialogEditingWord = editingWord;
- showDialog(DIALOG_ADD_OR_EDIT);
+ /**
+ * Add or edit a word. If editingWord is null, it's an add; otherwise, it's an edit.
+ * @param editingWord the word to edit, or null if it's an add.
+ * @param editingShortcut the shortcut for this entry, or null if none.
+ */
+ private void showAddOrEditDialog(final String editingWord, final String editingShortcut) {
+ final Bundle args = new Bundle();
+ args.putInt(UserDictionaryAddWordContents.EXTRA_MODE, null == editingWord
+ ? UserDictionaryAddWordContents.MODE_INSERT
+ : UserDictionaryAddWordContents.MODE_EDIT);
+ 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(
+ com.android.settings.inputmethod.UserDictionaryAddWordFragment.class.getName(),
+ args, R.string.user_dict_settings_add_dialog_title, null, null, 0);
}
- private String getWord(int position) {
+ private String getWord(final int position) {
if (null == mCursor) return null;
mCursor.moveToPosition(position);
// Handle a possible race-condition
@@ -231,96 +214,45 @@ public class UserDictionarySettings extends ListFragment implements DialogCreata
mCursor.getColumnIndexOrThrow(UserDictionary.Words.WORD));
}
- @Override
- public Dialog onCreateDialog(int id) {
- final Activity activity = getActivity();
- final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(activity);
- final LayoutInflater inflater = LayoutInflater.from(dialogBuilder.getContext());
- final View content = inflater.inflate(R.layout.dialog_edittext, null);
- final EditText editText = (EditText) content.findViewById(R.id.edittext);
- editText.setText(mDialogEditingWord);
- // No prediction in soft keyboard mode. TODO: Create a better way to disable prediction
- editText.setInputType(InputType.TYPE_CLASS_TEXT
- | InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE);
-
- AlertDialog dialog = dialogBuilder
- .setTitle(mDialogEditingWord != null
- ? R.string.user_dict_settings_edit_dialog_title
- : R.string.user_dict_settings_add_dialog_title)
- .setView(content)
- .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- onAddOrEditFinished(editText.getText().toString());
- if (mAutoReturn) activity.onBackPressed();
- }})
- .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- if (mAutoReturn) activity.onBackPressed();
- }})
- .create();
- dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN |
- WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
- return dialog;
- }
+ private String getShortcut(final int position) {
+ if (null == mCursor) return null;
+ mCursor.moveToPosition(position);
+ // Handle a possible race-condition
+ if (mCursor.isAfterLast()) return null;
- private void showDialog(int dialogId) {
- if (mDialogFragment != null) {
- Log.e(TAG, "Old dialog fragment not null!");
- }
- mDialogFragment = new SettingsDialogFragment(this, dialogId);
- mDialogFragment.show(getActivity().getFragmentManager(), Integer.toString(dialogId));
+ return mCursor.getString(
+ mCursor.getColumnIndexOrThrow(UserDictionary.Words.SHORTCUT));
}
- private void onAddOrEditFinished(String word) {
- if (mDialogEditingWord != null) {
- // The user was editing a word, so do a delete/add
- deleteWord(mDialogEditingWord);
- }
-
- // Disallow duplicates
- deleteWord(word);
-
- // TODO: present UI for picking whether to add word to all locales, or current.
- if (null == mLocale) {
- // Null means insert with the default system locale.
- UserDictionary.Words.addWord(getActivity(), word.toString(),
- FREQUENCY_FOR_USER_DICTIONARY_ADDS, UserDictionary.Words.LOCALE_TYPE_CURRENT);
- } else if ("".equals(mLocale)) {
- // Empty string means insert for all languages.
- UserDictionary.Words.addWord(getActivity(), word.toString(),
- FREQUENCY_FOR_USER_DICTIONARY_ADDS, UserDictionary.Words.LOCALE_TYPE_ALL);
+ public static void deleteWord(final String word, final String shortcut,
+ final ContentResolver resolver) {
+ if (TextUtils.isEmpty(shortcut)) {
+ resolver.delete(
+ UserDictionary.Words.CONTENT_URI, DELETE_SELECTION_WITHOUT_SHORTCUT,
+ new String[] { word });
} else {
- // TODO: fix the framework so that it can accept a locale when we add a word
- // to the user dictionary instead of querying the system locale.
- final Locale prevLocale = Locale.getDefault();
- Locale.setDefault(Utils.createLocaleFromString(mLocale));
- UserDictionary.Words.addWord(getActivity(), word.toString(),
- FREQUENCY_FOR_USER_DICTIONARY_ADDS, UserDictionary.Words.LOCALE_TYPE_CURRENT);
- Locale.setDefault(prevLocale);
- }
- if (null != mCursor && !mCursor.requery()) {
- throw new IllegalStateException("can't requery on already-closed cursor.");
+ resolver.delete(
+ UserDictionary.Words.CONTENT_URI, DELETE_SELECTION_WITH_SHORTCUT,
+ new String[] { word, shortcut });
}
- mAddedWordAlready = true;
}
- private void deleteWord(String word) {
- getActivity().getContentResolver().delete(
- UserDictionary.Words.CONTENT_URI, DELETE_SELECTION, new String[] { word });
- }
-
- private static class MyAdapter extends SimpleCursorAdapter implements SectionIndexer,
- View.OnClickListener {
+ private static class MyAdapter extends SimpleCursorAdapter implements SectionIndexer {
private AlphabetIndexer mIndexer;
- private UserDictionarySettings mSettings;
private ViewBinder mViewBinder = new ViewBinder() {
public boolean setViewValue(View v, Cursor c, int columnIndex) {
- if (v instanceof ImageView && columnIndex == INDEX_ID) {
- v.setOnClickListener(MyAdapter.this);
- v.setTag(c.getString(INDEX_WORD));
+ if (columnIndex == INDEX_SHORTCUT) {
+ final String shortcut = c.getString(INDEX_SHORTCUT);
+ if (TextUtils.isEmpty(shortcut)) {
+ v.setVisibility(View.GONE);
+ } else {
+ ((TextView)v).setText(shortcut);
+ v.setVisibility(View.VISIBLE);
+ }
+ v.invalidate();
return true;
}
@@ -332,7 +264,6 @@ public class UserDictionarySettings extends ListFragment implements DialogCreata
UserDictionarySettings settings) {
super(context, layout, c, from, to);
- mSettings = settings;
if (null != c) {
final String alphabet = context.getString(
com.android.internal.R.string.fast_scroll_alphabet);
@@ -353,9 +284,5 @@ public class UserDictionarySettings extends ListFragment implements DialogCreata
public Object[] getSections() {
return null == mIndexer ? null : mIndexer.getSections();
}
-
- public void onClick(View v) {
- mSettings.deleteWord((String) v.getTag());
- }
}
}
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 582891d..a029342 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -407,7 +407,7 @@ public class Utils {
final int paddingBottom = res.getDimensionPixelSize(
com.android.internal.R.dimen.preference_fragment_padding_bottom);
- final int effectivePaddingSide = ignoreSidePadding ? 0 : paddingBottom;
+ final int effectivePaddingSide = ignoreSidePadding ? 0 : paddingSide;
list.setPadding(effectivePaddingSide, 0, effectivePaddingSide, paddingBottom);
}
}
diff --git a/src/com/android/settings/WirelessSettings.java b/src/com/android/settings/WirelessSettings.java
index 13f6902..3b67ec3 100644
--- a/src/com/android/settings/WirelessSettings.java
+++ b/src/com/android/settings/WirelessSettings.java
@@ -37,7 +37,7 @@ import android.widget.Switch;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyProperties;
import com.android.settings.nfc.NfcEnabler;
-import com.android.settings.wifi.p2p.WifiP2pEnabler;
+import com.android.settings.NsdEnabler;
public class WirelessSettings extends SettingsPreferenceFragment {
@@ -46,11 +46,10 @@ public class WirelessSettings extends SettingsPreferenceFragment {
private static final String KEY_WIMAX_SETTINGS = "wimax_settings";
private static final String KEY_ANDROID_BEAM_SETTINGS = "android_beam_settings";
private static final String KEY_VPN_SETTINGS = "vpn_settings";
- private static final String KEY_TOGGLE_WIFI_P2P = "toggle_wifi_p2p";
- private static final String KEY_WIFI_P2P_SETTINGS = "wifi_p2p_settings";
private static final String KEY_TETHER_SETTINGS = "tether_settings";
private static final String KEY_PROXY_SETTINGS = "proxy_settings";
private static final String KEY_MOBILE_NETWORK_SETTINGS = "mobile_network_settings";
+ private static final String KEY_TOGGLE_NSD = "toggle_nsd"; //network service discovery
public static final String EXIT_ECM_RESULT = "exit_ecm_result";
public static final int REQUEST_CODE_EXIT_ECM = 1;
@@ -59,8 +58,7 @@ public class WirelessSettings extends SettingsPreferenceFragment {
private CheckBoxPreference mAirplaneModePreference;
private NfcEnabler mNfcEnabler;
private NfcAdapter mNfcAdapter;
-
- private WifiP2pEnabler mWifiP2pEnabler;
+ private NsdEnabler mNsdEnabler;
/**
* Invoked on each preference click in this hierarchy, overrides
@@ -101,12 +99,15 @@ public class WirelessSettings extends SettingsPreferenceFragment {
mAirplaneModePreference = (CheckBoxPreference) findPreference(KEY_TOGGLE_AIRPLANE);
CheckBoxPreference nfc = (CheckBoxPreference) findPreference(KEY_TOGGLE_NFC);
PreferenceScreen androidBeam = (PreferenceScreen) findPreference(KEY_ANDROID_BEAM_SETTINGS);
-
- CheckBoxPreference wifiP2p = (CheckBoxPreference) findPreference(KEY_TOGGLE_WIFI_P2P);
+ CheckBoxPreference nsd = (CheckBoxPreference) findPreference(KEY_TOGGLE_NSD);
mAirplaneModeEnabler = new AirplaneModeEnabler(activity, mAirplaneModePreference);
mNfcEnabler = new NfcEnabler(activity, nfc, androidBeam);
+ // Remove NSD checkbox by default
+ getPreferenceScreen().removePreference(nsd);
+ //mNsdEnabler = new NsdEnabler(activity, nsd);
+
String toggleable = Settings.System.getString(activity.getContentResolver(),
Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
@@ -153,15 +154,6 @@ public class WirelessSettings extends SettingsPreferenceFragment {
getPreferenceScreen().removePreference(findPreference(KEY_MOBILE_NETWORK_SETTINGS));
}
- WifiP2pManager p2p = (WifiP2pManager) activity.getSystemService(Context.WIFI_P2P_SERVICE);
-
- if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT)) {
- getPreferenceScreen().removePreference(wifiP2p);
- } else {
- mWifiP2pEnabler = new WifiP2pEnabler(activity, wifiP2p);
- }
- getPreferenceScreen().removePreference(findPreference(KEY_WIFI_P2P_SETTINGS));
-
// Enable Proxy selector settings if allowed.
Preference mGlobalProxy = findPreference(KEY_PROXY_SETTINGS);
DevicePolicyManager mDPM = (DevicePolicyManager)
@@ -189,9 +181,8 @@ public class WirelessSettings extends SettingsPreferenceFragment {
if (mNfcEnabler != null) {
mNfcEnabler.resume();
}
-
- if (mWifiP2pEnabler != null) {
- mWifiP2pEnabler.resume();
+ if (mNsdEnabler != null) {
+ mNsdEnabler.resume();
}
}
@@ -203,9 +194,8 @@ public class WirelessSettings extends SettingsPreferenceFragment {
if (mNfcEnabler != null) {
mNfcEnabler.pause();
}
-
- if (mWifiP2pEnabler != null) {
- mWifiP2pEnabler.pause();
+ if (mNsdEnabler != null) {
+ mNsdEnabler.pause();
}
}
@@ -218,4 +208,9 @@ public class WirelessSettings extends SettingsPreferenceFragment {
mAirplaneModePreference.isChecked());
}
}
+
+ @Override
+ protected int getHelpResource() {
+ return R.string.help_url_more_networks;
+ }
}
diff --git a/src/com/android/settings/accounts/AccountPreferenceBase.java b/src/com/android/settings/accounts/AccountPreferenceBase.java
index a0d6a7f..2759a8f 100644
--- a/src/com/android/settings/accounts/AccountPreferenceBase.java
+++ b/src/com/android/settings/accounts/AccountPreferenceBase.java
@@ -17,6 +17,7 @@
package com.android.settings.accounts;
import java.util.ArrayList;
+import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@@ -27,6 +28,7 @@ import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorDescription;
import android.accounts.OnAccountsUpdateListener;
+import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SyncAdapterType;
@@ -38,6 +40,7 @@ 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;
class AccountPreferenceBase extends SettingsPreferenceFragment
@@ -46,12 +49,12 @@ class AccountPreferenceBase extends SettingsPreferenceFragment
protected static final String TAG = "AccountSettings";
public static final String AUTHORITIES_FILTER_KEY = "authorities";
public static final String ACCOUNT_TYPES_FILTER_KEY = "account_types";
- private Map<String, AuthenticatorDescription> mTypeToAuthDescription
- = new HashMap<String, AuthenticatorDescription>();
- protected AuthenticatorDescription[] mAuthDescs;
private final Handler mHandler = new Handler();
private Object mStatusChangeListenerHandle;
private HashMap<String, ArrayList<String>> mAccountTypeToAuthorities = null;
+ private AuthenticatorHelper mAuthenticatorHelper = new AuthenticatorHelper();
+ private java.text.DateFormat mDateFormat;
+ private java.text.DateFormat mTimeFormat;
/**
* Overload to handle account updates.
@@ -75,6 +78,16 @@ class AccountPreferenceBase extends SettingsPreferenceFragment
}
@Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ final Activity activity = getActivity();
+
+ mDateFormat = DateFormat.getDateFormat(activity);
+ mTimeFormat = DateFormat.getTimeFormat(activity);
+ }
+
+ @Override
public void onResume() {
super.onResume();
mStatusChangeListenerHandle = ContentResolver.addStatusChangeListener(
@@ -91,7 +104,6 @@ class AccountPreferenceBase extends SettingsPreferenceFragment
ContentResolver.removeStatusChangeListener(mStatusChangeListenerHandle);
}
-
private SyncStatusObserver mSyncStatusObserver = new SyncStatusObserver() {
public void onStatusChanged(int which) {
mHandler.post(new Runnable() {
@@ -124,64 +136,21 @@ class AccountPreferenceBase extends SettingsPreferenceFragment
}
/**
- * Gets an icon associated with a particular account type. If none found, return null.
- * @param accountType the type of account
- * @return a drawable for the icon or null if one cannot be found.
- */
- protected Drawable getDrawableForType(final String accountType) {
- Drawable icon = null;
- if (mTypeToAuthDescription.containsKey(accountType)) {
- try {
- AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType);
- Context authContext = getActivity().createPackageContext(desc.packageName, 0);
- icon = authContext.getResources().getDrawable(desc.iconId);
- } catch (PackageManager.NameNotFoundException e) {
- // TODO: place holder icon for missing account icons?
- Log.w(TAG, "No icon name for account type " + accountType);
- } catch (Resources.NotFoundException e) {
- // TODO: place holder icon for missing account icons?
- Log.w(TAG, "No icon resource for account type " + accountType);
- }
- }
- return icon;
- }
-
- /**
- * Gets the label associated with a particular account type. If none found, return null.
- * @param accountType the type of account
- * @return a CharSequence for the label or null if one cannot be found.
- */
- protected CharSequence getLabelForType(final String accountType) {
- CharSequence label = null;
- if (mTypeToAuthDescription.containsKey(accountType)) {
- try {
- AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType);
- Context authContext = getActivity().createPackageContext(desc.packageName, 0);
- label = authContext.getResources().getText(desc.labelId);
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "No label name for account type " + accountType);
- } catch (Resources.NotFoundException e) {
- Log.w(TAG, "No label icon for account type " + accountType);
- }
- }
- return label;
- }
-
- /**
* Gets the preferences.xml file associated with a particular account type.
* @param accountType the type of account
* @return a PreferenceScreen inflated from accountPreferenceId.
*/
- protected PreferenceScreen addPreferencesForType(final String accountType) {
+ public PreferenceScreen addPreferencesForType(final String accountType,
+ PreferenceScreen parent) {
PreferenceScreen prefs = null;
- if (mTypeToAuthDescription.containsKey(accountType)) {
+ if (mAuthenticatorHelper.containsAccountType(accountType)) {
AuthenticatorDescription desc = null;
try {
- desc = mTypeToAuthDescription.get(accountType);
+ desc = mAuthenticatorHelper.getAccountTypeDescription(accountType);
if (desc != null && desc.accountPreferencesId != 0) {
Context authContext = getActivity().createPackageContext(desc.packageName, 0);
prefs = getPreferenceManager().inflateFromResource(authContext,
- desc.accountPreferencesId, getPreferenceScreen());
+ desc.accountPreferencesId, parent);
}
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Couldn't load preferences.xml file from " + desc.packageName);
@@ -192,15 +161,21 @@ class AccountPreferenceBase extends SettingsPreferenceFragment
return prefs;
}
- /**
- * Updates provider icons. Subclasses should call this in onCreate()
- * and update any UI that depends on AuthenticatorDescriptions in onAuthDescriptionsUpdated().
- */
- protected void updateAuthDescriptions() {
- mAuthDescs = AccountManager.get(getActivity()).getAuthenticatorTypes();
- for (int i = 0; i < mAuthDescs.length; i++) {
- mTypeToAuthDescription.put(mAuthDescs[i].type, mAuthDescs[i]);
- }
+ public void updateAuthDescriptions() {
+ mAuthenticatorHelper.updateAuthDescriptions(getActivity());
onAuthDescriptionsUpdated();
}
+
+ protected Drawable getDrawableForType(final String accountType) {
+ return mAuthenticatorHelper.getDrawableForType(getActivity(), accountType);
+ }
+
+ protected CharSequence getLabelForType(final String accountType) {
+ return mAuthenticatorHelper.getLabelForType(getActivity(), accountType);
+ }
+
+ protected String formatSyncDate(Date date) {
+ // TODO: Switch to using DateUtils.formatDateTime
+ return mDateFormat.format(date) + " " + mTimeFormat.format(date);
+ }
}
diff --git a/src/com/android/settings/accounts/AccountSyncSettings.java b/src/com/android/settings/accounts/AccountSyncSettings.java
index 6847607..196908e 100644
--- a/src/com/android/settings/accounts/AccountSyncSettings.java
+++ b/src/com/android/settings/accounts/AccountSyncSettings.java
@@ -63,9 +63,9 @@ import java.util.List;
public class AccountSyncSettings extends AccountPreferenceBase {
public static final String ACCOUNT_KEY = "account";
- protected static final int MENU_REMOVE_ACCOUNT_ID = Menu.FIRST;
- private static final int MENU_SYNC_NOW_ID = Menu.FIRST + 1;
- private static final int MENU_SYNC_CANCEL_ID = Menu.FIRST + 2;
+ private static final int MENU_SYNC_NOW_ID = Menu.FIRST;
+ private static final int MENU_SYNC_CANCEL_ID = Menu.FIRST + 1;
+ private static final int MENU_REMOVE_ACCOUNT_ID = Menu.FIRST + 2;
private static final int REALLY_REMOVE_DIALOG = 100;
private static final int FAILED_REMOVAL_DIALOG = 101;
private static final int CANT_DO_ONETIME_SYNC_DIALOG = 102;
@@ -73,8 +73,6 @@ public class AccountSyncSettings extends AccountPreferenceBase {
private TextView mProviderId;
private ImageView mProviderIcon;
private TextView mErrorInfoView;
- private java.text.DateFormat mDateFormat;
- private java.text.DateFormat mTimeFormat;
private Account mAccount;
// List of all accounts, updated when accounts are added/removed
// We need to re-scan the accounts on sync events, in case sync state changes.
@@ -98,6 +96,10 @@ public class AccountSyncSettings extends AccountPreferenceBase {
.removeAccount(mAccount,
new AccountManagerCallback<Boolean>() {
public void run(AccountManagerFuture<Boolean> future) {
+ // If already out of this screen, don't proceed.
+ if (!AccountSyncSettings.this.isResumed()) {
+ return;
+ }
boolean failed = true;
try {
if (future.getResult() == true) {
@@ -168,11 +170,6 @@ public class AccountSyncSettings extends AccountPreferenceBase {
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- final Activity activity = getActivity();
-
- mDateFormat = DateFormat.getDateFormat(activity);
- mTimeFormat = DateFormat.getTimeFormat(activity);
-
Bundle arguments = getArguments();
if (arguments == null) {
Log.e(TAG, "No arguments provided when starting intent. ACCOUNT_KEY needed.");
@@ -208,11 +205,13 @@ public class AccountSyncSettings extends AccountPreferenceBase {
new SyncStateCheckBoxPreference(getActivity(), account, authority);
item.setPersistent(false);
final ProviderInfo providerInfo = getPackageManager().resolveContentProvider(authority, 0);
- CharSequence providerLabel = providerInfo != null
- ? providerInfo.loadLabel(getPackageManager()) : null;
+ if (providerInfo == null) {
+ return;
+ }
+ CharSequence providerLabel = providerInfo.loadLabel(getPackageManager());
if (TextUtils.isEmpty(providerLabel)) {
Log.e(TAG, "Provider needs a label for authority '" + authority + "'");
- providerLabel = authority;
+ return;
}
String title = getString(R.string.sync_item_title, providerLabel);
item.setTitle(title);
@@ -222,17 +221,16 @@ public class AccountSyncSettings extends AccountPreferenceBase {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- super.onCreateOptionsMenu(menu, inflater);
- MenuItem removeAccount = menu.add(0, MENU_REMOVE_ACCOUNT_ID, 0,
- getString(R.string.remove_account_label))
- .setIcon(R.drawable.ic_menu_delete_holo_dark);
MenuItem syncNow = menu.add(0, MENU_SYNC_NOW_ID, 0,
- getString(R.string.sync_menu_sync_now))
+ getString(R.string.sync_menu_sync_now))
.setIcon(R.drawable.ic_menu_refresh_holo_dark);
MenuItem syncCancel = menu.add(0, MENU_SYNC_CANCEL_ID, 0,
- getString(R.string.sync_menu_sync_cancel))
+ getString(R.string.sync_menu_sync_cancel))
.setIcon(com.android.internal.R.drawable.ic_menu_close_clear_cancel);
+ MenuItem removeAccount = menu.add(0, MENU_REMOVE_ACCOUNT_ID, 0,
+ getString(R.string.remove_account_label))
+ .setIcon(R.drawable.ic_menu_delete_holo_dark);
removeAccount.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER |
MenuItem.SHOW_AS_ACTION_WITH_TEXT);
@@ -240,6 +238,8 @@ public class AccountSyncSettings extends AccountPreferenceBase {
MenuItem.SHOW_AS_ACTION_WITH_TEXT);
syncCancel.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER |
MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+
+ super.onCreateOptionsMenu(menu, inflater);
}
@Override
@@ -394,11 +394,14 @@ public class AccountSyncSettings extends AccountPreferenceBase {
}
final long successEndTime = (status == null) ? 0 : status.lastSuccessTime;
- if (successEndTime != 0) {
+ if (!syncEnabled) {
+ syncPref.setSummary(R.string.sync_disabled);
+ } else if (activelySyncing) {
+ syncPref.setSummary(R.string.sync_in_progress);
+ } else if (successEndTime != 0) {
date.setTime(successEndTime);
- final String timeString = mDateFormat.format(date) + " "
- + mTimeFormat.format(date);
- syncPref.setSummary(timeString);
+ final String timeString = formatSyncDate(date);
+ syncPref.setSummary(getResources().getString(R.string.last_synced, timeString));
} else {
syncPref.setSummary("");
}
@@ -498,25 +501,12 @@ public class AccountSyncSettings extends AccountPreferenceBase {
if (mAccount != null) {
mProviderIcon.setImageDrawable(getDrawableForType(mAccount.type));
mProviderId.setText(getLabelForType(mAccount.type));
- PreferenceScreen prefs = addPreferencesForType(mAccount.type);
- if (prefs != null) {
- updatePreferenceIntents(prefs);
- }
}
addPreferencesFromResource(R.xml.account_sync_settings);
}
- private void updatePreferenceIntents(PreferenceScreen prefs) {
- for (int i = 0; i < prefs.getPreferenceCount(); i++) {
- Intent intent = prefs.getPreference(i).getIntent();
- if (intent != null) {
- intent.putExtra(ACCOUNT_KEY, mAccount);
- // This is somewhat of a hack. Since the preference screen we're accessing comes
- // from another package, we need to modify the intent to launch it with
- // FLAG_ACTIVITY_NEW_TASK.
- // TODO: Do something smarter if we ever have PreferenceScreens of our own.
- intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
- }
- }
+ @Override
+ protected int getHelpResource() {
+ return R.string.help_url_accounts;
}
}
diff --git a/src/com/android/settings/accounts/AuthenticatorHelper.java b/src/com/android/settings/accounts/AuthenticatorHelper.java
new file mode 100644
index 0000000..ab2fe74
--- /dev/null
+++ b/src/com/android/settings/accounts/AuthenticatorHelper.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2012 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.accounts;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AuthenticatorDescription;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.ScaleDrawable;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+public class AuthenticatorHelper {
+
+ private static final String TAG = "AccountTypesHelper";
+ private Map<String, AuthenticatorDescription> mTypeToAuthDescription
+ = new HashMap<String, AuthenticatorDescription>();
+ private AuthenticatorDescription[] mAuthDescs;
+ private ArrayList<String> mEnabledAccountTypes = new ArrayList<String>();
+ private Map<String, Drawable> mAccTypeIconCache = new HashMap<String, Drawable>();
+
+ public AuthenticatorHelper() {
+ }
+
+ public String[] getEnabledAccountTypes() {
+ return mEnabledAccountTypes.toArray(new String[mEnabledAccountTypes.size()]);
+ }
+
+ /**
+ * Gets an icon associated with a particular account type. If none found, return null.
+ * @param accountType the type of account
+ * @return a drawable for the icon or null if one cannot be found.
+ */
+ public Drawable getDrawableForType(Context context, final String accountType) {
+ Drawable icon = null;
+ if (mAccTypeIconCache.containsKey(accountType)) {
+ return mAccTypeIconCache.get(accountType);
+ }
+ if (mTypeToAuthDescription.containsKey(accountType)) {
+ try {
+ AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType);
+ Context authContext = context.createPackageContext(desc.packageName, 0);
+ icon = authContext.getResources().getDrawable(desc.iconId);
+ mAccTypeIconCache.put(accountType, icon);
+ } catch (PackageManager.NameNotFoundException e) {
+ } catch (Resources.NotFoundException e) {
+ }
+ }
+ if (icon == null) {
+ icon = context.getPackageManager().getDefaultActivityIcon();
+ }
+ return icon;
+ }
+
+ /**
+ * Gets the label associated with a particular account type. If none found, return null.
+ * @param accountType the type of account
+ * @return a CharSequence for the label or null if one cannot be found.
+ */
+ public CharSequence getLabelForType(Context context, final String accountType) {
+ CharSequence label = null;
+ if (mTypeToAuthDescription.containsKey(accountType)) {
+ try {
+ AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType);
+ Context authContext = context.createPackageContext(desc.packageName, 0);
+ label = authContext.getResources().getText(desc.labelId);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "No label name for account type " + accountType);
+ } catch (Resources.NotFoundException e) {
+ Log.w(TAG, "No label icon for account type " + accountType);
+ }
+ }
+ return label;
+ }
+
+ /**
+ * Updates provider icons. Subclasses should call this in onCreate()
+ * and update any UI that depends on AuthenticatorDescriptions in onAuthDescriptionsUpdated().
+ */
+ public void updateAuthDescriptions(Context context) {
+ mAuthDescs = AccountManager.get(context).getAuthenticatorTypes();
+ for (int i = 0; i < mAuthDescs.length; i++) {
+ mTypeToAuthDescription.put(mAuthDescs[i].type, mAuthDescs[i]);
+ }
+ }
+
+ public void onAccountsUpdated(Context context, Account[] accounts) {
+ if (accounts == null) {
+ accounts = AccountManager.get(context).getAccounts();
+ }
+ mEnabledAccountTypes.clear();
+ mAccTypeIconCache.clear();
+ for (Account account: accounts) {
+ if (!mEnabledAccountTypes.contains(account.type)) {
+ mEnabledAccountTypes.add(account.type);
+ }
+ }
+ }
+
+ public boolean containsAccountType(String accountType) {
+ return mTypeToAuthDescription.containsKey(accountType);
+ }
+
+ public AuthenticatorDescription getAccountTypeDescription(String accountType) {
+ return mTypeToAuthDescription.get(accountType);
+ }
+
+ public boolean hasAccountPreferences(final String accountType) {
+ if (containsAccountType(accountType)) {
+ AuthenticatorDescription desc = getAccountTypeDescription(accountType);
+ if (desc != null && desc.accountPreferencesId != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/com/android/settings/accounts/ManageAccountsSettings.java b/src/com/android/settings/accounts/ManageAccountsSettings.java
index 0177491..bb1ebdd 100644
--- a/src/com/android/settings/accounts/ManageAccountsSettings.java
+++ b/src/com/android/settings/accounts/ManageAccountsSettings.java
@@ -21,16 +21,22 @@ import android.accounts.AccountManager;
import android.accounts.OnAccountsUpdateListener;
import android.app.ActionBar;
import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.Intent;
import android.content.SyncAdapterType;
import android.content.SyncInfo;
import android.content.SyncStatusInfo;
+import android.content.pm.PackageManager;
+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.text.format.DateFormat;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -44,16 +50,22 @@ import android.widget.Switch;
import android.widget.TextView;
import com.android.settings.AccountPreference;
-import com.android.settings.DialogCreatable;
import com.android.settings.R;
+import com.android.settings.Settings;
import java.util.ArrayList;
+import java.util.Date;
import java.util.HashSet;
public class ManageAccountsSettings extends AccountPreferenceBase
- implements OnAccountsUpdateListener, DialogCreatable {
+ implements OnAccountsUpdateListener {
- private static final int MENU_ADD_ACCOUNT = Menu.FIRST;
+ private static final String ACCOUNT_KEY = "account"; // to pass to auth settings
+ public static final String KEY_ACCOUNT_TYPE = "account_type";
+ public static final String KEY_ACCOUNT_LABEL = "account_label";
+
+ private static final int MENU_SYNC_NOW_ID = Menu.FIRST;
+ private static final int MENU_SYNC_CANCEL_ID = Menu.FIRST + 1;
private static final int REQUEST_SHOW_SYNC_SETTINGS = 1;
@@ -61,12 +73,19 @@ public class ManageAccountsSettings extends AccountPreferenceBase
private TextView mErrorInfoView;
private SettingsDialogFragment mDialogFragment;
- private Switch mAutoSyncSwitch;
+ // If an account type is set, then show only accounts of that type
+ private String mAccountType;
+ // Temporary hack, to deal with backward compatibility
+ private Account mFirstAccount;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
+ Bundle args = getArguments();
+ if (args != null && args.containsKey(KEY_ACCOUNT_TYPE)) {
+ mAccountType = args.getString(KEY_ACCOUNT_TYPE);
+ }
addPreferencesFromResource(R.xml.manage_accounts_settings);
setHasOptionsMenu(true);
}
@@ -76,12 +95,6 @@ public class ManageAccountsSettings extends AccountPreferenceBase
super.onStart();
Activity activity = getActivity();
AccountManager.get(activity).addOnAccountsUpdatedListener(this, null, true);
- activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
- ActionBar.DISPLAY_SHOW_CUSTOM);
- activity.getActionBar().setCustomView(mAutoSyncSwitch, new ActionBar.LayoutParams(
- ActionBar.LayoutParams.WRAP_CONTENT,
- ActionBar.LayoutParams.WRAP_CONTENT,
- Gravity.CENTER_VERTICAL | Gravity.RIGHT));
}
@Override
@@ -101,23 +114,12 @@ public class ManageAccountsSettings extends AccountPreferenceBase
mErrorInfoView = (TextView)view.findViewById(R.id.sync_settings_error_info);
mErrorInfoView.setVisibility(View.GONE);
- mAutoSyncSwitch = new Switch(activity);
-
- // TODO Where to put the switch in tablet multipane layout?
- final int padding = activity.getResources().getDimensionPixelSize(
- R.dimen.action_bar_switch_padding);
- mAutoSyncSwitch.setPadding(0, 0, padding, 0);
- mAutoSyncSwitch.setChecked(ContentResolver.getMasterSyncAutomatically());
- mAutoSyncSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- ContentResolver.setMasterSyncAutomatically(isChecked);
- onSyncStateUpdated();
- }
- });
-
mAuthorities = activity.getIntent().getStringArrayExtra(AUTHORITIES_FILTER_KEY);
+ Bundle args = getArguments();
+ if (args != null && args.containsKey(KEY_ACCOUNT_LABEL)) {
+ getActivity().setTitle(args.getString(KEY_ACCOUNT_LABEL));
+ }
updateAuthDescriptions();
}
@@ -150,29 +152,60 @@ public class ManageAccountsSettings extends AccountPreferenceBase
}
@Override
- public void showDialog(int dialogId) {
- if (mDialogFragment != null) {
- Log.e(TAG, "Old dialog fragment not null!");
- }
- mDialogFragment = new SettingsDialogFragment(this, dialogId);
- mDialogFragment.show(getActivity().getFragmentManager(), Integer.toString(dialogId));
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ MenuItem syncNow = menu.add(0, MENU_SYNC_NOW_ID, 0,
+ getString(R.string.sync_menu_sync_now))
+ .setIcon(R.drawable.ic_menu_refresh_holo_dark);
+ MenuItem syncCancel = menu.add(0, MENU_SYNC_CANCEL_ID, 0,
+ getString(R.string.sync_menu_sync_cancel))
+ .setIcon(com.android.internal.R.drawable.ic_menu_close_clear_cancel);
+ super.onCreateOptionsMenu(menu, inflater);
}
@Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- MenuItem addAccountItem = menu.add(0, MENU_ADD_ACCOUNT, 0, R.string.add_account_label);
- addAccountItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM
- | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+ public void onPrepareOptionsMenu(Menu menu) {
+ super.onPrepareOptionsMenu(menu);
+ boolean syncActive = ContentResolver.getCurrentSync() != null;
+ menu.findItem(MENU_SYNC_NOW_ID).setVisible(!syncActive && mFirstAccount != null);
+ menu.findItem(MENU_SYNC_CANCEL_ID).setVisible(syncActive && mFirstAccount != null);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- final int itemId = item.getItemId();
- if (itemId == MENU_ADD_ACCOUNT) {
- onAddAccountClicked();
+ switch (item.getItemId()) {
+ case MENU_SYNC_NOW_ID:
+ requestOrCancelSyncForAccounts(true);
+ return true;
+ case MENU_SYNC_CANCEL_ID:
+ requestOrCancelSyncForAccounts(false);
return true;
- } else {
- return super.onOptionsItemSelected(item);
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void requestOrCancelSyncForAccounts(boolean sync) {
+ SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypes();
+ Bundle extras = new Bundle();
+ extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+ int count = getPreferenceScreen().getPreferenceCount();
+ // For each account
+ for (int i = 0; i < count; i++) {
+ Preference pref = getPreferenceScreen().getPreference(i);
+ if (pref instanceof AccountPreference) {
+ Account account = ((AccountPreference) pref).getAccount();
+ // For all available sync authorities, sync those that are enabled for the account
+ for (int j = 0; j < syncAdapters.length; j++) {
+ SyncAdapterType sa = syncAdapters[j];
+ if (syncAdapters[j].accountType.equals(mAccountType)
+ && ContentResolver.getSyncAutomatically(account, sa.authority)) {
+ if (sync) {
+ ContentResolver.requestSync(account, sa.authority, extras);
+ } else {
+ ContentResolver.cancelSync(account, sa.authority);
+ }
+ }
+ }
+ }
}
}
@@ -180,15 +213,12 @@ public class ManageAccountsSettings extends AccountPreferenceBase
protected void onSyncStateUpdated() {
// Catch any delayed delivery of update messages
if (getActivity() == null) return;
- // Set background connection state
- if (mAutoSyncSwitch != null) {
- mAutoSyncSwitch.setChecked(ContentResolver.getMasterSyncAutomatically());
- }
// iterate over all the preferences, setting the state properly for each
SyncInfo currentSync = ContentResolver.getCurrentSync();
boolean anySyncFailed = false; // true if sync on any account failed
+ Date date = new Date();
// only track userfacing sync adapters when deciding if account is synced or not
final SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypes();
@@ -208,8 +238,10 @@ public class ManageAccountsSettings extends AccountPreferenceBase
AccountPreference accountPref = (AccountPreference) pref;
Account account = accountPref.getAccount();
int syncCount = 0;
+ long lastSuccessTime = 0;
boolean syncIsFailing = false;
final ArrayList<String> authorities = accountPref.getAuthorities();
+ boolean syncingNow = false;
if (authorities != null) {
for (String authority : authorities) {
SyncStatusInfo status = ContentResolver.getSyncStatus(account, authority);
@@ -230,6 +262,10 @@ public class ManageAccountsSettings extends AccountPreferenceBase
syncIsFailing = true;
anySyncFailed = true;
}
+ syncingNow |= activelySyncing;
+ if (status != null && lastSuccessTime < status.lastSuccessTime) {
+ lastSuccessTime = status.lastSuccessTime;
+ }
syncCount += syncEnabled && userFacing.contains(authority) ? 1 : 0;
}
} else {
@@ -237,15 +273,26 @@ public class ManageAccountsSettings extends AccountPreferenceBase
Log.v(TAG, "no syncadapters found for " + account);
}
}
- int syncStatus = AccountPreference.SYNC_DISABLED;
if (syncIsFailing) {
- syncStatus = AccountPreference.SYNC_ERROR;
+ accountPref.setSyncStatus(AccountPreference.SYNC_ERROR, true);
} else if (syncCount == 0) {
- syncStatus = AccountPreference.SYNC_DISABLED;
+ accountPref.setSyncStatus(AccountPreference.SYNC_DISABLED, true);
} else if (syncCount > 0) {
- syncStatus = AccountPreference.SYNC_ENABLED;
+ if (syncingNow) {
+ accountPref.setSyncStatus(AccountPreference.SYNC_IN_PROGRESS, true);
+ } else {
+ accountPref.setSyncStatus(AccountPreference.SYNC_ENABLED, true);
+ if (lastSuccessTime > 0) {
+ accountPref.setSyncStatus(AccountPreference.SYNC_ENABLED, false);
+ date.setTime(lastSuccessTime);
+ final String timeString = formatSyncDate(date);
+ accountPref.setSummary(getResources().getString(
+ R.string.last_synced, timeString));
+ }
+ }
+ } else {
+ accountPref.setSyncStatus(AccountPreference.SYNC_DISABLED, true);
}
- accountPref.setSyncStatus(syncStatus);
}
mErrorInfoView.setVisibility(anySyncFailed ? View.VISIBLE : View.GONE);
@@ -255,8 +302,12 @@ public class ManageAccountsSettings extends AccountPreferenceBase
public void onAccountsUpdated(Account[] accounts) {
if (getActivity() == null) return;
getPreferenceScreen().removeAll();
+ mFirstAccount = null;
+ addPreferencesFromResource(R.xml.manage_accounts_settings);
for (int i = 0, n = accounts.length; i < n; i++) {
final Account account = accounts[i];
+ // If an account type is specified for this screen, skip other types
+ if (mAccountType != null && !account.type.equals(mAccountType)) continue;
final ArrayList<String> auths = getAuthoritiesForAccountType(account.type);
boolean showAccount = true;
@@ -273,26 +324,53 @@ public class ManageAccountsSettings extends AccountPreferenceBase
if (showAccount) {
final Drawable icon = getDrawableForType(account.type);
final AccountPreference preference =
- new AccountPreference(getActivity(), account, icon, auths);
+ new AccountPreference(getActivity(), account, icon, auths, false);
getPreferenceScreen().addPreference(preference);
+ if (mFirstAccount == null) {
+ mFirstAccount = account;
+ }
}
}
+ if (mAccountType != null && mFirstAccount != null) {
+ addAuthenticatorSettings();
+ }
onSyncStateUpdated();
}
+ private void addAuthenticatorSettings() {
+ PreferenceScreen prefs = addPreferencesForType(mAccountType, getPreferenceScreen());
+ if (prefs != null) {
+ updatePreferenceIntents(prefs);
+ }
+ }
+
+ private void updatePreferenceIntents(PreferenceScreen prefs) {
+ PackageManager pm = getActivity().getPackageManager();
+ for (int i = 0; i < prefs.getPreferenceCount();) {
+ Intent intent = prefs.getPreference(i).getIntent();
+ if (intent != null) {
+ ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ if (ri == null) {
+ prefs.removePreference(prefs.getPreference(i));
+ continue;
+ } else {
+ intent.putExtra(ACCOUNT_KEY, mFirstAccount);
+ intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
+ }
+ }
+ i++;
+ }
+ }
+
@Override
protected void onAuthDescriptionsUpdated() {
// Update account icons for all account preference items
for (int i = 0; i < getPreferenceScreen().getPreferenceCount(); i++) {
- AccountPreference pref = (AccountPreference) getPreferenceScreen().getPreference(i);
- pref.setProviderIcon(getDrawableForType(pref.getAccount().type));
- pref.setSummary(getLabelForType(pref.getAccount().type));
+ Preference pref = getPreferenceScreen().getPreference(i);
+ if (pref instanceof AccountPreference) {
+ AccountPreference accPref = (AccountPreference) pref;
+ accPref.setSummary(getLabelForType(accPref.getAccount().type));
+ }
}
}
-
- public void onAddAccountClicked() {
- Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
- intent.putExtra(AUTHORITIES_FILTER_KEY, mAuthorities);
- startActivity(intent);
- }
}
diff --git a/src/com/android/settings/accounts/SyncSettings.java b/src/com/android/settings/accounts/SyncSettings.java
new file mode 100644
index 0000000..20c296a
--- /dev/null
+++ b/src/com/android/settings/accounts/SyncSettings.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.accounts;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.OnAccountsUpdateListener;
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceChangeListener;
+import android.preference.PreferenceScreen;
+import android.util.Log;
+
+import com.android.settings.AccountPreference;
+import com.android.settings.DialogCreatable;
+import com.android.settings.R;
+
+import java.util.ArrayList;
+
+public class SyncSettings extends AccountPreferenceBase
+ implements OnAccountsUpdateListener, DialogCreatable {
+
+ private static final String KEY_SYNC_SWITCH = "sync_switch";
+
+ private String[] mAuthorities;
+
+ private SettingsDialogFragment mDialogFragment;
+ private CheckBoxPreference mAutoSyncPreference;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ addPreferencesFromResource(R.xml.sync_settings);
+ mAutoSyncPreference =
+ (CheckBoxPreference) getPreferenceScreen().findPreference(KEY_SYNC_SWITCH);
+ mAutoSyncPreference.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ ContentResolver.setMasterSyncAutomatically((Boolean) newValue);
+ return true;
+ }
+ });
+
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ Activity activity = getActivity();
+ AccountManager.get(activity).addOnAccountsUpdatedListener(this, null, true);
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ final Activity activity = getActivity();
+ mAutoSyncPreference.setChecked(ContentResolver.getMasterSyncAutomatically());
+ mAuthorities = activity.getIntent().getStringArrayExtra(AUTHORITIES_FILTER_KEY);
+
+ updateAuthDescriptions();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ final Activity activity = getActivity();
+ AccountManager.get(activity).removeOnAccountsUpdatedListener(this);
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(PreferenceScreen preferences, Preference preference) {
+ if (preference instanceof AccountPreference) {
+ startAccountSettings((AccountPreference) preference);
+ } else {
+ return false;
+ }
+ return true;
+ }
+
+ private void startAccountSettings(AccountPreference acctPref) {
+ Intent intent = new Intent("android.settings.ACCOUNT_SYNC_SETTINGS");
+ intent.putExtra(AccountSyncSettings.ACCOUNT_KEY, acctPref.getAccount());
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ finish();
+ }
+
+ @Override
+ public void showDialog(int dialogId) {
+ if (mDialogFragment != null) {
+ Log.e(TAG, "Old dialog fragment not null!");
+ }
+ mDialogFragment = new SettingsDialogFragment(this, dialogId);
+ mDialogFragment.show(getActivity().getFragmentManager(), Integer.toString(dialogId));
+ }
+
+ private void removeAccountPreferences() {
+ PreferenceScreen parent = getPreferenceScreen();
+ for (int i = 0; i < parent.getPreferenceCount(); ) {
+ if (parent.getPreference(i) instanceof AccountPreference) {
+ parent.removePreference(parent.getPreference(i));
+ } else {
+ i++;
+ }
+ }
+ }
+
+ @Override
+ public void onAccountsUpdated(Account[] accounts) {
+ if (getActivity() == null) return;
+
+ removeAccountPreferences();
+ for (int i = 0, n = accounts.length; i < n; i++) {
+ final Account account = accounts[i];
+ final ArrayList<String> auths = getAuthoritiesForAccountType(account.type);
+
+ boolean showAccount = true;
+ if (mAuthorities != null && auths != null) {
+ showAccount = false;
+ for (String requestedAuthority : mAuthorities) {
+ if (auths.contains(requestedAuthority)) {
+ showAccount = true;
+ break;
+ }
+ }
+ }
+
+ if (showAccount) {
+ final Drawable icon = getDrawableForType(account.type);
+ final AccountPreference preference =
+ new AccountPreference(getActivity(), account, icon, auths, true);
+ getPreferenceScreen().addPreference(preference);
+ preference.setSummary(getLabelForType(account.type));
+ }
+ }
+ onSyncStateUpdated();
+ }
+
+ @Override
+ protected void onAuthDescriptionsUpdated() {
+ // Update account icons for all account preference items
+ for (int i = 0; i < getPreferenceScreen().getPreferenceCount(); i++) {
+ Preference pref = getPreferenceScreen().getPreference(i);
+ if (pref instanceof AccountPreference) {
+ AccountPreference accPref = (AccountPreference)
+ getPreferenceScreen().getPreference(i);
+ accPref.setIcon(getDrawableForType(accPref.getAccount().type));
+ accPref.setSummary(getLabelForType(accPref.getAccount().type));
+ }
+ }
+ }
+}
diff --git a/src/com/android/settings/accounts/SyncSettingsActivity.java b/src/com/android/settings/accounts/SyncSettingsActivity.java
new file mode 100644
index 0000000..ed0089e
--- /dev/null
+++ b/src/com/android/settings/accounts/SyncSettingsActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2012 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.accounts;
+
+import android.content.Intent;
+import android.preference.PreferenceActivity;
+
+/**
+ * Launcher activity for the SyncSettings fragment.
+ *
+ */
+public class SyncSettingsActivity extends PreferenceActivity {
+ @Override
+ public Intent getIntent() {
+ Intent modIntent = new Intent(super.getIntent());
+ modIntent.putExtra(EXTRA_SHOW_FRAGMENT, SyncSettings.class.getName());
+ modIntent.putExtra(EXTRA_NO_HEADERS, true);
+ return modIntent;
+ }
+} \ No newline at end of file
diff --git a/src/com/android/settings/applications/AppViewHolder.java b/src/com/android/settings/applications/AppViewHolder.java
new file mode 100644
index 0000000..d23f187
--- /dev/null
+++ b/src/com/android/settings/applications/AppViewHolder.java
@@ -0,0 +1,64 @@
+package com.android.settings.applications;
+
+import com.android.settings.R;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+// View Holder used when displaying views
+public class AppViewHolder {
+ public ApplicationsState.AppEntry entry;
+ public View rootView;
+ public TextView appName;
+ public ImageView appIcon;
+ public TextView appSize;
+ public TextView disabled;
+ public CheckBox checkBox;
+
+ static public AppViewHolder createOrRecycle(LayoutInflater inflater, View convertView) {
+ if (convertView == null) {
+ convertView = inflater.inflate(R.layout.manage_applications_item, null);
+
+ // Creates a ViewHolder and store references to the two children views
+ // we want to bind data to.
+ AppViewHolder holder = new AppViewHolder();
+ holder.rootView = convertView;
+ holder.appName = (TextView) convertView.findViewById(R.id.app_name);
+ holder.appIcon = (ImageView) convertView.findViewById(R.id.app_icon);
+ holder.appSize = (TextView) convertView.findViewById(R.id.app_size);
+ holder.disabled = (TextView) convertView.findViewById(R.id.app_disabled);
+ holder.checkBox = (CheckBox) convertView.findViewById(R.id.app_on_sdcard);
+ convertView.setTag(holder);
+ return holder;
+ } else {
+ // Get the ViewHolder back to get fast access to the TextView
+ // and the ImageView.
+ return (AppViewHolder)convertView.getTag();
+ }
+ }
+
+ void updateSizeText(CharSequence invalidSizeStr, int whichSize) {
+ if (ManageApplications.DEBUG) Log.i(ManageApplications.TAG, "updateSizeText of " + entry.label + " " + entry
+ + ": " + entry.sizeStr);
+ if (entry.sizeStr != null) {
+ switch (whichSize) {
+ case ManageApplications.SIZE_INTERNAL:
+ appSize.setText(entry.internalSizeStr);
+ break;
+ case ManageApplications.SIZE_EXTERNAL:
+ appSize.setText(entry.externalSizeStr);
+ break;
+ default:
+ appSize.setText(entry.sizeStr);
+ break;
+ }
+ } else if (entry.size == ApplicationsState.SIZE_INVALID) {
+ appSize.setText(invalidSizeStr);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/com/android/settings/applications/ApplicationsState.java b/src/com/android/settings/applications/ApplicationsState.java
index 799b34a..0a5c26e 100644
--- a/src/com/android/settings/applications/ApplicationsState.java
+++ b/src/com/android/settings/applications/ApplicationsState.java
@@ -227,24 +227,21 @@ public class ApplicationsState {
PackageIntentReceiver mPackageIntentReceiver;
boolean mResumed;
- Callbacks mCurCallbacks;
- // Information about all applications. Synchronize on mAppEntries
+ // Information about all applications. Synchronize on mEntriesMap
// to protect access to these.
+ final ArrayList<Session> mSessions = new ArrayList<Session>();
+ final ArrayList<Session> mRebuildingSessions = new ArrayList<Session>();
final InterestingConfigChanges mInterestingConfigChanges = new InterestingConfigChanges();
final HashMap<String, AppEntry> mEntriesMap = new HashMap<String, AppEntry>();
final ArrayList<AppEntry> mAppEntries = new ArrayList<AppEntry>();
List<ApplicationInfo> mApplications = new ArrayList<ApplicationInfo>();
long mCurId = 1;
String mCurComputingSizePkg;
+ boolean mSessionsChanged;
- // Rebuilding of app list. Synchronized on mRebuildSync.
- final Object mRebuildSync = new Object();
- boolean mRebuildRequested;
- boolean mRebuildAsync;
- AppFilter mRebuildFilter;
- Comparator<AppEntry> mRebuildComparator;
- ArrayList<AppEntry> mRebuildResult;
+ // Temporary for dispatching session callbacks. Only touched by main thread.
+ final ArrayList<Session> mActiveSessions = new ArrayList<Session>();
/**
* Receives notifications when applications are added/removed.
@@ -262,6 +259,9 @@ public class ApplicationsState {
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
mContext.registerReceiver(this, sdFilter);
}
+ void unregisterReceiver() {
+ mContext.unregisterReceiver(this);
+ }
@Override
public void onReceive(Context context, Intent intent) {
String actionStr = intent.getAction();
@@ -300,6 +300,21 @@ public class ApplicationsState {
}
}
+ void rebuildActiveSessions() {
+ synchronized (mEntriesMap) {
+ if (!mSessionsChanged) {
+ return;
+ }
+ mActiveSessions.clear();
+ for (int i=0; i<mSessions.size(); i++) {
+ Session s = mSessions.get(i);
+ if (s.mResumed) {
+ mActiveSessions.add(s);
+ }
+ }
+ }
+ }
+
class MainHandler extends Handler {
static final int MSG_REBUILD_COMPLETE = 1;
static final int MSG_PACKAGE_LIST_CHANGED = 2;
@@ -310,35 +325,39 @@ public class ApplicationsState {
@Override
public void handleMessage(Message msg) {
+ rebuildActiveSessions();
switch (msg.what) {
case MSG_REBUILD_COMPLETE: {
- if (mCurCallbacks != null) {
- mCurCallbacks.onRebuildComplete((ArrayList<AppEntry>)msg.obj);
+ Session s = (Session)msg.obj;
+ if (mActiveSessions.contains(s)) {
+ s.mCallbacks.onRebuildComplete(s.mLastAppList);
}
} break;
case MSG_PACKAGE_LIST_CHANGED: {
- if (mCurCallbacks != null) {
- mCurCallbacks.onPackageListChanged();
+ for (int i=0; i<mActiveSessions.size(); i++) {
+ mActiveSessions.get(i).mCallbacks.onPackageListChanged();
}
} break;
case MSG_PACKAGE_ICON_CHANGED: {
- if (mCurCallbacks != null) {
- mCurCallbacks.onPackageIconChanged();
+ for (int i=0; i<mActiveSessions.size(); i++) {
+ mActiveSessions.get(i).mCallbacks.onPackageIconChanged();
}
} break;
case MSG_PACKAGE_SIZE_CHANGED: {
- if (mCurCallbacks != null) {
- mCurCallbacks.onPackageSizeChanged((String)msg.obj);
+ for (int i=0; i<mActiveSessions.size(); i++) {
+ mActiveSessions.get(i).mCallbacks.onPackageSizeChanged(
+ (String)msg.obj);
}
} break;
case MSG_ALL_SIZES_COMPUTED: {
- if (mCurCallbacks != null) {
- mCurCallbacks.onAllSizesComputed();
+ for (int i=0; i<mActiveSessions.size(); i++) {
+ mActiveSessions.get(i).mCallbacks.onAllSizesComputed();
}
} break;
case MSG_RUNNING_STATE_CHANGED: {
- if (mCurCallbacks != null) {
- mCurCallbacks.onRunningStateChanged(msg.arg1 != 0);
+ for (int i=0; i<mActiveSessions.size(); i++) {
+ mActiveSessions.get(i).mCallbacks.onRunningStateChanged(
+ msg.arg1 != 0);
}
} break;
}
@@ -391,157 +410,226 @@ public class ApplicationsState {
}
}
- void resume(Callbacks callbacks) {
- if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock...");
- synchronized (mEntriesMap) {
- mCurCallbacks = callbacks;
- mResumed = true;
- if (mPackageIntentReceiver == null) {
- mPackageIntentReceiver = new PackageIntentReceiver();
- mPackageIntentReceiver.registerReceiver();
- }
- mApplications = mPm.getInstalledApplications(
- PackageManager.GET_UNINSTALLED_PACKAGES |
- PackageManager.GET_DISABLED_COMPONENTS);
- if (mApplications == null) {
- mApplications = new ArrayList<ApplicationInfo>();
- }
-
- if (mInterestingConfigChanges.applyNewConfig(mContext.getResources())) {
- // If an interesting part of the configuration has changed, we
- // should completely reload the app entries.
- mEntriesMap.clear();
- mAppEntries.clear();
- } else {
- for (int i=0; i<mAppEntries.size(); i++) {
- mAppEntries.get(i).sizeStale = true;
- }
- }
+ public class Session {
+ final Callbacks mCallbacks;
+ boolean mResumed;
- for (int i=0; i<mApplications.size(); i++) {
- final ApplicationInfo info = mApplications.get(i);
- // Need to trim out any applications that are disabled by
- // something different than the user.
- if (!info.enabled && info.enabledSetting
- != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
- mApplications.remove(i);
- i--;
- continue;
- }
- final AppEntry entry = mEntriesMap.get(info.packageName);
- if (entry != null) {
- entry.info = info;
+ // Rebuilding of app list. Synchronized on mRebuildSync.
+ final Object mRebuildSync = new Object();
+ boolean mRebuildRequested;
+ boolean mRebuildAsync;
+ AppFilter mRebuildFilter;
+ Comparator<AppEntry> mRebuildComparator;
+ ArrayList<AppEntry> mRebuildResult;
+ ArrayList<AppEntry> mLastAppList;
+
+ Session(Callbacks callbacks) {
+ mCallbacks = callbacks;
+ }
+
+ public void resume() {
+ if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock...");
+ synchronized (mEntriesMap) {
+ if (!mResumed) {
+ mResumed = true;
+ mSessionsChanged = true;
+ doResumeIfNeededLocked();
}
}
- mCurComputingSizePkg = null;
- if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
- mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
- }
if (DEBUG_LOCKING) Log.v(TAG, "...resume releasing lock");
}
- }
- void pause() {
- if (DEBUG_LOCKING) Log.v(TAG, "pause about to acquire lock...");
- synchronized (mEntriesMap) {
- mCurCallbacks = null;
- mResumed = false;
- if (DEBUG_LOCKING) Log.v(TAG, "...pause releasing lock");
+ public void pause() {
+ if (DEBUG_LOCKING) Log.v(TAG, "pause about to acquire lock...");
+ synchronized (mEntriesMap) {
+ if (mResumed) {
+ mResumed = false;
+ mSessionsChanged = true;
+ mBackgroundHandler.removeMessages(BackgroundHandler.MSG_REBUILD_LIST, this);
+ doPauseIfNeededLocked();
+ }
+ if (DEBUG_LOCKING) Log.v(TAG, "...pause releasing lock");
+ }
}
- }
- // Creates a new list of app entries with the given filter and comparator.
- ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator) {
- synchronized (mRebuildSync) {
- mRebuildRequested = true;
- mRebuildAsync = false;
- mRebuildFilter = filter;
- mRebuildComparator = comparator;
- mRebuildResult = null;
- if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_REBUILD_LIST)) {
- mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_REBUILD_LIST);
- }
-
- // We will wait for .25s for the list to be built.
- long waitend = SystemClock.uptimeMillis()+250;
-
- while (mRebuildResult == null) {
- long now = SystemClock.uptimeMillis();
- if (now >= waitend) {
- break;
+ // Creates a new list of app entries with the given filter and comparator.
+ ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator) {
+ synchronized (mRebuildSync) {
+ synchronized (mEntriesMap) {
+ mRebuildingSessions.add(this);
+ mRebuildRequested = true;
+ mRebuildAsync = false;
+ mRebuildFilter = filter;
+ mRebuildComparator = comparator;
+ mRebuildResult = null;
+ if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_REBUILD_LIST)) {
+ Message msg = mBackgroundHandler.obtainMessage(
+ BackgroundHandler.MSG_REBUILD_LIST);
+ mBackgroundHandler.sendMessage(msg);
+ }
}
- try {
- mRebuildSync.wait(waitend - now);
- } catch (InterruptedException e) {
+
+ // We will wait for .25s for the list to be built.
+ long waitend = SystemClock.uptimeMillis()+250;
+
+ while (mRebuildResult == null) {
+ long now = SystemClock.uptimeMillis();
+ if (now >= waitend) {
+ break;
+ }
+ try {
+ mRebuildSync.wait(waitend - now);
+ } catch (InterruptedException e) {
+ }
}
- }
- mRebuildAsync = true;
+ mRebuildAsync = true;
- return mRebuildResult;
+ return mRebuildResult;
+ }
}
- }
- void handleRebuildList() {
- AppFilter filter;
- Comparator<AppEntry> comparator;
- synchronized (mRebuildSync) {
- if (!mRebuildRequested) {
- return;
+ void handleRebuildList() {
+ AppFilter filter;
+ Comparator<AppEntry> comparator;
+ synchronized (mRebuildSync) {
+ if (!mRebuildRequested) {
+ return;
+ }
+
+ filter = mRebuildFilter;
+ comparator = mRebuildComparator;
+ mRebuildRequested = false;
+ mRebuildFilter = null;
+ mRebuildComparator = null;
}
- filter = mRebuildFilter;
- comparator = mRebuildComparator;
- mRebuildRequested = false;
- mRebuildFilter = null;
- mRebuildComparator = null;
- }
+ Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
- Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
+ if (filter != null) {
+ filter.init();
+ }
+
+ List<ApplicationInfo> apps;
+ synchronized (mEntriesMap) {
+ apps = new ArrayList<ApplicationInfo>(mApplications);
+ }
- if (filter != null) {
- filter.init();
+ ArrayList<AppEntry> filteredApps = new ArrayList<AppEntry>();
+ if (DEBUG) Log.i(TAG, "Rebuilding...");
+ for (int i=0; i<apps.size(); i++) {
+ ApplicationInfo info = apps.get(i);
+ if (filter == null || filter.filterApp(info)) {
+ synchronized (mEntriesMap) {
+ if (DEBUG_LOCKING) Log.v(TAG, "rebuild acquired lock");
+ AppEntry entry = getEntryLocked(info);
+ entry.ensureLabel(mContext);
+ if (DEBUG) Log.i(TAG, "Using " + info.packageName + ": " + entry);
+ filteredApps.add(entry);
+ if (DEBUG_LOCKING) Log.v(TAG, "rebuild releasing lock");
+ }
+ }
+ }
+
+ Collections.sort(filteredApps, comparator);
+
+ synchronized (mRebuildSync) {
+ if (!mRebuildRequested) {
+ mLastAppList = filteredApps;
+ if (!mRebuildAsync) {
+ mRebuildResult = filteredApps;
+ mRebuildSync.notifyAll();
+ } else {
+ if (!mMainHandler.hasMessages(MainHandler.MSG_REBUILD_COMPLETE, this)) {
+ Message msg = mMainHandler.obtainMessage(
+ MainHandler.MSG_REBUILD_COMPLETE, this);
+ mMainHandler.sendMessage(msg);
+ }
+ }
+ }
+ }
+
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
}
-
- List<ApplicationInfo> apps;
+
+ public void release() {
+ pause();
+ synchronized (mEntriesMap) {
+ mSessions.remove(this);
+ }
+ }
+ }
+
+ public Session newSession(Callbacks callbacks) {
+ Session s = new Session(callbacks);
synchronized (mEntriesMap) {
- apps = new ArrayList<ApplicationInfo>(mApplications);
+ mSessions.add(s);
}
+ return s;
+ }
- ArrayList<AppEntry> filteredApps = new ArrayList<AppEntry>();
- if (DEBUG) Log.i(TAG, "Rebuilding...");
- for (int i=0; i<apps.size(); i++) {
- ApplicationInfo info = apps.get(i);
- if (filter == null || filter.filterApp(info)) {
- synchronized (mEntriesMap) {
- if (DEBUG_LOCKING) Log.v(TAG, "rebuild acquired lock");
- AppEntry entry = getEntryLocked(info);
- entry.ensureLabel(mContext);
- if (DEBUG) Log.i(TAG, "Using " + info.packageName + ": " + entry);
- filteredApps.add(entry);
- if (DEBUG_LOCKING) Log.v(TAG, "rebuild releasing lock");
- }
+ void doResumeIfNeededLocked() {
+ if (mResumed) {
+ return;
+ }
+ mResumed = true;
+ if (mPackageIntentReceiver == null) {
+ mPackageIntentReceiver = new PackageIntentReceiver();
+ mPackageIntentReceiver.registerReceiver();
+ }
+ mApplications = mPm.getInstalledApplications(
+ PackageManager.GET_UNINSTALLED_PACKAGES |
+ PackageManager.GET_DISABLED_COMPONENTS);
+ if (mApplications == null) {
+ mApplications = new ArrayList<ApplicationInfo>();
+ }
+
+ if (mInterestingConfigChanges.applyNewConfig(mContext.getResources())) {
+ // If an interesting part of the configuration has changed, we
+ // should completely reload the app entries.
+ mEntriesMap.clear();
+ mAppEntries.clear();
+ } else {
+ for (int i=0; i<mAppEntries.size(); i++) {
+ mAppEntries.get(i).sizeStale = true;
}
}
- Collections.sort(filteredApps, comparator);
-
- synchronized (mRebuildSync) {
- if (!mRebuildRequested) {
- if (!mRebuildAsync) {
- mRebuildResult = filteredApps;
- mRebuildSync.notifyAll();
- } else {
- if (!mMainHandler.hasMessages(MainHandler.MSG_REBUILD_COMPLETE)) {
- Message msg = mMainHandler.obtainMessage(
- MainHandler.MSG_REBUILD_COMPLETE, filteredApps);
- mMainHandler.sendMessage(msg);
- }
- }
+ for (int i=0; i<mApplications.size(); i++) {
+ final ApplicationInfo info = mApplications.get(i);
+ // Need to trim out any applications that are disabled by
+ // something different than the user.
+ if (!info.enabled && info.enabledSetting
+ != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ mApplications.remove(i);
+ i--;
+ continue;
+ }
+ final AppEntry entry = mEntriesMap.get(info.packageName);
+ if (entry != null) {
+ entry.info = info;
}
}
+ mCurComputingSizePkg = null;
+ if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
+ mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
+ }
+ }
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ void doPauseIfNeededLocked() {
+ if (!mResumed) {
+ return;
+ }
+ for (int i=0; i<mSessions.size(); i++) {
+ if (mSessions.get(i).mResumed) {
+ return;
+ }
+ }
+ mResumed = false;
+ if (mPackageIntentReceiver != null) {
+ mPackageIntentReceiver.unregisterReceiver();
+ mPackageIntentReceiver = null;
+ }
}
AppEntry getEntry(String packageName) {
@@ -772,7 +860,18 @@ public class ApplicationsState {
@Override
public void handleMessage(Message msg) {
// Always try rebuilding list first thing, if needed.
- handleRebuildList();
+ ArrayList<Session> rebuildingSessions = null;
+ synchronized (mEntriesMap) {
+ if (mRebuildingSessions.size() > 0) {
+ rebuildingSessions = new ArrayList<Session>(mRebuildingSessions);
+ mRebuildingSessions.clear();
+ }
+ }
+ if (rebuildingSessions != null) {
+ for (int i=0; i<rebuildingSessions.size(); i++) {
+ rebuildingSessions.get(i).handleRebuildList();
+ }
+ }
switch (msg.what) {
case MSG_REBUILD_LIST: {
diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java
index faa531a..c2e358c 100644
--- a/src/com/android/settings/applications/InstalledAppDetails.java
+++ b/src/com/android/settings/applications/InstalledAppDetails.java
@@ -17,6 +17,7 @@
package com.android.settings.applications;
import com.android.settings.R;
+import com.android.settings.Utils;
import com.android.settings.applications.ApplicationsState.AppEntry;
import android.app.Activity;
@@ -25,8 +26,12 @@ import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.Fragment;
+import android.app.INotificationManager;
+import android.app.NotificationManager;
import android.app.admin.DevicePolicyManager;
+import android.appwidget.AppWidgetManager;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -49,13 +54,15 @@ import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.preference.PreferenceActivity;
+import android.text.SpannableString;
+import android.text.TextUtils;
import android.text.format.Formatter;
+import android.text.style.BulletSpan;
import android.util.Log;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
-import android.content.ComponentName;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -65,6 +72,7 @@ import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.Switch;
import android.widget.TextView;
/**
@@ -87,8 +95,10 @@ public class InstalledAppDetails extends Fragment
private PackageManager mPm;
private IUsbManager mUsbManager;
+ private AppWidgetManager mAppWidgetManager;
private DevicePolicyManager mDpm;
private ApplicationsState mState;
+ private ApplicationsState.Session mSession;
private ApplicationsState.AppEntry mAppEntry;
private PackageInfo mPackageInfo;
private CanBeOnSdCardChecker mCanBeOnSdCardChecker;
@@ -115,7 +125,8 @@ public class InstalledAppDetails extends Fragment
private Button mForceStopButton;
private Button mClearDataButton;
private Button mMoveAppButton;
-
+ private CompoundButton mNotificationSwitch;
+
private PackageMoveObserver mPackageMoveObserver;
private boolean mHaveSizes = false;
@@ -150,6 +161,7 @@ public class InstalledAppDetails extends Fragment
private static final int DLG_FORCE_STOP = DLG_BASE + 5;
private static final int DLG_MOVE_FAILED = DLG_BASE + 6;
private static final int DLG_DISABLE = DLG_BASE + 7;
+ private static final int DLG_DISABLE_NOTIFICATIONS = DLG_BASE + 8;
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
@@ -268,6 +280,16 @@ public class InstalledAppDetails extends Fragment
}
}
+ private boolean isThisASystemPackage() {
+ try {
+ PackageInfo sys = mPm.getPackageInfo("android", PackageManager.GET_SIGNATURES);
+ return (mPackageInfo != null && mPackageInfo.signatures != null &&
+ sys.signatures[0].equals(mPackageInfo.signatures[0]));
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
private void initUninstallButtons() {
mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
boolean enabled = true;
@@ -287,9 +309,7 @@ public class InstalledAppDetails extends Fragment
intent.addCategory(Intent.CATEGORY_HOME);
intent.setPackage(mAppEntry.info.packageName);
List<ResolveInfo> homes = mPm.queryIntentActivities(intent, 0);
- if ((homes != null && homes.size() > 0) ||
- (mPackageInfo != null && mPackageInfo.signatures != null &&
- sys.signatures[0].equals(mPackageInfo.signatures[0]))) {
+ if ((homes != null && homes.size() > 0) || isThisASystemPackage()) {
// Disable button for core system applications.
mUninstallButton.setText(R.string.disable_text);
} else if (mAppEntry.info.enabled) {
@@ -319,15 +339,35 @@ public class InstalledAppDetails extends Fragment
}
}
+ private void initNotificationButton() {
+ INotificationManager nm = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ boolean enabled = true; // default on
+ try {
+ enabled = nm.areNotificationsEnabledForPackage(mAppEntry.info.packageName);
+ } catch (android.os.RemoteException ex) {
+ // this does not bode well
+ }
+ mNotificationSwitch.setChecked(enabled);
+ if (isThisASystemPackage()) {
+ mNotificationSwitch.setEnabled(false);
+ } else {
+ mNotificationSwitch.setEnabled(true);
+ mNotificationSwitch.setOnCheckedChangeListener(this);
+ }
+ }
+
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mState = ApplicationsState.getInstance(getActivity().getApplication());
+ mSession = mState.newSession(this);
mPm = getActivity().getPackageManager();
IBinder b = ServiceManager.getService(Context.USB_SERVICE);
mUsbManager = IUsbManager.Stub.asInterface(b);
+ mAppWidgetManager = AppWidgetManager.getInstance(getActivity());
mDpm = (DevicePolicyManager)getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
mCanBeOnSdCardChecker = new CanBeOnSdCardChecker();
@@ -368,6 +408,8 @@ public class InstalledAppDetails extends Fragment
mScreenCompatSection = view.findViewById(R.id.screen_compatibility_section);
mAskCompatibilityCB = (CheckBox)view.findViewById(R.id.ask_compatibility_cb);
mEnableCompatibilityCB = (CheckBox)view.findViewById(R.id.enable_compatibility_cb);
+
+ mNotificationSwitch = (CompoundButton) view.findViewById(R.id.notification_switch);
return view;
}
@@ -397,7 +439,7 @@ public class InstalledAppDetails extends Fragment
public void onResume() {
super.onResume();
- mState.resume(this);
+ mSession.resume();
if (!refreshUi()) {
setIntentAndFinish(true, true);
}
@@ -406,7 +448,7 @@ public class InstalledAppDetails extends Fragment
@Override
public void onPause() {
super.onPause();
- mState.pause();
+ mSession.pause();
}
@Override
@@ -473,20 +515,55 @@ public class InstalledAppDetails extends Fragment
// Intent list cannot be null. so pass empty list
List<IntentFilter> intentList = new ArrayList<IntentFilter>();
mPm.getPreferredActivities(intentList, prefActList, packageName);
- if(localLOGV) Log.i(TAG, "Have "+prefActList.size()+" number of activities in prefered list");
+ if (localLOGV)
+ Log.i(TAG, "Have " + prefActList.size() + " number of activities in preferred list");
boolean hasUsbDefaults = false;
try {
hasUsbDefaults = mUsbManager.hasDefaults(packageName);
} catch (RemoteException e) {
Log.e(TAG, "mUsbManager.hasDefaults", e);
}
- TextView autoLaunchView = (TextView)mRootView.findViewById(R.id.auto_launch);
- if (prefActList.size() <= 0 && !hasUsbDefaults) {
- // Disable clear activities button
- autoLaunchView.setText(R.string.auto_launch_disable_text);
- mActivitiesButton.setEnabled(false);
+ boolean hasBindAppWidgetPermission =
+ mAppWidgetManager.hasBindAppWidgetPermission(mAppEntry.info.packageName);
+
+ TextView autoLaunchTitleView = (TextView) mRootView.findViewById(R.id.auto_launch_title);
+ TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch);
+ boolean autoLaunchEnabled = prefActList.size() > 0 || hasUsbDefaults;
+ if (!autoLaunchEnabled && !hasBindAppWidgetPermission) {
+ resetLaunchDefaultsUi(autoLaunchTitleView, autoLaunchView);
} else {
- autoLaunchView.setText(R.string.auto_launch_enable_text);
+ boolean useBullets = hasBindAppWidgetPermission && autoLaunchEnabled;
+
+ if (hasBindAppWidgetPermission) {
+ autoLaunchTitleView.setText(R.string.auto_launch_label_generic);
+ } else {
+ autoLaunchTitleView.setText(R.string.auto_launch_label);
+ }
+
+ CharSequence text = null;
+ int bulletIndent = getResources()
+ .getDimensionPixelSize(R.dimen.installed_app_details_bullet_offset);
+ if (autoLaunchEnabled) {
+ CharSequence autoLaunchEnableText = getText(R.string.auto_launch_enable_text);
+ SpannableString s = new SpannableString(autoLaunchEnableText);
+ if (useBullets) {
+ s.setSpan(new BulletSpan(bulletIndent), 0, autoLaunchEnableText.length(), 0);
+ }
+ text = (text == null) ?
+ TextUtils.concat(s, "\n") : TextUtils.concat(text, "\n", s, "\n");
+ }
+ if (hasBindAppWidgetPermission) {
+ CharSequence alwaysAllowBindAppWidgetsText =
+ getText(R.string.always_allow_bind_appwidgets_text);
+ SpannableString s = new SpannableString(alwaysAllowBindAppWidgetsText);
+ if (useBullets) {
+ s.setSpan(new BulletSpan(bulletIndent),
+ 0, alwaysAllowBindAppWidgetsText.length(), 0);
+ }
+ text = (text == null) ?
+ TextUtils.concat(s, "\n") : TextUtils.concat(text, "\n", s, "\n");
+ }
+ autoLaunchView.setText(text);
mActivitiesButton.setEnabled(true);
mActivitiesButton.setOnClickListener(this);
}
@@ -529,6 +606,13 @@ public class InstalledAppDetails extends Fragment
return true;
}
+ private void resetLaunchDefaultsUi(TextView title, TextView autoLaunchView) {
+ title.setText(R.string.auto_launch_label);
+ autoLaunchView.setText(R.string.auto_launch_disable_text);
+ // Disable clear activities button
+ mActivitiesButton.setEnabled(false);
+ }
+
private void setIntentAndFinish(boolean finish, boolean appChanged) {
if(localLOGV) Log.i(TAG, "appChanged="+appChanged);
Intent intent = new Intent();
@@ -614,6 +698,7 @@ public class InstalledAppDetails extends Fragment
initUninstallButtons();
initDataButtons();
initMoveButton();
+ initNotificationButton();
} else {
mMoveAppButton.setText(R.string.moving);
mMoveAppButton.setEnabled(false);
@@ -780,6 +865,26 @@ public class InstalledAppDetails extends Fragment
})
.setNegativeButton(R.string.dlg_cancel, null)
.create();
+ case DLG_DISABLE_NOTIFICATIONS:
+ return new AlertDialog.Builder(getActivity())
+ .setTitle(getActivity().getText(R.string.app_disable_notifications_dlg_title))
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setMessage(getActivity().getText(R.string.app_disable_notifications_dlg_text))
+ .setPositiveButton(R.string.dlg_ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ // Disable the package's notifications
+ getOwner().setNotificationsEnabled(false);
+ }
+ })
+ .setNegativeButton(R.string.dlg_cancel,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ // Re-enable the checkbox
+ getOwner().mNotificationSwitch.setChecked(true);
+ }
+ })
+ .create();
}
throw new IllegalArgumentException("unknown id " + id);
}
@@ -834,7 +939,7 @@ public class InstalledAppDetails extends Fragment
Activity.RESULT_CANCELED, null, null);
}
}
-
+
static class DisableChanger extends AsyncTask<Object, Object, Object> {
final PackageManager mPm;
final WeakReference<InstalledAppDetails> mActivity;
@@ -855,6 +960,18 @@ public class InstalledAppDetails extends Fragment
}
}
+ private void setNotificationsEnabled(boolean enabled) {
+ String packageName = mAppEntry.info.packageName;
+ INotificationManager nm = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ try {
+ final boolean enable = mNotificationSwitch.isChecked();
+ nm.setNotificationsEnabledForPackage(packageName, enabled);
+ } catch (android.os.RemoteException ex) {
+ mNotificationSwitch.setChecked(!enabled); // revert
+ }
+ }
+
/*
* Method implementing functionality of buttons clicked
* @see android.view.View.OnClickListener#onClick(android.view.View)
@@ -884,13 +1001,19 @@ public class InstalledAppDetails extends Fragment
} catch (RemoteException e) {
Log.e(TAG, "mUsbManager.clearDefaults", e);
}
- mActivitiesButton.setEnabled(false);
+ mAppWidgetManager.setBindAppWidgetPermission(packageName, false);
+ TextView autoLaunchTitleView =
+ (TextView) mRootView.findViewById(R.id.auto_launch_title);
+ TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch);
+ resetLaunchDefaultsUi(autoLaunchTitleView, autoLaunchView);
} else if(v == mClearDataButton) {
if (mAppEntry.info.manageSpaceActivityName != null) {
- Intent intent = new Intent(Intent.ACTION_DEFAULT);
- intent.setClassName(mAppEntry.info.packageName,
- mAppEntry.info.manageSpaceActivityName);
- startActivityForResult(intent, -1);
+ if (!Utils.isMonkeyRunning()) {
+ Intent intent = new Intent(Intent.ACTION_DEFAULT);
+ intent.setClassName(mAppEntry.info.packageName,
+ mAppEntry.info.manageSpaceActivityName);
+ startActivityForResult(intent, -1);
+ }
} else {
showDialogInner(DLG_CLEAR_DATA, 0);
}
@@ -925,6 +1048,12 @@ public class InstalledAppDetails extends Fragment
} else if (buttonView == mEnableCompatibilityCB) {
am.setPackageScreenCompatMode(packageName, isChecked ?
ActivityManager.COMPAT_MODE_ENABLED : ActivityManager.COMPAT_MODE_DISABLED);
+ } else if (buttonView == mNotificationSwitch) {
+ if (!isChecked) {
+ showDialogInner(DLG_DISABLE_NOTIFICATIONS, 0);
+ } else {
+ setNotificationsEnabled(true);
+ }
}
}
}
diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java
index 948ddb0..1cc9dcc 100644
--- a/src/com/android/settings/applications/ManageApplications.java
+++ b/src/com/android/settings/applications/ManageApplications.java
@@ -16,22 +16,37 @@
package com.android.settings.applications;
-import static com.android.settings.Utils.prepareCustomPreferencesList;
+import static android.net.NetworkPolicyManager.POLICY_NONE;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
import android.app.Activity;
+import android.app.AlertDialog;
import android.app.Fragment;
+import android.app.INotificationManager;
+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;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.net.NetworkPolicyManager;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
+import android.os.Handler;
+import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.StatFs;
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;
import android.text.format.Formatter;
import android.util.Log;
import android.view.LayoutInflater;
@@ -41,27 +56,27 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
-import android.view.inputmethod.InputMethodManager;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
-import android.widget.CheckBox;
import android.widget.Filter;
import android.widget.Filterable;
-import android.widget.ImageView;
import android.widget.ListView;
-import android.widget.TabHost;
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.Settings.RunningServicesActivity;
import com.android.settings.Settings.StorageUseActivity;
import com.android.settings.applications.ApplicationsState.AppEntry;
+import com.android.settings.deviceinfo.StorageMeasurement;
+import com.android.settings.Utils;
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.List;
final class CanBeOnSdCardChecker {
final IPackageManager mPm;
@@ -86,8 +101,7 @@ final class CanBeOnSdCardChecker {
if ((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
canBe = true;
} else {
- if ((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0 &&
- (info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
if (info.installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL ||
info.installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
canBe = true;
@@ -105,6 +119,11 @@ final class CanBeOnSdCardChecker {
}
}
+interface AppClickListener {
+ void onItemClick(ManageApplications.TabInfo tab, AdapterView<?> parent,
+ View view, int position, long id);
+}
+
/**
* Activity to pick an application that will be used to display installation information and
* options to uninstall/delete user data for system applications. This activity
@@ -112,14 +131,20 @@ final class CanBeOnSdCardChecker {
* intent.
*/
public class ManageApplications extends Fragment implements
- OnItemClickListener,
- TabHost.TabContentFactory, TabHost.OnTabChangeListener {
+ AppClickListener, DialogInterface.OnClickListener,
+ DialogInterface.OnDismissListener {
+
static final String TAG = "ManageApplications";
static final boolean DEBUG = false;
-
+
+ private static final String EXTRA_SORT_ORDER = "sortOrder";
+ private static final String EXTRA_SHOW_BACKGROUND = "showBackground";
+ private static final String EXTRA_DEFAULT_LIST_TYPE = "defaultListType";
+ private static final String EXTRA_RESET_DIALOG = "resetDialog";
+
// attributes used as keys when passing values to InstalledAppDetails activity
public static final String APP_CHG = "chg";
-
+
// constant value that can be used to check return code from sub activity.
private static final int INSTALLED_APP_DETAILS = 1;
@@ -139,16 +164,270 @@ public class ManageApplications extends Fragment implements
public static final int SORT_ORDER_SIZE = MENU_OPTIONS_BASE + 5;
public static final int SHOW_RUNNING_SERVICES = MENU_OPTIONS_BASE + 6;
public static final int SHOW_BACKGROUND_PROCESSES = MENU_OPTIONS_BASE + 7;
+ public static final int RESET_APP_PREFERENCES = MENU_OPTIONS_BASE + 8;
// sort order
private int mSortOrder = SORT_ORDER_ALPHA;
- // Filter value
- private int mFilterApps = FILTER_APPS_THIRD_PARTY;
private ApplicationsState mApplicationsState;
- private ApplicationsAdapter mApplicationsAdapter;
-
+
+ public static class TabInfo implements OnItemClickListener {
+ public final ManageApplications mOwner;
+ public final ApplicationsState mApplicationsState;
+ public final CharSequence mLabel;
+ public final int mListType;
+ public final int mFilter;
+ public final AppClickListener mClickListener;
+ public final CharSequence mInvalidSizeStr;
+ public final CharSequence mComputingSizeStr;
+ private final Bundle mSavedInstanceState;
+
+ public ApplicationsAdapter mApplications;
+ public LayoutInflater mInflater;
+ public View mRootView;
+
+ private IMediaContainerService mContainerService;
+
+ private View mLoadingContainer;
+
+ private View mListContainer;
+
+ // ListView used to display list
+ private ListView mListView;
+ // Custom view used to display running processes
+ private RunningProcessesView mRunningProcessesView;
+
+ private LinearColorBar mColorBar;
+ private TextView mStorageChartLabel;
+ private TextView mUsedStorageText;
+ private TextView mFreeStorageText;
+ private long mFreeStorage = 0, mAppStorage = 0, mTotalStorage = 0;
+ private long mLastUsedStorage, mLastAppStorage, mLastFreeStorage;
+
+ final Runnable mRunningProcessesAvail = new Runnable() {
+ public void run() {
+ handleRunningProcessesAvail();
+ }
+ };
+
+ public TabInfo(ManageApplications owner, ApplicationsState apps,
+ CharSequence label, int listType, AppClickListener clickListener,
+ Bundle savedInstanceState) {
+ mOwner = owner;
+ mApplicationsState = apps;
+ mLabel = label;
+ mListType = listType;
+ switch (listType) {
+ case LIST_TYPE_DOWNLOADED: mFilter = FILTER_APPS_THIRD_PARTY; break;
+ case LIST_TYPE_SDCARD: mFilter = FILTER_APPS_SDCARD; break;
+ default: mFilter = FILTER_APPS_ALL; break;
+ }
+ mClickListener = clickListener;
+ mInvalidSizeStr = owner.getActivity().getText(R.string.invalid_size_value);
+ mComputingSizeStr = owner.getActivity().getText(R.string.computing_size);
+ mSavedInstanceState = savedInstanceState;
+ }
+
+ public void setContainerService(IMediaContainerService containerService) {
+ mContainerService = containerService;
+ updateStorageUsage();
+ }
+
+ public View build(LayoutInflater inflater, ViewGroup contentParent, View contentChild) {
+ if (mRootView != null) {
+ return mRootView;
+ }
+
+ mInflater = inflater;
+ mRootView = inflater.inflate(mListType == LIST_TYPE_RUNNING
+ ? R.layout.manage_applications_running
+ : R.layout.manage_applications_apps, null);
+ mLoadingContainer = mRootView.findViewById(R.id.loading_container);
+ mLoadingContainer.setVisibility(View.VISIBLE);
+ mListContainer = mRootView.findViewById(R.id.list_container);
+ if (mListContainer != null) {
+ // Create adapter and list view here
+ View emptyView = mListContainer.findViewById(com.android.internal.R.id.empty);
+ ListView lv = (ListView) mListContainer.findViewById(android.R.id.list);
+ if (emptyView != null) {
+ lv.setEmptyView(emptyView);
+ }
+ lv.setOnItemClickListener(this);
+ lv.setSaveEnabled(true);
+ lv.setItemsCanFocus(true);
+ lv.setTextFilterEnabled(true);
+ mListView = lv;
+ mApplications = new ApplicationsAdapter(mApplicationsState, this, mFilter);
+ mListView.setAdapter(mApplications);
+ mListView.setRecyclerListener(mApplications);
+ mColorBar = (LinearColorBar)mListContainer.findViewById(R.id.storage_color_bar);
+ mStorageChartLabel = (TextView)mListContainer.findViewById(R.id.storageChartLabel);
+ mUsedStorageText = (TextView)mListContainer.findViewById(R.id.usedStorageText);
+ mFreeStorageText = (TextView)mListContainer.findViewById(R.id.freeStorageText);
+ Utils.prepareCustomPreferencesList(contentParent, contentChild, mListView, false);
+ if (mFilter == FILTER_APPS_SDCARD) {
+ mStorageChartLabel.setText(mOwner.getActivity().getText(
+ R.string.sd_card_storage));
+ } else {
+ mStorageChartLabel.setText(mOwner.getActivity().getText(
+ R.string.internal_storage));
+ }
+ applyCurrentStorage();
+ }
+ mRunningProcessesView = (RunningProcessesView)mRootView.findViewById(
+ R.id.running_processes);
+ if (mRunningProcessesView != null) {
+ mRunningProcessesView.doCreate(mSavedInstanceState);
+ }
+
+ return mRootView;
+ }
+
+ public void detachView() {
+ if (mRootView != null) {
+ ViewGroup group = (ViewGroup)mRootView.getParent();
+ if (group != null) {
+ group.removeView(mRootView);
+ }
+ }
+ }
+
+ public void resume(int sortOrder) {
+ if (mApplications != null) {
+ mApplications.resume(sortOrder);
+ }
+ if (mRunningProcessesView != null) {
+ boolean haveData = mRunningProcessesView.doResume(mOwner, mRunningProcessesAvail);
+ if (haveData) {
+ mRunningProcessesView.setVisibility(View.VISIBLE);
+ mLoadingContainer.setVisibility(View.INVISIBLE);
+ } else {
+ mLoadingContainer.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+
+ public void pause() {
+ if (mApplications != null) {
+ mApplications.pause();
+ }
+ if (mRunningProcessesView != null) {
+ mRunningProcessesView.doPause();
+ }
+ }
+
+ void updateStorageUsage() {
+ // Make sure a callback didn't come at an inopportune time.
+ if (mOwner.getActivity() == null) return;
+ // Doesn't make sense for stuff that is not an app list.
+ if (mApplications == null) return;
+
+ mFreeStorage = 0;
+ mAppStorage = 0;
+ mTotalStorage = 0;
+
+ if (mFilter == FILTER_APPS_SDCARD) {
+ if (mContainerService != null) {
+ try {
+ final long[] stats = mContainerService.getFileSystemStats(
+ Environment.getExternalStorageDirectory().getPath());
+ mTotalStorage = stats[0];
+ mFreeStorage = stats[1];
+ } catch (RemoteException e) {
+ Log.w(TAG, "Problem in container service", e);
+ }
+ }
+
+ if (mApplications != null) {
+ final int N = mApplications.getCount();
+ for (int i=0; i<N; i++) {
+ ApplicationsState.AppEntry ae = mApplications.getAppEntry(i);
+ mAppStorage += ae.externalCodeSize + ae.externalDataSize;
+ }
+ }
+ } else {
+ if (mContainerService != null) {
+ try {
+ final long[] stats = mContainerService.getFileSystemStats(
+ Environment.getDataDirectory().getPath());
+ mTotalStorage = stats[0];
+ mFreeStorage = stats[1];
+ } catch (RemoteException e) {
+ Log.w(TAG, "Problem in container service", e);
+ }
+ }
+
+ final boolean emulatedStorage = Environment.isExternalStorageEmulated();
+ if (mApplications != null) {
+ final int N = mApplications.getCount();
+ for (int i=0; i<N; i++) {
+ ApplicationsState.AppEntry ae = mApplications.getAppEntry(i);
+ mAppStorage += ae.codeSize + ae.dataSize;
+ if (emulatedStorage) {
+ mAppStorage += ae.externalCodeSize + ae.externalDataSize;
+ }
+ }
+ }
+ mFreeStorage += mApplicationsState.sumCacheSizes();
+ }
+
+ applyCurrentStorage();
+ }
+
+ void applyCurrentStorage() {
+ // If view hierarchy is not yet created, no views to update.
+ if (mRootView == null) {
+ return;
+ }
+ if (mTotalStorage > 0) {
+ mColorBar.setRatios((mTotalStorage-mFreeStorage-mAppStorage)/(float)mTotalStorage,
+ mAppStorage/(float)mTotalStorage, mFreeStorage/(float)mTotalStorage);
+ long usedStorage = mTotalStorage - mFreeStorage;
+ if (mLastUsedStorage != usedStorage) {
+ mLastUsedStorage = usedStorage;
+ String sizeStr = Formatter.formatShortFileSize(
+ mOwner.getActivity(), usedStorage);
+ mUsedStorageText.setText(mOwner.getActivity().getResources().getString(
+ R.string.service_foreground_processes, sizeStr));
+ }
+ if (mLastFreeStorage != mFreeStorage) {
+ mLastFreeStorage = mFreeStorage;
+ String sizeStr = Formatter.formatShortFileSize(
+ mOwner.getActivity(), mFreeStorage);
+ mFreeStorageText.setText(mOwner.getActivity().getResources().getString(
+ R.string.service_background_processes, sizeStr));
+ }
+ } else {
+ mColorBar.setRatios(0, 0, 0);
+ if (mLastUsedStorage != -1) {
+ mLastUsedStorage = -1;
+ mUsedStorageText.setText("");
+ }
+ if (mLastFreeStorage != -1) {
+ mLastFreeStorage = -1;
+ mFreeStorageText.setText("");
+ }
+ }
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ mClickListener.onItemClick(this, parent, view, position, id);
+ }
+
+ void handleRunningProcessesAvail() {
+ mLoadingContainer.startAnimation(AnimationUtils.loadAnimation(
+ mOwner.getActivity(), android.R.anim.fade_out));
+ mRunningProcessesView.startAnimation(AnimationUtils.loadAnimation(
+ mOwner.getActivity(), android.R.anim.fade_in));
+ mRunningProcessesView.setVisibility(View.VISIBLE);
+ mLoadingContainer.setVisibility(View.GONE);
+ }
+ }
+ private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
+ TabInfo mCurTab = null;
+
// Size resource used for packages whose size computation failed for some reason
- private CharSequence mInvalidSizeStr;
+ CharSequence mInvalidSizeStr;
private CharSequence mComputingSizeStr;
// layout inflater object used to inflate views
@@ -156,85 +435,75 @@ public class ManageApplications extends Fragment implements
private String mCurrentPkgName;
- private View mLoadingContainer;
-
- private View mListContainer;
-
- // ListView used to display list
- private ListView mListView;
- // Custom view used to display running processes
- private RunningProcessesView mRunningProcessesView;
-
- LinearColorBar mColorBar;
- TextView mStorageChartLabel;
- TextView mUsedStorageText;
- TextView mFreeStorageText;
-
private Menu mOptionsMenu;
-
- // These are for keeping track of activity and tab switch state.
- private int mCurView;
- private boolean mCreatedRunning;
- private boolean mResumedRunning;
+ // These are for keeping track of activity and spinner switch state.
private boolean mActivityResumed;
- private StatFs mDataFileStats;
- private StatFs mSDCardFileStats;
- private boolean mLastShowedInternalStorage = true;
- private long mLastUsedStorage, mLastAppStorage, mLastFreeStorage;
-
- static final String TAB_DOWNLOADED = "Downloaded";
- static final String TAB_RUNNING = "Running";
- static final String TAB_ALL = "All";
- static final String TAB_SDCARD = "OnSdCard";
- private View mRootView;
+ static final int LIST_TYPE_DOWNLOADED = 0;
+ static final int LIST_TYPE_RUNNING = 1;
+ static final int LIST_TYPE_SDCARD = 2;
+ static final int LIST_TYPE_ALL = 3;
private boolean mShowBackground = false;
- // -------------- Copied from TabActivity --------------
+ private int mDefaultListType = -1;
- private TabHost mTabHost;
- private String mDefaultTab = null;
+ private ViewGroup mContentContainer;
+ private View mRootView;
+ private ViewPager mViewPager;
- // -------------- Copied from TabActivity --------------
+ AlertDialog mResetDialog;
- final Runnable mRunningProcessesAvail = new Runnable() {
- public void run() {
- handleRunningProcessesAvail();
- }
- };
+ class MyPagerAdapter extends PagerAdapter
+ implements ViewPager.OnPageChangeListener {
+ int mCurPos = 0;
- // View Holder used when displaying views
- static class AppViewHolder {
- ApplicationsState.AppEntry entry;
- TextView appName;
- ImageView appIcon;
- TextView appSize;
- TextView disabled;
- CheckBox checkBox;
+ @Override
+ public int getCount() {
+ return mTabs.size();
+ }
- void updateSizeText(ManageApplications ma, int whichSize) {
- if (DEBUG) Log.i(TAG, "updateSizeText of " + entry.label + " " + entry
- + ": " + entry.sizeStr);
- if (entry.sizeStr != null) {
- switch (whichSize) {
- case SIZE_INTERNAL:
- appSize.setText(entry.internalSizeStr);
- break;
- case SIZE_EXTERNAL:
- appSize.setText(entry.externalSizeStr);
- break;
- default:
- appSize.setText(entry.sizeStr);
- break;
- }
- } else if (entry.size == ApplicationsState.SIZE_INVALID) {
- appSize.setText(ma.mInvalidSizeStr);
+ @Override
+ public Object instantiateItem(ViewGroup container, int position) {
+ TabInfo tab = mTabs.get(position);
+ View root = tab.build(mInflater, mContentContainer, mRootView);
+ container.addView(root);
+ return root;
+ }
+
+ @Override
+ public void destroyItem(ViewGroup container, int position, Object object) {
+ container.removeView((View)object);
+ }
+
+ @Override
+ public boolean isViewFromObject(View view, Object object) {
+ return view == object;
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ return mTabs.get(position).mLabel;
+ }
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ mCurPos = position;
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ if (state == ViewPager.SCROLL_STATE_IDLE) {
+ updateCurrentTab(mCurPos);
}
}
}
-
+
/*
* Custom adapter implementation for the ListView
* This adapter maintains a map for each displayed application and its properties
@@ -244,14 +513,18 @@ public class ManageApplications extends Fragment implements
* the getId methods via the package name into the internal maps and indices.
* The order of applications in the list is mirrored in mAppLocalList
*/
- class ApplicationsAdapter extends BaseAdapter implements Filterable,
+ static class ApplicationsAdapter extends BaseAdapter implements Filterable,
ApplicationsState.Callbacks, AbsListView.RecyclerListener {
private final ApplicationsState mState;
+ private final ApplicationsState.Session mSession;
+ private final TabInfo mTab;
+ private final Context mContext;
private final ArrayList<View> mActive = new ArrayList<View>();
+ private final int mFilterMode;
private ArrayList<ApplicationsState.AppEntry> mBaseEntries;
private ArrayList<ApplicationsState.AppEntry> mEntries;
private boolean mResumed;
- private int mLastFilterMode=-1, mLastSortMode=-1;
+ private int mLastSortMode=-1;
private boolean mWaitingForData;
private int mWhichSize = SIZE_TOTAL;
CharSequence mCurFilterPrefix;
@@ -272,39 +545,41 @@ public class ManageApplications extends Fragment implements
mCurFilterPrefix = constraint;
mEntries = (ArrayList<ApplicationsState.AppEntry>)results.values;
notifyDataSetChanged();
- updateStorageUsage();
+ mTab.updateStorageUsage();
}
};
- public ApplicationsAdapter(ApplicationsState state) {
+ public ApplicationsAdapter(ApplicationsState state, TabInfo tab, int filterMode) {
mState = state;
+ mSession = state.newSession(this);
+ mTab = tab;
+ mContext = tab.mOwner.getActivity();
+ mFilterMode = filterMode;
}
- public void resume(int filter, int sort) {
+ public void resume(int sort) {
if (DEBUG) Log.i(TAG, "Resume! mResumed=" + mResumed);
if (!mResumed) {
mResumed = true;
- mState.resume(this);
- mLastFilterMode = filter;
+ mSession.resume();
mLastSortMode = sort;
rebuild(true);
} else {
- rebuild(filter, sort);
+ rebuild(sort);
}
}
public void pause() {
if (mResumed) {
mResumed = false;
- mState.pause();
+ mSession.pause();
}
}
- public void rebuild(int filter, int sort) {
- if (filter == mLastFilterMode && sort == mLastSortMode) {
+ public void rebuild(int sort) {
+ if (sort == mLastSortMode) {
return;
}
- mLastFilterMode = filter;
mLastSortMode = sort;
rebuild(true);
}
@@ -319,7 +594,7 @@ public class ManageApplications extends Fragment implements
} else {
mWhichSize = SIZE_INTERNAL;
}
- switch (mLastFilterMode) {
+ switch (mFilterMode) {
case FILTER_APPS_THIRD_PARTY:
filterObj = ApplicationsState.THIRD_PARTY_FILTER;
break;
@@ -352,7 +627,7 @@ public class ManageApplications extends Fragment implements
break;
}
ArrayList<ApplicationsState.AppEntry> entries
- = mState.rebuild(filterObj, comparatorObj);
+ = mSession.rebuild(filterObj, comparatorObj);
if (entries == null && !eraseold) {
// Don't have new list yet, but can continue using the old one.
return;
@@ -364,15 +639,15 @@ public class ManageApplications extends Fragment implements
mEntries = null;
}
notifyDataSetChanged();
- updateStorageUsage();
+ mTab.updateStorageUsage();
if (entries == null) {
mWaitingForData = true;
- mListContainer.setVisibility(View.INVISIBLE);
- mLoadingContainer.setVisibility(View.VISIBLE);
+ mTab.mListContainer.setVisibility(View.INVISIBLE);
+ mTab.mLoadingContainer.setVisibility(View.VISIBLE);
} else {
- mListContainer.setVisibility(View.VISIBLE);
- mLoadingContainer.setVisibility(View.GONE);
+ mTab.mListContainer.setVisibility(View.VISIBLE);
+ mTab.mLoadingContainer.setVisibility(View.GONE);
}
}
@@ -398,24 +673,24 @@ public class ManageApplications extends Fragment implements
@Override
public void onRunningStateChanged(boolean running) {
- getActivity().setProgressBarIndeterminateVisibility(running);
+ mTab.mOwner.getActivity().setProgressBarIndeterminateVisibility(running);
}
@Override
public void onRebuildComplete(ArrayList<AppEntry> apps) {
- if (mLoadingContainer.getVisibility() == View.VISIBLE) {
- mLoadingContainer.startAnimation(AnimationUtils.loadAnimation(
- getActivity(), android.R.anim.fade_out));
- mListContainer.startAnimation(AnimationUtils.loadAnimation(
- getActivity(), android.R.anim.fade_in));
+ if (mTab.mLoadingContainer.getVisibility() == View.VISIBLE) {
+ mTab.mLoadingContainer.startAnimation(AnimationUtils.loadAnimation(
+ mContext, android.R.anim.fade_out));
+ mTab.mListContainer.startAnimation(AnimationUtils.loadAnimation(
+ mContext, android.R.anim.fade_in));
}
- mListContainer.setVisibility(View.VISIBLE);
- mLoadingContainer.setVisibility(View.GONE);
+ mTab.mListContainer.setVisibility(View.VISIBLE);
+ mTab.mLoadingContainer.setVisibility(View.GONE);
mWaitingForData = false;
mBaseEntries = apps;
mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries);
notifyDataSetChanged();
- updateStorageUsage();
+ mTab.updateStorageUsage();
}
@Override
@@ -435,9 +710,9 @@ public class ManageApplications extends Fragment implements
AppViewHolder holder = (AppViewHolder)mActive.get(i).getTag();
if (holder.entry.info.packageName.equals(packageName)) {
synchronized (holder.entry) {
- holder.updateSizeText(ManageApplications.this, mWhichSize);
+ holder.updateSizeText(mTab.mInvalidSizeStr, mWhichSize);
}
- if (holder.entry.info.packageName.equals(mCurrentPkgName)
+ if (holder.entry.info.packageName.equals(mTab.mOwner.mCurrentPkgName)
&& mLastSortMode == SORT_ORDER_SIZE) {
// We got the size information for the last app the
// user viewed, and are sorting by size... they may
@@ -445,7 +720,7 @@ public class ManageApplications extends Fragment implements
// the list with the new size to reflect it to the user.
rebuild(false);
}
- updateStorageUsage();
+ mTab.updateStorageUsage();
return;
}
}
@@ -456,6 +731,7 @@ public class ManageApplications extends Fragment implements
if (mLastSortMode == SORT_ORDER_SIZE) {
rebuild(false);
}
+ mTab.updateStorageUsage();
}
public int getCount() {
@@ -477,28 +753,8 @@ public class ManageApplications extends Fragment implements
public View getView(int position, View convertView, ViewGroup parent) {
// A ViewHolder keeps references to children views to avoid unnecessary calls
// to findViewById() on each row.
- AppViewHolder holder;
-
- // When convertView is not null, we can reuse it directly, there is no need
- // to reinflate it. We only inflate a new View when the convertView supplied
- // by ListView is null.
- if (convertView == null) {
- convertView = mInflater.inflate(R.layout.manage_applications_item, null);
-
- // Creates a ViewHolder and store references to the two children views
- // we want to bind data to.
- holder = new AppViewHolder();
- holder.appName = (TextView) convertView.findViewById(R.id.app_name);
- holder.appIcon = (ImageView) convertView.findViewById(R.id.app_icon);
- holder.appSize = (TextView) convertView.findViewById(R.id.app_size);
- holder.disabled = (TextView) convertView.findViewById(R.id.app_disabled);
- holder.checkBox = (CheckBox) convertView.findViewById(R.id.app_on_sdcard);
- convertView.setTag(holder);
- } else {
- // Get the ViewHolder back to get fast access to the TextView
- // and the ImageView.
- holder = (AppViewHolder) convertView.getTag();
- }
+ AppViewHolder holder = AppViewHolder.createOrRecycle(mTab.mInflater, convertView);
+ convertView = holder.rootView;
// Bind the data efficiently with the holder
ApplicationsState.AppEntry entry = mEntries.get(position);
@@ -506,7 +762,7 @@ public class ManageApplications extends Fragment implements
holder.entry = entry;
if (entry.label != null) {
holder.appName.setText(entry.label);
- holder.appName.setTextColor(getActivity().getResources().getColorStateList(
+ holder.appName.setTextColor(mContext.getResources().getColorStateList(
entry.info.enabled ? android.R.color.primary_text_dark
: android.R.color.secondary_text_dark));
}
@@ -514,13 +770,13 @@ public class ManageApplications extends Fragment implements
if (entry.icon != null) {
holder.appIcon.setImageDrawable(entry.icon);
}
- holder.updateSizeText(ManageApplications.this, mWhichSize);
+ holder.updateSizeText(mTab.mInvalidSizeStr, mWhichSize);
if (InstalledAppDetails.SUPPORT_DISABLE_APPS) {
holder.disabled.setVisibility(entry.info.enabled ? View.GONE : View.VISIBLE);
} else {
holder.disabled.setVisibility(View.GONE);
}
- if (mLastFilterMode == FILTER_APPS_SDCARD) {
+ if (mFilterMode == FILTER_APPS_SDCARD) {
holder.checkBox.setVisibility(View.VISIBLE);
holder.checkBox.setChecked((entry.info.flags
& ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
@@ -551,10 +807,9 @@ public class ManageApplications extends Fragment implements
setHasOptionsMenu(true);
mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication());
- mApplicationsAdapter = new ApplicationsAdapter(mApplicationsState);
Intent intent = getActivity().getIntent();
String action = intent.getAction();
- String defaultTabTag = TAB_DOWNLOADED;
+ int defaultListType = LIST_TYPE_DOWNLOADED;
String className = getArguments() != null
? getArguments().getString("classname") : null;
if (className == null) {
@@ -562,33 +817,54 @@ public class ManageApplications extends Fragment implements
}
if (className.equals(RunningServicesActivity.class.getName())
|| className.endsWith(".RunningServices")) {
- defaultTabTag = TAB_RUNNING;
+ defaultListType = LIST_TYPE_RUNNING;
} else if (className.equals(StorageUseActivity.class.getName())
|| Intent.ACTION_MANAGE_PACKAGE_STORAGE.equals(action)
|| className.endsWith(".StorageUse")) {
mSortOrder = SORT_ORDER_SIZE;
- mFilterApps = FILTER_APPS_ALL;
- defaultTabTag = TAB_ALL;
+ defaultListType = LIST_TYPE_ALL;
} else if (Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS.equals(action)) {
- // Select the all-apps tab, with the default sorting
- defaultTabTag = TAB_ALL;
+ // Select the all-apps list, with the default sorting
+ defaultListType = LIST_TYPE_ALL;
}
-
+
if (savedInstanceState != null) {
- mSortOrder = savedInstanceState.getInt("sortOrder", mSortOrder);
- mFilterApps = savedInstanceState.getInt("filterApps", mFilterApps);
- String tmp = savedInstanceState.getString("defaultTabTag");
- if (tmp != null) defaultTabTag = tmp;
- mShowBackground = savedInstanceState.getBoolean("showBackground", false);
+ mSortOrder = savedInstanceState.getInt(EXTRA_SORT_ORDER, mSortOrder);
+ int tmp = savedInstanceState.getInt(EXTRA_DEFAULT_LIST_TYPE, -1);
+ if (tmp != -1) defaultListType = tmp;
+ mShowBackground = savedInstanceState.getBoolean(EXTRA_SHOW_BACKGROUND, false);
}
-
- mDefaultTab = defaultTabTag;
-
- mDataFileStats = new StatFs("/data");
- mSDCardFileStats = new StatFs(Environment.getExternalStorageDirectory().toString());
+
+ mDefaultListType = defaultListType;
+
+ final Intent containerIntent = new Intent().setComponent(
+ StorageMeasurement.DEFAULT_CONTAINER_COMPONENT);
+ getActivity().bindService(containerIntent, mContainerConnection, Context.BIND_AUTO_CREATE);
mInvalidSizeStr = getActivity().getText(R.string.invalid_size_value);
mComputingSizeStr = getActivity().getText(R.string.computing_size);
+
+ TabInfo tab = new TabInfo(this, mApplicationsState,
+ getActivity().getString(R.string.filter_apps_third_party),
+ LIST_TYPE_DOWNLOADED, this, savedInstanceState);
+ mTabs.add(tab);
+
+ if (!Environment.isExternalStorageEmulated()) {
+ tab = new TabInfo(this, mApplicationsState,
+ getActivity().getString(R.string.filter_apps_onsdcard),
+ LIST_TYPE_SDCARD, this, savedInstanceState);
+ mTabs.add(tab);
+ }
+
+ tab = new TabInfo(this, mApplicationsState,
+ getActivity().getString(R.string.filter_apps_running),
+ LIST_TYPE_RUNNING, this, savedInstanceState);
+ mTabs.add(tab);
+
+ tab = new TabInfo(this, mApplicationsState,
+ getActivity().getString(R.string.filter_apps_all),
+ LIST_TYPE_ALL, this, savedInstanceState);
+ mTabs.add(tab);
}
@@ -596,61 +872,41 @@ public class ManageApplications extends Fragment implements
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// initialize the inflater
mInflater = inflater;
- mRootView = inflater.inflate(R.layout.manage_applications, null);
- mLoadingContainer = mRootView.findViewById(R.id.loading_container);
- mListContainer = mRootView.findViewById(R.id.list_container);
- // Create adapter and list view here
- ListView lv = (ListView) mListContainer.findViewById(android.R.id.list);
- View emptyView = mListContainer.findViewById(com.android.internal.R.id.empty);
- if (emptyView != null) {
- lv.setEmptyView(emptyView);
- }
- lv.setOnItemClickListener(this);
- lv.setSaveEnabled(true);
- lv.setItemsCanFocus(true);
- lv.setOnItemClickListener(this);
- lv.setTextFilterEnabled(true);
- mListView = lv;
- lv.setRecyclerListener(mApplicationsAdapter);
- mListView.setAdapter(mApplicationsAdapter);
- mColorBar = (LinearColorBar)mListContainer.findViewById(R.id.storage_color_bar);
- mStorageChartLabel = (TextView)mListContainer.findViewById(R.id.storageChartLabel);
- mUsedStorageText = (TextView)mListContainer.findViewById(R.id.usedStorageText);
- mFreeStorageText = (TextView)mListContainer.findViewById(R.id.freeStorageText);
- mRunningProcessesView = (RunningProcessesView)mRootView.findViewById(
- R.id.running_processes);
-
- mCreatedRunning = mResumedRunning = false;
- mCurView = VIEW_NOTHING;
-
- mTabHost = (TabHost) mInflater.inflate(R.layout.manage_apps_tab_content, container, false);
- mTabHost.setup();
- final TabHost tabHost = mTabHost;
- tabHost.addTab(tabHost.newTabSpec(TAB_DOWNLOADED)
- .setIndicator(getActivity().getString(R.string.filter_apps_third_party),
- getActivity().getResources().getDrawable(R.drawable.ic_tab_download))
- .setContent(this));
- if (!Environment.isExternalStorageEmulated()) {
- tabHost.addTab(tabHost.newTabSpec(TAB_SDCARD)
- .setIndicator(getActivity().getString(R.string.filter_apps_onsdcard),
- getActivity().getResources().getDrawable(R.drawable.ic_tab_sdcard))
- .setContent(this));
- }
- tabHost.addTab(tabHost.newTabSpec(TAB_RUNNING)
- .setIndicator(getActivity().getString(R.string.filter_apps_running),
- getActivity().getResources().getDrawable(R.drawable.ic_tab_running))
- .setContent(this));
- tabHost.addTab(tabHost.newTabSpec(TAB_ALL)
- .setIndicator(getActivity().getString(R.string.filter_apps_all),
- getActivity().getResources().getDrawable(R.drawable.ic_tab_all))
- .setContent(this));
- tabHost.setCurrentTabByTag(mDefaultTab);
- tabHost.setOnTabChangedListener(this);
-
- // adjust padding around tabwidget as needed
- prepareCustomPreferencesList(container, mTabHost, mListView, false);
-
- return mTabHost;
+
+ View rootView = mInflater.inflate(R.layout.manage_applications_content,
+ container, false);
+ mContentContainer = container;
+ mRootView = rootView;
+
+ mViewPager = (ViewPager) rootView.findViewById(R.id.pager);
+ MyPagerAdapter adapter = new MyPagerAdapter();
+ mViewPager.setAdapter(adapter);
+ mViewPager.setOnPageChangeListener(adapter);
+ PagerTabStrip tabs = (PagerTabStrip) rootView.findViewById(R.id.tabs);
+ tabs.setTabIndicatorColorResource(android.R.color.holo_blue_light);
+
+ // We have to do this now because PreferenceFrameLayout looks at it
+ // only when the view is added.
+ if (container instanceof PreferenceFrameLayout) {
+ ((PreferenceFrameLayout.LayoutParams) rootView.getLayoutParams()).removeBorders = true;
+ }
+
+ if (savedInstanceState != null && savedInstanceState.getBoolean(EXTRA_RESET_DIALOG)) {
+ buildResetDialog();
+ }
+
+ if (savedInstanceState == null) {
+ // First time init: make sure view pager is showing the correct tab.
+ for (int i = 0; i < mTabs.size(); i++) {
+ TabInfo tab = mTabs.get(i);
+ if (tab.mListType == mDefaultListType) {
+ mViewPager.setCurrentItem(i);
+ break;
+ }
+ }
+ }
+
+ return rootView;
}
@Override
@@ -662,32 +918,50 @@ public class ManageApplications extends Fragment implements
public void onResume() {
super.onResume();
mActivityResumed = true;
- showCurrentTab();
+ updateCurrentTab(mViewPager.getCurrentItem());
updateOptionsMenu();
- mTabHost.getTabWidget().setEnabled(true);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
- outState.putInt("sortOrder", mSortOrder);
- outState.putInt("filterApps", mFilterApps);
- if (mDefaultTab != null) {
- outState.putString("defautTabTag", mDefaultTab);
+ outState.putInt(EXTRA_SORT_ORDER, mSortOrder);
+ if (mDefaultListType != -1) {
+ outState.putInt(EXTRA_DEFAULT_LIST_TYPE, mDefaultListType);
+ }
+ outState.putBoolean(EXTRA_SHOW_BACKGROUND, mShowBackground);
+ if (mResetDialog != null) {
+ outState.putBoolean(EXTRA_RESET_DIALOG, true);
}
- outState.putBoolean("showBackground", mShowBackground);
}
@Override
public void onPause() {
super.onPause();
mActivityResumed = false;
- mApplicationsAdapter.pause();
- if (mResumedRunning) {
- mRunningProcessesView.doPause();
- mResumedRunning = false;
+ for (int i=0; i<mTabs.size(); i++) {
+ mTabs.get(i).pause();
+ }
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (mResetDialog != null) {
+ mResetDialog.dismiss();
+ mResetDialog = null;
+ }
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+
+ // We are going to keep the tab data structures around, but they
+ // are no longer attached to their view hierarchy.
+ for (int i=0; i<mTabs.size(); i++) {
+ mTabs.get(i).detachView();
}
- mTabHost.getTabWidget().setEnabled(false);
}
@Override
@@ -696,7 +970,17 @@ public class ManageApplications extends Fragment implements
mApplicationsState.requestSize(mCurrentPkgName);
}
}
-
+
+ TabInfo tabForType(int type) {
+ for (int i = 0; i < mTabs.size(); i++) {
+ TabInfo tab = mTabs.get(i);
+ if (tab.mListType == type) {
+ return tab;
+ }
+ }
+ return null;
+ }
+
// utility method used to start sub activity
private void startApplicationDetailsActivity() {
// start new fragment to display extended information
@@ -724,6 +1008,8 @@ public class ManageApplications extends Fragment implements
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
menu.add(0, SHOW_BACKGROUND_PROCESSES, 3, R.string.show_background_processes)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ menu.add(0, RESET_APP_PREFERENCES, 4, R.string.reset_app_preferences)
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
updateOptionsMenu();
}
@@ -736,7 +1022,13 @@ public class ManageApplications extends Fragment implements
public void onDestroyOptionsMenu() {
mOptionsMenu = null;
}
-
+
+ @Override
+ public void onDestroy() {
+ getActivity().unbindService(mContainerConnection);
+ super.onDestroy();
+ }
+
void updateOptionsMenu() {
if (mOptionsMenu == null) {
return;
@@ -746,18 +1038,112 @@ public class ManageApplications extends Fragment implements
* The running processes screen doesn't use the mApplicationsAdapter
* so bringing up this menu in that case doesn't make any sense.
*/
- if (mCurView == VIEW_RUNNING) {
- boolean showingBackground = mRunningProcessesView != null
- ? mRunningProcessesView.mAdapter.getShowBackground() : false;
+ if (mCurTab != null && mCurTab.mListType == LIST_TYPE_RUNNING) {
+ TabInfo tab = tabForType(LIST_TYPE_RUNNING);
+ boolean showingBackground = tab != null && tab.mRunningProcessesView != null
+ ? tab.mRunningProcessesView.mAdapter.getShowBackground() : false;
mOptionsMenu.findItem(SORT_ORDER_ALPHA).setVisible(false);
mOptionsMenu.findItem(SORT_ORDER_SIZE).setVisible(false);
mOptionsMenu.findItem(SHOW_RUNNING_SERVICES).setVisible(showingBackground);
mOptionsMenu.findItem(SHOW_BACKGROUND_PROCESSES).setVisible(!showingBackground);
+ mOptionsMenu.findItem(RESET_APP_PREFERENCES).setVisible(false);
} else {
mOptionsMenu.findItem(SORT_ORDER_ALPHA).setVisible(mSortOrder != SORT_ORDER_ALPHA);
mOptionsMenu.findItem(SORT_ORDER_SIZE).setVisible(mSortOrder != SORT_ORDER_SIZE);
mOptionsMenu.findItem(SHOW_RUNNING_SERVICES).setVisible(false);
mOptionsMenu.findItem(SHOW_BACKGROUND_PROCESSES).setVisible(false);
+ mOptionsMenu.findItem(RESET_APP_PREFERENCES).setVisible(true);
+ }
+ }
+
+ void buildResetDialog() {
+ if (mResetDialog == null) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setTitle(R.string.reset_app_preferences_title);
+ builder.setMessage(R.string.reset_app_preferences_desc);
+ builder.setPositiveButton(R.string.reset_app_preferences_button, this);
+ builder.setNegativeButton(R.string.cancel, null);
+ mResetDialog = builder.show();
+ mResetDialog.setOnDismissListener(this);
+ }
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ if (mResetDialog == dialog) {
+ mResetDialog = null;
+ }
+ }
+
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (mResetDialog == dialog) {
+ final PackageManager pm = getActivity().getPackageManager();
+ final INotificationManager nm = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ final NetworkPolicyManager npm = NetworkPolicyManager.from(getActivity());
+ final Handler handler = new Handler(getActivity().getMainLooper());
+ (new AsyncTask<Void, Void, Void>() {
+ @Override protected Void doInBackground(Void... params) {
+ List<ApplicationInfo> apps = pm.getInstalledApplications(
+ PackageManager.GET_DISABLED_COMPONENTS);
+ for (int i=0; i<apps.size(); i++) {
+ ApplicationInfo app = apps.get(i);
+ try {
+ if (DEBUG) Log.v(TAG, "Enabling notifications: " + app.packageName);
+ nm.setNotificationsEnabledForPackage(app.packageName, true);
+ } catch (android.os.RemoteException ex) {
+ }
+ if (DEBUG) Log.v(TAG, "Clearing preferred: " + app.packageName);
+ pm.clearPackagePreferredActivities(app.packageName);
+ if (!app.enabled) {
+ if (DEBUG) Log.v(TAG, "Enabling app: " + app.packageName);
+ if (pm.getApplicationEnabledSetting(app.packageName)
+ == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ pm.setApplicationEnabledSetting(app.packageName,
+ PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
+ PackageManager.DONT_KILL_APP);
+ }
+ }
+ }
+ // We should have cleared all of the preferred apps above;
+ // just in case some may be lingering, retrieve whatever is
+ // still set and remove it.
+ ArrayList<IntentFilter> filters = new ArrayList<IntentFilter>();
+ ArrayList<ComponentName> prefActivities = new ArrayList<ComponentName>();
+ pm.getPreferredActivities(filters, prefActivities, null);
+ for (int i=0; i<prefActivities.size(); i++) {
+ if (DEBUG) Log.v(TAG, "Clearing preferred: "
+ + prefActivities.get(i).getPackageName());
+ pm.clearPackagePreferredActivities(prefActivities.get(i).getPackageName());
+ }
+ final int[] restrictedAppIds = npm.getAppsWithPolicy(
+ POLICY_REJECT_METERED_BACKGROUND);
+ for (int i : restrictedAppIds) {
+ if (DEBUG) Log.v(TAG, "Clearing data policy: " + i);
+ npm.setAppPolicy(i, POLICY_NONE);
+ }
+ handler.post(new Runnable() {
+ @Override public void run() {
+ if (DEBUG) Log.v(TAG, "Done clearing");
+ if (getActivity() != null && mActivityResumed) {
+ if (DEBUG) Log.v(TAG, "Updating UI!");
+ for (int i=0; i<mTabs.size(); i++) {
+ TabInfo tab = mTabs.get(i);
+ if (tab.mApplications != null) {
+ tab.mApplications.pause();
+ }
+ }
+ if (mCurTab != null) {
+ mCurTab.resume(mSortOrder);
+ }
+ }
+ }
+ });
+ return null;
+ }
+ }).execute();
}
}
@@ -766,200 +1152,78 @@ public class ManageApplications extends Fragment implements
int menuId = item.getItemId();
if ((menuId == SORT_ORDER_ALPHA) || (menuId == SORT_ORDER_SIZE)) {
mSortOrder = menuId;
- if (mCurView != VIEW_RUNNING) {
- mApplicationsAdapter.rebuild(mFilterApps, mSortOrder);
+ if (mCurTab != null && mCurTab.mApplications != null) {
+ mCurTab.mApplications.rebuild(mSortOrder);
}
} else if (menuId == SHOW_RUNNING_SERVICES) {
mShowBackground = false;
- mRunningProcessesView.mAdapter.setShowBackground(false);
+ if (mCurTab != null && mCurTab.mRunningProcessesView != null) {
+ mCurTab.mRunningProcessesView.mAdapter.setShowBackground(false);
+ }
} else if (menuId == SHOW_BACKGROUND_PROCESSES) {
mShowBackground = true;
- mRunningProcessesView.mAdapter.setShowBackground(true);
+ if (mCurTab != null && mCurTab.mRunningProcessesView != null) {
+ mCurTab.mRunningProcessesView.mAdapter.setShowBackground(true);
+ }
+ } else if (menuId == RESET_APP_PREFERENCES) {
+ buildResetDialog();
+ } else {
+ // Handle the home button
+ return false;
}
updateOptionsMenu();
return true;
}
- public void onItemClick(AdapterView<?> parent, View view, int position,
+ public void onItemClick(TabInfo tab, AdapterView<?> parent, View view, int position,
long id) {
- ApplicationsState.AppEntry entry = mApplicationsAdapter.getAppEntry(position);
- mCurrentPkgName = entry.info.packageName;
- startApplicationDetailsActivity();
- }
-
- public View createTabContent(String tag) {
- return mRootView;
- }
-
- static final int VIEW_NOTHING = 0;
- static final int VIEW_LIST = 1;
- static final int VIEW_RUNNING = 2;
-
- void updateStorageUsage() {
- if (mCurView == VIEW_RUNNING) {
- return;
+ if (tab.mApplications != null && tab.mApplications.getCount() > position) {
+ ApplicationsState.AppEntry entry = tab.mApplications.getAppEntry(position);
+ mCurrentPkgName = entry.info.packageName;
+ startApplicationDetailsActivity();
}
+ }
- long freeStorage = 0;
- long appStorage = 0;
- long totalStorage = 0;
- CharSequence newLabel = null;
+ public void updateCurrentTab(int position) {
+ TabInfo tab = mTabs.get(position);
+ mCurTab = tab;
- if (mFilterApps == FILTER_APPS_SDCARD) {
- if (mLastShowedInternalStorage) {
- mLastShowedInternalStorage = false;
- }
- newLabel = getActivity().getText(R.string.sd_card_storage);
- mSDCardFileStats.restat(Environment.getExternalStorageDirectory().toString());
- try {
- totalStorage = (long)mSDCardFileStats.getBlockCount() *
- mSDCardFileStats.getBlockSize();
- freeStorage = (long) mSDCardFileStats.getAvailableBlocks() *
- mSDCardFileStats.getBlockSize();
- } catch (IllegalArgumentException e) {
- // use the old value of mFreeMem
- }
- final int N = mApplicationsAdapter.getCount();
- for (int i=0; i<N; i++) {
- ApplicationsState.AppEntry ae = mApplicationsAdapter.getAppEntry(i);
- appStorage += ae.externalCodeSize + ae.externalDataSize;
- }
+ // Put things in the correct paused/resumed state.
+ if (mActivityResumed) {
+ mCurTab.build(mInflater, mContentContainer, mRootView);
+ mCurTab.resume(mSortOrder);
} else {
- if (!mLastShowedInternalStorage) {
- mLastShowedInternalStorage = true;
- }
- newLabel = getActivity().getText(R.string.internal_storage);
- mDataFileStats.restat("/data");
- try {
- totalStorage = (long)mDataFileStats.getBlockCount() *
- mDataFileStats.getBlockSize();
- freeStorage = (long) mDataFileStats.getAvailableBlocks() *
- mDataFileStats.getBlockSize();
- } catch (IllegalArgumentException e) {
- }
- final boolean emulatedStorage = Environment.isExternalStorageEmulated();
- final int N = mApplicationsAdapter.getCount();
- for (int i=0; i<N; i++) {
- ApplicationsState.AppEntry ae = mApplicationsAdapter.getAppEntry(i);
- appStorage += ae.codeSize + ae.dataSize;
- if (emulatedStorage) {
- appStorage += ae.externalCodeSize + ae.externalDataSize;
- }
- }
- freeStorage += mApplicationsState.sumCacheSizes();
- }
- if (newLabel != null) {
- mStorageChartLabel.setText(newLabel);
+ mCurTab.pause();
}
- if (totalStorage > 0) {
- mColorBar.setRatios((totalStorage-freeStorage-appStorage)/(float)totalStorage,
- appStorage/(float)totalStorage, freeStorage/(float)totalStorage);
- long usedStorage = totalStorage - freeStorage;
- if (mLastUsedStorage != usedStorage) {
- mLastUsedStorage = usedStorage;
- String sizeStr = Formatter.formatShortFileSize(getActivity(), usedStorage);
- mUsedStorageText.setText(getActivity().getResources().getString(
- R.string.service_foreground_processes, sizeStr));
- }
- if (mLastFreeStorage != freeStorage) {
- mLastFreeStorage = freeStorage;
- String sizeStr = Formatter.formatShortFileSize(getActivity(), freeStorage);
- mFreeStorageText.setText(getActivity().getResources().getString(
- R.string.service_background_processes, sizeStr));
- }
- } else {
- mColorBar.setRatios(0, 0, 0);
- if (mLastUsedStorage != -1) {
- mLastUsedStorage = -1;
- mUsedStorageText.setText("");
- }
- if (mLastFreeStorage != -1) {
- mLastFreeStorage = -1;
- mFreeStorageText.setText("");
+ for (int i=0; i<mTabs.size(); i++) {
+ TabInfo t = mTabs.get(i);
+ if (t != mCurTab) {
+ t.pause();
}
}
- }
- private void selectView(int which) {
- if (which == VIEW_LIST) {
- if (mResumedRunning) {
- mRunningProcessesView.doPause();
- mResumedRunning = false;
- }
- if (mCurView != which) {
- mRunningProcessesView.setVisibility(View.GONE);
- mListContainer.setVisibility(View.VISIBLE);
- mLoadingContainer.setVisibility(View.GONE);
- }
- if (mActivityResumed) {
- mApplicationsAdapter.resume(mFilterApps, mSortOrder);
- }
- } else if (which == VIEW_RUNNING) {
- if (!mCreatedRunning) {
- mRunningProcessesView.doCreate(null);
- mRunningProcessesView.mAdapter.setShowBackground(mShowBackground);
- mCreatedRunning = true;
- }
- boolean haveData = true;
- if (mActivityResumed && !mResumedRunning) {
- haveData = mRunningProcessesView.doResume(this, mRunningProcessesAvail);
- mResumedRunning = true;
- }
- mApplicationsAdapter.pause();
- if (mCurView != which) {
- if (haveData) {
- mRunningProcessesView.setVisibility(View.VISIBLE);
- } else {
- mLoadingContainer.setVisibility(View.VISIBLE);
- }
- mListContainer.setVisibility(View.GONE);
- }
- }
- mCurView = which;
+ mCurTab.updateStorageUsage();
+ updateOptionsMenu();
final Activity host = getActivity();
if (host != null) {
host.invalidateOptionsMenu();
}
}
- void handleRunningProcessesAvail() {
- if (mCurView == VIEW_RUNNING) {
- mLoadingContainer.startAnimation(AnimationUtils.loadAnimation(
- getActivity(), android.R.anim.fade_out));
- mRunningProcessesView.startAnimation(AnimationUtils.loadAnimation(
- getActivity(), android.R.anim.fade_in));
- mRunningProcessesView.setVisibility(View.VISIBLE);
- mLoadingContainer.setVisibility(View.GONE);
- }
- }
+ private volatile IMediaContainerService mContainerService;
- public void showCurrentTab() {
- String tabId = mDefaultTab = mTabHost.getCurrentTabTag();
- int newOption;
- if (TAB_DOWNLOADED.equalsIgnoreCase(tabId)) {
- newOption = FILTER_APPS_THIRD_PARTY;
- } else if (TAB_ALL.equalsIgnoreCase(tabId)) {
- newOption = FILTER_APPS_ALL;
- } else if (TAB_SDCARD.equalsIgnoreCase(tabId)) {
- newOption = FILTER_APPS_SDCARD;
- } else if (TAB_RUNNING.equalsIgnoreCase(tabId)) {
- ((InputMethodManager)getActivity().getSystemService(Context.INPUT_METHOD_SERVICE))
- .hideSoftInputFromWindow(
- getActivity().getWindow().getDecorView().getWindowToken(), 0);
- selectView(VIEW_RUNNING);
- return;
- } else {
- // Invalid option. Do nothing
- return;
+ private final ServiceConnection mContainerConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mContainerService = IMediaContainerService.Stub.asInterface(service);
+ for (int i=0; i<mTabs.size(); i++) {
+ mTabs.get(i).setContainerService(mContainerService);
+ }
}
-
- mFilterApps = newOption;
- selectView(VIEW_LIST);
- updateStorageUsage();
- updateOptionsMenu();
- }
- public void onTabChanged(String tabId) {
- showCurrentTab();
- }
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mContainerService = null;
+ }
+ };
}
diff --git a/src/com/android/settings/applications/RunningServiceDetails.java b/src/com/android/settings/applications/RunningServiceDetails.java
index 08cb0e0..f91abd6 100644
--- a/src/com/android/settings/applications/RunningServiceDetails.java
+++ b/src/com/android/settings/applications/RunningServiceDetails.java
@@ -553,6 +553,7 @@ public class RunningServiceDetails extends Fragment
@Override
public void onRefreshUi(int what) {
+ if (getActivity() == null) return;
switch (what) {
case REFRESH_TIME:
updateTimes();
diff --git a/src/com/android/settings/bluetooth/BluetoothEventManager.java b/src/com/android/settings/bluetooth/BluetoothEventManager.java
index 9140b25..a6d9bcf 100644
--- a/src/com/android/settings/bluetooth/BluetoothEventManager.java
+++ b/src/com/android/settings/bluetooth/BluetoothEventManager.java
@@ -139,8 +139,6 @@ final class BluetoothEventManager {
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- Log.v(TAG, "Received " + intent.getAction());
-
String action = intent.getAction();
BluetoothDevice device = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
diff --git a/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java b/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java
index 4996858..b80e42a 100644
--- a/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java
+++ b/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java
@@ -30,10 +30,14 @@ import android.text.Editable;
import android.text.InputFilter;
import android.text.TextWatcher;
import android.util.Log;
+import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.WindowManager;
+import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
+import android.widget.TextView;
import com.android.settings.R;
@@ -94,19 +98,23 @@ public final class BluetoothNameDialogFragment extends DialogFragment implements
.setPositiveButton(R.string.bluetooth_rename_button,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
- if (mLocalAdapter != null) {
- String deviceName = mDeviceNameView.getText().toString();
- Log.d(TAG, "Setting device name to " + deviceName);
- mLocalAdapter.setName(deviceName);
- }
+ String deviceName = mDeviceNameView.getText().toString();
+ setDeviceName(deviceName);
}
})
.setNegativeButton(android.R.string.cancel, null)
.create();
+ mAlertDialog.getWindow().setSoftInputMode(
+ WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
return mAlertDialog;
}
+ private void setDeviceName(String deviceName) {
+ Log.d(TAG, "Setting device name to " + deviceName);
+ mLocalAdapter.setName(deviceName);
+ }
+
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putString(KEY_NAME, mDeviceNameView.getText().toString());
@@ -123,6 +131,18 @@ public final class BluetoothNameDialogFragment extends DialogFragment implements
});
mDeviceNameView.setText(deviceName); // set initial value before adding listener
mDeviceNameView.addTextChangedListener(this);
+ mDeviceNameView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ if (actionId == EditorInfo.IME_ACTION_DONE) {
+ setDeviceName(v.getText().toString());
+ mAlertDialog.dismiss();
+ return true; // action handled
+ } else {
+ return false; // not handled
+ }
+ }
+ });
return view;
}
diff --git a/src/com/android/settings/bluetooth/BluetoothSettings.java b/src/com/android/settings/bluetooth/BluetoothSettings.java
index 7c8cb6e..d30e428 100755
--- a/src/com/android/settings/bluetooth/BluetoothSettings.java
+++ b/src/com/android/settings/bluetooth/BluetoothSettings.java
@@ -182,6 +182,7 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment {
.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);
}
@Override
@@ -378,4 +379,9 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment {
preference.setOnSettingsClickListener(mDeviceProfilesListener);
}
}
+
+ @Override
+ protected int getHelpResource() {
+ return R.string.help_url_bluetooth;
+ }
}
diff --git a/src/com/android/settings/bluetooth/RequestPermissionActivity.java b/src/com/android/settings/bluetooth/RequestPermissionActivity.java
index 07a7316..529312d 100644
--- a/src/com/android/settings/bluetooth/RequestPermissionActivity.java
+++ b/src/com/android/settings/bluetooth/RequestPermissionActivity.java
@@ -173,6 +173,11 @@ public class RequestPermissionActivity extends Activity implements
mDialog = builder.create();
mDialog.show();
+
+ if (getResources().getBoolean(R.bool.auto_confirm_bluetooth_activation_dialog) == true) {
+ // dismiss dialog immediately if settings say so
+ onClick(null, DialogInterface.BUTTON_POSITIVE);
+ }
}
@Override
diff --git a/src/com/android/settings/bluetooth/RequestPermissionHelperActivity.java b/src/com/android/settings/bluetooth/RequestPermissionHelperActivity.java
index 9b5946b..5c4b828 100644
--- a/src/com/android/settings/bluetooth/RequestPermissionHelperActivity.java
+++ b/src/com/android/settings/bluetooth/RequestPermissionHelperActivity.java
@@ -62,6 +62,12 @@ public class RequestPermissionHelperActivity extends AlertActivity implements
}
createDialog();
+
+ if (getResources().getBoolean(R.bool.auto_confirm_bluetooth_activation_dialog) == true) {
+ // dismiss dialog immediately if settings say so
+ onClick(null, BUTTON_POSITIVE);
+ dismiss();
+ }
}
void createDialog() {
diff --git a/src/com/android/settings/deviceinfo/Memory.java b/src/com/android/settings/deviceinfo/Memory.java
index 728e558..cb344bf 100644
--- a/src/com/android/settings/deviceinfo/Memory.java
+++ b/src/com/android/settings/deviceinfo/Memory.java
@@ -44,6 +44,7 @@ import android.widget.Toast;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.Utils;
public class Memory extends SettingsPreferenceFragment {
private static final String TAG = "MemorySettings";
@@ -51,8 +52,6 @@ public class Memory extends SettingsPreferenceFragment {
private static final int DLG_CONFIRM_UNMOUNT = 1;
private static final int DLG_ERROR_UNMOUNT = 2;
- private static final int MENU_ID_USB = Menu.FIRST;
-
private Resources mResources;
// The mountToggle Preference that has last been clicked.
@@ -92,9 +91,6 @@ public class Memory extends SettingsPreferenceFragment {
}
StorageVolume[] storageVolumes = mStorageManager.getVolumeList();
- // mass storage is enabled if primary volume supports it
- boolean massStorageEnabled = (storageVolumes.length > 0
- && storageVolumes[0].allowMassStorage());
int length = storageVolumes.length;
mStorageVolumePreferenceCategories = new StorageVolumePreferenceCategory[length];
for (int i = 0; i < length; i++) {
@@ -106,8 +102,13 @@ public class Memory extends SettingsPreferenceFragment {
mStorageVolumePreferenceCategories[i].init();
}
- // only show options menu if we are not using the legacy USB mass storage support
- setHasOptionsMenu(!massStorageEnabled);
+ setHasOptionsMenu(true);
+ }
+
+ private boolean isMassStorageEnabled() {
+ // mass storage is enabled if primary volume supports it
+ final StorageVolume[] storageVolumes = mStorageManager.getVolumeList();
+ return (storageVolumes.length > 0 && storageVolumes[0].allowMassStorage());
}
@Override
@@ -163,15 +164,19 @@ public class Memory extends SettingsPreferenceFragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- menu.add(Menu.NONE, MENU_ID_USB, 0, R.string.storage_menu_usb)
- //.setIcon(com.android.internal.R.drawable.stat_sys_data_usb)
- .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+ inflater.inflate(R.menu.storage, menu);
+ }
+
+ @Override
+ public void onPrepareOptionsMenu(Menu menu) {
+ final MenuItem usb = menu.findItem(R.id.storage_usb);
+ usb.setVisible(!isMassStorageEnabled());
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case MENU_ID_USB:
+ case R.id.storage_usb:
if (getActivity() instanceof PreferenceActivity) {
((PreferenceActivity) getActivity()).startPreferencePanel(
UsbSettings.class.getCanonicalName(),
@@ -204,7 +209,10 @@ public class Memory extends SettingsPreferenceFragment {
StorageVolumePreferenceCategory svpc = mStorageVolumePreferenceCategories[i];
Intent intent = svpc.intentForClick(preference);
if (intent != null) {
- startActivity(intent);
+ // Don't go across app boundary if monkey is running
+ if (!Utils.isMonkeyRunning()) {
+ startActivity(intent);
+ }
return true;
}
diff --git a/src/com/android/settings/deviceinfo/Status.java b/src/com/android/settings/deviceinfo/Status.java
index 434dfe7..d4fea37 100644
--- a/src/com/android/settings/deviceinfo/Status.java
+++ b/src/com/android/settings/deviceinfo/Status.java
@@ -329,7 +329,8 @@ public class Status extends PreferenceActivity {
private void updateNetworkType() {
// Whether EDGE, UMTS, etc...
- setSummary(KEY_NETWORK_TYPE, TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE, sUnknown);
+ setSummaryText(KEY_NETWORK_TYPE, mTelephonyManager.getNetworkTypeName() +
+ ":" + mTelephonyManager.getNetworkType());
}
private void updateDataState() {
diff --git a/src/com/android/settings/deviceinfo/StorageMeasurement.java b/src/com/android/settings/deviceinfo/StorageMeasurement.java
index b4004e9..2792d09 100644
--- a/src/com/android/settings/deviceinfo/StorageMeasurement.java
+++ b/src/com/android/settings/deviceinfo/StorageMeasurement.java
@@ -31,7 +31,7 @@ import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
-import android.os.StatFs;
+import android.os.RemoteException;
import android.os.storage.StorageVolume;
import android.util.Log;
@@ -53,7 +53,7 @@ import java.util.concurrent.ConcurrentHashMap;
* know about by just keeping an array of measurement types of the following
* properties:
*
- * Filesystem stats (using StatFs)
+ * Filesystem stats (using DefaultContainerService)
* Directory measurements (using DefaultContainerService.measureDir)
* Application measurements (using PackageManager)
*
@@ -81,7 +81,7 @@ public class StorageMeasurement {
private static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer";
- private static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
+ public static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
DEFAULT_CONTAINER_PACKAGE, "com.android.defcontainer.DefaultContainerService");
private final MeasurementHandler mHandler;
@@ -258,8 +258,6 @@ public class StorageMeasurement {
return;
}
- measureApproximateStorage();
-
synchronized (mLock) {
if (mBound) {
removeMessages(MSG_DISCONNECT);
@@ -274,6 +272,7 @@ public class StorageMeasurement {
}
case MSG_CONNECTED: {
IMediaContainerService imcs = (IMediaContainerService) msg.obj;
+ measureApproximateStorage(imcs);
measureExactStorage(imcs);
break;
}
@@ -367,15 +366,16 @@ public class StorageMeasurement {
sendEmptyMessage(MSG_COMPLETED);
}
- private void measureApproximateStorage() {
- final StatFs stat = new StatFs(mStorageVolume != null
- ? mStorageVolume.getPath() : Environment.getDataDirectory().getPath());
- final long blockSize = stat.getBlockSize();
- final long totalBlocks = stat.getBlockCount();
- final long availableBlocks = stat.getAvailableBlocks();
-
- mTotalSize = totalBlocks * blockSize;
- mAvailSize = availableBlocks * blockSize;
+ private void measureApproximateStorage(IMediaContainerService imcs) {
+ final String path = mStorageVolume != null ? mStorageVolume.getPath()
+ : Environment.getDataDirectory().getPath();
+ try {
+ final long[] stats = imcs.getFileSystemStats(path);
+ mTotalSize = stats[0];
+ mAvailSize = stats[1];
+ } catch (RemoteException e) {
+ Log.w(TAG, "Problem in container service", e);
+ }
sendInternalApproximateUpdate();
}
diff --git a/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java b/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java
index 39a08d8..bbf1d01 100644
--- a/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java
+++ b/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java
@@ -162,7 +162,7 @@ public class StorageVolumePreferenceCategory extends PreferenceCategory implemen
mResources = resources;
mStorageVolume = storageVolume;
mStorageManager = storageManager;
- setTitle(storageVolume != null ? storageVolume.getDescription()
+ setTitle(storageVolume != null ? storageVolume.getDescription(context)
: resources.getText(R.string.internal_storage));
mMeasurement = StorageMeasurement.getInstance(context, storageVolume, isPrimary);
mMeasurement.setReceiver(this);
diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
index b813ec6..fa2b02d 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
@@ -21,6 +21,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.SensorManager;
+import android.net.Uri;
import android.os.BatteryStats;
import android.os.BatteryStats.Uid;
import android.os.Bundle;
@@ -37,6 +38,7 @@ import android.preference.PreferenceFragment;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.telephony.SignalStrength;
+import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import android.view.Menu;
@@ -72,6 +74,7 @@ public class PowerUsageSummary extends PreferenceFragment implements Runnable {
private static final int MENU_STATS_TYPE = Menu.FIRST;
private static final int MENU_STATS_REFRESH = Menu.FIRST + 1;
+ private static final int MENU_HELP = Menu.FIRST + 2;
private static BatteryStatsImpl sStatsXfer;
@@ -318,6 +321,16 @@ public class PowerUsageSummary extends PreferenceFragment implements Runnable {
.setAlphabeticShortcut('r');
refresh.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM |
MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+
+ String helpUrl;
+ if (!TextUtils.isEmpty(helpUrl = getResources().getString(R.string.help_url_battery))) {
+ final MenuItem help = menu.add(0, MENU_HELP, 0, R.string.help_label);
+ Intent helpIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(helpUrl));
+ helpIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ help.setIntent(helpIntent);
+ help.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+ }
}
@Override
diff --git a/src/com/android/settings/inputmethod/CheckBoxAndSettingsPreference.java b/src/com/android/settings/inputmethod/CheckBoxAndSettingsPreference.java
index 952146d..28b8616 100644
--- a/src/com/android/settings/inputmethod/CheckBoxAndSettingsPreference.java
+++ b/src/com/android/settings/inputmethod/CheckBoxAndSettingsPreference.java
@@ -34,8 +34,7 @@ public class CheckBoxAndSettingsPreference extends CheckBoxPreference {
private SettingsPreferenceFragment mFragment;
private TextView mTitleText;
private TextView mSummaryText;
- private View mCheckBox;
- private ImageView mSetingsButton;
+ private ImageView mSettingsButton;
private Intent mSettingsIntent;
public CheckBoxAndSettingsPreference(Context context, AttributeSet attrs) {
@@ -47,28 +46,28 @@ public class CheckBoxAndSettingsPreference extends CheckBoxPreference {
@Override
protected void onBindView(View view) {
super.onBindView(view);
- mCheckBox = view.findViewById(R.id.inputmethod_pref);
- mCheckBox.setOnClickListener(
+ View textLayout = view.findViewById(R.id.inputmethod_pref);
+ textLayout.setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View arg0) {
onCheckBoxClicked();
}
});
- mSetingsButton = (ImageView)view.findViewById(R.id.inputmethod_settings);
+
+ mSettingsButton = (ImageView) view.findViewById(R.id.inputmethod_settings);
mTitleText = (TextView)view.findViewById(android.R.id.title);
mSummaryText = (TextView)view.findViewById(android.R.id.summary);
- mSetingsButton.setOnClickListener(
+ mSettingsButton.setOnClickListener(
new OnClickListener() {
@Override
- public void onClick(View arg0) {
- onSettingsButtonClicked(arg0);
+ public void onClick(View clickedView) {
+ onSettingsButtonClicked();
}
});
enableSettingsButton();
}
-
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
@@ -88,23 +87,23 @@ public class CheckBoxAndSettingsPreference extends CheckBoxPreference {
}
}
- protected void onSettingsButtonClicked(View arg0) {
+ protected void onSettingsButtonClicked() {
if (mFragment != null && mSettingsIntent != null) {
mFragment.startActivity(mSettingsIntent);
}
}
private void enableSettingsButton() {
- if (mSetingsButton != null) {
+ if (mSettingsButton != null) {
if (mSettingsIntent == null) {
- mSetingsButton.setVisibility(View.GONE);
+ mSettingsButton.setVisibility(View.GONE);
} else {
final boolean checked = isChecked();
- mSetingsButton.setEnabled(checked);
- mSetingsButton.setClickable(checked);
- mSetingsButton.setFocusable(checked);
+ mSettingsButton.setEnabled(checked);
+ mSettingsButton.setClickable(checked);
+ mSettingsButton.setFocusable(checked);
if (!checked) {
- mSetingsButton.setAlpha(DISABLED_ALPHA);
+ mSettingsButton.setAlpha(DISABLED_ALPHA);
}
}
}
diff --git a/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java b/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java
index 4454389..bc8b458 100644
--- a/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java
+++ b/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java
@@ -17,6 +17,7 @@
package com.android.settings.inputmethod;
import com.android.settings.R;
+import com.android.settings.Settings.KeyboardLayoutPickerActivity;
import com.android.settings.Settings.SpellCheckersSettingsActivity;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.Utils;
@@ -29,27 +30,30 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.database.ContentObserver;
+import android.hardware.input.InputManager;
+import android.hardware.input.KeyboardLayout;
import android.os.Bundle;
import android.os.Handler;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceCategory;
-import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.provider.Settings;
import android.provider.Settings.System;
import android.text.TextUtils;
+import android.view.InputDevice;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.Set;
+import java.util.TreeSet;
public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
- implements Preference.OnPreferenceChangeListener{
+ implements Preference.OnPreferenceChangeListener, InputManager.InputDeviceListener,
+ KeyboardLayoutDialogFragment.OnSetupKeyboardLayoutsListener {
private static final String KEY_PHONE_LANGUAGE = "phone_language";
private static final String KEY_CURRENT_INPUT_METHOD = "current_input_method";
@@ -68,17 +72,22 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
private int mDefaultInputMethodSelectorVisibility = 0;
private ListPreference mShowInputMethodSelectorPref;
+ private PreferenceCategory mKeyboardSettingsCategory;
+ private PreferenceCategory mHardKeyboardCategory;
+ private PreferenceCategory mGameControllerCategory;
private Preference mLanguagePref;
- private ArrayList<InputMethodPreference> mInputMethodPreferenceList =
+ private final ArrayList<InputMethodPreference> mInputMethodPreferenceList =
new ArrayList<InputMethodPreference>();
- private boolean mHaveHardKeyboard;
- private PreferenceCategory mHardKeyboardCategory;
+ private final ArrayList<PreferenceScreen> mHardKeyboardPreferenceList =
+ new ArrayList<PreferenceScreen>();
+ private InputManager mIm;
private InputMethodManager mImm;
private List<InputMethodInfo> mImis;
private boolean mIsOnlyImeSettings;
private Handler mHandler;
@SuppressWarnings("unused")
private SettingsObserver mSettingsObserver;
+ private Intent mIntentWaitingForResult;
@Override
public void onCreate(Bundle icicle) {
@@ -108,18 +117,58 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
new VoiceInputOutputSettings(this).onCreate();
- // Hard keyboard
- final Configuration config = getResources().getConfiguration();
- mHaveHardKeyboard = (config.keyboard == Configuration.KEYBOARD_QWERTY);
+ // Get references to dynamically constructed categories.
+ mHardKeyboardCategory = (PreferenceCategory)findPreference("hard_keyboard");
+ mKeyboardSettingsCategory = (PreferenceCategory)findPreference(
+ "keyboard_settings_category");
+ mGameControllerCategory = (PreferenceCategory)findPreference(
+ "game_controller_settings_category");
- // IME
+ // Filter out irrelevant features if invoked from IME settings button.
mIsOnlyImeSettings = Settings.ACTION_INPUT_METHOD_SETTINGS.equals(
getActivity().getIntent().getAction());
getActivity().getIntent().setAction(null);
+ if (mIsOnlyImeSettings) {
+ getPreferenceScreen().removeAll();
+ getPreferenceScreen().addPreference(mHardKeyboardCategory);
+ if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
+ getPreferenceScreen().addPreference(mShowInputMethodSelectorPref);
+ }
+ getPreferenceScreen().addPreference(mKeyboardSettingsCategory);
+ }
+
+ // Build IME preference category.
mImm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
mImis = mImm.getInputMethodList();
- createImePreferenceHierarchy((PreferenceGroup)findPreference("keyboard_settings_category"));
+ mKeyboardSettingsCategory.removeAll();
+ if (!mIsOnlyImeSettings) {
+ final PreferenceScreen currentIme = new PreferenceScreen(getActivity(), null);
+ currentIme.setKey(KEY_CURRENT_INPUT_METHOD);
+ currentIme.setTitle(getResources().getString(R.string.current_input_method));
+ mKeyboardSettingsCategory.addPreference(currentIme);
+ }
+
+ mInputMethodPreferenceList.clear();
+ final int N = (mImis == null ? 0 : mImis.size());
+ for (int i = 0; i < N; ++i) {
+ final InputMethodInfo imi = mImis.get(i);
+ final InputMethodPreference pref = getInputMethodPreference(imi, N);
+ mInputMethodPreferenceList.add(pref);
+ }
+
+ if (!mInputMethodPreferenceList.isEmpty()) {
+ Collections.sort(mInputMethodPreferenceList);
+ for (int i = 0; i < N; ++i) {
+ mKeyboardSettingsCategory.addPreference(mInputMethodPreferenceList.get(i));
+ }
+ }
+
+ // Build hard keyboard and game controller preference categories.
+ mIm = (InputManager)getActivity().getSystemService(Context.INPUT_SERVICE);
+ updateInputDevices();
+
+ // Spell Checker
final Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setClass(getActivity(), SpellCheckersSettingsActivity.class);
final SpellCheckersPreference scp = ((SpellCheckersPreference)findPreference(
@@ -143,7 +192,7 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
private void updateUserDictionaryPreference(Preference userDictionaryPreference) {
final Activity activity = getActivity();
- final Set<String> localeList = UserDictionaryList.getUserDictionaryLocalesList(activity);
+ final TreeSet<String> localeList = UserDictionaryList.getUserDictionaryLocalesSet(activity);
if (null == localeList) {
// The locale list is null if and only if the user dictionary service is
// not present or disabled. In this case we need to remove the preference.
@@ -153,12 +202,14 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
new Intent(UserDictionaryList.USER_DICTIONARY_SETTINGS_INTENT_ACTION);
userDictionaryPreference.setTitle(R.string.user_dict_single_settings_title);
userDictionaryPreference.setIntent(intent);
+ userDictionaryPreference.setFragment(
+ com.android.settings.UserDictionarySettings.class.getName());
// If the size of localeList is 0, we don't set the locale parameter in the
// extras. This will be interpreted by the UserDictionarySettings class as
// meaning "the current locale".
- // Note that with the current code for UserDictionaryList#getUserDictionaryLocalesList()
+ // Note that with the current code for UserDictionaryList#getUserDictionaryLocalesSet()
// the locale list always has at least one element, since it always includes the current
- // locale explicitly. @see UserDictionaryList.getUserDictionaryLocalesList().
+ // locale explicitly. @see UserDictionaryList.getUserDictionaryLocalesSet().
if (localeList.size() == 1) {
final String locale = (String)localeList.toArray()[0];
userDictionaryPreference.getExtras().putString("locale", locale);
@@ -172,6 +223,9 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
@Override
public void onResume() {
super.onResume();
+
+ mIm.registerInputDeviceListener(this, null);
+
if (!mIsOnlyImeSettings) {
if (mLanguagePref != null) {
Configuration conf = getResources().getConfiguration();
@@ -189,7 +243,7 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
}
// Hard keyboard
- if (mHaveHardKeyboard) {
+ if (!mHardKeyboardPreferenceList.isEmpty()) {
for (int i = 0; i < sHardKeyboardKeys.length; ++i) {
CheckBoxPreference chkPref = (CheckBoxPreference)
mHardKeyboardCategory.findPreference(sHardKeyboardKeys[i]);
@@ -198,6 +252,8 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
}
}
+ updateInputDevices();
+
// IME
InputMethodAndSubtypeUtil.loadInputMethodSubtypeList(
this, getContentResolver(), mImis, null);
@@ -207,11 +263,29 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
@Override
public void onPause() {
super.onPause();
+
+ mIm.unregisterInputDeviceListener(this);
+
if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
mShowInputMethodSelectorPref.setOnPreferenceChangeListener(null);
}
InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(
- this, getContentResolver(), mImis, mHaveHardKeyboard);
+ this, getContentResolver(), mImis, !mHardKeyboardPreferenceList.isEmpty());
+ }
+
+ @Override
+ public void onInputDeviceAdded(int deviceId) {
+ updateInputDevices();
+ }
+
+ @Override
+ public void onInputDeviceChanged(int deviceId) {
+ updateInputDevices();
+ }
+
+ @Override
+ public void onInputDeviceRemoved(int deviceId) {
+ updateInputDevices();
}
@Override
@@ -230,7 +304,7 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
}
} else if (preference instanceof CheckBoxPreference) {
final CheckBoxPreference chkPref = (CheckBoxPreference) preference;
- if (mHaveHardKeyboard) {
+ if (!mHardKeyboardPreferenceList.isEmpty()) {
for (int i = 0; i < sHardKeyboardKeys.length; ++i) {
if (chkPref == mHardKeyboardCategory.findPreference(sHardKeyboardKeys[i])) {
System.putInt(getContentResolver(), sSystemSettingNames[i],
@@ -239,6 +313,11 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
}
}
}
+ if (chkPref == mGameControllerCategory.findPreference("vibrate_input_devices")) {
+ System.putInt(getContentResolver(), Settings.System.VIBRATE_INPUT_DEVICES,
+ chkPref.isChecked() ? 1 : 0);
+ return true;
+ }
}
return super.onPreferenceTreeClick(preferenceScreen, preference);
}
@@ -315,47 +394,118 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
return pref;
}
- private void createImePreferenceHierarchy(PreferenceGroup root) {
- final Preference hardKeyPref = findPreference("hard_keyboard");
- if (mIsOnlyImeSettings) {
- getPreferenceScreen().removeAll();
- if (hardKeyPref != null && mHaveHardKeyboard) {
- getPreferenceScreen().addPreference(hardKeyPref);
- }
- if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
- getPreferenceScreen().addPreference(mShowInputMethodSelectorPref);
+ private void updateInputDevices() {
+ updateHardKeyboards();
+ updateGameControllers();
+ }
+
+ private void updateHardKeyboards() {
+ mHardKeyboardPreferenceList.clear();
+ if (getResources().getConfiguration().keyboard == Configuration.KEYBOARD_QWERTY) {
+ final int[] devices = InputDevice.getDeviceIds();
+ for (int i = 0; i < devices.length; i++) {
+ InputDevice device = InputDevice.getDevice(devices[i]);
+ if (device != null
+ && !device.isVirtual()
+ && device.isFullKeyboard()) {
+ final String inputDeviceDescriptor = device.getDescriptor();
+ final String keyboardLayoutDescriptor =
+ mIm.getCurrentKeyboardLayoutForInputDevice(inputDeviceDescriptor);
+ final KeyboardLayout keyboardLayout = keyboardLayoutDescriptor != null ?
+ mIm.getKeyboardLayout(keyboardLayoutDescriptor) : null;
+
+ final PreferenceScreen pref = new PreferenceScreen(getActivity(), null);
+ pref.setTitle(device.getName());
+ if (keyboardLayout != null) {
+ pref.setSummary(keyboardLayout.toString());
+ } else {
+ pref.setSummary(R.string.keyboard_layout_default_label);
+ }
+ pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ showKeyboardLayoutDialog(inputDeviceDescriptor);
+ return true;
+ }
+ });
+ mHardKeyboardPreferenceList.add(pref);
+ }
}
- getPreferenceScreen().addPreference(root);
}
- if (hardKeyPref != null) {
- if (mHaveHardKeyboard) {
- mHardKeyboardCategory = (PreferenceCategory) hardKeyPref;
- } else {
- getPreferenceScreen().removePreference(hardKeyPref);
+
+ if (!mHardKeyboardPreferenceList.isEmpty()) {
+ for (int i = mHardKeyboardCategory.getPreferenceCount(); i-- > 0; ) {
+ final Preference pref = mHardKeyboardCategory.getPreference(i);
+ if (pref.getOrder() < 1000) {
+ mHardKeyboardCategory.removePreference(pref);
+ }
}
+
+ Collections.sort(mHardKeyboardPreferenceList);
+ final int count = mHardKeyboardPreferenceList.size();
+ for (int i = 0; i < count; i++) {
+ final Preference pref = mHardKeyboardPreferenceList.get(i);
+ pref.setOrder(i);
+ mHardKeyboardCategory.addPreference(pref);
+ }
+
+ getPreferenceScreen().addPreference(mHardKeyboardCategory);
+ } else {
+ getPreferenceScreen().removePreference(mHardKeyboardCategory);
}
- root.removeAll();
- mInputMethodPreferenceList.clear();
+ }
- if (!mIsOnlyImeSettings) {
- // Current IME selection
- final PreferenceScreen currentIme = new PreferenceScreen(getActivity(), null);
- currentIme.setKey(KEY_CURRENT_INPUT_METHOD);
- currentIme.setTitle(getResources().getString(R.string.current_input_method));
- root.addPreference(currentIme);
+ private void showKeyboardLayoutDialog(String inputDeviceDescriptor) {
+ KeyboardLayoutDialogFragment fragment =
+ new KeyboardLayoutDialogFragment(inputDeviceDescriptor);
+ fragment.setTargetFragment(this, 0);
+ fragment.show(getActivity().getFragmentManager(), "keyboardLayout");
+ }
+
+ @Override
+ public void onSetupKeyboardLayouts(String inputDeviceDescriptor) {
+ final Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setClass(getActivity(), KeyboardLayoutPickerActivity.class);
+ intent.putExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_DESCRIPTOR,
+ inputDeviceDescriptor);
+ mIntentWaitingForResult = intent;
+ startActivityForResult(intent, 0);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ if (mIntentWaitingForResult != null) {
+ String inputDeviceDescriptor = mIntentWaitingForResult.getStringExtra(
+ KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_DESCRIPTOR);
+ mIntentWaitingForResult = null;
+ showKeyboardLayoutDialog(inputDeviceDescriptor);
}
+ }
- final int N = (mImis == null ? 0 : mImis.size());
- for (int i = 0; i < N; ++i) {
- final InputMethodInfo imi = mImis.get(i);
- final InputMethodPreference pref = getInputMethodPreference(imi, N);
- mInputMethodPreferenceList.add(pref);
+ private void updateGameControllers() {
+ if (haveInputDeviceWithVibrator()) {
+ getPreferenceScreen().addPreference(mGameControllerCategory);
+
+ CheckBoxPreference chkPref = (CheckBoxPreference)
+ mGameControllerCategory.findPreference("vibrate_input_devices");
+ chkPref.setChecked(System.getInt(getContentResolver(),
+ Settings.System.VIBRATE_INPUT_DEVICES, 1) > 0);
+ } else {
+ getPreferenceScreen().removePreference(mGameControllerCategory);
}
+ }
- Collections.sort(mInputMethodPreferenceList);
- for (int i = 0; i < N; ++i) {
- root.addPreference(mInputMethodPreferenceList.get(i));
+ private boolean haveInputDeviceWithVibrator() {
+ final int[] devices = InputDevice.getDeviceIds();
+ for (int i = 0; i < devices.length; i++) {
+ InputDevice device = InputDevice.getDevice(devices[i]);
+ if (device != null && !device.isVirtual() && device.getVibrator().hasVibrator()) {
+ return true;
+ }
}
+ return false;
}
private class SettingsObserver extends ContentObserver {
diff --git a/src/com/android/settings/inputmethod/InputMethodAndSubtypeUtil.java b/src/com/android/settings/inputmethod/InputMethodAndSubtypeUtil.java
index cb4058f..c7d8c89 100644
--- a/src/com/android/settings/inputmethod/InputMethodAndSubtypeUtil.java
+++ b/src/com/android/settings/inputmethod/InputMethodAndSubtypeUtil.java
@@ -22,6 +22,7 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.PreferenceScreen;
@@ -36,6 +37,7 @@ import android.view.inputmethod.InputMethodSubtype;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
public class InputMethodAndSubtypeUtil {
@@ -46,6 +48,7 @@ public class InputMethodAndSubtypeUtil {
private static final char INPUT_METHOD_SEPARATER = ':';
private static final char INPUT_METHOD_SUBTYPE_SEPARATER = ';';
private static final int NOT_A_SUBTYPE_ID = -1;
+ private static final Locale ENGLISH_LOCALE = new Locale("en");
private static final TextUtils.SimpleStringSplitter sStringInputMethodSplitter
= new TextUtils.SimpleStringSplitter(INPUT_METHOD_SEPARATER);
@@ -183,7 +186,7 @@ public class InputMethodAndSubtypeUtil {
getEnabledInputMethodsAndSubtypeList(resolver);
HashSet<String> disabledSystemIMEs = getDisabledSystemIMEs(resolver);
- final boolean onlyOneIME = inputMethodInfos.size() == 1;
+ final int imiCount = inputMethodInfos.size();
boolean needsToResetSelectedSubtype = false;
for (InputMethodInfo imi : inputMethodInfos) {
final String imiId = imi.getId();
@@ -193,11 +196,11 @@ public class InputMethodAndSubtypeUtil {
// pref is instance of CheckBoxPreference in the Configure input method screen.
final boolean isImeChecked = (pref instanceof CheckBoxPreference) ?
((CheckBoxPreference) pref).isChecked()
- : enabledIMEAndSubtypesMap.containsKey(imiId);
+ : enabledIMEAndSubtypesMap.containsKey(imiId);
final boolean isCurrentInputMethod = imiId.equals(currentInputMethodId);
- final boolean auxIme = isAuxiliaryIme(imi);
final boolean systemIme = isSystemIme(imi);
- if (((onlyOneIME || (systemIme && !auxIme)) && !hasHardKeyboard) || isImeChecked) {
+ if ((!hasHardKeyboard && isAlwaysCheckedIme(imi, context.getActivity(), imiCount))
+ || isImeChecked) {
if (!enabledIMEAndSubtypesMap.containsKey(imiId)) {
// imiId has just been enabled
enabledIMEAndSubtypesMap.put(imiId, new HashSet<String>());
@@ -373,4 +376,47 @@ public class InputMethodAndSubtypeUtil {
public static boolean isAuxiliaryIme(InputMethodInfo imi) {
return imi.isAuxiliaryIme();
}
+
+ public static boolean isAlwaysCheckedIme(InputMethodInfo imi, Context context, int imiCount) {
+ if (imiCount <= 1) {
+ return true;
+ }
+ if (!isSystemIme(imi)) {
+ return false;
+ }
+ if (isAuxiliaryIme(imi)) {
+ return false;
+ }
+ if (isValidDefaultIme(imi, context)) {
+ return true;
+ }
+ return containsSubtypeOf(imi, ENGLISH_LOCALE.getLanguage());
+ }
+
+ private static boolean isValidDefaultIme(InputMethodInfo imi, Context context) {
+ if (imi.getIsDefaultResourceId() != 0) {
+ try {
+ Resources res = context.createPackageContext(
+ imi.getPackageName(), 0).getResources();
+ if (res.getBoolean(imi.getIsDefaultResourceId())
+ && containsSubtypeOf(imi, context.getResources().getConfiguration().
+ locale.getLanguage())) {
+ return true;
+ }
+ } catch (PackageManager.NameNotFoundException ex) {
+ } catch (Resources.NotFoundException ex) {
+ }
+ }
+ return false;
+ }
+
+ private static boolean containsSubtypeOf(InputMethodInfo imi, String language) {
+ final int N = imi.getSubtypeCount();
+ for (int i = 0; i < N; ++i) {
+ if (imi.getSubtypeAt(i).getLocale().startsWith(language)) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/src/com/android/settings/inputmethod/InputMethodPreference.java b/src/com/android/settings/inputmethod/InputMethodPreference.java
index 4ecdb8e..f555d21 100644
--- a/src/com/android/settings/inputmethod/InputMethodPreference.java
+++ b/src/com/android/settings/inputmethod/InputMethodPreference.java
@@ -39,6 +39,7 @@ import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import android.widget.ImageView;
import android.widget.TextView;
+import android.widget.Toast;
import java.util.Comparator;
import java.util.List;
@@ -51,6 +52,7 @@ public class InputMethodPreference extends CheckBoxPreference
private final InputMethodInfo mImi;
private final InputMethodManager mImm;
private final Intent mSettingsIntent;
+ private final boolean mAlwaysChecked;
private final boolean mIsSystemIme;
private AlertDialog mDialog = null;
@@ -66,10 +68,10 @@ public class InputMethodPreference extends CheckBoxPreference
return;
}
if (isChecked()) {
- setChecked(false);
+ setChecked(false, true /* save */);
} else {
if (mIsSystemIme) {
- setChecked(true);
+ setChecked(true, true /* save */);
} else {
showSecurityWarnDialog(mImi, InputMethodPreference.this);
}
@@ -87,9 +89,10 @@ public class InputMethodPreference extends CheckBoxPreference
mImm = imm;
mImi = imi;
updateSummary();
+ mAlwaysChecked = InputMethodAndSubtypeUtil.isAlwaysCheckedIme(
+ imi, fragment.getActivity(), imiCount);
mIsSystemIme = InputMethodAndSubtypeUtil.isSystemIme(imi);
- final boolean isAuxIme = InputMethodAndSubtypeUtil.isAuxiliaryIme(imi);
- if (imiCount <= 1 || (mIsSystemIme && !isAuxIme)) {
+ if (mAlwaysChecked) {
setEnabled(false);
}
}
@@ -126,8 +129,12 @@ public class InputMethodPreference extends CheckBoxPreference
mFragment.startActivity(mSettingsIntent);
} catch (ActivityNotFoundException e) {
Log.d(TAG, "IME's Settings Activity Not Found: " + e);
- // If the IME's settings activity does not exist, we can just
- // do nothing...
+ final String msg = mFragment.getString(
+ R.string.failed_to_open_app_settings_toast,
+ mImi.loadLabel(
+ mFragment.getActivity().getPackageManager()));
+ Toast.makeText(
+ mFragment.getActivity(), msg, Toast.LENGTH_LONG).show();
}
}
});
@@ -222,14 +229,25 @@ public class InputMethodPreference extends CheckBoxPreference
setSummary(summary);
}
- @Override
- public void setChecked(boolean checked) {
+ /**
+ * Sets the checkbox state and optionally saves the settings.
+ * @param checked whether to check the box
+ * @param save whether to save IME settings
+ */
+ public void setChecked(boolean checked, boolean save) {
super.setChecked(checked);
- saveImeSettings();
+ if (save) {
+ saveImeSettings();
+ }
updateSummary();
}
- private void showSecurityWarnDialog(InputMethodInfo imi, final CheckBoxPreference chkPref) {
+ @Override
+ public void setChecked(boolean checked) {
+ setChecked(checked, false);
+ }
+
+ private void showSecurityWarnDialog(InputMethodInfo imi, final InputMethodPreference chkPref) {
if (mDialog != null && mDialog.isShowing()) {
mDialog.dismiss();
}
@@ -241,7 +259,7 @@ public class InputMethodPreference extends CheckBoxPreference
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- chkPref.setChecked(true);
+ chkPref.setChecked(true, true);
}
})
.setNegativeButton(android.R.string.cancel,
diff --git a/src/com/android/settings/inputmethod/KeyboardLayoutDialogFragment.java b/src/com/android/settings/inputmethod/KeyboardLayoutDialogFragment.java
new file mode 100644
index 0000000..a232a0f
--- /dev/null
+++ b/src/com/android/settings/inputmethod/KeyboardLayoutDialogFragment.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.inputmethod;
+
+import com.android.settings.R;
+import com.android.settings.Settings.KeyboardLayoutPickerActivity;
+
+import android.app.AlertDialog;
+import android.app.Activity;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.AsyncTaskLoader;
+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.InputManager;
+import android.hardware.input.KeyboardLayout;
+import android.hardware.input.InputManager.InputDeviceListener;
+import android.os.Bundle;
+import android.view.InputDevice;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.CheckedTextView;
+import android.widget.RadioButton;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class KeyboardLayoutDialogFragment extends DialogFragment
+ implements InputDeviceListener, LoaderCallbacks<KeyboardLayoutDialogFragment.Keyboards> {
+ private static final String KEY_INPUT_DEVICE_DESCRIPTOR = "inputDeviceDescriptor";
+
+ private String mInputDeviceDescriptor;
+ private int mInputDeviceId = -1;
+ private InputManager mIm;
+ private KeyboardLayoutAdapter mAdapter;
+
+ public KeyboardLayoutDialogFragment() {
+ }
+
+ public KeyboardLayoutDialogFragment(String inputDeviceDescriptor) {
+ mInputDeviceDescriptor = inputDeviceDescriptor;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+
+ Context context = activity.getBaseContext();
+ mIm = (InputManager)context.getSystemService(Context.INPUT_SERVICE);
+ mAdapter = new KeyboardLayoutAdapter(context);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (savedInstanceState != null) {
+ mInputDeviceDescriptor = savedInstanceState.getString(KEY_INPUT_DEVICE_DESCRIPTOR);
+ }
+
+ getLoaderManager().initLoader(0, null, this);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putString(KEY_INPUT_DEVICE_DESCRIPTOR, mInputDeviceDescriptor);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ Context context = getActivity();
+ LayoutInflater inflater = LayoutInflater.from(context);
+ AlertDialog.Builder builder = new AlertDialog.Builder(context)
+ .setTitle(R.string.keyboard_layout_dialog_title)
+ .setPositiveButton(R.string.keyboard_layout_dialog_setup_button,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ onSetupLayoutsButtonClicked();
+ }
+ })
+ .setSingleChoiceItems(mAdapter, -1,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ onKeyboardLayoutClicked(which);
+ }
+ })
+ .setView(inflater.inflate(R.layout.keyboard_layout_dialog_switch_hint, null));
+ updateSwitchHintVisibility();
+ return builder.create();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ mIm.registerInputDeviceListener(this, null);
+
+ InputDevice inputDevice = mIm.getInputDeviceByDescriptor(mInputDeviceDescriptor);
+ if (inputDevice == null) {
+ dismiss();
+ return;
+ }
+ mInputDeviceId = inputDevice.getId();
+ }
+
+ @Override
+ public void onPause() {
+ mIm.unregisterInputDeviceListener(this);
+ mInputDeviceId = -1;
+
+ super.onPause();
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ super.onCancel(dialog);
+ dismiss();
+ }
+
+ private void onSetupLayoutsButtonClicked() {
+ ((OnSetupKeyboardLayoutsListener)getTargetFragment()).onSetupKeyboardLayouts(
+ mInputDeviceDescriptor);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ show(getActivity().getFragmentManager(), "layout");
+ }
+
+ private void onKeyboardLayoutClicked(int which) {
+ if (which >= 0 && which < mAdapter.getCount()) {
+ KeyboardLayout keyboardLayout = mAdapter.getItem(which);
+ if (keyboardLayout != null) {
+ mIm.setCurrentKeyboardLayoutForInputDevice(mInputDeviceDescriptor,
+ keyboardLayout.getDescriptor());
+ }
+ dismiss();
+ }
+ }
+
+ @Override
+ public Loader<Keyboards> onCreateLoader(int id, Bundle args) {
+ return new KeyboardLayoutLoader(getActivity().getBaseContext(), mInputDeviceDescriptor);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Keyboards> loader, Keyboards data) {
+ mAdapter.clear();
+ mAdapter.addAll(data.keyboardLayouts);
+ mAdapter.setCheckedItem(data.current);
+ AlertDialog dialog = (AlertDialog)getDialog();
+ if (dialog != null) {
+ dialog.getListView().setItemChecked(data.current, true);
+ }
+ updateSwitchHintVisibility();
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Keyboards> loader) {
+ mAdapter.clear();
+ updateSwitchHintVisibility();
+ }
+
+ @Override
+ public void onInputDeviceAdded(int deviceId) {
+ }
+
+ @Override
+ public void onInputDeviceChanged(int deviceId) {
+ if (mInputDeviceId >= 0 && deviceId == mInputDeviceId) {
+ getLoaderManager().restartLoader(0, null, this);
+ }
+ }
+
+ @Override
+ public void onInputDeviceRemoved(int deviceId) {
+ if (mInputDeviceId >= 0 && deviceId == mInputDeviceId) {
+ dismiss();
+ }
+ }
+
+ private void updateSwitchHintVisibility() {
+ AlertDialog dialog = (AlertDialog)getDialog();
+ if (dialog != null) {
+ View customPanel = dialog.findViewById(com.android.internal.R.id.customPanel);
+ customPanel.setVisibility(mAdapter.getCount() > 1 ? View.VISIBLE : View.GONE);
+ }
+ }
+
+ private static final class KeyboardLayoutAdapter extends ArrayAdapter<KeyboardLayout> {
+ private final LayoutInflater mInflater;
+ private int mCheckedItem = -1;
+
+ public KeyboardLayoutAdapter(Context context) {
+ super(context, com.android.internal.R.layout.simple_list_item_2_single_choice);
+ mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ public void setCheckedItem(int position) {
+ mCheckedItem = position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ KeyboardLayout item = getItem(position);
+ String label, collection;
+ if (item != null) {
+ label = item.getLabel();
+ collection = item.getCollection();
+ } else {
+ label = getContext().getString(R.string.keyboard_layout_default_label);
+ collection = "";
+ }
+
+ boolean checked = (position == mCheckedItem);
+ if (collection.isEmpty()) {
+ return inflateOneLine(convertView, parent, label, checked);
+ } else {
+ return inflateTwoLine(convertView, parent, label, collection, checked);
+ }
+ }
+
+ private View inflateOneLine(View convertView, ViewGroup parent,
+ String label, boolean checked) {
+ View view = convertView;
+ if (view == null || isTwoLine(view)) {
+ view = mInflater.inflate(
+ com.android.internal.R.layout.simple_list_item_single_choice,
+ parent, false);
+ setTwoLine(view, false);
+ }
+ CheckedTextView headline = (CheckedTextView) view.findViewById(android.R.id.text1);
+ headline.setText(label);
+ headline.setChecked(checked);
+ return view;
+ }
+
+ private View inflateTwoLine(View convertView, ViewGroup parent,
+ String label, String collection, boolean checked) {
+ View view = convertView;
+ if (view == null || !isTwoLine(view)) {
+ view = mInflater.inflate(
+ com.android.internal.R.layout.simple_list_item_2_single_choice,
+ parent, false);
+ setTwoLine(view, true);
+ }
+ TextView headline = (TextView) view.findViewById(android.R.id.text1);
+ TextView subText = (TextView) view.findViewById(android.R.id.text2);
+ RadioButton radioButton =
+ (RadioButton)view.findViewById(com.android.internal.R.id.radio);
+ headline.setText(label);
+ subText.setText(collection);
+ radioButton.setChecked(checked);
+ return view;
+ }
+
+ private static boolean isTwoLine(View view) {
+ return view.getTag() == Boolean.TRUE;
+ }
+
+ private static void setTwoLine(View view, boolean twoLine) {
+ view.setTag(Boolean.valueOf(twoLine));
+ }
+ }
+
+ private static final class KeyboardLayoutLoader extends AsyncTaskLoader<Keyboards> {
+ private final String mInputDeviceDescriptor;
+
+ public KeyboardLayoutLoader(Context context, String inputDeviceDescriptor) {
+ super(context);
+ mInputDeviceDescriptor = inputDeviceDescriptor;
+ }
+
+ @Override
+ public Keyboards loadInBackground() {
+ Keyboards keyboards = new Keyboards();
+ InputManager im = (InputManager)getContext().getSystemService(Context.INPUT_SERVICE);
+ String[] keyboardLayoutDescriptors = im.getKeyboardLayoutsForInputDevice(
+ mInputDeviceDescriptor);
+ for (String keyboardLayoutDescriptor : keyboardLayoutDescriptors) {
+ KeyboardLayout keyboardLayout = im.getKeyboardLayout(keyboardLayoutDescriptor);
+ if (keyboardLayout != null) {
+ keyboards.keyboardLayouts.add(keyboardLayout);
+ }
+ }
+ Collections.sort(keyboards.keyboardLayouts);
+
+ String currentKeyboardLayoutDescriptor =
+ im.getCurrentKeyboardLayoutForInputDevice(mInputDeviceDescriptor);
+ if (currentKeyboardLayoutDescriptor != null) {
+ final int numKeyboardLayouts = keyboards.keyboardLayouts.size();
+ for (int i = 0; i < numKeyboardLayouts; i++) {
+ if (keyboards.keyboardLayouts.get(i).getDescriptor().equals(
+ currentKeyboardLayoutDescriptor)) {
+ keyboards.current = i;
+ break;
+ }
+ }
+ }
+
+ if (keyboards.keyboardLayouts.isEmpty()) {
+ keyboards.keyboardLayouts.add(null); // default layout
+ keyboards.current = 0;
+ }
+ return keyboards;
+ }
+
+ @Override
+ protected void onStartLoading() {
+ super.onStartLoading();
+ forceLoad();
+ }
+
+ @Override
+ protected void onStopLoading() {
+ super.onStopLoading();
+ cancelLoad();
+ }
+ }
+
+ public static final class Keyboards {
+ public final ArrayList<KeyboardLayout> keyboardLayouts = new ArrayList<KeyboardLayout>();
+ public int current = -1;
+ }
+
+ public interface OnSetupKeyboardLayoutsListener {
+ public void onSetupKeyboardLayouts(String inputDeviceDescriptor);
+ }
+} \ No newline at end of file
diff --git a/src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment.java b/src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment.java
new file mode 100644
index 0000000..932dd10
--- /dev/null
+++ b/src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.inputmethod;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+
+import android.content.Context;
+import android.hardware.input.InputManager;
+import android.hardware.input.InputManager.InputDeviceListener;
+import android.hardware.input.KeyboardLayout;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+import android.view.InputDevice;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+public class KeyboardLayoutPickerFragment extends SettingsPreferenceFragment
+ implements InputDeviceListener {
+ private String mInputDeviceDescriptor;
+ private int mInputDeviceId = -1;
+ private InputManager mIm;
+ private KeyboardLayout[] mKeyboardLayouts;
+ private HashMap<CheckBoxPreference, KeyboardLayout> mPreferenceMap =
+ new HashMap<CheckBoxPreference, KeyboardLayout>();
+
+ /**
+ * Intent extra: The input device descriptor of the keyboard whose keyboard
+ * layout is to be changed.
+ */
+ public static final String EXTRA_INPUT_DEVICE_DESCRIPTOR = "input_device_descriptor";
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ mInputDeviceDescriptor = getActivity().getIntent().getStringExtra(
+ EXTRA_INPUT_DEVICE_DESCRIPTOR);
+ if (mInputDeviceDescriptor == null) {
+ getActivity().finish();
+ }
+
+ mIm = (InputManager)getSystemService(Context.INPUT_SERVICE);
+ mKeyboardLayouts = mIm.getKeyboardLayouts();
+ Arrays.sort(mKeyboardLayouts);
+ setPreferenceScreen(createPreferenceHierarchy());
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ mIm.registerInputDeviceListener(this, null);
+
+ InputDevice inputDevice = mIm.getInputDeviceByDescriptor(mInputDeviceDescriptor);
+ if (inputDevice == null) {
+ getActivity().finish();
+ return;
+ }
+ mInputDeviceId = inputDevice.getId();
+
+ updateCheckedState();
+ }
+
+ @Override
+ public void onPause() {
+ mIm.unregisterInputDeviceListener(this);
+ mInputDeviceId = -1;
+
+ super.onPause();
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
+ Preference preference) {
+ if (preference instanceof CheckBoxPreference) {
+ CheckBoxPreference checkboxPref = (CheckBoxPreference)preference;
+ KeyboardLayout layout = mPreferenceMap.get(checkboxPref);
+ if (layout != null) {
+ boolean checked = checkboxPref.isChecked();
+ if (checked) {
+ mIm.addKeyboardLayoutForInputDevice(mInputDeviceDescriptor,
+ layout.getDescriptor());
+ } else {
+ mIm.removeKeyboardLayoutForInputDevice(mInputDeviceDescriptor,
+ layout.getDescriptor());
+ }
+ return true;
+ }
+ }
+ return super.onPreferenceTreeClick(preferenceScreen, preference);
+ }
+
+ @Override
+ public void onInputDeviceAdded(int deviceId) {
+ }
+
+ @Override
+ public void onInputDeviceChanged(int deviceId) {
+ if (mInputDeviceId >= 0 && deviceId == mInputDeviceId) {
+ updateCheckedState();
+ }
+ }
+
+ @Override
+ public void onInputDeviceRemoved(int deviceId) {
+ if (mInputDeviceId >= 0 && deviceId == mInputDeviceId) {
+ getActivity().finish();
+ }
+ }
+
+ private PreferenceScreen createPreferenceHierarchy() {
+ PreferenceScreen root = getPreferenceManager().createPreferenceScreen(getActivity());
+ Context context = getActivity();
+
+ for (KeyboardLayout layout : mKeyboardLayouts) {
+ CheckBoxPreference pref = new CheckBoxPreference(context);
+ pref.setTitle(layout.getLabel());
+ pref.setSummary(layout.getCollection());
+ root.addPreference(pref);
+ mPreferenceMap.put(pref, layout);
+ }
+ return root;
+ }
+
+ private void updateCheckedState() {
+ String[] enabledKeyboardLayouts = mIm.getKeyboardLayoutsForInputDevice(
+ mInputDeviceDescriptor);
+ Arrays.sort(enabledKeyboardLayouts);
+
+ for (Map.Entry<CheckBoxPreference, KeyboardLayout> entry : mPreferenceMap.entrySet()) {
+ entry.getKey().setChecked(Arrays.binarySearch(enabledKeyboardLayouts,
+ entry.getValue().getDescriptor()) >= 0);
+ }
+ }
+}
diff --git a/src/com/android/settings/inputmethod/SingleSpellCheckerPreference.java b/src/com/android/settings/inputmethod/SingleSpellCheckerPreference.java
index 2a62017..5b28142 100644
--- a/src/com/android/settings/inputmethod/SingleSpellCheckerPreference.java
+++ b/src/com/android/settings/inputmethod/SingleSpellCheckerPreference.java
@@ -19,6 +19,7 @@ package com.android.settings.inputmethod;
import com.android.settings.R;
import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
@@ -33,6 +34,7 @@ import android.view.textservice.TextServicesManager;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.TextView;
+import android.widget.Toast;
public class SingleSpellCheckerPreference extends Preference {
private static final float DISABLED_ALPHA = 0.4f;
@@ -177,7 +179,13 @@ public class SingleSpellCheckerPreference extends Preference {
private void onSettingsButtonClicked(View arg0) {
if (mFragment != null && mSettingsIntent != null) {
- mFragment.startActivity(mSettingsIntent);
+ try {
+ mFragment.startActivity(mSettingsIntent);
+ } catch (ActivityNotFoundException e) {
+ final String msg = mFragment.getString(R.string.failed_to_open_app_settings_toast,
+ mSpellCheckerInfo.loadLabel(mFragment.getActivity().getPackageManager()));
+ Toast.makeText(mFragment.getActivity(), msg, Toast.LENGTH_LONG).show();
+ }
}
}
diff --git a/src/com/android/settings/inputmethod/UserDictionaryAddWordActivity.java b/src/com/android/settings/inputmethod/UserDictionaryAddWordActivity.java
new file mode 100644
index 0000000..e52ab7a
--- /dev/null
+++ b/src/com/android/settings/inputmethod/UserDictionaryAddWordActivity.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.inputmethod;
+
+import com.android.settings.R;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+
+public class UserDictionaryAddWordActivity extends Activity {
+
+ private static final String STATE_KEY_IS_OPEN = "isOpen";
+
+ public static final String MODE_EDIT_ACTION = "com.android.settings.USER_DICTIONARY_EDIT";
+ public static final String MODE_INSERT_ACTION = "com.android.settings.USER_DICTIONARY_INSERT";
+
+ private UserDictionaryAddWordContents mContents;
+
+ @Override
+ public void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.user_dictionary_add_word);
+ final Intent intent = getIntent();
+ final String action = intent.getAction();
+ final int mode;
+ if (MODE_EDIT_ACTION.equals(action)) {
+ mode = UserDictionaryAddWordContents.MODE_EDIT;
+ } else if (MODE_INSERT_ACTION.equals(action)) {
+ mode = UserDictionaryAddWordContents.MODE_INSERT;
+ } else {
+ // Can never come here because we only support these two actions in the manifest
+ throw new RuntimeException("Unsupported action: " + action);
+ }
+
+ // The following will get the EXTRA_WORD and EXTRA_LOCALE fields that are in the intent.
+ // We do need to add the action by hand, because UserDictionaryAddWordContents expects
+ // it to be in the bundle, in the EXTRA_MODE key.
+ final Bundle args = intent.getExtras();
+ args.putInt(UserDictionaryAddWordContents.EXTRA_MODE, mode);
+
+ if (null != savedInstanceState) {
+ // Override options if we have a saved state.
+ args.putAll(savedInstanceState);
+ }
+
+ mContents = new UserDictionaryAddWordContents(getWindow().getDecorView(), args);
+ }
+
+ @Override
+ public void onSaveInstanceState(final Bundle outState) {
+ mContents.saveStateIntoBundle(outState);
+ }
+
+ public void onClickCancel(final View v) {
+ finish();
+ }
+
+ public void onClickConfirm(final View v) {
+ mContents.apply(this);
+ finish();
+ }
+}
diff --git a/src/com/android/settings/inputmethod/UserDictionaryAddWordContents.java b/src/com/android/settings/inputmethod/UserDictionaryAddWordContents.java
new file mode 100644
index 0000000..e46b19c
--- /dev/null
+++ b/src/com/android/settings/inputmethod/UserDictionaryAddWordContents.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.inputmethod;
+
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.provider.UserDictionary;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.EditText;
+
+import com.android.settings.R;
+import com.android.settings.UserDictionarySettings;
+import com.android.settings.Utils;
+
+import java.util.ArrayList;
+import java.util.Locale;
+import java.util.TreeSet;
+
+/**
+ * A container class to factor common code to UserDictionaryAddWordFragment
+ * and UserDictionaryAddWordActivity.
+ */
+public class UserDictionaryAddWordContents {
+ public static final String EXTRA_MODE = "mode";
+ public static final String EXTRA_WORD = "word";
+ public static final String EXTRA_SHORTCUT = "shortcut";
+ public static final String EXTRA_LOCALE = "locale";
+
+ public static final int MODE_EDIT = 0;
+ public static final int MODE_INSERT = 1;
+
+ private static final int FREQUENCY_FOR_USER_DICTIONARY_ADDS = 250;
+
+ private final int mMode; // Either MODE_EDIT or MODE_INSERT
+ private final EditText mWordEditText;
+ private final EditText mShortcutEditText;
+ private String mLocale;
+ private final String mOldWord;
+ private final String mOldShortcut;
+
+ /* package */ UserDictionaryAddWordContents(final View view, final Bundle args) {
+ mWordEditText = (EditText)view.findViewById(R.id.user_dictionary_add_word_text);
+ mShortcutEditText = (EditText)view.findViewById(R.id.user_dictionary_add_shortcut);
+ final String word = args.getString(EXTRA_WORD);
+ if (null != word) {
+ mWordEditText.setText(word);
+ mWordEditText.setSelection(word.length());
+ }
+ final String shortcut = args.getString(EXTRA_SHORTCUT);
+ if (null != shortcut && null != mShortcutEditText) {
+ mShortcutEditText.setText(shortcut);
+ }
+ mMode = args.getInt(EXTRA_MODE); // default return value for #getInt() is 0 = MODE_EDIT
+ mOldWord = args.getString(EXTRA_WORD);
+ mOldShortcut = args.getString(EXTRA_SHORTCUT);
+ updateLocale(args.getString(EXTRA_LOCALE));
+ }
+
+ // locale may be null, this means default locale
+ // It may also be the empty string, which means "all locales"
+ /* package */ void updateLocale(final String locale) {
+ mLocale = null == locale ? Locale.getDefault().toString() : locale;
+ }
+
+ /* package */ void saveStateIntoBundle(final Bundle outState) {
+ outState.putString(EXTRA_WORD, mWordEditText.getText().toString());
+ if (null != mShortcutEditText) {
+ outState.putString(EXTRA_SHORTCUT, mShortcutEditText.getText().toString());
+ }
+ outState.putString(EXTRA_LOCALE, mLocale);
+ }
+
+ /* package */ void delete(final Context context) {
+ if (MODE_EDIT == mMode && !TextUtils.isEmpty(mOldWord)) {
+ // Mode edit: remove the old entry.
+ final ContentResolver resolver = context.getContentResolver();
+ UserDictionarySettings.deleteWord(mOldWord, mOldShortcut, resolver);
+ }
+ // If we are in add mode, nothing was added, so we don't need to do anything.
+ }
+
+ /* package */ void apply(final Context context) {
+ final ContentResolver resolver = context.getContentResolver();
+ if (MODE_EDIT == mMode && !TextUtils.isEmpty(mOldWord)) {
+ // Mode edit: remove the old entry.
+ UserDictionarySettings.deleteWord(mOldWord, mOldShortcut, resolver);
+ }
+ final String newWord = mWordEditText.getText().toString();
+ final String newShortcut;
+ if (null == mShortcutEditText) {
+ newShortcut = null;
+ } else {
+ final String tmpShortcut = mShortcutEditText.getText().toString();
+ if (TextUtils.isEmpty(tmpShortcut)) {
+ newShortcut = null;
+ } else {
+ newShortcut = tmpShortcut;
+ }
+ }
+ if (TextUtils.isEmpty(newWord)) {
+ // If the word is somehow empty, don't insert it.
+ return;
+ }
+ // If there is no shortcut, and the word already exists in the database, then we
+ // should not insert, because either A. the word exists with no shortcut, in which
+ // case the exact same thing we want to insert is already there, or B. the word
+ // exists with at least one shortcut, in which case it has priority on our word.
+ if (hasWord(newWord, context)) return;
+
+ // Disallow duplicates. If the same word with no shortcut is defined, remove it; if
+ // the same word with the same shortcut is defined, remove it; but we don't mind if
+ // there is the same word with a different, non-empty shortcut.
+ UserDictionarySettings.deleteWord(newWord, null, resolver);
+ if (!TextUtils.isEmpty(newShortcut)) {
+ // If newShortcut is empty we just deleted this, no need to do it again
+ UserDictionarySettings.deleteWord(newWord, newShortcut, resolver);
+ }
+
+ // In this class we use the empty string to represent 'all locales' and mLocale cannot
+ // be null. However the addWord method takes null to mean 'all locales'.
+ UserDictionary.Words.addWord(context, newWord.toString(),
+ FREQUENCY_FOR_USER_DICTIONARY_ADDS, newShortcut,
+ TextUtils.isEmpty(mLocale) ? null : Utils.createLocaleFromString(mLocale));
+ }
+
+ private static final String[] HAS_WORD_PROJECTION = { UserDictionary.Words.WORD };
+ private static final String HAS_WORD_SELECTION_ONE_LOCALE = UserDictionary.Words.WORD
+ + "=? AND " + UserDictionary.Words.LOCALE + "=?";
+ private static final String HAS_WORD_SELECTION_ALL_LOCALES = UserDictionary.Words.WORD
+ + "=? AND " + UserDictionary.Words.LOCALE + " is null";
+ private boolean hasWord(final String word, final Context context) {
+ final Cursor cursor;
+ // mLocale == "" indicates this is an entry for all languages. Here, mLocale can't
+ // be null at all (it's ensured by the updateLocale method).
+ if ("".equals(mLocale)) {
+ cursor = context.getContentResolver().query(UserDictionary.Words.CONTENT_URI,
+ HAS_WORD_PROJECTION, HAS_WORD_SELECTION_ALL_LOCALES,
+ new String[] { word }, null /* sort order */);
+ } else {
+ cursor = context.getContentResolver().query(UserDictionary.Words.CONTENT_URI,
+ HAS_WORD_PROJECTION, HAS_WORD_SELECTION_ONE_LOCALE,
+ new String[] { word, mLocale }, null /* sort order */);
+ }
+ try {
+ if (null == cursor) return false;
+ return cursor.getCount() > 0;
+ } finally {
+ if (null != cursor) cursor.close();
+ }
+ }
+
+ public static class LocaleRenderer {
+ private final String mLocaleString;
+ private final String mDescription;
+ // LocaleString may NOT be null.
+ public LocaleRenderer(final Context context, final String localeString) {
+ mLocaleString = localeString;
+ if (null == localeString) {
+ mDescription = context.getString(R.string.user_dict_settings_more_languages);
+ } else if ("".equals(localeString)) {
+ mDescription = context.getString(R.string.user_dict_settings_all_languages);
+ } else {
+ mDescription = Utils.createLocaleFromString(localeString).getDisplayName();
+ }
+ }
+ @Override
+ public String toString() {
+ return mDescription;
+ }
+ public String getLocaleString() {
+ return mLocaleString;
+ }
+ // "More languages..." is null ; "All languages" is the empty string.
+ public boolean isMoreLanguages() {
+ return null == mLocaleString;
+ }
+ }
+
+ private static void addLocaleDisplayNameToList(final Context context,
+ final ArrayList<LocaleRenderer> list, final String locale) {
+ if (null != locale) {
+ list.add(new LocaleRenderer(context, locale));
+ }
+ }
+
+ // Helper method to get the list of locales to display for this word
+ public ArrayList<LocaleRenderer> getLocalesList(final Activity activity) {
+ final TreeSet<String> locales = UserDictionaryList.getUserDictionaryLocalesSet(activity);
+ // Remove our locale if it's in, because we're always gonna put it at the top
+ locales.remove(mLocale); // mLocale may not be null
+ final String systemLocale = Locale.getDefault().toString();
+ // The system locale should be inside. We want it at the 2nd spot.
+ locales.remove(systemLocale); // system locale may not be null
+ locales.remove(""); // Remove the empty string if it's there
+ final ArrayList<LocaleRenderer> localesList = new ArrayList<LocaleRenderer>();
+ // Add the passed locale, then the system locale at the top of the list. Add an
+ // "all languages" entry at the bottom of the list.
+ addLocaleDisplayNameToList(activity, localesList, mLocale);
+ if (!systemLocale.equals(mLocale)) {
+ addLocaleDisplayNameToList(activity, localesList, systemLocale);
+ }
+ for (final String l : locales) {
+ // TODO: sort in unicode order
+ addLocaleDisplayNameToList(activity, localesList, l);
+ }
+ if (!"".equals(mLocale)) {
+ // If mLocale is "", then we already inserted the "all languages" item, so don't do it
+ addLocaleDisplayNameToList(activity, localesList, ""); // meaning: all languages
+ }
+ localesList.add(new LocaleRenderer(activity, null)); // meaning: select another locale
+ return localesList;
+ }
+}
diff --git a/src/com/android/settings/inputmethod/UserDictionaryAddWordFragment.java b/src/com/android/settings/inputmethod/UserDictionaryAddWordFragment.java
new file mode 100644
index 0000000..97ffa19
--- /dev/null
+++ b/src/com/android/settings/inputmethod/UserDictionaryAddWordFragment.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.inputmethod;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Spinner;
+
+import com.android.settings.R;
+import com.android.settings.inputmethod.UserDictionaryAddWordContents.LocaleRenderer;
+
+import java.util.ArrayList;
+import java.util.Locale;
+
+/**
+ * Fragment to add a word/shortcut to the user dictionary.
+ *
+ * As opposed to the UserDictionaryActivity, this is only invoked within Settings
+ * from the UserDictionarySettings.
+ */
+public class UserDictionaryAddWordFragment extends Fragment
+ implements AdapterView.OnItemSelectedListener,
+ com.android.internal.app.LocalePicker.LocaleSelectionListener {
+
+ private static final int OPTIONS_MENU_DELETE = Menu.FIRST;
+
+ private UserDictionaryAddWordContents mContents;
+ private View mRootView;
+ private boolean mIsDeleting = false;
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
+ mRootView = inflater.inflate(R.layout.user_dictionary_add_word_fullscreen, null);
+ mIsDeleting = false;
+ if (null == mContents) {
+ mContents = new UserDictionaryAddWordContents(mRootView, getArguments());
+ }
+ return mRootView;
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ MenuItem actionItem = menu.add(0, OPTIONS_MENU_DELETE, 0, R.string.delete)
+ .setIcon(android.R.drawable.ic_menu_delete);
+ actionItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM |
+ MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+ }
+
+ /**
+ * Callback for the framework when a menu option is pressed.
+ *
+ * This class only supports the delete menu item.
+ * @param MenuItem the item that was pressed
+ * @return false to allow normal menu processing to proceed, true to consume it here
+ */
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == OPTIONS_MENU_DELETE) {
+ mContents.delete(getActivity());
+ mIsDeleting = true;
+ getActivity().onBackPressed();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ // We are being shown: display the word
+ updateSpinner();
+ }
+
+ private void updateSpinner() {
+ final ArrayList<LocaleRenderer> localesList = mContents.getLocalesList(getActivity());
+
+ final Spinner localeSpinner =
+ (Spinner)mRootView.findViewById(R.id.user_dictionary_add_locale);
+ final ArrayAdapter<LocaleRenderer> adapter = new ArrayAdapter<LocaleRenderer>(getActivity(),
+ android.R.layout.simple_spinner_item, localesList);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ localeSpinner.setAdapter(adapter);
+ localeSpinner.setOnItemSelectedListener(this);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ // We are being hidden: commit changes to the user dictionary, unless we were deleting it
+ if (!mIsDeleting) {
+ mContents.apply(getActivity());
+ }
+ }
+
+ @Override
+ public void onItemSelected(final AdapterView<?> parent, final View view, final int pos,
+ final long id) {
+ final LocaleRenderer locale = (LocaleRenderer)parent.getItemAtPosition(pos);
+ if (locale.isMoreLanguages()) {
+ PreferenceActivity preferenceActivity = (PreferenceActivity)getActivity();
+ preferenceActivity.startPreferenceFragment(new UserDictionaryLocalePicker(this), true);
+ } else {
+ mContents.updateLocale(locale.getLocaleString());
+ }
+ }
+
+ @Override
+ public void onNothingSelected(final AdapterView<?> parent) {
+ // I'm not sure we can come here, but if we do, that's the right thing to do.
+ final Bundle args = getArguments();
+ mContents.updateLocale(args.getString(UserDictionaryAddWordContents.EXTRA_LOCALE));
+ }
+
+ // Called by the locale picker
+ @Override
+ public void onLocaleSelected(final Locale locale) {
+ mContents.updateLocale(locale.toString());
+ getActivity().onBackPressed();
+ }
+}
diff --git a/src/com/android/settings/inputmethod/UserDictionaryList.java b/src/com/android/settings/inputmethod/UserDictionaryList.java
index 6b1ca7b..5390be6 100644
--- a/src/com/android/settings/inputmethod/UserDictionaryList.java
+++ b/src/com/android/settings/inputmethod/UserDictionaryList.java
@@ -29,7 +29,6 @@ import android.preference.PreferenceGroup;
import android.provider.UserDictionary;
import java.util.Locale;
-import java.util.Set;
import java.util.TreeSet;
public class UserDictionaryList extends SettingsPreferenceFragment {
@@ -43,12 +42,12 @@ public class UserDictionaryList extends SettingsPreferenceFragment {
setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getActivity()));
}
- static Set<String> getUserDictionaryLocalesList(Activity activity) {
+ static TreeSet<String> getUserDictionaryLocalesSet(Activity activity) {
@SuppressWarnings("deprecation")
final Cursor cursor = activity.managedQuery(UserDictionary.Words.CONTENT_URI,
new String[] { UserDictionary.Words.LOCALE },
null, null, null);
- final Set<String> localeList = new TreeSet<String>();
+ final TreeSet<String> localeList = new TreeSet<String>();
if (null == cursor) {
// The user dictionary service is not present or disabled. Return null.
return null;
@@ -70,7 +69,8 @@ public class UserDictionaryList extends SettingsPreferenceFragment {
protected void createUserDictSettings(PreferenceGroup userDictGroup) {
final Activity activity = getActivity();
userDictGroup.removeAll();
- final Set<String> localeList = UserDictionaryList.getUserDictionaryLocalesList(activity);
+ final TreeSet<String> localeList =
+ UserDictionaryList.getUserDictionaryLocalesSet(activity);
if (localeList.isEmpty()) {
userDictGroup.addPreference(createUserDictionaryPreference(null, activity));
@@ -100,6 +100,7 @@ public class UserDictionaryList extends SettingsPreferenceFragment {
newPref.getExtras().putString("locale", locale);
}
newPref.setIntent(intent);
+ newPref.setFragment(com.android.settings.UserDictionarySettings.class.getName());
return newPref;
}
diff --git a/src/com/android/settings/inputmethod/UserDictionaryLocalePicker.java b/src/com/android/settings/inputmethod/UserDictionaryLocalePicker.java
new file mode 100644
index 0000000..b9ccdfd
--- /dev/null
+++ b/src/com/android/settings/inputmethod/UserDictionaryLocalePicker.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.inputmethod;
+
+import java.util.Locale;
+
+public class UserDictionaryLocalePicker extends com.android.internal.app.LocalePicker {
+ public UserDictionaryLocalePicker(final UserDictionaryAddWordFragment parent) {
+ super();
+ setLocaleSelectionListener(parent);
+ }
+}
diff --git a/src/com/android/settings/net/ChartDataLoader.java b/src/com/android/settings/net/ChartDataLoader.java
index 09e6e3b..e0336b7 100644
--- a/src/com/android/settings/net/ChartDataLoader.java
+++ b/src/com/android/settings/net/ChartDataLoader.java
@@ -21,41 +21,44 @@ import static android.net.NetworkStats.SET_FOREGROUND;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import android.content.AsyncTaskLoader;
import android.content.Context;
-import android.net.INetworkStatsService;
+import android.net.INetworkStatsSession;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.os.Bundle;
import android.os.RemoteException;
+import com.android.settings.DataUsageSummary.AppItem;
+
/**
* Loader for historical chart data for both network and UID details.
*/
public class ChartDataLoader extends AsyncTaskLoader<ChartData> {
private static final String KEY_TEMPLATE = "template";
- private static final String KEY_UIDS = "uids";
+ private static final String KEY_APP = "app";
private static final String KEY_FIELDS = "fields";
- private final INetworkStatsService mStatsService;
+ private final INetworkStatsSession mSession;
private final Bundle mArgs;
- public static Bundle buildArgs(NetworkTemplate template, int[] uids) {
- return buildArgs(template, uids, FIELD_RX_BYTES | FIELD_TX_BYTES);
+ public static Bundle buildArgs(NetworkTemplate template, AppItem app) {
+ return buildArgs(template, app, FIELD_RX_BYTES | FIELD_TX_BYTES);
}
- public static Bundle buildArgs(NetworkTemplate template, int[] uids, int fields) {
+ public static Bundle buildArgs(NetworkTemplate template, AppItem app, int fields) {
final Bundle args = new Bundle();
args.putParcelable(KEY_TEMPLATE, template);
- args.putIntArray(KEY_UIDS, uids);
+ args.putParcelable(KEY_APP, app);
args.putInt(KEY_FIELDS, fields);
return args;
}
- public ChartDataLoader(Context context, INetworkStatsService statsService, Bundle args) {
+ public ChartDataLoader(Context context, INetworkStatsSession session, Bundle args) {
super(context);
- mStatsService = statsService;
+ mSession = session;
mArgs = args;
}
@@ -68,11 +71,11 @@ public class ChartDataLoader extends AsyncTaskLoader<ChartData> {
@Override
public ChartData loadInBackground() {
final NetworkTemplate template = mArgs.getParcelable(KEY_TEMPLATE);
- final int[] uids = mArgs.getIntArray(KEY_UIDS);
+ final AppItem app = mArgs.getParcelable(KEY_APP);
final int fields = mArgs.getInt(KEY_FIELDS);
try {
- return loadInBackground(template, uids, fields);
+ return loadInBackground(template, app, fields);
} catch (RemoteException e) {
// since we can't do much without history, and we don't want to
// leave with half-baked UI, we bail hard.
@@ -80,26 +83,31 @@ public class ChartDataLoader extends AsyncTaskLoader<ChartData> {
}
}
- private ChartData loadInBackground(NetworkTemplate template, int[] uids, int fields)
+ private ChartData loadInBackground(NetworkTemplate template, AppItem app, int fields)
throws RemoteException {
final ChartData data = new ChartData();
- data.network = mStatsService.getHistoryForNetwork(template, fields);
-
- if (uids != null) {
- data.detailDefault = null;
- data.detailForeground = null;
+ data.network = mSession.getHistoryForNetwork(template, fields);
+ if (app != null) {
// load stats for current uid and template
- for (int uid : uids) {
+ final int size = app.uids.size();
+ for (int i = 0; i < size; i++) {
+ final int uid = app.uids.keyAt(i);
data.detailDefault = collectHistoryForUid(
template, uid, SET_DEFAULT, data.detailDefault);
data.detailForeground = collectHistoryForUid(
template, uid, SET_FOREGROUND, data.detailForeground);
}
- data.detail = new NetworkStatsHistory(data.detailForeground.getBucketDuration());
- data.detail.recordEntireHistory(data.detailDefault);
- data.detail.recordEntireHistory(data.detailForeground);
+ if (size > 0) {
+ data.detail = new NetworkStatsHistory(data.detailForeground.getBucketDuration());
+ data.detail.recordEntireHistory(data.detailDefault);
+ data.detail.recordEntireHistory(data.detailForeground);
+ } else {
+ data.detailDefault = new NetworkStatsHistory(HOUR_IN_MILLIS);
+ data.detailForeground = new NetworkStatsHistory(HOUR_IN_MILLIS);
+ data.detail = new NetworkStatsHistory(HOUR_IN_MILLIS);
+ }
}
return data;
@@ -124,7 +132,7 @@ public class ChartDataLoader extends AsyncTaskLoader<ChartData> {
private NetworkStatsHistory collectHistoryForUid(
NetworkTemplate template, int uid, int set, NetworkStatsHistory existing)
throws RemoteException {
- final NetworkStatsHistory history = mStatsService.getHistoryForUid(
+ final NetworkStatsHistory history = mSession.getHistoryForUid(
template, uid, set, TAG_NONE, FIELD_RX_BYTES | FIELD_TX_BYTES);
if (existing != null) {
diff --git a/src/com/android/settings/net/DataUsageMeteredSettings.java b/src/com/android/settings/net/DataUsageMeteredSettings.java
new file mode 100644
index 0000000..ad12311
--- /dev/null
+++ b/src/com/android/settings/net/DataUsageMeteredSettings.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.net;
+
+import static android.net.NetworkPolicy.LIMIT_DISABLED;
+import static android.net.wifi.WifiInfo.removeDoubleQuotes;
+import static com.android.settings.DataUsageSummary.hasReadyMobileRadio;
+import static com.android.settings.DataUsageSummary.hasWifiRadio;
+
+import android.content.Context;
+import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
+import android.net.NetworkTemplate;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.Preference;
+import android.preference.PreferenceCategory;
+import android.telephony.TelephonyManager;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+
+/**
+ * Panel to configure {@link NetworkPolicy#metered} for networks.
+ */
+public class DataUsageMeteredSettings extends SettingsPreferenceFragment {
+
+ private static final boolean SHOW_MOBILE_CATEGORY = false;
+
+ private NetworkPolicyManager mPolicyManager;
+ private WifiManager mWifiManager;
+
+ private NetworkPolicyEditor mPolicyEditor;
+
+ private PreferenceCategory mMobileCategory;
+ private PreferenceCategory mWifiCategory;
+ private Preference mWifiDisabled;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ final Context context = getActivity();
+
+ mPolicyManager = NetworkPolicyManager.from(context);
+ mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+
+ mPolicyEditor = new NetworkPolicyEditor(mPolicyManager);
+ mPolicyEditor.read();
+
+ addPreferencesFromResource(R.xml.data_usage_metered_prefs);
+ mMobileCategory = (PreferenceCategory) findPreference("mobile");
+ mWifiCategory = (PreferenceCategory) findPreference("wifi");
+ mWifiDisabled = findPreference("wifi_disabled");
+
+ updateNetworks(context);
+ }
+
+ private void updateNetworks(Context context) {
+ if (SHOW_MOBILE_CATEGORY && hasReadyMobileRadio(context)) {
+ mMobileCategory.removeAll();
+ mMobileCategory.addPreference(buildMobilePref(context));
+ } else {
+ getPreferenceScreen().removePreference(mMobileCategory);
+ }
+
+ mWifiCategory.removeAll();
+ if (hasWifiRadio(context) && mWifiManager.isWifiEnabled()) {
+ for (WifiConfiguration config : mWifiManager.getConfiguredNetworks()) {
+ if (config.SSID != null) {
+ mWifiCategory.addPreference(buildWifiPref(context, config));
+ }
+ }
+ } else {
+ mWifiCategory.addPreference(mWifiDisabled);
+ }
+ }
+
+ private Preference buildMobilePref(Context context) {
+ final TelephonyManager tele = TelephonyManager.from(context);
+ final NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll(
+ tele.getSubscriberId());
+ final MeteredPreference pref = new MeteredPreference(context, template);
+ pref.setTitle(tele.getNetworkOperatorName());
+ return pref;
+ }
+
+ private Preference buildWifiPref(Context context, WifiConfiguration config) {
+ final String networkId = removeDoubleQuotes(config.SSID);
+ final NetworkTemplate template = NetworkTemplate.buildTemplateWifi(networkId);
+ final MeteredPreference pref = new MeteredPreference(context, template);
+ pref.setTitle(networkId);
+ return pref;
+ }
+
+ private class MeteredPreference extends CheckBoxPreference {
+ private final NetworkTemplate mTemplate;
+ private boolean mBinding;
+
+ public MeteredPreference(Context context, NetworkTemplate template) {
+ super(context);
+ mTemplate = template;
+
+ setPersistent(false);
+
+ mBinding = true;
+ final NetworkPolicy policy = mPolicyEditor.getPolicy(template);
+ if (policy != null) {
+ if (policy.limitBytes != LIMIT_DISABLED) {
+ setChecked(true);
+ setEnabled(false);
+ } else {
+ setChecked(policy.metered);
+ }
+ } else {
+ setChecked(false);
+ }
+ mBinding = false;
+ }
+
+ @Override
+ protected void notifyChanged() {
+ super.notifyChanged();
+ if (!mBinding) {
+ mPolicyEditor.setPolicyMetered(mTemplate, isChecked());
+ }
+ }
+ }
+}
diff --git a/src/com/android/settings/net/NetworkPolicyEditor.java b/src/com/android/settings/net/NetworkPolicyEditor.java
index 5ba8ca4..5fe4c06 100644
--- a/src/com/android/settings/net/NetworkPolicyEditor.java
+++ b/src/com/android/settings/net/NetworkPolicyEditor.java
@@ -16,6 +16,7 @@
package com.android.settings.net;
+import static android.net.NetworkPolicy.CYCLE_NONE;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.SNOOZE_NEVER;
import static android.net.NetworkPolicy.WARNING_DISABLED;
@@ -27,11 +28,10 @@ import static android.net.NetworkTemplate.buildTemplateMobile4g;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static com.android.internal.util.Preconditions.checkNotNull;
-import android.net.INetworkPolicyManager;
import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
import android.net.NetworkTemplate;
import android.os.AsyncTask;
-import android.os.RemoteException;
import android.text.format.Time;
import com.android.internal.util.Objects;
@@ -43,27 +43,23 @@ import java.util.HashSet;
/**
* Utility class to modify list of {@link NetworkPolicy}. Specifically knows
- * about which policies can coexist. Not thread safe.
+ * about which policies can coexist. This editor offers thread safety when
+ * talking with {@link NetworkPolicyManager}.
*/
public class NetworkPolicyEditor {
// TODO: be more robust when missing policies from service
public static final boolean ENABLE_SPLIT_POLICIES = false;
- private INetworkPolicyManager mPolicyService;
+ private NetworkPolicyManager mPolicyManager;
private ArrayList<NetworkPolicy> mPolicies = Lists.newArrayList();
- public NetworkPolicyEditor(INetworkPolicyManager policyService) {
- mPolicyService = checkNotNull(policyService);
+ public NetworkPolicyEditor(NetworkPolicyManager policyManager) {
+ mPolicyManager = checkNotNull(policyManager);
}
public void read() {
- final NetworkPolicy[] policies;
- try {
- policies = mPolicyService.getNetworkPolicies();
- } catch (RemoteException e) {
- throw new RuntimeException("problem reading policies", e);
- }
+ final NetworkPolicy[] policies = mPolicyManager.getNetworkPolicies();
boolean modified = false;
mPolicies.clear();
@@ -78,12 +74,6 @@ public class NetworkPolicyEditor {
modified = true;
}
- // drop any WIFI policies that were defined
- if (policy.template.getMatchRule() == MATCH_WIFI) {
- modified = true;
- continue;
- }
-
mPolicies.add(policy);
}
@@ -109,11 +99,7 @@ public class NetworkPolicyEditor {
}
public void write(NetworkPolicy[] policies) {
- try {
- mPolicyService.setNetworkPolicies(policies);
- } catch (RemoteException e) {
- throw new RuntimeException("problem writing policies", e);
- }
+ mPolicyManager.setNetworkPolicies(policies);
}
public boolean hasLimitedPolicy(NetworkTemplate template) {
@@ -139,24 +125,39 @@ public class NetworkPolicyEditor {
return null;
}
+ @Deprecated
private static NetworkPolicy buildDefaultPolicy(NetworkTemplate template) {
// TODO: move this into framework to share with NetworkPolicyManagerService
- final Time time = new Time();
- time.setToNow();
- final int cycleDay = time.monthDay;
+ final int cycleDay;
+ final String cycleTimezone;
+ final boolean metered;
+
+ if (template.getMatchRule() == MATCH_WIFI) {
+ cycleDay = CYCLE_NONE;
+ cycleTimezone = Time.TIMEZONE_UTC;
+ metered = false;
+ } else {
+ final Time time = new Time();
+ time.setToNow();
+ cycleDay = time.monthDay;
+ cycleTimezone = time.timezone;
+ metered = true;
+ }
- return new NetworkPolicy(
- template, cycleDay, WARNING_DISABLED, LIMIT_DISABLED, SNOOZE_NEVER);
+ return new NetworkPolicy(template, cycleDay, cycleTimezone, WARNING_DISABLED,
+ LIMIT_DISABLED, SNOOZE_NEVER, SNOOZE_NEVER, metered, true);
}
public int getPolicyCycleDay(NetworkTemplate template) {
return getPolicy(template).cycleDay;
}
- public void setPolicyCycleDay(NetworkTemplate template, int cycleDay) {
+ public void setPolicyCycleDay(NetworkTemplate template, int cycleDay, String cycleTimezone) {
final NetworkPolicy policy = getOrCreatePolicy(template);
policy.cycleDay = cycleDay;
- policy.lastSnooze = SNOOZE_NEVER;
+ policy.cycleTimezone = cycleTimezone;
+ policy.inferred = false;
+ policy.clearSnooze();
writeAsync();
}
@@ -167,7 +168,8 @@ public class NetworkPolicyEditor {
public void setPolicyWarningBytes(NetworkTemplate template, long warningBytes) {
final NetworkPolicy policy = getOrCreatePolicy(template);
policy.warningBytes = warningBytes;
- policy.lastSnooze = SNOOZE_NEVER;
+ policy.inferred = false;
+ policy.clearSnooze();
writeAsync();
}
@@ -178,10 +180,50 @@ public class NetworkPolicyEditor {
public void setPolicyLimitBytes(NetworkTemplate template, long limitBytes) {
final NetworkPolicy policy = getOrCreatePolicy(template);
policy.limitBytes = limitBytes;
- policy.lastSnooze = SNOOZE_NEVER;
+ policy.inferred = false;
+ policy.clearSnooze();
writeAsync();
}
+ public boolean getPolicyMetered(NetworkTemplate template) {
+ final NetworkPolicy policy = getPolicy(template);
+ if (policy != null) {
+ return policy.metered;
+ } else {
+ return false;
+ }
+ }
+
+ public void setPolicyMetered(NetworkTemplate template, boolean metered) {
+ boolean modified = false;
+
+ NetworkPolicy policy = getPolicy(template);
+ if (metered) {
+ if (policy == null) {
+ policy = buildDefaultPolicy(template);
+ policy.metered = true;
+ policy.inferred = false;
+ mPolicies.add(policy);
+ modified = true;
+ } else if (!policy.metered) {
+ policy.metered = true;
+ policy.inferred = false;
+ modified = true;
+ }
+
+ } else {
+ if (policy == null) {
+ // ignore when policy doesn't exist
+ } else if (policy.metered) {
+ policy.metered = false;
+ policy.inferred = false;
+ modified = true;
+ }
+ }
+
+ if (modified) writeAsync();
+ }
+
/**
* Remove any split {@link NetworkPolicy}.
*/
@@ -198,6 +240,7 @@ public class NetworkPolicyEditor {
return modified;
}
+ @Deprecated
public boolean isMobilePolicySplit(String subscriberId) {
boolean has3g = false;
boolean has4g = false;
@@ -217,6 +260,7 @@ public class NetworkPolicyEditor {
return has3g && has4g;
}
+ @Deprecated
public void setMobilePolicySplit(String subscriberId, boolean split) {
if (setMobilePolicySplitInternal(subscriberId, split)) {
writeAsync();
@@ -229,6 +273,7 @@ public class NetworkPolicyEditor {
*
* @return {@code true} when any {@link NetworkPolicy} was mutated.
*/
+ @Deprecated
private boolean setMobilePolicySplitInternal(String subscriberId, boolean split) {
final boolean beforeSplit = isMobilePolicySplit(subscriberId);
@@ -249,21 +294,21 @@ public class NetworkPolicyEditor {
: policy4g;
mPolicies.remove(policy3g);
mPolicies.remove(policy4g);
- mPolicies.add(
- new NetworkPolicy(templateAll, restrictive.cycleDay, restrictive.warningBytes,
- restrictive.limitBytes, SNOOZE_NEVER));
+ mPolicies.add(new NetworkPolicy(templateAll, restrictive.cycleDay,
+ restrictive.cycleTimezone, restrictive.warningBytes, restrictive.limitBytes,
+ SNOOZE_NEVER, SNOOZE_NEVER, restrictive.metered, restrictive.inferred));
return true;
} else if (!beforeSplit && split) {
// duplicate existing policy into two rules
final NetworkPolicy policyAll = getPolicy(templateAll);
mPolicies.remove(policyAll);
- mPolicies.add(
- new NetworkPolicy(template3g, policyAll.cycleDay, policyAll.warningBytes,
- policyAll.limitBytes, SNOOZE_NEVER));
- mPolicies.add(
- new NetworkPolicy(template4g, policyAll.cycleDay, policyAll.warningBytes,
- policyAll.limitBytes, SNOOZE_NEVER));
+ mPolicies.add(new NetworkPolicy(template3g, policyAll.cycleDay, policyAll.cycleTimezone,
+ policyAll.warningBytes, policyAll.limitBytes, SNOOZE_NEVER, SNOOZE_NEVER,
+ policyAll.metered, policyAll.inferred));
+ mPolicies.add(new NetworkPolicy(template4g, policyAll.cycleDay, policyAll.cycleTimezone,
+ policyAll.warningBytes, policyAll.limitBytes, SNOOZE_NEVER, SNOOZE_NEVER,
+ policyAll.metered, policyAll.inferred));
return true;
} else {
return false;
diff --git a/src/com/android/settings/net/SummaryForAllUidLoader.java b/src/com/android/settings/net/SummaryForAllUidLoader.java
index c01de45..68dc799 100644
--- a/src/com/android/settings/net/SummaryForAllUidLoader.java
+++ b/src/com/android/settings/net/SummaryForAllUidLoader.java
@@ -18,7 +18,7 @@ package com.android.settings.net;
import android.content.AsyncTaskLoader;
import android.content.Context;
-import android.net.INetworkStatsService;
+import android.net.INetworkStatsSession;
import android.net.NetworkStats;
import android.net.NetworkTemplate;
import android.os.Bundle;
@@ -29,7 +29,7 @@ public class SummaryForAllUidLoader extends AsyncTaskLoader<NetworkStats> {
private static final String KEY_START = "start";
private static final String KEY_END = "end";
- private final INetworkStatsService mStatsService;
+ private final INetworkStatsSession mSession;
private final Bundle mArgs;
public static Bundle buildArgs(NetworkTemplate template, long start, long end) {
@@ -40,10 +40,9 @@ public class SummaryForAllUidLoader extends AsyncTaskLoader<NetworkStats> {
return args;
}
- public SummaryForAllUidLoader(
- Context context, INetworkStatsService statsService, Bundle args) {
+ public SummaryForAllUidLoader(Context context, INetworkStatsSession session, Bundle args) {
super(context);
- mStatsService = statsService;
+ mSession = session;
mArgs = args;
}
@@ -60,7 +59,7 @@ public class SummaryForAllUidLoader extends AsyncTaskLoader<NetworkStats> {
final long end = mArgs.getLong(KEY_END);
try {
- return mStatsService.getSummaryForAllUid(template, start, end, false);
+ return mSession.getSummaryForAllUid(template, start, end, false);
} catch (RemoteException e) {
return null;
}
diff --git a/src/com/android/settings/net/UidDetailProvider.java b/src/com/android/settings/net/UidDetailProvider.java
index 57d585b..dd2b8c0 100644
--- a/src/com/android/settings/net/UidDetailProvider.java
+++ b/src/com/android/settings/net/UidDetailProvider.java
@@ -22,6 +22,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
import android.net.TrafficStats;
import android.text.TextUtils;
@@ -39,21 +40,42 @@ public class UidDetailProvider {
mUidDetailCache = new SparseArray<UidDetail>();
}
- public synchronized void clearCache() {
- mUidDetailCache.clear();
+ public void clearCache() {
+ synchronized (mUidDetailCache) {
+ mUidDetailCache.clear();
+ }
}
/**
* Resolve best descriptive label for the given UID.
*/
- public synchronized UidDetail getUidDetail(int uid, boolean blocking) {
- final UidDetail cached = mUidDetailCache.get(uid);
- if (cached != null) {
- return cached;
+ public UidDetail getUidDetail(int uid, boolean blocking) {
+ UidDetail detail;
+
+ synchronized (mUidDetailCache) {
+ detail = mUidDetailCache.get(uid);
+ }
+
+ if (detail != null) {
+ return detail;
} else if (!blocking) {
return null;
}
+ detail = buildUidDetail(uid);
+
+ synchronized (mUidDetailCache) {
+ mUidDetailCache.put(uid, detail);
+ }
+
+ return detail;
+ }
+
+ /**
+ * Build {@link UidDetail} object, blocking until all {@link Drawable}
+ * lookup is finished.
+ */
+ private UidDetail buildUidDetail(int uid) {
final Resources res = mContext.getResources();
final PackageManager pm = mContext.getPackageManager();
@@ -66,19 +88,16 @@ public class UidDetailProvider {
case android.os.Process.SYSTEM_UID:
detail.label = res.getString(R.string.process_kernel_label);
detail.icon = pm.getDefaultActivityIcon();
- mUidDetailCache.put(uid, detail);
return detail;
case TrafficStats.UID_REMOVED:
detail.label = res.getString(R.string.data_usage_uninstalled_apps);
detail.icon = pm.getDefaultActivityIcon();
- mUidDetailCache.put(uid, detail);
return detail;
case TrafficStats.UID_TETHERING:
final ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
Context.CONNECTIVITY_SERVICE);
detail.label = res.getString(Utils.getTetheringLabel(cm));
detail.icon = pm.getDefaultActivityIcon();
- mUidDetailCache.put(uid, detail);
return detail;
}
@@ -113,7 +132,6 @@ public class UidDetailProvider {
detail.label = Integer.toString(uid);
}
- mUidDetailCache.put(uid, detail);
return detail;
}
}
diff --git a/src/com/android/settings/tts/TextToSpeechSettings.java b/src/com/android/settings/tts/TextToSpeechSettings.java
index 517eade..fbcdb4f 100644
--- a/src/com/android/settings/tts/TextToSpeechSettings.java
+++ b/src/com/android/settings/tts/TextToSpeechSettings.java
@@ -192,23 +192,6 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment implements
checkVoiceData(mCurrentEngine);
}
- private void maybeUpdateTtsLanguage(String currentEngine) {
- if (currentEngine != null && mTts != null) {
- final String localeString = mEnginesHelper.getLocalePrefForEngine(
- currentEngine);
- if (localeString != null) {
- final String[] locale = TtsEngines.parseLocalePref(localeString);
- final Locale newLocale = new Locale(locale[0], locale[1], locale[2]);
- final Locale engineLocale = mTts.getLanguage();
-
- if (!newLocale.equals(engineLocale)) {
- if (DBG) Log.d(TAG, "Loading language ahead of sample check : " + locale);
- mTts.setLanguage(newLocale);
- }
- }
- }
- }
-
/**
* Ask the current default engine to return a string of sample text to be
* spoken to the user.
@@ -218,7 +201,6 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment implements
if (TextUtils.isEmpty(currentEngine)) currentEngine = mTts.getDefaultEngine();
- maybeUpdateTtsLanguage(currentEngine);
Locale currentLocale = mTts.getLanguage();
// TODO: This is currently a hidden private API. The intent extras
diff --git a/src/com/android/settings/tts/TtsEngineSettingsFragment.java b/src/com/android/settings/tts/TtsEngineSettingsFragment.java
index 18d1fdb..fcc2f2e 100644
--- a/src/com/android/settings/tts/TtsEngineSettingsFragment.java
+++ b/src/com/android/settings/tts/TtsEngineSettingsFragment.java
@@ -51,6 +51,25 @@ public class TtsEngineSettingsFragment extends SettingsPreferenceFragment implem
private Preference mInstallVoicesPreference;
private Intent mEngineSettingsIntent;
+ private TextToSpeech mTts;
+
+ private final TextToSpeech.OnInitListener mTtsInitListener = new TextToSpeech.OnInitListener() {
+ @Override
+ public void onInit(int status) {
+ if (status != TextToSpeech.SUCCESS) {
+ finishFragment();
+ } else {
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mLocalePreference.setEnabled(true);
+ updateVoiceDetails();
+ }
+ });
+ }
+ }
+ };
+
public TtsEngineSettingsFragment() {
super();
}
@@ -83,7 +102,15 @@ public class TtsEngineSettingsFragment extends SettingsPreferenceFragment implem
}
mInstallVoicesPreference.setEnabled(false);
- updateVoiceDetails();
+ mLocalePreference.setEnabled(false);
+ mTts = new TextToSpeech(getActivity().getApplicationContext(), mTtsInitListener,
+ getEngineName());
+ }
+
+ @Override
+ public void onDestroy() {
+ mTts.shutdown();
+ super.onDestroy();
}
private void updateVoiceDetails() {
@@ -153,8 +180,7 @@ public class TtsEngineSettingsFragment extends SettingsPreferenceFragment implem
mLocalePreference.setValueIndex(selectedLanguageIndex);
} else {
mLocalePreference.setValueIndex(0);
- mEnginesHelper.updateLocalePrefForEngine(getEngineName(),
- availableLangs.get(0));
+ updateLanguageTo(availableLangs.get(0));
}
}
@@ -191,13 +217,23 @@ public class TtsEngineSettingsFragment extends SettingsPreferenceFragment implem
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (preference == mLocalePreference) {
- mEnginesHelper.updateLocalePrefForEngine(getEngineName(), (String) newValue);
+ updateLanguageTo((String) newValue);
return true;
}
return false;
}
+ private void updateLanguageTo(String locale) {
+ mEnginesHelper.updateLocalePrefForEngine(getEngineName(), locale);
+ if (getEngineName().equals(mTts.getCurrentEngine())) {
+ String[] localeArray = TtsEngines.parseLocalePref(locale);
+ if (localeArray != null) {
+ mTts.setLanguage(new Locale(localeArray[0], localeArray[1], localeArray[2]));
+ }
+ }
+ }
+
private String getEngineName() {
return getArguments().getString(TtsEnginePreference.FRAGMENT_ARGS_NAME);
}
diff --git a/src/com/android/settings/users/UserDetailsSettings.java b/src/com/android/settings/users/UserDetailsSettings.java
new file mode 100644
index 0000000..84cabe9
--- /dev/null
+++ b/src/com/android/settings/users/UserDetailsSettings.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2012 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.users;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.preference.CheckBoxPreference;
+import android.preference.EditTextPreference;
+import android.preference.Preference;
+import android.preference.PreferenceGroup;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+
+import com.android.settings.DialogCreatable;
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+
+import java.util.HashMap;
+import java.util.List;
+
+public class UserDetailsSettings extends SettingsPreferenceFragment
+ implements Preference.OnPreferenceChangeListener, DialogCreatable {
+
+ private static final String TAG = "UserDetailsSettings";
+
+ private static final int MENU_REMOVE_USER = Menu.FIRST;
+ private static final int DIALOG_CONFIRM_REMOVE = 1;
+
+ private static final String KEY_USER_NAME = "user_name";
+ private static final String KEY_INSTALLED_APPS = "market_apps_category";
+ private static final String KEY_SYSTEM_APPS = "system_apps_category";
+ public static final String EXTRA_USER_ID = "user_id";
+
+ private static final String[] SYSTEM_APPS = {
+ "com.google.android.browser",
+ "com.google.android.gm",
+ "com.google.android.youtube"
+ };
+
+ static class AppState {
+ boolean dirty;
+ boolean enabled;
+
+ AppState(boolean enabled) {
+ this.enabled = enabled;
+ }
+ }
+
+ private HashMap<String, AppState> mAppStates = new HashMap<String, AppState>();
+ private PreferenceGroup mSystemAppGroup;
+ private PreferenceGroup mInstalledAppGroup;
+ private EditTextPreference mNamePref;
+
+ private IPackageManager mIPm;
+ private PackageManager mPm;
+ private int mUserId;
+ private boolean mNewUser;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ addPreferencesFromResource(R.xml.user_details);
+ Bundle args = getArguments();
+ mNewUser = args == null || args.getInt(EXTRA_USER_ID, -1) == -1;
+ mUserId = mNewUser ? -1 : args.getInt(EXTRA_USER_ID, -1);
+ mIPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
+
+ if (mUserId == -1) {
+ try {
+ mUserId = mIPm.createUser(getString(R.string.user_new_user_name), 0).id;
+ } catch (RemoteException re) {
+ }
+ }
+ mSystemAppGroup = (PreferenceGroup) findPreference(KEY_SYSTEM_APPS);
+ mInstalledAppGroup = (PreferenceGroup) findPreference(KEY_INSTALLED_APPS);
+ mNamePref = (EditTextPreference) findPreference(KEY_USER_NAME);
+ mNamePref.setOnPreferenceChangeListener(this);
+
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mPm = getActivity().getPackageManager();
+ if (mUserId > 0) {
+ initExistingUser();
+ } else {
+ initNewUser();
+ }
+ refreshApps();
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ MenuItem addAccountItem = menu.add(0, MENU_REMOVE_USER, 0,
+ mNewUser ? R.string.user_discard_user_menu : R.string.user_remove_user_menu);
+ addAccountItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM
+ | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ final int itemId = item.getItemId();
+ if (itemId == MENU_REMOVE_USER) {
+ onRemoveUserClicked();
+ return true;
+ } else {
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void initExistingUser() {
+ List<UserInfo> users = mPm.getUsers();
+ UserInfo foundUser = null;
+ for (UserInfo user : users) {
+ if (user.id == mUserId) {
+ foundUser = user;
+ break;
+ }
+ }
+ if (foundUser != null) {
+ mNamePref.setSummary(foundUser.name);
+ mNamePref.setText(foundUser.name);
+ }
+ }
+
+ private void initNewUser() {
+ // TODO: Check if there's already a "New user" and localize
+ mNamePref.setText(getString(R.string.user_new_user_name));
+ mNamePref.setSummary(getString(R.string.user_new_user_name));
+ }
+
+ private void onRemoveUserClicked() {
+ if (mNewUser) {
+ removeUserNow();
+ } else {
+ showDialog(DIALOG_CONFIRM_REMOVE);
+ }
+ }
+
+ private void removeUserNow() {
+ try {
+ mIPm.removeUser(mUserId);
+ } catch (RemoteException re) {
+ // Couldn't remove user. Shouldn't happen
+ Log.e(TAG, "Couldn't remove user " + mUserId + "\n" + re);
+ }
+ finish();
+ }
+
+ private void insertAppInfo(PreferenceGroup group, HashMap<String, AppState> appStateMap,
+ PackageInfo info, boolean defaultState) {
+ if (info != null) {
+ String pkgName = info.packageName;
+ String name = info.applicationInfo.loadLabel(mPm).toString();
+ Drawable icon = info.applicationInfo.loadIcon(mPm);
+ AppState appState = appStateMap.get(info.packageName);
+ boolean enabled = appState == null ? defaultState : appState.enabled;
+ CheckBoxPreference appPref = new CheckBoxPreference(getActivity());
+ appPref.setTitle(name != null ? name : pkgName);
+ appPref.setIcon(icon);
+ appPref.setChecked(enabled);
+ appPref.setKey(pkgName);
+ appPref.setPersistent(false);
+ appPref.setOnPreferenceChangeListener(this);
+ group.addPreference(appPref);
+ }
+ }
+
+ private void refreshApps() {
+ mSystemAppGroup.removeAll();
+ mInstalledAppGroup.removeAll();
+
+ boolean firstTime = mAppStates.isEmpty();
+
+ final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+ mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ List<ResolveInfo> apps = mPm.queryIntentActivities(mainIntent, 0);
+
+ for (ResolveInfo resolveInfo : apps) {
+ PackageInfo info;
+ try {
+ info = mIPm.getPackageInfo(resolveInfo.activityInfo.packageName,
+ 0 /* flags */,
+ mUserId < 0 ? 0 : mUserId);
+ } catch (RemoteException re) {
+ continue;
+ }
+ if (firstTime) {
+ mAppStates.put(resolveInfo.activityInfo.packageName,
+ new AppState(info.applicationInfo.enabled));
+ }
+ if ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ if (mSystemAppGroup.findPreference(info.packageName) != null) {
+ continue;
+ }
+ insertAppInfo(mSystemAppGroup, mAppStates, info, false);
+ } else {
+ if (mInstalledAppGroup.findPreference(info.packageName) != null) {
+ continue;
+ }
+ insertAppInfo(mInstalledAppGroup, mAppStates, info, false);
+ }
+ }
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (preference instanceof CheckBoxPreference) {
+ String packageName = preference.getKey();
+ int newState = ((Boolean) newValue) ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+ : PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
+ try {
+ mIPm.setApplicationEnabledSetting(packageName, newState, 0, mUserId);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Unable to change enabled state of package " + packageName
+ + " for user " + mUserId);
+ }
+ } else if (preference == mNamePref) {
+ String name = (String) newValue;
+ if (TextUtils.isEmpty(name)) {
+ return false;
+ }
+ try {
+ mIPm.updateUserName(mUserId, (String) newValue);
+ mNamePref.setSummary((String) newValue);
+ } catch (RemoteException re) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public Dialog onCreateDialog(int dialogId) {
+ switch (dialogId) {
+ case DIALOG_CONFIRM_REMOVE:
+ return new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.user_confirm_remove_title)
+ .setMessage(R.string.user_confirm_remove_message)
+ .setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ removeUserNow();
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, null)
+ .create();
+ default:
+ return null;
+ }
+ }
+}
diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java
new file mode 100644
index 0000000..9380586
--- /dev/null
+++ b/src/com/android/settings/users/UserSettings.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2012 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.users;
+
+import android.content.pm.UserInfo;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceGroup;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+
+import java.util.List;
+
+public class UserSettings extends SettingsPreferenceFragment
+ implements OnPreferenceClickListener {
+
+ private static final String KEY_USER_LIST = "user_list";
+ private static final int MENU_ADD_USER = Menu.FIRST;
+
+ private PreferenceGroup mUserListCategory;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ addPreferencesFromResource(R.xml.user_settings);
+ mUserListCategory = (PreferenceGroup) findPreference(KEY_USER_LIST);
+
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ updateUserList();
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ MenuItem addAccountItem = menu.add(0, MENU_ADD_USER, 0, R.string.user_add_user_menu);
+ addAccountItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM
+ | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ final int itemId = item.getItemId();
+ if (itemId == MENU_ADD_USER) {
+ onAddUserClicked();
+ return true;
+ } else {
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void onAddUserClicked() {
+ ((PreferenceActivity) getActivity()).startPreferencePanel(
+ UserDetailsSettings.class.getName(), null, R.string.user_details_title,
+ null, this, 0);
+ }
+
+ private void updateUserList() {
+ List<UserInfo> users = getActivity().getPackageManager().getUsers();
+
+ mUserListCategory.removeAll();
+ for (UserInfo user : users) {
+ if (user.id == 0) continue;
+ Preference pref = new Preference(getActivity());
+ pref.setTitle(user.name);
+ pref.setOnPreferenceClickListener(this);
+ pref.setKey("id=" + user.id);
+ mUserListCategory.addPreference(pref);
+ }
+ }
+
+ @Override
+ public boolean onPreferenceClick(Preference pref) {
+ String sid = pref.getKey();
+ if (sid != null && sid.startsWith("id=")) {
+ int id = Integer.parseInt(sid.substring(3));
+ Bundle args = new Bundle();
+ args.putInt(UserDetailsSettings.EXTRA_USER_ID, id);
+ ((PreferenceActivity) getActivity()).startPreferencePanel(
+ UserDetailsSettings.class.getName(),
+ args, 0, pref.getTitle(), this, 0);
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/com/android/settings/vpn2/VpnDialog.java b/src/com/android/settings/vpn2/VpnDialog.java
index aaae887..ca85d99 100644
--- a/src/com/android/settings/vpn2/VpnDialog.java
+++ b/src/com/android/settings/vpn2/VpnDialog.java
@@ -102,8 +102,10 @@ class VpnDialog extends AlertDialog implements TextWatcher,
mName.setText(mProfile.name);
mType.setSelection(mProfile.type);
mServer.setText(mProfile.server);
- mUsername.setText(mProfile.username);
- mPassword.setText(mProfile.password);
+ if (mProfile.saveLogin) {
+ mUsername.setText(mProfile.username);
+ mPassword.setText(mProfile.password);
+ }
mSearchDomains.setText(mProfile.searchDomains);
mDnsServers.setText(mProfile.dnsServers);
mRoutes.setText(mProfile.routes);
@@ -111,7 +113,7 @@ class VpnDialog extends AlertDialog implements TextWatcher,
mL2tpSecret.setText(mProfile.l2tpSecret);
mIpsecIdentifier.setText(mProfile.ipsecIdentifier);
mIpsecSecret.setText(mProfile.ipsecSecret);
- loadCertificates(mIpsecUserCert, Credentials.USER_CERTIFICATE,
+ loadCertificates(mIpsecUserCert, Credentials.USER_PRIVATE_KEY,
0, mProfile.ipsecUserCert);
loadCertificates(mIpsecCaCert, Credentials.CA_CERTIFICATE,
R.string.vpn_no_ca_cert, mProfile.ipsecCaCert);
diff --git a/src/com/android/settings/vpn2/VpnSettings.java b/src/com/android/settings/vpn2/VpnSettings.java
index 655306a..a26623b 100644
--- a/src/com/android/settings/vpn2/VpnSettings.java
+++ b/src/com/android/settings/vpn2/VpnSettings.java
@@ -38,6 +38,7 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView.AdapterContextMenuInfo;
+import android.widget.Toast;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
@@ -324,10 +325,12 @@ public class VpnSettings extends SettingsPreferenceFragment implements
private String[] getDefaultNetwork() throws Exception {
LinkProperties network = mService.getActiveLinkProperties();
if (network == null) {
+ Toast.makeText(getActivity(), R.string.vpn_no_network, Toast.LENGTH_LONG).show();
throw new IllegalStateException("Network is not available");
}
String interfaze = network.getInterfaceName();
if (interfaze == null) {
+ Toast.makeText(getActivity(), R.string.vpn_no_network, Toast.LENGTH_LONG).show();
throw new IllegalStateException("Cannot get the default interface");
}
String gateway = null;
@@ -339,6 +342,7 @@ public class VpnSettings extends SettingsPreferenceFragment implements
}
}
if (gateway == null) {
+ Toast.makeText(getActivity(), R.string.vpn_no_network, Toast.LENGTH_LONG).show();
throw new IllegalStateException("Cannot get the default gateway");
}
return new String[] {interfaze, gateway};
@@ -356,9 +360,12 @@ public class VpnSettings extends SettingsPreferenceFragment implements
String caCert = "";
String serverCert = "";
if (!profile.ipsecUserCert.isEmpty()) {
- byte[] value = mKeyStore.get(Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert);
- privateKey = (value == null) ? null : new String(value, Charsets.UTF_8);
- value = mKeyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert);
+ /*
+ * VPN has a special exception in keystore to allow it to use system
+ * UID certs.
+ */
+ privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert;
+ byte[] value = mKeyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert);
userCert = (value == null) ? null : new String(value, Charsets.UTF_8);
}
if (!profile.ipsecCaCert.isEmpty()) {
@@ -370,7 +377,7 @@ public class VpnSettings extends SettingsPreferenceFragment implements
serverCert = (value == null) ? null : new String(value, Charsets.UTF_8);
}
if (privateKey == null || userCert == null || caCert == null || serverCert == null) {
- // TODO: find out a proper way to handle this. Delete these keys?
+ Toast.makeText(getActivity(), R.string.vpn_missing_cert, Toast.LENGTH_LONG).show();
throw new IllegalStateException("Cannot load credentials");
}
@@ -457,6 +464,11 @@ public class VpnSettings extends SettingsPreferenceFragment implements
}
}
+ @Override
+ protected int getHelpResource() {
+ return R.string.help_url_vpn;
+ }
+
private class VpnPreference extends Preference {
private VpnProfile mProfile;
private int mState = -1;
diff --git a/src/com/android/settings/widget/ChartDataUsageView.java b/src/com/android/settings/widget/ChartDataUsageView.java
index f10ea5e..19de100 100644
--- a/src/com/android/settings/widget/ChartDataUsageView.java
+++ b/src/com/android/settings/widget/ChartDataUsageView.java
@@ -16,6 +16,9 @@
package com.android.settings.widget;
+import static android.net.TrafficStats.GB_IN_BYTES;
+import static android.net.TrafficStats.MB_IN_BYTES;
+
import android.content.Context;
import android.content.res.Resources;
import android.net.NetworkPolicy;
@@ -26,6 +29,7 @@ import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.format.DateUtils;
+import android.text.format.Time;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
@@ -34,16 +38,15 @@ import com.android.internal.util.Objects;
import com.android.settings.R;
import com.android.settings.widget.ChartSweepView.OnSweepListener;
+import java.util.Arrays;
+import java.util.Calendar;
+
/**
* Specific {@link ChartView} that displays {@link ChartNetworkSeriesView} along
* with {@link ChartSweepView} for inspection ranges and warning/limits.
*/
public class ChartDataUsageView extends ChartView {
- private static final long KB_IN_BYTES = 1024;
- private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
- private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
-
private static final int MSG_UPDATE_AXIS = 100;
private static final long DELAY_MILLIS = 250;
@@ -234,7 +237,7 @@ public class ChartDataUsageView extends ChartView {
final long maxSweep = Math.max(mSweepWarning.getValue(), mSweepLimit.getValue());
final long maxSeries = Math.max(mSeries.getMaxVisible(), mDetailSeries.getMaxVisible());
final long maxVisible = Math.max(maxSeries, maxSweep) * 12 / 10;
- final long maxDefault = Math.max(maxVisible, 2 * GB_IN_BYTES);
+ final long maxDefault = Math.max(maxVisible, 50 * MB_IN_BYTES);
newMax = Math.max(maxDefault, newMax);
// only invalidate when vertMax actually changed
@@ -293,7 +296,7 @@ public class ChartDataUsageView extends ChartView {
}
private OnSweepListener mHorizListener = new OnSweepListener() {
- /** {@inheritDoc} */
+ @Override
public void onSweep(ChartSweepView sweep, boolean sweepDone) {
updatePrimaryRange();
@@ -303,7 +306,7 @@ public class ChartDataUsageView extends ChartView {
}
}
- /** {@inheritDoc} */
+ @Override
public void requestEdit(ChartSweepView sweep) {
// ignored
}
@@ -321,7 +324,7 @@ public class ChartDataUsageView extends ChartView {
}
private OnSweepListener mVertListener = new OnSweepListener() {
- /** {@inheritDoc} */
+ @Override
public void onSweep(ChartSweepView sweep, boolean sweepDone) {
if (sweepDone) {
clearUpdateAxisDelayed(sweep);
@@ -338,7 +341,7 @@ public class ChartDataUsageView extends ChartView {
}
}
- /** {@inheritDoc} */
+ @Override
public void requestEdit(ChartSweepView sweep) {
if (sweep == mSweepWarning && mListener != null) {
mListener.requestWarningEdit();
@@ -450,7 +453,7 @@ public class ChartDataUsageView extends ChartView {
}
public static class TimeAxis implements ChartAxis {
- private static final long TICK_INTERVAL = DateUtils.DAY_IN_MILLIS * 7;
+ private static final int FIRST_DAY_OF_WEEK = Calendar.getInstance().getFirstDayOfWeek() - 1;
private long mMin;
private long mMax;
@@ -466,7 +469,7 @@ public class ChartDataUsageView extends ChartView {
return Objects.hashCode(mMin, mMax, mSize);
}
- /** {@inheritDoc} */
+ @Override
public boolean setBounds(long min, long max) {
if (mMin != min || mMax != max) {
mMin = min;
@@ -477,7 +480,7 @@ public class ChartDataUsageView extends ChartView {
}
}
- /** {@inheritDoc} */
+ @Override
public boolean setSize(float size) {
if (mSize != size) {
mSize = size;
@@ -487,35 +490,49 @@ public class ChartDataUsageView extends ChartView {
}
}
- /** {@inheritDoc} */
+ @Override
public float convertToPoint(long value) {
return (mSize * (value - mMin)) / (mMax - mMin);
}
- /** {@inheritDoc} */
+ @Override
public long convertToValue(float point) {
return (long) (mMin + ((point * (mMax - mMin)) / mSize));
}
- /** {@inheritDoc} */
+ @Override
public long buildLabel(Resources res, SpannableStringBuilder builder, long value) {
// TODO: convert to better string
builder.replace(0, builder.length(), Long.toString(value));
return value;
}
- /** {@inheritDoc} */
+ @Override
public float[] getTickPoints() {
- // tick mark for every week
- final int tickCount = (int) ((mMax - mMin) / TICK_INTERVAL);
- final float[] tickPoints = new float[tickCount];
- for (int i = 0; i < tickCount; i++) {
- tickPoints[i] = convertToPoint(mMax - (TICK_INTERVAL * (i + 1)));
+ final float[] ticks = new float[32];
+ int i = 0;
+
+ // tick mark for first day of each week
+ final Time time = new Time();
+ time.set(mMax);
+ time.monthDay -= time.weekDay - FIRST_DAY_OF_WEEK;
+ time.hour = time.minute = time.second = 0;
+
+ time.normalize(true);
+ long timeMillis = time.toMillis(true);
+ while (timeMillis > mMin) {
+ if (timeMillis <= mMax) {
+ ticks[i++] = convertToPoint(timeMillis);
+ }
+ time.monthDay -= 7;
+ time.normalize(true);
+ timeMillis = time.toMillis(true);
}
- return tickPoints;
+
+ return Arrays.copyOf(ticks, i);
}
- /** {@inheritDoc} */
+ @Override
public int shouldAdjustAxis(long value) {
// time axis never adjusts
return 0;
@@ -527,12 +544,14 @@ public class ChartDataUsageView extends ChartView {
private long mMax;
private float mSize;
+ private static final boolean LOG_SCALE = false;
+
@Override
public int hashCode() {
return Objects.hashCode(mMin, mMax, mSize);
}
- /** {@inheritDoc} */
+ @Override
public boolean setBounds(long min, long max) {
if (mMin != min || mMax != max) {
mMin = min;
@@ -543,7 +562,7 @@ public class ChartDataUsageView extends ChartView {
}
}
- /** {@inheritDoc} */
+ @Override
public boolean setSize(float size) {
if (mSize != size) {
mSize = size;
@@ -553,27 +572,35 @@ public class ChartDataUsageView extends ChartView {
}
}
- /** {@inheritDoc} */
+ @Override
public float convertToPoint(long value) {
- // derived polynomial fit to make lower values more visible
- final double normalized = ((double) value - mMin) / (mMax - mMin);
- final double fraction = Math.pow(
- 10, 0.36884343106175121463 * Math.log10(normalized) + -0.04328199452018252624);
- return (float) (fraction * mSize);
+ if (LOG_SCALE) {
+ // derived polynomial fit to make lower values more visible
+ final double normalized = ((double) value - mMin) / (mMax - mMin);
+ final double fraction = Math.pow(10,
+ 0.36884343106175121463 * Math.log10(normalized) + -0.04328199452018252624);
+ return (float) (fraction * mSize);
+ } else {
+ return (mSize * (value - mMin)) / (mMax - mMin);
+ }
}
- /** {@inheritDoc} */
+ @Override
public long convertToValue(float point) {
- final double normalized = point / mSize;
- final double fraction = 1.3102228476089056629
- * Math.pow(normalized, 2.7111774693164631640);
- return (long) (mMin + (fraction * (mMax - mMin)));
+ if (LOG_SCALE) {
+ final double normalized = point / mSize;
+ final double fraction = 1.3102228476089056629
+ * Math.pow(normalized, 2.7111774693164631640);
+ return (long) (mMin + (fraction * (mMax - mMin)));
+ } else {
+ return (long) (mMin + ((point * (mMax - mMin)) / mSize));
+ }
}
private static final Object sSpanSize = new Object();
private static final Object sSpanUnit = new Object();
- /** {@inheritDoc} */
+ @Override
public long buildLabel(Resources res, SpannableStringBuilder builder, long value) {
final CharSequence unit;
@@ -598,26 +625,18 @@ public class ChartDataUsageView extends ChartView {
resultRounded = unitFactor * Math.round(result);
}
- final int[] sizeBounds = findOrCreateSpan(builder, sSpanSize, "^1");
- builder.replace(sizeBounds[0], sizeBounds[1], size);
- final int[] unitBounds = findOrCreateSpan(builder, sSpanUnit, "^2");
- builder.replace(unitBounds[0], unitBounds[1], unit);
+ setText(builder, sSpanSize, size, "^1");
+ setText(builder, sSpanUnit, unit, "^2");
return (long) resultRounded;
}
- /** {@inheritDoc} */
+ @Override
public float[] getTickPoints() {
final long range = mMax - mMin;
- final long tickJump;
- if (range < 6 * GB_IN_BYTES) {
- tickJump = 256 * MB_IN_BYTES;
- } else if (range < 12 * GB_IN_BYTES) {
- tickJump = 512 * MB_IN_BYTES;
- } else {
- tickJump = 1 * GB_IN_BYTES;
- }
+ // target about 16 ticks on screen, rounded to nearest power of 2
+ final long tickJump = roundUpToPowerOfTwo(range / 16);
final int tickCount = (int) (range / tickJump);
final float[] tickPoints = new float[tickCount];
long value = mMin;
@@ -629,7 +648,7 @@ public class ChartDataUsageView extends ChartView {
return tickPoints;
}
- /** {@inheritDoc} */
+ @Override
public int shouldAdjustAxis(long value) {
final float point = convertToPoint(value);
if (point < mSize * 0.1) {
@@ -642,8 +661,8 @@ public class ChartDataUsageView extends ChartView {
}
}
- private static int[] findOrCreateSpan(
- SpannableStringBuilder builder, Object key, CharSequence bootstrap) {
+ private static void setText(
+ SpannableStringBuilder builder, Object key, CharSequence text, String bootstrap) {
int start = builder.getSpanStart(key);
int end = builder.getSpanEnd(key);
if (start == -1) {
@@ -651,7 +670,24 @@ public class ChartDataUsageView extends ChartView {
end = start + bootstrap.length();
builder.setSpan(key, start, end, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
}
- return new int[] { start, end };
+ builder.replace(start, end, text);
}
+ private static long roundUpToPowerOfTwo(long i) {
+ // NOTE: borrowed from Hashtable.roundUpToPowerOfTwo()
+
+ i--; // If input is a power of two, shift its high-order bit right
+
+ // "Smear" the high-order bit all the way to the right
+ i |= i >>> 1;
+ i |= i >>> 2;
+ i |= i >>> 4;
+ i |= i >>> 8;
+ i |= i >>> 16;
+ i |= i >>> 32;
+
+ i++;
+
+ return i > 0 ? i : Long.MAX_VALUE;
+ }
}
diff --git a/src/com/android/settings/widget/ChartGridView.java b/src/com/android/settings/widget/ChartGridView.java
index b930c62..f358ada 100644
--- a/src/com/android/settings/widget/ChartGridView.java
+++ b/src/com/android/settings/widget/ChartGridView.java
@@ -16,6 +16,8 @@
package com.android.settings.widget;
+import static com.android.settings.DataUsageSummary.formatDateRange;
+
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -30,7 +32,6 @@ import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
-import com.android.settings.DataUsageSummary;
import com.android.settings.R;
import com.google.common.base.Preconditions;
@@ -82,8 +83,8 @@ public class ChartGridView extends View {
void setBounds(long start, long end) {
final Context context = getContext();
- mLayoutStart = makeLayout(DataUsageSummary.formatDateRange(context, start, start, true));
- mLayoutEnd = makeLayout(DataUsageSummary.formatDateRange(context, end, end, true));
+ mLayoutStart = makeLayout(formatDateRange(context, start, start));
+ mLayoutEnd = makeLayout(formatDateRange(context, end, end));
invalidate();
}
diff --git a/src/com/android/settings/widget/ChartNetworkSeriesView.java b/src/com/android/settings/widget/ChartNetworkSeriesView.java
index 7a4617b..5a37bfc 100644
--- a/src/com/android/settings/widget/ChartNetworkSeriesView.java
+++ b/src/com/android/settings/widget/ChartNetworkSeriesView.java
@@ -44,6 +44,8 @@ public class ChartNetworkSeriesView extends View {
private static final String TAG = "ChartNetworkSeriesView";
private static final boolean LOGD = false;
+ private static final boolean ESTIMATE_ENABLED = false;
+
private ChartAxis mHoriz;
private ChartAxis mVert;
@@ -252,36 +254,38 @@ public class ChartNetworkSeriesView extends View {
mMax = totalData;
- // build estimated data
- mPathEstimate.moveTo(lastX, lastY);
+ if (ESTIMATE_ENABLED) {
+ // build estimated data
+ mPathEstimate.moveTo(lastX, lastY);
- final long now = System.currentTimeMillis();
- final long bucketDuration = mStats.getBucketDuration();
+ final long now = System.currentTimeMillis();
+ final long bucketDuration = mStats.getBucketDuration();
- // long window is average over two weeks
- entry = mStats.getValues(lastTime - WEEK_IN_MILLIS * 2, lastTime, now, entry);
- final long longWindow = (entry.rxBytes + entry.txBytes) * bucketDuration
- / entry.bucketDuration;
+ // long window is average over two weeks
+ entry = mStats.getValues(lastTime - WEEK_IN_MILLIS * 2, lastTime, now, entry);
+ final long longWindow = (entry.rxBytes + entry.txBytes) * bucketDuration
+ / entry.bucketDuration;
- long futureTime = 0;
- while (lastX < width) {
- futureTime += bucketDuration;
+ long futureTime = 0;
+ while (lastX < width) {
+ futureTime += bucketDuration;
- // short window is day average last week
- final long lastWeekTime = lastTime - WEEK_IN_MILLIS + (futureTime % WEEK_IN_MILLIS);
- entry = mStats.getValues(lastWeekTime - DAY_IN_MILLIS, lastWeekTime, now, entry);
- final long shortWindow = (entry.rxBytes + entry.txBytes) * bucketDuration
- / entry.bucketDuration;
+ // short window is day average last week
+ final long lastWeekTime = lastTime - WEEK_IN_MILLIS + (futureTime % WEEK_IN_MILLIS);
+ entry = mStats.getValues(lastWeekTime - DAY_IN_MILLIS, lastWeekTime, now, entry);
+ final long shortWindow = (entry.rxBytes + entry.txBytes) * bucketDuration
+ / entry.bucketDuration;
- totalData += (longWindow * 7 + shortWindow * 3) / 10;
+ totalData += (longWindow * 7 + shortWindow * 3) / 10;
- lastX = mHoriz.convertToPoint(lastTime + futureTime);
- lastY = mVert.convertToPoint(totalData);
+ lastX = mHoriz.convertToPoint(lastTime + futureTime);
+ lastY = mVert.convertToPoint(totalData);
- mPathEstimate.lineTo(lastX, lastY);
- }
+ mPathEstimate.lineTo(lastX, lastY);
+ }
- mMaxEstimate = totalData;
+ mMaxEstimate = totalData;
+ }
invalidate();
}
@@ -291,7 +295,7 @@ public class ChartNetworkSeriesView extends View {
}
public void setEstimateVisible(boolean estimateVisible) {
- mEstimateVisible = estimateVisible;
+ mEstimateVisible = ESTIMATE_ENABLED ? estimateVisible : false;
invalidate();
}
diff --git a/src/com/android/settings/widget/InvertedChartAxis.java b/src/com/android/settings/widget/InvertedChartAxis.java
index 2d820d9..6875749 100644
--- a/src/com/android/settings/widget/InvertedChartAxis.java
+++ b/src/com/android/settings/widget/InvertedChartAxis.java
@@ -30,33 +30,33 @@ public class InvertedChartAxis implements ChartAxis {
mWrapped = wrapped;
}
- /** {@inheritDoc} */
+ @Override
public boolean setBounds(long min, long max) {
return mWrapped.setBounds(min, max);
}
- /** {@inheritDoc} */
+ @Override
public boolean setSize(float size) {
mSize = size;
return mWrapped.setSize(size);
}
- /** {@inheritDoc} */
+ @Override
public float convertToPoint(long value) {
return mSize - mWrapped.convertToPoint(value);
}
- /** {@inheritDoc} */
+ @Override
public long convertToValue(float point) {
return mWrapped.convertToValue(mSize - point);
}
- /** {@inheritDoc} */
+ @Override
public long buildLabel(Resources res, SpannableStringBuilder builder, long value) {
return mWrapped.buildLabel(res, builder, value);
}
- /** {@inheritDoc} */
+ @Override
public float[] getTickPoints() {
final float[] points = mWrapped.getTickPoints();
for (int i = 0; i < points.length; i++) {
@@ -65,7 +65,7 @@ public class InvertedChartAxis implements ChartAxis {
return points;
}
- /** {@inheritDoc} */
+ @Override
public int shouldAdjustAxis(long value) {
return mWrapped.shouldAdjustAxis(value);
}
diff --git a/src/com/android/settings/widget/SettingsAppWidgetProvider.java b/src/com/android/settings/widget/SettingsAppWidgetProvider.java
index 656d072..1d411f7 100644
--- a/src/com/android/settings/widget/SettingsAppWidgetProvider.java
+++ b/src/com/android/settings/widget/SettingsAppWidgetProvider.java
@@ -97,9 +97,9 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
/**
* Minimum and maximum brightnesses. Don't go to 0 since that makes the display unusable
*/
- private static final int MINIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_DIM + 10;
- private static final int MAXIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_ON;
- private static final int DEFAULT_BACKLIGHT = (int) (android.os.Power.BRIGHTNESS_ON * 0.4f);
+ private static final int MINIMUM_BACKLIGHT = android.os.PowerManager.BRIGHTNESS_DIM + 10;
+ private static final int MAXIMUM_BACKLIGHT = android.os.PowerManager.BRIGHTNESS_ON;
+ private static final int DEFAULT_BACKLIGHT = (int) (android.os.PowerManager.BRIGHTNESS_ON * 0.4f);
/** Minimum brightness at which the indicator is shown at half-full and ON */
private static final int HALF_BRIGHTNESS_THRESHOLD = (int) (0.3 * MAXIMUM_BACKLIGHT);
/** Minimum brightness at which the indicator is shown at full */
@@ -605,9 +605,10 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
@Override
public void onEnabled(Context context) {
+ Class clazz = com.android.settings.widget.SettingsAppWidgetProvider.class;
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(
- new ComponentName("com.android.settings", ".widget.SettingsAppWidgetProvider"),
+ new ComponentName(context.getPackageName(), clazz.getName()),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
checkObserver(context);
@@ -618,7 +619,7 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
Class clazz = com.android.settings.widget.SettingsAppWidgetProvider.class;
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(
- new ComponentName("com.android.settings", ".widget.SettingsAppWidgetProvider"),
+ new ComponentName(context.getPackageName(), clazz.getName()),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
if (sSettingsObserver != null) {
diff --git a/src/com/android/settings/wifi/AdvancedWifiSettings.java b/src/com/android/settings/wifi/AdvancedWifiSettings.java
index c213512..bb50d2a 100644
--- a/src/com/android/settings/wifi/AdvancedWifiSettings.java
+++ b/src/com/android/settings/wifi/AdvancedWifiSettings.java
@@ -43,7 +43,7 @@ public class AdvancedWifiSettings extends SettingsPreferenceFragment
private static final String KEY_FREQUENCY_BAND = "frequency_band";
private static final String KEY_NOTIFY_OPEN_NETWORKS = "notify_open_networks";
private static final String KEY_SLEEP_POLICY = "sleep_policy";
- private static final String KEY_ENABLE_WIFI_WATCHDOG = "wifi_enable_watchdog_service";
+ private static final String KEY_POOR_NETWORK_DETECTION = "wifi_poor_network_detection";
private WifiManager mWifiManager;
@@ -73,14 +73,15 @@ public class AdvancedWifiSettings extends SettingsPreferenceFragment
Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0) == 1);
notifyOpenNetworks.setEnabled(mWifiManager.isWifiEnabled());
- CheckBoxPreference watchdogEnabled =
- (CheckBoxPreference) findPreference(KEY_ENABLE_WIFI_WATCHDOG);
- if (watchdogEnabled != null) {
- watchdogEnabled.setChecked(Secure.getInt(getContentResolver(),
- Secure.WIFI_WATCHDOG_ON, 1) == 1);
-
- //TODO: Bring this back after changing watchdog behavior
- getPreferenceScreen().removePreference(watchdogEnabled);
+ CheckBoxPreference poorNetworkDetection =
+ (CheckBoxPreference) findPreference(KEY_POOR_NETWORK_DETECTION);
+ if (poorNetworkDetection != null) {
+ if (Utils.isWifiOnly(getActivity())) {
+ getPreferenceScreen().removePreference(poorNetworkDetection);
+ } else {
+ poorNetworkDetection.setChecked(Secure.getInt(getContentResolver(),
+ Secure.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED, 1) == 1);
+ }
}
ListPreference frequencyPref = (ListPreference) findPreference(KEY_FREQUENCY_BAND);
@@ -143,9 +144,9 @@ public class AdvancedWifiSettings extends SettingsPreferenceFragment
Secure.putInt(getContentResolver(),
Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
((CheckBoxPreference) preference).isChecked() ? 1 : 0);
- } else if (KEY_ENABLE_WIFI_WATCHDOG.equals(key)) {
+ } else if (KEY_POOR_NETWORK_DETECTION.equals(key)) {
Secure.putInt(getContentResolver(),
- Secure.WIFI_WATCHDOG_ON,
+ Secure.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED,
((CheckBoxPreference) preference).isChecked() ? 1 : 0);
} else {
return super.onPreferenceTreeClick(screen, preference);
diff --git a/src/com/android/settings/wifi/WifiConfigController.java b/src/com/android/settings/wifi/WifiConfigController.java
index c64a225..2598a0e 100644
--- a/src/com/android/settings/wifi/WifiConfigController.java
+++ b/src/com/android/settings/wifi/WifiConfigController.java
@@ -33,12 +33,13 @@ import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiConfiguration.ProxySettings;
import android.net.wifi.WifiConfiguration.Status;
import android.net.wifi.WifiInfo;
-import android.net.wifi.WpsInfo;
+import android.os.Handler;
import android.security.Credentials;
import android.security.KeyStore;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
+import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -46,6 +47,7 @@ import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
+import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
@@ -61,7 +63,9 @@ import java.util.Iterator;
*/
public class WifiConfigController implements TextWatcher,
View.OnClickListener, AdapterView.OnItemSelectedListener {
- private static final String KEYSTORE_SPACE = "keystore://";
+ private static final String KEYSTORE_SPACE = WifiConfiguration.KEYSTORE_URI;
+
+ private static final String PHASE2_PREFIX = "auth=";
private final WifiConfigUiBase mConfigUi;
private final View mView;
@@ -87,12 +91,6 @@ public class WifiConfigController implements TextWatcher,
private static final int DHCP = 0;
private static final int STATIC_IP = 1;
- /* These values come from "wifi_network_setup" resource array */
- public static final int MANUAL = 0;
- public static final int WPS_PBC = 1;
- public static final int WPS_KEYPAD = 2;
- public static final int WPS_DISPLAY = 3;
-
/* These values come from "wifi_proxy_settings" resource array */
public static final int PROXY_NONE = 0;
public static final int PROXY_STATIC = 1;
@@ -105,7 +103,6 @@ public class WifiConfigController implements TextWatcher,
private static final String TAG = "WifiConfigController";
- private Spinner mNetworkSetupSpinner;
private Spinner mIpSettingsSpinner;
private TextView mIpAddressView;
private TextView mGatewayView;
@@ -125,12 +122,16 @@ public class WifiConfigController implements TextWatcher,
// True when this instance is used in SetupWizard XL context.
private final boolean mInXlSetupWizard;
+ private final Handler mTextViewChangedHandler;
+
static boolean requireKeyStore(WifiConfiguration config) {
if (config == null) {
return false;
}
- String values[] = {config.ca_cert.value(), config.client_cert.value(),
- config.private_key.value()};
+ if (!TextUtils.isEmpty(config.key_id.value())) {
+ return true;
+ }
+ String values[] = { config.ca_cert.value(), config.client_cert.value() };
for (String value : values) {
if (value != null && value.startsWith(KEYSTORE_SPACE)) {
return true;
@@ -150,9 +151,15 @@ public class WifiConfigController implements TextWatcher,
accessPoint.security;
mEdit = edit;
+ mTextViewChangedHandler = new Handler();
final Context context = mConfigUi.getContext();
final Resources resources = context.getResources();
+ mIpSettingsSpinner = (Spinner) mView.findViewById(R.id.ip_settings);
+ mIpSettingsSpinner.setOnItemSelectedListener(this);
+ mProxySettingsSpinner = (Spinner) mView.findViewById(R.id.proxy_settings);
+ mProxySettingsSpinner.setOnItemSelectedListener(this);
+
if (mAccessPoint == null) { // new network
mConfigUi.setTitle(R.string.wifi_add_network);
@@ -172,15 +179,16 @@ public class WifiConfigController implements TextWatcher,
} else {
mView.findViewById(R.id.type).setVisibility(View.VISIBLE);
}
+
+ showIpConfigFields();
+ showProxyFields();
+ mView.findViewById(R.id.wifi_advanced_toggle).setVisibility(View.VISIBLE);
+ mView.findViewById(R.id.wifi_advanced_togglebox).setOnClickListener(this);
+
mConfigUi.setSubmitButton(context.getString(R.string.wifi_save));
} else {
mConfigUi.setTitle(mAccessPoint.ssid);
- mIpSettingsSpinner = (Spinner) mView.findViewById(R.id.ip_settings);
- mIpSettingsSpinner.setOnItemSelectedListener(this);
- mProxySettingsSpinner = (Spinner) mView.findViewById(R.id.proxy_settings);
- mProxySettingsSpinner.setOnItemSelectedListener(this);
-
ViewGroup group = (ViewGroup) mView.findViewById(R.id.info);
DetailedState state = mAccessPoint.getState();
@@ -222,18 +230,6 @@ public class WifiConfigController implements TextWatcher,
} else {
mProxySettingsSpinner.setSelection(PROXY_NONE);
}
-
- if (config.status == Status.DISABLED &&
- config.disableReason == WifiConfiguration.DISABLED_DNS_FAILURE) {
- addRow(group, R.string.wifi_disabled_heading,
- context.getString(R.string.wifi_disabled_help));
- }
-
- }
-
- /* Show network setup options only for a new network */
- if (mAccessPoint.networkId == INVALID_NETWORK_ID && mAccessPoint.wpsAvailable) {
- showNetworkSetupFields();
}
if (mAccessPoint.networkId == INVALID_NETWORK_ID || mEdit) {
@@ -277,15 +273,14 @@ public class WifiConfigController implements TextWatcher,
}
/* show submit button if password, ip and proxy settings are valid */
- private void enableSubmitIfAppropriate() {
+ void enableSubmitIfAppropriate() {
Button submit = mConfigUi.getSubmitButton();
if (submit == null) return;
- boolean enabled = false;
+ boolean enabled = false;
boolean passwordInvalid = false;
- /* Check password invalidity for manual network set up alone */
- if (chosenNetworkSetupMethod() == MANUAL &&
+ if (mPasswordView != null &&
((mAccessPointSecurity == AccessPoint.SECURITY_WEP && mPasswordView.length() == 0) ||
(mAccessPointSecurity == AccessPoint.SECURITY_PSK && mPasswordView.length() < 8))) {
passwordInvalid = true;
@@ -364,16 +359,19 @@ public class WifiConfigController implements TextWatcher,
config.eap.setValue((String) mEapMethodSpinner.getSelectedItem());
config.phase2.setValue((mPhase2Spinner.getSelectedItemPosition() == 0) ? "" :
- "auth=" + mPhase2Spinner.getSelectedItem());
+ PHASE2_PREFIX + mPhase2Spinner.getSelectedItem());
config.ca_cert.setValue((mEapCaCertSpinner.getSelectedItemPosition() == 0) ? "" :
KEYSTORE_SPACE + Credentials.CA_CERTIFICATE +
(String) mEapCaCertSpinner.getSelectedItem());
config.client_cert.setValue((mEapUserCertSpinner.getSelectedItemPosition() == 0) ?
"" : KEYSTORE_SPACE + Credentials.USER_CERTIFICATE +
(String) mEapUserCertSpinner.getSelectedItem());
- config.private_key.setValue((mEapUserCertSpinner.getSelectedItemPosition() == 0) ?
- "" : KEYSTORE_SPACE + Credentials.USER_PRIVATE_KEY +
+ final boolean isEmptyKeyId = (mEapUserCertSpinner.getSelectedItemPosition() == 0);
+ config.key_id.setValue(isEmptyKeyId ? "" : Credentials.USER_PRIVATE_KEY +
(String) mEapUserCertSpinner.getSelectedItem());
+ config.engine.setValue(isEmptyKeyId ? WifiConfiguration.ENGINE_DISABLE :
+ WifiConfiguration.ENGINE_ENABLE);
+ config.engine_id.setValue(isEmptyKeyId ? "" : WifiConfiguration.KEYSTORE_ENGINE_ID);
config.identity.setValue((mEapIdentityView.length() == 0) ? "" :
mEapIdentityView.getText().toString());
config.anonymous_identity.setValue((mEapAnonymousView.length() == 0) ? "" :
@@ -411,7 +409,7 @@ public class WifiConfigController implements TextWatcher,
mProxySettingsSpinner.getSelectedItemPosition() == PROXY_STATIC) ?
ProxySettings.STATIC : ProxySettings.NONE;
- if (mProxySettings == ProxySettings.STATIC) {
+ if (mProxySettings == ProxySettings.STATIC && mProxyHostView != null) {
String host = mProxyHostView.getText().toString();
String portStr = mProxyPortView.getText().toString();
String exclusionList = mProxyExclusionListView.getText().toString();
@@ -434,7 +432,11 @@ public class WifiConfigController implements TextWatcher,
}
private int validateIpConfigFields(LinkProperties linkProperties) {
+ if (mIpAddressView == null) return 0;
+
String ipAddr = mIpAddressView.getText().toString();
+ if (TextUtils.isEmpty(ipAddr)) return R.string.wifi_ip_settings_invalid_ip_address;
+
InetAddress inetAddr = null;
try {
inetAddr = NetworkUtils.numericToInetAddress(ipAddr);
@@ -445,31 +447,52 @@ public class WifiConfigController implements TextWatcher,
int networkPrefixLength = -1;
try {
networkPrefixLength = Integer.parseInt(mNetworkPrefixLengthView.getText().toString());
+ if (networkPrefixLength < 0 || networkPrefixLength > 32) {
+ return R.string.wifi_ip_settings_invalid_network_prefix_length;
+ }
+ linkProperties.addLinkAddress(new LinkAddress(inetAddr, networkPrefixLength));
} catch (NumberFormatException e) {
- // Use -1
- }
- if (networkPrefixLength < 0 || networkPrefixLength > 32) {
- return R.string.wifi_ip_settings_invalid_network_prefix_length;
+ // Set the hint as default after user types in ip address
+ mNetworkPrefixLengthView.setText(mConfigUi.getContext().getString(
+ R.string.wifi_network_prefix_length_hint));
}
- linkProperties.addLinkAddress(new LinkAddress(inetAddr, networkPrefixLength));
String gateway = mGatewayView.getText().toString();
- InetAddress gatewayAddr = null;
- try {
- gatewayAddr = NetworkUtils.numericToInetAddress(gateway);
- } catch (IllegalArgumentException e) {
- return R.string.wifi_ip_settings_invalid_gateway;
+ if (TextUtils.isEmpty(gateway)) {
+ try {
+ //Extract a default gateway from IP address
+ InetAddress netPart = NetworkUtils.getNetworkPart(inetAddr, networkPrefixLength);
+ byte[] addr = netPart.getAddress();
+ addr[addr.length-1] = 1;
+ mGatewayView.setText(InetAddress.getByAddress(addr).getHostAddress());
+ } catch (RuntimeException ee) {
+ } catch (java.net.UnknownHostException u) {
+ }
+ } else {
+ InetAddress gatewayAddr = null;
+ try {
+ gatewayAddr = NetworkUtils.numericToInetAddress(gateway);
+ } catch (IllegalArgumentException e) {
+ return R.string.wifi_ip_settings_invalid_gateway;
+ }
+ linkProperties.addRoute(new RouteInfo(gatewayAddr));
}
- linkProperties.addRoute(new RouteInfo(gatewayAddr));
String dns = mDns1View.getText().toString();
InetAddress dnsAddr = null;
- try {
- dnsAddr = NetworkUtils.numericToInetAddress(dns);
- } catch (IllegalArgumentException e) {
- return R.string.wifi_ip_settings_invalid_dns;
+
+ if (TextUtils.isEmpty(dns)) {
+ //If everything else is valid, provide hint as a default option
+ mDns1View.setText(mConfigUi.getContext().getString(R.string.wifi_dns1_hint));
+ } else {
+ try {
+ dnsAddr = NetworkUtils.numericToInetAddress(dns);
+ } catch (IllegalArgumentException e) {
+ return R.string.wifi_ip_settings_invalid_dns;
+ }
+ linkProperties.addDns(dnsAddr);
}
- linkProperties.addDns(dnsAddr);
+
if (mDns2View.length() > 0) {
dns = mDns2View.getText().toString();
try {
@@ -482,39 +505,6 @@ public class WifiConfigController implements TextWatcher,
return 0;
}
- int chosenNetworkSetupMethod() {
- if (mNetworkSetupSpinner != null) {
- return mNetworkSetupSpinner.getSelectedItemPosition();
- }
- return MANUAL;
- }
-
- WpsInfo getWpsConfig() {
- WpsInfo config = new WpsInfo();
- switch (mNetworkSetupSpinner.getSelectedItemPosition()) {
- case WPS_PBC:
- config.setup = WpsInfo.PBC;
- break;
- case WPS_KEYPAD:
- config.setup = WpsInfo.KEYPAD;
- break;
- case WPS_DISPLAY:
- config.setup = WpsInfo.DISPLAY;
- break;
- default:
- config.setup = WpsInfo.INVALID;
- Log.e(TAG, "WPS not selected type");
- return config;
- }
- config.pin = ((TextView) mView.findViewById(R.id.wps_pin)).getText().toString();
- config.BSSID = (mAccessPoint != null) ? mAccessPoint.bssid : null;
-
- config.proxySettings = mProxySettings;
- config.ipAssignment = mIpAssignment;
- config.linkProperties = new LinkProperties(mLinkProperties);
- return config;
- }
-
private void showSecurityFields() {
if (mInXlSetupWizard) {
// Note: XL SetupWizard won't hide "EAP" settings here.
@@ -560,11 +550,18 @@ public class WifiConfigController implements TextWatcher,
if (mAccessPoint != null && mAccessPoint.networkId != INVALID_NETWORK_ID) {
WifiConfiguration config = mAccessPoint.getConfig();
setSelection(mEapMethodSpinner, config.eap.value());
- setSelection(mPhase2Spinner, config.phase2.value());
- setCertificate(mEapCaCertSpinner, Credentials.CA_CERTIFICATE,
+
+ final String phase2Method = config.phase2.value();
+ if (phase2Method != null && phase2Method.startsWith(PHASE2_PREFIX)) {
+ setSelection(mPhase2Spinner, phase2Method.substring(PHASE2_PREFIX.length()));
+ } else {
+ setSelection(mPhase2Spinner, phase2Method);
+ }
+
+ setCertificate(mEapCaCertSpinner, KEYSTORE_SPACE + Credentials.CA_CERTIFICATE,
config.ca_cert.value());
setCertificate(mEapUserCertSpinner, Credentials.USER_PRIVATE_KEY,
- config.private_key.value());
+ config.key_id.value());
mEapIdentityView.setText(config.identity.value());
mEapAnonymousView.setText(config.anonymous_identity.value());
}
@@ -585,33 +582,6 @@ public class WifiConfigController implements TextWatcher,
mView.findViewById(R.id.l_anonymous).setVisibility(View.VISIBLE);
}
}
-
- private void showNetworkSetupFields() {
- mView.findViewById(R.id.setup_fields).setVisibility(View.VISIBLE);
-
- if (mNetworkSetupSpinner == null) {
- mNetworkSetupSpinner = (Spinner) mView.findViewById(R.id.network_setup);
- mNetworkSetupSpinner.setOnItemSelectedListener(this);
- }
-
- int pos = mNetworkSetupSpinner.getSelectedItemPosition();
-
- /* Show pin text input if needed */
- if (pos == WPS_KEYPAD) {
- mView.findViewById(R.id.wps_fields).setVisibility(View.VISIBLE);
- } else {
- mView.findViewById(R.id.wps_fields).setVisibility(View.GONE);
- }
-
- /* show/hide manual security fields appropriately */
- if ((pos == WPS_DISPLAY) || (pos == WPS_KEYPAD)
- || (pos == WPS_PBC)) {
- mView.findViewById(R.id.security_fields).setVisibility(View.GONE);
- } else {
- mView.findViewById(R.id.security_fields).setVisibility(View.VISIBLE);
- }
-
- }
private void showIpConfigFields() {
WifiConfiguration config = null;
@@ -724,7 +694,6 @@ public class WifiConfigController implements TextWatcher,
}
private void setCertificate(Spinner spinner, String prefix, String cert) {
- prefix = KEYSTORE_SPACE + prefix;
if (cert != null && cert.startsWith(prefix)) {
setSelection(spinner, cert.substring(prefix.length()));
}
@@ -749,7 +718,11 @@ public class WifiConfigController implements TextWatcher,
@Override
public void afterTextChanged(Editable s) {
- enableSubmitIfAppropriate();
+ mTextViewChangedHandler.post(new Runnable() {
+ public void run() {
+ enableSubmitIfAppropriate();
+ }
+ });
}
@Override
@@ -765,10 +738,14 @@ public class WifiConfigController implements TextWatcher,
@Override
public void onClick(View view) {
if (view.getId() == R.id.show_password) {
+ int pos = mPasswordView.getSelectionEnd();
mPasswordView.setInputType(
InputType.TYPE_CLASS_TEXT | (((CheckBox) view).isChecked() ?
InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD :
InputType.TYPE_TEXT_VARIATION_PASSWORD));
+ if (pos >= 0) {
+ ((EditText)mPasswordView).setSelection(pos);
+ }
} else if (view.getId() == R.id.wifi_advanced_togglebox) {
if (((CheckBox) view).isChecked()) {
mView.findViewById(R.id.wifi_advanced_fields).setVisibility(View.VISIBLE);
@@ -785,8 +762,6 @@ public class WifiConfigController implements TextWatcher,
showSecurityFields();
} else if (parent == mEapMethodSpinner) {
showSecurityFields();
- } else if (parent == mNetworkSetupSpinner) {
- showNetworkSetupFields();
} else if (parent == mProxySettingsSpinner) {
showProxyFields();
} else {
diff --git a/src/com/android/settings/wifi/WifiDialog.java b/src/com/android/settings/wifi/WifiDialog.java
index f980d0c..82b0cc6 100644
--- a/src/com/android/settings/wifi/WifiDialog.java
+++ b/src/com/android/settings/wifi/WifiDialog.java
@@ -56,6 +56,9 @@ class WifiDialog extends AlertDialog implements WifiConfigUiBase {
setInverseBackgroundForced(true);
mController = new WifiConfigController(this, mView, mAccessPoint, mEdit);
super.onCreate(savedInstanceState);
+ /* During creation, the submit button can be unavailable to determine
+ * visibility. Right after creation, update button visibility */
+ mController.enableSubmitIfAppropriate();
}
@Override
diff --git a/src/com/android/settings/wifi/WifiPickerActivity.java b/src/com/android/settings/wifi/WifiPickerActivity.java
index 6a2f865..12612b5 100644
--- a/src/com/android/settings/wifi/WifiPickerActivity.java
+++ b/src/com/android/settings/wifi/WifiPickerActivity.java
@@ -29,6 +29,8 @@ public class WifiPickerActivity extends PreferenceActivity implements ButtonBarH
private static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
private static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
private static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
+ private static final String EXTRA_WIFI_SHOW_ACTION_BAR = "wifi_show_action_bar";
+ private static final String EXTRA_WIFI_SHOW_MENUS = "wifi_show_menus";
@Override
public Intent getIntent() {
@@ -67,6 +69,14 @@ public class WifiPickerActivity extends PreferenceActivity implements ButtonBarH
intent.putExtra(EXTRA_PREFS_SET_BACK_TEXT,
orgIntent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT));
}
+ if (orgIntent.hasExtra(EXTRA_WIFI_SHOW_ACTION_BAR)) {
+ intent.putExtra(EXTRA_WIFI_SHOW_ACTION_BAR,
+ orgIntent.getBooleanExtra(EXTRA_WIFI_SHOW_ACTION_BAR, true));
+ }
+ if (orgIntent.hasExtra(EXTRA_WIFI_SHOW_MENUS)) {
+ intent.putExtra(EXTRA_WIFI_SHOW_MENUS,
+ orgIntent.getBooleanExtra(EXTRA_WIFI_SHOW_MENUS, true));
+ }
if (resultTo == null) {
startActivity(intent);
diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java
index 29c1229..28b0f36 100644
--- a/src/com/android/settings/wifi/WifiSettings.java
+++ b/src/com/android/settings/wifi/WifiSettings.java
@@ -20,23 +20,23 @@ import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
import android.app.ActionBar;
import android.app.Activity;
-import android.app.AlertDialog;
import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.wifi.ScanResult;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.net.wifi.WpsResult;
+import android.net.wifi.WpsInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -45,22 +45,30 @@ import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
import android.security.Credentials;
import android.security.KeyStore;
+import android.util.AttributeSet;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Gravity;
+import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
import android.widget.AdapterView.AdapterContextMenuInfo;
+import android.widget.ImageButton;
+import android.widget.PopupMenu;
+import android.widget.PopupMenu.OnMenuItemClickListener;
+import android.widget.RelativeLayout;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
-import com.android.internal.util.AsyncChannel;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.wifi.p2p.WifiP2pSettings;
import java.util.ArrayList;
import java.util.Collection;
@@ -70,27 +78,29 @@ import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
- * This currently provides three types of UI.
+ * Two types of UI are provided here.
*
- * Two are for phones with relatively small screens: "for SetupWizard" and "for usual Settings".
- * Users just need to launch WifiSettings Activity as usual. The request will be appropriately
- * handled by ActivityManager, and they will have appropriate look-and-feel with this fragment.
+ * The first is for "usual Settings", appearing as any other Setup fragment.
*
- * Third type is for Setup Wizard with X-Large, landscape UI. Users need to launch
- * {@link WifiSettingsForSetupWizardXL} Activity, which contains this fragment but also has
- * other decorations specific to that screen.
+ * The second is for Setup Wizard, with a simplified interface that hides the action bar
+ * and menus.
*/
public class WifiSettings extends SettingsPreferenceFragment
implements DialogInterface.OnClickListener {
private static final String TAG = "WifiSettings";
- private static final int MENU_ID_SCAN = Menu.FIRST;
- private static final int MENU_ID_ADD_NETWORK = Menu.FIRST + 1;
- private static final int MENU_ID_ADVANCED = Menu.FIRST + 2;
- private static final int MENU_ID_CONNECT = Menu.FIRST + 3;
- private static final int MENU_ID_FORGET = Menu.FIRST + 4;
- private static final int MENU_ID_MODIFY = Menu.FIRST + 5;
+ private static final int MENU_ID_WPS_PBC = Menu.FIRST;
+ private static final int MENU_ID_WPS_PIN = Menu.FIRST + 1;
+ private static final int MENU_ID_P2P = Menu.FIRST + 2;
+ private static final int MENU_ID_ADD_NETWORK = Menu.FIRST + 3;
+ private static final int MENU_ID_ADVANCED = Menu.FIRST + 4;
+ private static final int MENU_ID_SCAN = Menu.FIRST + 5;
+ private static final int MENU_ID_CONNECT = Menu.FIRST + 6;
+ private static final int MENU_ID_FORGET = Menu.FIRST + 7;
+ private static final int MENU_ID_MODIFY = Menu.FIRST + 8;
private static final int WIFI_DIALOG_ID = 1;
+ private static final int WPS_PBC_DIALOG_ID = 2;
+ private static final int WPS_PIN_DIALOG_ID = 3;
// Combo scans can take 5-6s to complete - set to 10s.
private static final int WIFI_RESCAN_INTERVAL_MS = 10 * 1000;
@@ -104,6 +114,13 @@ public class WifiSettings extends SettingsPreferenceFragment
private final Scanner mScanner;
private WifiManager mWifiManager;
+ private WifiManager.Channel mChannel;
+ private WifiManager.ActionListener mConnectListener;
+ private WifiManager.ActionListener mSaveListener;
+ private WifiManager.ActionListener mForgetListener;
+ private boolean mP2pSupported;
+
+
private WifiEnabler mWifiEnabler;
// An access point being editted is stored here.
private AccessPoint mSelectedAccessPoint;
@@ -124,15 +141,28 @@ public class WifiSettings extends SettingsPreferenceFragment
// this boolean extra specifies whether to disable the Next button when not connected
private static final String EXTRA_ENABLE_NEXT_ON_CONNECT = "wifi_enable_next_on_connect";
+ // this boolean extra specifies whether to auto finish when connection is established
+ private static final String EXTRA_AUTO_FINISH_ON_CONNECT = "wifi_auto_finish_on_connect";
+
+ // this boolean extra is set if we are being invoked by the Setup Wizard
+ private static final String EXTRA_IS_FIRST_RUN = "firstRun";
+
+ private static final String EXTRA_WIFI_DISABLE_BACK = "wifi_disable_back";
+
// should Next button only be enabled when we have a connection?
private boolean mEnableNextOnConnection;
- private boolean mInXlSetupWizard;
+
+ // should activity finish once we have a connection?
+ private boolean mAutoFinishOnConnection;
// Save the dialog details
private boolean mDlgEdit;
private AccessPoint mDlgAccessPoint;
private Bundle mAccessPointSavedState;
+ // the action bar uses a different set of controls for Setup Wizard
+ private boolean mSetupWizardMode;
+
/* End of "used in Wifi Setup context" */
public WifiSettings() {
@@ -145,7 +175,6 @@ public class WifiSettings extends SettingsPreferenceFragment
mFilter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
- mFilter.addAction(WifiManager.ERROR_ACTION);
mReceiver = new BroadcastReceiver() {
@Override
@@ -158,10 +187,54 @@ public class WifiSettings extends SettingsPreferenceFragment
}
@Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
+ public void onCreate(Bundle icicle) {
+ // Set this flag early, as it's needed by getHelpResource(), which is called by super
+ mSetupWizardMode = getActivity().getIntent().getBooleanExtra(EXTRA_IS_FIRST_RUN, false);
+
+ super.onCreate(icicle);
+ }
- mInXlSetupWizard = (activity instanceof WifiSettingsForSetupWizardXL);
+ @Override
+ public View onCreateView(final LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ if (mSetupWizardMode) {
+ View view = inflater.inflate(R.layout.setup_preference, container, false);
+ View other = view.findViewById(R.id.other_network);
+ other.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mWifiManager.isWifiEnabled()) {
+ onAddNetworkPressed();
+ }
+ }
+ });
+ final ImageButton b = (ImageButton) view.findViewById(R.id.more);
+ if (b != null) {
+ b.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mWifiManager.isWifiEnabled()) {
+ PopupMenu pm = new PopupMenu(inflater.getContext(), b);
+ pm.inflate(R.menu.wifi_setup);
+ pm.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ if (R.id.wifi_wps == item.getItemId()) {
+ showDialog(WPS_PBC_DIALOG_ID);
+ return true;
+ }
+ return false;
+ }
+ });
+ pm.show();
+ }
+ }
+ });
+ }
+ return view;
+ } else {
+ return super.onCreateView(inflater, container, savedInstanceState);
+ }
}
@Override
@@ -170,8 +243,40 @@ public class WifiSettings extends SettingsPreferenceFragment
// Preference (probably in onCreate()), while WifiSettings exceptionally set it up in
// this method.
+ mP2pSupported = getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT);
mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
- mWifiManager.asyncConnect(getActivity(), new WifiServiceHandler());
+ mChannel = mWifiManager.initialize(getActivity(), getActivity().getMainLooper(), null);
+
+ mConnectListener = new WifiManager.ActionListener() {
+ public void onSuccess() {
+ }
+ public void onFailure(int reason) {
+ Toast.makeText(getActivity(),
+ R.string.wifi_failed_connect_message,
+ Toast.LENGTH_SHORT).show();
+ }
+ };
+
+ mSaveListener = new WifiManager.ActionListener() {
+ public void onSuccess() {
+ }
+ public void onFailure(int reason) {
+ Toast.makeText(getActivity(),
+ R.string.wifi_failed_save_message,
+ Toast.LENGTH_SHORT).show();
+ }
+ };
+
+ mForgetListener = new WifiManager.ActionListener() {
+ public void onSuccess() {
+ }
+ public void onFailure(int reason) {
+ Toast.makeText(getActivity(),
+ R.string.wifi_failed_forget_message,
+ Toast.LENGTH_SHORT).show();
+ }
+ };
+
if (savedInstanceState != null
&& savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)) {
mDlgEdit = savedInstanceState.getBoolean(SAVE_DIALOG_EDIT_MODE);
@@ -181,6 +286,24 @@ public class WifiSettings extends SettingsPreferenceFragment
final Activity activity = getActivity();
final Intent intent = activity.getIntent();
+ // first if we're supposed to finish once we have a connection
+ mAutoFinishOnConnection = intent.getBooleanExtra(EXTRA_AUTO_FINISH_ON_CONNECT, false);
+
+ if (mAutoFinishOnConnection) {
+ // Hide the next button
+ if (hasNextButton()) {
+ getNextButton().setVisibility(View.GONE);
+ }
+
+ final ConnectivityManager connectivity = (ConnectivityManager)
+ getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
+ if (connectivity != null
+ && connectivity.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnected()) {
+ activity.finish();
+ return;
+ }
+ }
+
// if we're supposed to enable/disable the Next button based on our current connection
// state, start it off in the right state
mEnableNextOnConnection = intent.getBooleanExtra(EXTRA_ENABLE_NEXT_ON_CONNECT, false);
@@ -197,11 +320,15 @@ public class WifiSettings extends SettingsPreferenceFragment
}
}
- if (mInXlSetupWizard) {
- addPreferencesFromResource(R.xml.wifi_access_points_for_wifi_setup_xl);
- } else {
- addPreferencesFromResource(R.xml.wifi_settings);
+ addPreferencesFromResource(R.xml.wifi_settings);
+
+ // Back key is disabled if requested
+ if (intent.getBooleanExtra(EXTRA_WIFI_DISABLE_BACK, false)) {
+ getView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_BACK);
+ }
+ // On/off switch is hidden for Setup Wizard
+ if (!mSetupWizardMode) {
Switch actionBarSwitch = new Switch(activity);
if (activity instanceof PreferenceActivity) {
@@ -225,7 +352,9 @@ public class WifiSettings extends SettingsPreferenceFragment
mEmptyView = (TextView) getView().findViewById(android.R.id.empty);
getListView().setEmptyView(mEmptyView);
- registerForContextMenu(getListView());
+ if (!mSetupWizardMode) {
+ registerForContextMenu(getListView());
+ }
setHasOptionsMenu(true);
// After confirming PreferenceScreen is available, we call super.
@@ -242,7 +371,7 @@ public class WifiSettings extends SettingsPreferenceFragment
getActivity().registerReceiver(mReceiver, mFilter);
if (mKeyStoreNetworkId != INVALID_NETWORK_ID &&
KeyStore.getInstance().state() == KeyStore.State.UNLOCKED) {
- mWifiManager.connectNetwork(mKeyStoreNetworkId);
+ mWifiManager.connect(mChannel, mKeyStoreNetworkId, mConnectListener);
}
mKeyStoreNetworkId = INVALID_NETWORK_ID;
@@ -261,16 +390,37 @@ public class WifiSettings extends SettingsPreferenceFragment
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- // We don't want menus in Setup Wizard XL.
- if (!mInXlSetupWizard) {
- final boolean wifiIsEnabled = mWifiManager.isWifiEnabled();
- menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.wifi_menu_scan)
- //.setIcon(R.drawable.ic_menu_scan_network)
+ final boolean wifiIsEnabled = mWifiManager.isWifiEnabled();
+ if (mSetupWizardMode) {
+ // FIXME: add setIcon() when graphics are available
+ menu.add(Menu.NONE, MENU_ID_WPS_PBC, 0, R.string.wifi_menu_wps_pbc)
+ .setIcon(R.drawable.ic_wps)
+ .setEnabled(wifiIsEnabled)
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ menu.add(Menu.NONE, MENU_ID_ADD_NETWORK, 0, R.string.wifi_add_network)
+ .setEnabled(wifiIsEnabled)
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ } else {
+ menu.add(Menu.NONE, MENU_ID_WPS_PBC, 0, R.string.wifi_menu_wps_pbc)
+ .setIcon(R.drawable.ic_wps)
.setEnabled(wifiIsEnabled)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
menu.add(Menu.NONE, MENU_ID_ADD_NETWORK, 0, R.string.wifi_add_network)
+ .setIcon(R.drawable.ic_menu_add)
.setEnabled(wifiIsEnabled)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.wifi_menu_scan)
+ //.setIcon(R.drawable.ic_menu_scan_network)
+ .setEnabled(wifiIsEnabled)
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+ menu.add(Menu.NONE, MENU_ID_WPS_PIN, 0, R.string.wifi_menu_wps_pin)
+ .setEnabled(wifiIsEnabled)
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+ if (mP2pSupported) {
+ menu.add(Menu.NONE, MENU_ID_P2P, 0, R.string.wifi_menu_p2p)
+ .setEnabled(wifiIsEnabled)
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+ }
menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced)
//.setIcon(android.R.drawable.ic_menu_manage)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
@@ -296,6 +446,23 @@ public class WifiSettings extends SettingsPreferenceFragment
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
+ case MENU_ID_WPS_PBC:
+ showDialog(WPS_PBC_DIALOG_ID);
+ return true;
+ case MENU_ID_P2P:
+ if (getActivity() instanceof PreferenceActivity) {
+ ((PreferenceActivity) getActivity()).startPreferencePanel(
+ WifiP2pSettings.class.getCanonicalName(),
+ null,
+ R.string.wifi_p2p_settings_title, null,
+ this, 0);
+ } else {
+ startFragment(this, WifiP2pSettings.class.getCanonicalName(), -1, null);
+ }
+ return true;
+ case MENU_ID_WPS_PIN:
+ showDialog(WPS_PIN_DIALOG_ID);
+ return true;
case MENU_ID_SCAN:
if (mWifiManager.isWifiEnabled()) {
mScanner.forceScan();
@@ -323,9 +490,7 @@ public class WifiSettings extends SettingsPreferenceFragment
@Override
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) {
- if (mInXlSetupWizard) {
- ((WifiSettingsForSetupWizardXL)getActivity()).onCreateContextMenu(menu, view, info);
- } else if (info instanceof AdapterContextMenuInfo) {
+ if (info instanceof AdapterContextMenuInfo) {
Preference preference = (Preference) getListView().getItemAtPosition(
((AdapterContextMenuInfo) info).position);
@@ -353,23 +518,25 @@ public class WifiSettings extends SettingsPreferenceFragment
case MENU_ID_CONNECT: {
if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
if (!requireKeyStore(mSelectedAccessPoint.getConfig())) {
- mWifiManager.connectNetwork(mSelectedAccessPoint.networkId);
+ mWifiManager.connect(mChannel, mSelectedAccessPoint.networkId,
+ mConnectListener);
}
} else if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE) {
/** Bypass dialog for unsecured networks */
mSelectedAccessPoint.generateOpenNetworkConfig();
- mWifiManager.connectNetwork(mSelectedAccessPoint.getConfig());
+ mWifiManager.connect(mChannel, mSelectedAccessPoint.getConfig(),
+ mConnectListener);
} else {
- showConfigUi(mSelectedAccessPoint, true);
+ showDialog(mSelectedAccessPoint, true);
}
return true;
}
case MENU_ID_FORGET: {
- mWifiManager.forgetNetwork(mSelectedAccessPoint.networkId);
+ mWifiManager.forget(mChannel, mSelectedAccessPoint.networkId, mForgetListener);
return true;
}
case MENU_ID_MODIFY: {
- showConfigUi(mSelectedAccessPoint, true);
+ showDialog(mSelectedAccessPoint, true);
return true;
}
}
@@ -384,9 +551,9 @@ public class WifiSettings extends SettingsPreferenceFragment
if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE &&
mSelectedAccessPoint.networkId == INVALID_NETWORK_ID) {
mSelectedAccessPoint.generateOpenNetworkConfig();
- mWifiManager.connectNetwork(mSelectedAccessPoint.getConfig());
+ mWifiManager.connect(mChannel, mSelectedAccessPoint.getConfig(), mConnectListener);
} else {
- showConfigUi(mSelectedAccessPoint, false);
+ showDialog(mSelectedAccessPoint, false);
}
} else {
return super.onPreferenceTreeClick(screen, preference);
@@ -394,18 +561,6 @@ public class WifiSettings extends SettingsPreferenceFragment
return true;
}
- /**
- * Shows an appropriate Wifi configuration component.
- * Called when a user clicks "Add network" preference or one of available networks is selected.
- */
- private void showConfigUi(AccessPoint accessPoint, boolean edit) {
- if (mInXlSetupWizard) {
- ((WifiSettingsForSetupWizardXL)getActivity()).showConfigUi(accessPoint, edit);
- } else {
- showDialog(accessPoint, edit);
- }
- }
-
private void showDialog(AccessPoint accessPoint, boolean edit) {
if (mDialog != null) {
removeDialog(WIFI_DIALOG_ID);
@@ -421,18 +576,26 @@ public class WifiSettings extends SettingsPreferenceFragment
@Override
public Dialog onCreateDialog(int dialogId) {
- AccessPoint ap = mDlgAccessPoint; // For manual launch
- if (ap == null) { // For re-launch from saved state
- if (mAccessPointSavedState != null) {
- ap = new AccessPoint(getActivity(), mAccessPointSavedState);
- // For repeated orientation changes
- mDlgAccessPoint = ap;
- }
+ switch (dialogId) {
+ case WIFI_DIALOG_ID:
+ AccessPoint ap = mDlgAccessPoint; // For manual launch
+ if (ap == null) { // For re-launch from saved state
+ if (mAccessPointSavedState != null) {
+ ap = new AccessPoint(getActivity(), mAccessPointSavedState);
+ // For repeated orientation changes
+ mDlgAccessPoint = ap;
+ }
+ }
+ // If it's still null, fine, it's for Add Network
+ mSelectedAccessPoint = ap;
+ mDialog = new WifiDialog(getActivity(), this, ap, mDlgEdit);
+ return mDialog;
+ case WPS_PBC_DIALOG_ID:
+ return new WpsDialog(getActivity(), WpsInfo.PBC);
+ case WPS_PIN_DIALOG_ID:
+ return new WpsDialog(getActivity(), WpsInfo.DISPLAY);
}
- // If it's still null, fine, it's for Add Network
- mSelectedAccessPoint = ap;
- mDialog = new WifiDialog(getActivity(), this, ap, mDlgEdit);
- return mDialog;
+ return super.onCreateDialog(dialogId);
}
private boolean requireKeyStore(WifiConfiguration config) {
@@ -450,6 +613,9 @@ public class WifiSettings extends SettingsPreferenceFragment
* the strength of network and the security for it.
*/
private void updateAccessPoints() {
+ // Safeguard from some delayed event handling
+ if (getActivity() == null) return;
+
final int wifiState = mWifiManager.getWifiState();
switch (wifiState) {
@@ -457,16 +623,11 @@ public class WifiSettings extends SettingsPreferenceFragment
// AccessPoints are automatically sorted with TreeSet.
final Collection<AccessPoint> accessPoints = constructAccessPoints();
getPreferenceScreen().removeAll();
- if (mInXlSetupWizard) {
- ((WifiSettingsForSetupWizardXL)getActivity()).onAccessPointsUpdated(
- getPreferenceScreen(), accessPoints);
- } else {
- if(accessPoints.size() == 0) {
- addMessagePreference(R.string.wifi_empty_list_wifi_on);
- }
- for (AccessPoint accessPoint : accessPoints) {
- getPreferenceScreen().addPreference(accessPoint);
- }
+ if(accessPoints.size() == 0) {
+ addMessagePreference(R.string.wifi_empty_list_wifi_on);
+ }
+ for (AccessPoint accessPoint : accessPoints) {
+ getPreferenceScreen().addPreference(accessPoint);
}
break;
@@ -568,13 +729,10 @@ public class WifiSettings extends SettingsPreferenceFragment
//network state change events so the apps dont have to worry about
//ignoring supplicant state change when network is connected
//to get more fine grained information.
- if (!mConnected.get()) {
- updateConnectionState(WifiInfo.getDetailedStateOf((SupplicantState)
- intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE)));
- }
-
- if (mInXlSetupWizard) {
- ((WifiSettingsForSetupWizardXL)getActivity()).onSupplicantStateChanged(intent);
+ SupplicantState state = (SupplicantState) intent.getParcelableExtra(
+ WifiManager.EXTRA_NEW_STATE);
+ if (!mConnected.get() && SupplicantState.isHandshakeState(state)) {
+ updateConnectionState(WifiInfo.getDetailedStateOf(state));
}
} else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
@@ -583,16 +741,12 @@ public class WifiSettings extends SettingsPreferenceFragment
changeNextButtonState(info.isConnected());
updateAccessPoints();
updateConnectionState(info.getDetailedState());
+ if (mAutoFinishOnConnection && info.isConnected()) {
+ getActivity().finish();
+ return;
+ }
} else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
updateConnectionState(null);
- } else if (WifiManager.ERROR_ACTION.equals(action)) {
- int errorCode = intent.getIntExtra(WifiManager.EXTRA_ERROR_CODE, 0);
- switch (errorCode) {
- case WifiManager.WPS_OVERLAP_ERROR:
- Toast.makeText(context, R.string.wifi_wps_overlap_error,
- Toast.LENGTH_SHORT).show();
- break;
- }
}
}
@@ -622,10 +776,6 @@ public class WifiSettings extends SettingsPreferenceFragment
accessPoint.update(mLastInfo, mLastState);
}
}
-
- if (mInXlSetupWizard) {
- ((WifiSettingsForSetupWizardXL)getActivity()).updateConnectionState(mLastState);
- }
}
private void updateWifiState(int state) {
@@ -683,51 +833,6 @@ public class WifiSettings extends SettingsPreferenceFragment
}
}
- private class WifiServiceHandler extends Handler {
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
- if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
- //AsyncChannel in msg.obj
- } else {
- //AsyncChannel set up failure, ignore
- Log.e(TAG, "Failed to establish AsyncChannel connection");
- }
- break;
- case WifiManager.CMD_WPS_COMPLETED:
- WpsResult result = (WpsResult) msg.obj;
- if (result == null) break;
- AlertDialog.Builder dialog = new AlertDialog.Builder(getActivity())
- .setTitle(R.string.wifi_wps_setup_title)
- .setPositiveButton(android.R.string.ok, null);
- switch (result.status) {
- case FAILURE:
- dialog.setMessage(R.string.wifi_wps_failed);
- dialog.show();
- break;
- case IN_PROGRESS:
- dialog.setMessage(R.string.wifi_wps_in_progress);
- dialog.show();
- break;
- default:
- if (result.pin != null) {
- dialog.setMessage(getResources().getString(
- R.string.wifi_wps_pin_output, result.pin));
- dialog.show();
- }
- break;
- }
- break;
- //TODO: more connectivity feedback
- default:
- //Ignore
- break;
- }
- }
- }
-
/**
* Renames/replaces "Next" button when appropriate. "Next" button usually exists in
* Wifi setup screens, not in usual wifi settings screen.
@@ -735,59 +840,40 @@ public class WifiSettings extends SettingsPreferenceFragment
* @param connected true when the device is connected to a wifi network.
*/
private void changeNextButtonState(boolean connected) {
- if (mInXlSetupWizard) {
- ((WifiSettingsForSetupWizardXL)getActivity()).changeNextButtonState(connected);
- } else if (mEnableNextOnConnection && hasNextButton()) {
+ if (mEnableNextOnConnection && hasNextButton()) {
getNextButton().setEnabled(connected);
}
}
public void onClick(DialogInterface dialogInterface, int button) {
- if (mInXlSetupWizard) {
- if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) {
- forget();
- } else if (button == WifiDialog.BUTTON_SUBMIT) {
- ((WifiSettingsForSetupWizardXL)getActivity()).onConnectButtonPressed();
- }
- } else {
- if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) {
- forget();
- } else if (button == WifiDialog.BUTTON_SUBMIT) {
- submit(mDialog.getController());
- }
+ if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) {
+ forget();
+ } else if (button == WifiDialog.BUTTON_SUBMIT) {
+ submit(mDialog.getController());
}
-
}
/* package */ void submit(WifiConfigController configController) {
- int networkSetup = configController.chosenNetworkSetupMethod();
- switch(networkSetup) {
- case WifiConfigController.WPS_PBC:
- case WifiConfigController.WPS_DISPLAY:
- case WifiConfigController.WPS_KEYPAD:
- mWifiManager.startWps(configController.getWpsConfig());
- break;
- case WifiConfigController.MANUAL:
- final WifiConfiguration config = configController.getConfig();
-
- if (config == null) {
- if (mSelectedAccessPoint != null
- && !requireKeyStore(mSelectedAccessPoint.getConfig())
- && mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
- mWifiManager.connectNetwork(mSelectedAccessPoint.networkId);
- }
- } else if (config.networkId != INVALID_NETWORK_ID) {
- if (mSelectedAccessPoint != null) {
- saveNetwork(config);
- }
- } else {
- if (configController.isEdit() || requireKeyStore(config)) {
- saveNetwork(config);
- } else {
- mWifiManager.connectNetwork(config);
- }
- }
- break;
+
+ final WifiConfiguration config = configController.getConfig();
+
+ if (config == null) {
+ if (mSelectedAccessPoint != null
+ && !requireKeyStore(mSelectedAccessPoint.getConfig())
+ && mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
+ mWifiManager.connect(mChannel, mSelectedAccessPoint.networkId,
+ mConnectListener);
+ }
+ } else if (config.networkId != INVALID_NETWORK_ID) {
+ if (mSelectedAccessPoint != null) {
+ mWifiManager.save(mChannel, config, mSaveListener);
+ }
+ } else {
+ if (configController.isEdit() || requireKeyStore(config)) {
+ mWifiManager.save(mChannel, config, mSaveListener);
+ } else {
+ mWifiManager.connect(mChannel, config, mConnectListener);
+ }
}
if (mWifiManager.isWifiEnabled()) {
@@ -796,16 +882,14 @@ public class WifiSettings extends SettingsPreferenceFragment
updateAccessPoints();
}
- private void saveNetwork(WifiConfiguration config) {
- if (mInXlSetupWizard) {
- ((WifiSettingsForSetupWizardXL)getActivity()).onSaveNetwork(config);
- } else {
- mWifiManager.saveNetwork(config);
+ /* package */ void forget() {
+ if (mSelectedAccessPoint.networkId == INVALID_NETWORK_ID) {
+ // Should not happen, but a monkey seems to triger it
+ Log.e(TAG, "Failed to forget invalid network " + mSelectedAccessPoint.getConfig());
+ return;
}
- }
- /* package */ void forget() {
- mWifiManager.forgetNetwork(mSelectedAccessPoint.networkId);
+ mWifiManager.forget(mChannel, mSelectedAccessPoint.networkId, mForgetListener);
if (mWifiManager.isWifiEnabled()) {
mScanner.resume();
@@ -833,7 +917,7 @@ public class WifiSettings extends SettingsPreferenceFragment
/* package */ void onAddNetworkPressed() {
// No exact access point is selected.
mSelectedAccessPoint = null;
- showConfigUi(null, true);
+ showDialog(null, true);
}
/* package */ int getAccessPointsCount() {
@@ -862,4 +946,52 @@ public class WifiSettings extends SettingsPreferenceFragment
mScanner.resume();
}
}
+
+ @Override
+ protected int getHelpResource() {
+ if (mSetupWizardMode) {
+ return 0;
+ }
+ return R.string.help_url_wifi;
+ }
+
+ /**
+ * Used as the outer frame of all setup wizard pages that need to adjust their margins based
+ * on the total size of the available display. (e.g. side margins set to 10% of total width.)
+ */
+ public static class ProportionalOuterFrame extends RelativeLayout {
+ public ProportionalOuterFrame(Context context) {
+ super(context);
+ }
+ public ProportionalOuterFrame(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+ public ProportionalOuterFrame(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ /**
+ * Set our margins and title area height proportionally to the available display size
+ */
+ @Override
+ 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);
+ setPadding(
+ (int) (parentWidth * sideMargin),
+ 0,
+ (int) (parentWidth * sideMargin),
+ bottom);
+ View title = findViewById(R.id.title_area);
+ if (title != null) {
+ title.setMinimumHeight((int) (parentHeight * titleHeight));
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+
}
diff --git a/src/com/android/settings/wifi/WifiSettingsForSetupWizardXL.java b/src/com/android/settings/wifi/WifiSettingsForSetupWizardXL.java
index 4b6a081..8258b55 100644
--- a/src/com/android/settings/wifi/WifiSettingsForSetupWizardXL.java
+++ b/src/com/android/settings/wifi/WifiSettingsForSetupWizardXL.java
@@ -74,6 +74,7 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
private WifiSettings mWifiSettings;
private WifiManager mWifiManager;
+ private WifiManager.Channel mChannel;
/** Used for resizing a padding above title. Hiden when software keyboard is shown. */
private View mTopPadding;
@@ -142,10 +143,10 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
setContentView(R.layout.wifi_settings_for_setup_wizard_xl);
mWifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE);
+ mChannel = mWifiManager.initialize(this, getMainLooper(), null);
// There's no button here enabling wifi network, so we need to enable it without
// users' request.
mWifiManager.setWifiEnabled(true);
- mWifiManager.asyncConnect(this, new WifiServiceHandler());
mWifiSettings =
(WifiSettings)getFragmentManager().findFragmentById(R.id.wifi_setup_fragment);
@@ -201,25 +202,6 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
mConnectingStatusView = (TextView) findViewById(R.id.connecting_status);
}
- private class WifiServiceHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
- if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
- //AsyncChannel in msg.obj
- } else {
- //AsyncChannel set up failure, ignore
- Log.e(TAG, "Failed to establish AsyncChannel connection");
- }
- break;
- default:
- //Ignore
- break;
- }
- }
- }
-
private void restoreFirstVisibilityState() {
showDefaultTitle();
mAddNetworkButton.setVisibility(View.VISIBLE);
@@ -614,7 +596,13 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
Log.d(TAG, String.format("forgeting Wi-Fi network \"%s\" (id: %d)",
config.SSID, config.networkId));
}
- mWifiManager.forgetNetwork(config.networkId);
+ mWifiManager.forget(mChannel, config.networkId, new WifiManager.ActionListener() {
+ public void onSuccess() {
+ }
+ public void onFailure(int reason) {
+ //TODO: Add failure UI
+ }
+ });
}
mWifiSettingsFragmentLayout.setVisibility(View.GONE);
@@ -773,6 +761,12 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
*/
/* package */ void onSaveNetwork(WifiConfiguration config) {
// We want to both save and connect a network. connectNetwork() does both.
- mWifiManager.connectNetwork(config);
+ mWifiManager.connect(mChannel, config, new WifiManager.ActionListener() {
+ public void onSuccess() {
+ }
+ public void onFailure(int reason) {
+ //TODO: Add failure UI
+ }
+ });
}
}
diff --git a/src/com/android/settings/wifi/WifiSetupActivity.java b/src/com/android/settings/wifi/WifiSetupActivity.java
new file mode 100644
index 0000000..8415954
--- /dev/null
+++ b/src/com/android/settings/wifi/WifiSetupActivity.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.wifi;
+
+import com.android.settings.ButtonBarHandler;
+
+// dummy class for setup wizard theme
+public class WifiSetupActivity extends WifiPickerActivity implements ButtonBarHandler {
+
+} \ No newline at end of file
diff --git a/src/com/android/settings/wifi/WpsDialog.java b/src/com/android/settings/wifi/WpsDialog.java
new file mode 100644
index 0000000..40b2a35
--- /dev/null
+++ b/src/com/android/settings/wifi/WpsDialog.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.wifi;
+
+import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WpsInfo;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+import com.android.settings.R;
+
+
+/**
+ * Dialog to show WPS progress.
+ */
+public class WpsDialog extends AlertDialog {
+
+ private final static String TAG = "WpsDialog";
+
+ private View mView;
+ private TextView mTextView;
+ private ProgressBar mTimeoutBar;
+ private ProgressBar mProgressBar;
+ private Button mButton;
+ private Timer mTimer;
+
+ private static final int WPS_TIMEOUT_S = 120;
+
+ private WifiManager mWifiManager;
+ private WifiManager.Channel mChannel;
+ private WifiManager.WpsListener mWpsListener;
+ private int mWpsSetup;
+
+ private final IntentFilter mFilter;
+ private BroadcastReceiver mReceiver;
+
+ private Context mContext;
+ private Handler mHandler = new Handler();
+
+ private enum DialogState {
+ WPS_INIT,
+ WPS_START,
+ WPS_COMPLETE,
+ CONNECTED, //WPS + IP config is done
+ WPS_FAILED
+ }
+ DialogState mDialogState = DialogState.WPS_INIT;
+
+ public WpsDialog(Context context, int wpsSetup) {
+ super(context);
+ mContext = context;
+ mWpsSetup = wpsSetup;
+
+ class WpsListener implements WifiManager.WpsListener {
+ public void onStartSuccess(String pin) {
+ if (pin != null) {
+ updateDialog(DialogState.WPS_START, String.format(
+ mContext.getString(R.string.wifi_wps_onstart_pin), pin));
+ } else {
+ updateDialog(DialogState.WPS_START, mContext.getString(
+ R.string.wifi_wps_onstart_pbc));
+ }
+ }
+ public void onCompletion() {
+ updateDialog(DialogState.WPS_COMPLETE,
+ mContext.getString(R.string.wifi_wps_complete));
+ }
+
+ public void onFailure(int reason) {
+ String msg;
+ switch (reason) {
+ case WifiManager.WPS_OVERLAP_ERROR:
+ msg = mContext.getString(R.string.wifi_wps_failed_overlap);
+ break;
+ case WifiManager.WPS_WEP_PROHIBITED:
+ msg = mContext.getString(R.string.wifi_wps_failed_wep);
+ break;
+ case WifiManager.WPS_TKIP_ONLY_PROHIBITED:
+ msg = mContext.getString(R.string.wifi_wps_failed_tkip);
+ break;
+ case WifiManager.IN_PROGRESS:
+ msg = mContext.getString(R.string.wifi_wps_in_progress);
+ break;
+ default:
+ msg = mContext.getString(R.string.wifi_wps_failed_generic);
+ break;
+ }
+ updateDialog(DialogState.WPS_FAILED, msg);
+ }
+ }
+
+ mWpsListener = new WpsListener();
+
+
+ mFilter = new IntentFilter();
+ mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ handleEvent(context, intent);
+ }
+ };
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ mView = getLayoutInflater().inflate(R.layout.wifi_wps_dialog, null);
+
+ mTextView = (TextView) mView.findViewById(R.id.wps_dialog_txt);
+ mTextView.setText(R.string.wifi_wps_setup_msg);
+
+ mTimeoutBar = ((ProgressBar) mView.findViewById(R.id.wps_timeout_bar));
+ mTimeoutBar.setMax(WPS_TIMEOUT_S);
+ mTimeoutBar.setProgress(0);
+
+ mProgressBar = ((ProgressBar) mView.findViewById(R.id.wps_progress_bar));
+ mProgressBar.setVisibility(View.GONE);
+
+ mButton = ((Button) mView.findViewById(R.id.wps_dialog_btn));
+ mButton.setText(R.string.wifi_cancel);
+ mButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ dismiss();
+ }
+ });
+
+ mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+ mChannel = mWifiManager.initialize(mContext, mContext.getMainLooper(), null);
+
+ setView(mView);
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onStart() {
+ /*
+ * increment timeout bar per second.
+ */
+ mTimer = new Timer(false);
+ mTimer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ mHandler.post(new Runnable() {
+
+ @Override
+ public void run() {
+ mTimeoutBar.incrementProgressBy(1);
+ }
+ });
+ }
+ }, 1000, 1000);
+
+ mContext.registerReceiver(mReceiver, mFilter);
+
+ WpsInfo wpsConfig = new WpsInfo();
+ wpsConfig.setup = mWpsSetup;
+ mWifiManager.startWps(mChannel, wpsConfig, mWpsListener);
+ }
+
+ @Override
+ protected void onStop() {
+ if (mDialogState != DialogState.WPS_COMPLETE) {
+ mWifiManager.cancelWps(mChannel, null);
+ }
+
+ if (mReceiver != null) {
+ mContext.unregisterReceiver(mReceiver);
+ mReceiver = null;
+ }
+
+ if (mTimer != null) {
+ mTimer.cancel();
+ }
+ }
+
+ private void updateDialog(DialogState state, String msg) {
+ if (mDialogState.ordinal() >= state.ordinal()) {
+ //ignore.
+ return;
+ }
+ mDialogState = state;
+
+ switch(state) {
+ case WPS_COMPLETE:
+ mTimeoutBar.setVisibility(View.GONE);
+ mProgressBar.setVisibility(View.VISIBLE);
+ break;
+ case CONNECTED:
+ case WPS_FAILED:
+ mButton.setText(mContext.getString(R.string.dlg_ok));
+ mTimeoutBar.setVisibility(View.GONE);
+ mProgressBar.setVisibility(View.GONE);
+ if (mReceiver != null) {
+ mContext.unregisterReceiver(mReceiver);
+ mReceiver = null;
+ }
+ break;
+ }
+ mTextView.setText(msg);
+ }
+
+ private void handleEvent(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
+ NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
+ WifiManager.EXTRA_NETWORK_INFO);
+ final NetworkInfo.DetailedState state = info.getDetailedState();
+ if (state == DetailedState.CONNECTED &&
+ mDialogState == DialogState.WPS_COMPLETE) {
+ WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
+ if (wifiInfo != null) {
+ String msg = String.format(mContext.getString(
+ R.string.wifi_wps_connected), wifiInfo.getSSID());
+ updateDialog(DialogState.CONNECTED, msg);
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/com/android/settings/wifi/p2p/WifiP2pDialog.java b/src/com/android/settings/wifi/p2p/WifiP2pDialog.java
deleted file mode 100644
index e688905..0000000
--- a/src/com/android/settings/wifi/p2p/WifiP2pDialog.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.wifi.p2p;
-
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.net.wifi.WpsInfo;
-import android.net.wifi.p2p.WifiP2pConfig;
-import android.net.wifi.p2p.WifiP2pDevice;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.CheckBox;
-import android.widget.Spinner;
-import android.widget.TextView;
-
-import com.android.settings.R;
-
-/**
- * Dialog to setup a p2p connection
- */
-public class WifiP2pDialog extends AlertDialog implements AdapterView.OnItemSelectedListener {
-
- static final int BUTTON_SUBMIT = DialogInterface.BUTTON_POSITIVE;
-
- private final DialogInterface.OnClickListener mListener;
-
- private View mView;
- private TextView mDeviceName;
- private TextView mDeviceAddress;
-
- /* These values come from "wifi_p2p_wps_setup" resource array */
- private static final int WPS_PBC = 0;
- private static final int WPS_KEYPAD = 1;
- private static final int WPS_DISPLAY = 2;
-
- private int mWpsSetupIndex = WPS_PBC; //default is pbc
-
- WifiP2pDevice mDevice;
-
- public WifiP2pDialog(Context context, DialogInterface.OnClickListener listener,
- WifiP2pDevice device) {
- super(context);
- mListener = listener;
- mDevice = device;
- }
-
- public WifiP2pConfig getConfig() {
- WifiP2pConfig config = new WifiP2pConfig();
- config.deviceAddress = mDeviceAddress.getText().toString();
- config.wps = new WpsInfo();
- switch (mWpsSetupIndex) {
- case WPS_PBC:
- config.wps.setup = WpsInfo.PBC;
- break;
- case WPS_KEYPAD:
- config.wps.setup = WpsInfo.KEYPAD;
- config.wps.pin = ((TextView) mView.findViewById(R.id.wps_pin)).
- getText().toString();
- break;
- case WPS_DISPLAY:
- config.wps.setup = WpsInfo.DISPLAY;
- break;
- default:
- config.wps.setup = WpsInfo.PBC;
- break;
- }
- return config;
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
-
- mView = getLayoutInflater().inflate(R.layout.wifi_p2p_dialog, null);
- Spinner mWpsSetup = ((Spinner) mView.findViewById(R.id.wps_setup));
-
- setView(mView);
- setInverseBackgroundForced(true);
-
- Context context = getContext();
-
- setTitle(R.string.wifi_p2p_settings_title);
- mDeviceName = (TextView) mView.findViewById(R.id.device_name);
- mDeviceAddress = (TextView) mView.findViewById(R.id.device_address);
-
- setButton(BUTTON_SUBMIT, context.getString(R.string.wifi_connect), mListener);
- setButton(DialogInterface.BUTTON_NEGATIVE,
- context.getString(R.string.wifi_cancel), mListener);
-
- if (mDevice != null) {
- mDeviceName.setText(mDevice.deviceName);
- mDeviceAddress.setText(mDevice.deviceAddress);
- mWpsSetup.setSelection(mWpsSetupIndex); //keep pbc as default
- }
-
- mWpsSetup.setOnItemSelectedListener(this);
-
- super.onCreate(savedInstanceState);
- }
-
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- mWpsSetupIndex = position;
-
- if (mWpsSetupIndex == WPS_KEYPAD) {
- mView.findViewById(R.id.wps_pin_entry).setVisibility(View.VISIBLE);
- } else {
- mView.findViewById(R.id.wps_pin_entry).setVisibility(View.GONE);
- }
- return;
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- }
-
-}
diff --git a/src/com/android/settings/wifi/p2p/WifiP2pEnabler.java b/src/com/android/settings/wifi/p2p/WifiP2pEnabler.java
deleted file mode 100644
index 0747d64..0000000
--- a/src/com/android/settings/wifi/p2p/WifiP2pEnabler.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.wifi.p2p;
-
-import com.android.settings.R;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.wifi.p2p.WifiP2pManager;
-import android.os.Message;
-import android.preference.CheckBoxPreference;
-import android.preference.Preference;
-import android.provider.Settings;
-import android.util.Log;
-
-/**
- * WifiP2pEnabler is a helper to manage the Wifi p2p on/off
- */
-public class WifiP2pEnabler implements Preference.OnPreferenceChangeListener {
- private static final String TAG = "WifiP2pEnabler";
-
- private final Context mContext;
- private final CheckBoxPreference mCheckBox;
- private final IntentFilter mIntentFilter;
- private WifiP2pManager mWifiP2pManager;
- private WifiP2pManager.Channel mChannel;
-
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
-
- if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
- handleP2pStateChanged(intent.getIntExtra(
- WifiP2pManager.EXTRA_WIFI_STATE, WifiP2pManager.WIFI_P2P_STATE_DISABLED));
- }
- }
- };
-
- public WifiP2pEnabler(Context context, CheckBoxPreference checkBox) {
- mContext = context;
- mCheckBox = checkBox;
-
- mWifiP2pManager = (WifiP2pManager) context.getSystemService(Context.WIFI_P2P_SERVICE);
- if (mWifiP2pManager != null) {
- mChannel = mWifiP2pManager.initialize(mContext, mContext.getMainLooper(), null);
- if (mChannel == null) {
- //Failure to set up connection
- Log.e(TAG, "Failed to set up connection with wifi p2p service");
- mWifiP2pManager = null;
- mCheckBox.setEnabled(false);
- }
- } else {
- Log.e(TAG, "mWifiP2pManager is null!");
- }
- mIntentFilter = new IntentFilter(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
-
- }
-
- public void resume() {
- if (mWifiP2pManager == null) return;
- mContext.registerReceiver(mReceiver, mIntentFilter);
- mCheckBox.setOnPreferenceChangeListener(this);
- }
-
- public void pause() {
- if (mWifiP2pManager == null) return;
- mContext.unregisterReceiver(mReceiver);
- mCheckBox.setOnPreferenceChangeListener(null);
- }
-
- public boolean onPreferenceChange(Preference preference, Object value) {
-
- if (mWifiP2pManager == null) return false;
-
- mCheckBox.setEnabled(false);
- final boolean enable = (Boolean) value;
- if (enable) {
- mWifiP2pManager.enableP2p(mChannel);
- } else {
- mWifiP2pManager.disableP2p(mChannel);
- }
- return false;
- }
-
- private void handleP2pStateChanged(int state) {
- mCheckBox.setEnabled(true);
- switch (state) {
- case WifiP2pManager.WIFI_P2P_STATE_ENABLED:
- mCheckBox.setChecked(true);
- break;
- case WifiP2pManager.WIFI_P2P_STATE_DISABLED:
- mCheckBox.setChecked(false);
- break;
- default:
- Log.e(TAG,"Unhandled wifi state " + state);
- break;
- }
- }
-
-}
diff --git a/src/com/android/settings/wifi/p2p/WifiP2pSettings.java b/src/com/android/settings/wifi/p2p/WifiP2pSettings.java
index f6588b9..684ec30 100644
--- a/src/com/android/settings/wifi/p2p/WifiP2pSettings.java
+++ b/src/com/android/settings/wifi/p2p/WifiP2pSettings.java
@@ -32,8 +32,10 @@ import android.net.wifi.p2p.WifiP2pDevice;
import android.net.wifi.p2p.WifiP2pDeviceList;
import android.net.wifi.p2p.WifiP2pManager;
import android.net.wifi.p2p.WifiP2pManager.PeerListListener;
+import android.net.wifi.WpsInfo;
import android.os.Bundle;
-import android.os.Message;
+import android.os.Handler;
+import android.os.SystemProperties;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
@@ -45,6 +47,9 @@ 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;
@@ -60,36 +65,47 @@ public class WifiP2pSettings extends SettingsPreferenceFragment
implements PeerListListener {
private static final String TAG = "WifiP2pSettings";
+ private static final boolean DBG = false;
private static final int MENU_ID_SEARCH = Menu.FIRST;
- private static final int MENU_ID_CREATE_GROUP = Menu.FIRST + 1;
- private static final int MENU_ID_REMOVE_GROUP = Menu.FIRST + 2;
- private static final int MENU_ID_ADVANCED = Menu.FIRST +3;
-
+ private static final int MENU_ID_RENAME = Menu.FIRST + 1;
private final IntentFilter mIntentFilter = new IntentFilter();
private WifiP2pManager mWifiP2pManager;
private WifiP2pManager.Channel mChannel;
- private WifiP2pDialog mConnectDialog;
- private OnClickListener mConnectListener;
+ private OnClickListener mRenameListener;
private OnClickListener mDisconnectListener;
+ private OnClickListener mCancelConnectListener;
private WifiP2pPeer mSelectedWifiPeer;
+ private EditText mDeviceNameText;
+
+ private boolean mWifiP2pEnabled;
+ private boolean mWifiP2pSearching;
+ private int mConnectedDevices;
private PreferenceGroup mPeersGroup;
private Preference mThisDevicePref;
- private static final int DIALOG_CONNECT = 1;
- private static final int DIALOG_DISCONNECT = 2;
+ private static final int DIALOG_DISCONNECT = 1;
+ private static final int DIALOG_CANCEL_CONNECT = 2;
+ private static final int DIALOG_RENAME = 3;
+
+ private static final String SAVE_DIALOG_PEER = "PEER_STATE";
+ private static final String SAVE_DEVICE_NAME = "DEV_NAME";
private WifiP2pDevice mThisDevice;
private WifiP2pDeviceList mPeers = new WifiP2pDeviceList();
+ private String mSavedDeviceName;
+
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
- //TODO: nothing right now
+ mWifiP2pEnabled = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE,
+ WifiP2pManager.WIFI_P2P_STATE_DISABLED) == WifiP2pManager.WIFI_P2P_STATE_ENABLED;
+ handleP2pStateChanged();
} else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
if (mWifiP2pManager != null) {
mWifiP2pManager.requestPeers(mChannel, WifiP2pSettings.this);
@@ -99,26 +115,38 @@ public class WifiP2pSettings extends SettingsPreferenceFragment
NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(
WifiP2pManager.EXTRA_NETWORK_INFO);
if (networkInfo.isConnected()) {
- Log.d(TAG, "Connected");
+ if (DBG) Log.d(TAG, "Connected");
+ } else {
+ //start a search when we are disconnected
+ startSearch();
}
} else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
mThisDevice = (WifiP2pDevice) intent.getParcelableExtra(
WifiP2pManager.EXTRA_WIFI_P2P_DEVICE);
- Log.d(TAG, "Update device info: " + mThisDevice);
+ if (DBG) Log.d(TAG, "Update device info: " + mThisDevice);
updateDevicePref();
+ } else if (WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION.equals(action)) {
+ int discoveryState = intent.getIntExtra(WifiP2pManager.EXTRA_DISCOVERY_STATE,
+ WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED);
+ if (DBG) Log.d(TAG, "Discovery state changed: " + discoveryState);
+ if (discoveryState == WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED) {
+ updateSearchMenu(true);
+ } else {
+ updateSearchMenu(false);
+ }
}
}
};
@Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
+ public void onActivityCreated(Bundle savedInstanceState) {
addPreferencesFromResource(R.xml.wifi_p2p_settings);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
+ mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION);
final Activity activity = getActivity();
mWifiP2pManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
@@ -133,20 +161,29 @@ public class WifiP2pSettings extends SettingsPreferenceFragment
Log.e(TAG, "mWifiP2pManager is null !");
}
- //connect dialog listener
- mConnectListener = new OnClickListener() {
+ if (savedInstanceState != null && savedInstanceState.containsKey(SAVE_DIALOG_PEER)) {
+ WifiP2pDevice device = savedInstanceState.getParcelable(SAVE_DIALOG_PEER);
+ mSelectedWifiPeer = new WifiP2pPeer(getActivity(), device);
+ }
+ if (savedInstanceState != null && savedInstanceState.containsKey(SAVE_DEVICE_NAME)) {
+ mSavedDeviceName = savedInstanceState.getString(SAVE_DEVICE_NAME);
+ }
+
+ mRenameListener = new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == DialogInterface.BUTTON_POSITIVE) {
- WifiP2pConfig config = mConnectDialog.getConfig();
if (mWifiP2pManager != null) {
- mWifiP2pManager.connect(mChannel, config,
+ mWifiP2pManager.setDeviceName(mChannel,
+ mDeviceNameText.getText().toString(),
new WifiP2pManager.ActionListener() {
public void onSuccess() {
- Log.d(TAG, " connect success");
+ if (DBG) Log.d(TAG, " device rename success");
}
public void onFailure(int reason) {
- Log.d(TAG, " connect fail " + reason);
+ Toast.makeText(getActivity(),
+ R.string.wifi_p2p_failed_rename_message,
+ Toast.LENGTH_LONG).show();
}
});
}
@@ -162,96 +199,105 @@ public class WifiP2pSettings extends SettingsPreferenceFragment
if (mWifiP2pManager != null) {
mWifiP2pManager.removeGroup(mChannel, new WifiP2pManager.ActionListener() {
public void onSuccess() {
- Log.d(TAG, " remove group success");
+ if (DBG) Log.d(TAG, " remove group success");
}
public void onFailure(int reason) {
- Log.d(TAG, " remove group fail " + reason);
+ if (DBG) Log.d(TAG, " remove group fail " + reason);
}
});
}
}
}
};
+
+ //cancel connect dialog listener
+ mCancelConnectListener = new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ if (mWifiP2pManager != null) {
+ mWifiP2pManager.cancelConnect(mChannel,
+ new WifiP2pManager.ActionListener() {
+ public void onSuccess() {
+ if (DBG) Log.d(TAG, " cancel connect success");
+ }
+ public void onFailure(int reason) {
+ if (DBG) Log.d(TAG, " cancel connect fail " + reason);
+ }
+ });
+ }
+ }
+ }
+ };
+
setHasOptionsMenu(true);
+
+ final PreferenceScreen preferenceScreen = getPreferenceScreen();
+ preferenceScreen.removeAll();
+
+ preferenceScreen.setOrderingAsAdded(true);
+ mThisDevicePref = new Preference(getActivity());
+ preferenceScreen.addPreference(mThisDevicePref);
+
+ mPeersGroup = new PreferenceCategory(getActivity());
+ mPeersGroup.setTitle(R.string.wifi_p2p_peer_devices);
+
+ super.onActivityCreated(savedInstanceState);
}
@Override
public void onResume() {
super.onResume();
getActivity().registerReceiver(mReceiver, mIntentFilter);
-
- if (mWifiP2pManager != null) {
- mWifiP2pManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
- public void onSuccess() {
- Log.d(TAG, " discover success");
- }
- public void onFailure(int reason) {
- Log.d(TAG, " discover fail " + reason);
- }
- });
- }
}
@Override
public void onPause() {
super.onPause();
+ mWifiP2pManager.stopPeerDiscovery(mChannel, null);
getActivity().unregisterReceiver(mReceiver);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- menu.add(Menu.NONE, MENU_ID_SEARCH, 0, R.string.wifi_p2p_menu_search)
- .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
- menu.add(Menu.NONE, MENU_ID_CREATE_GROUP, 0, R.string.wifi_p2p_menu_create_group)
+ int textId = mWifiP2pSearching ? R.string.wifi_p2p_menu_searching :
+ R.string.wifi_p2p_menu_search;
+ menu.add(Menu.NONE, MENU_ID_SEARCH, 0, textId)
+ .setEnabled(mWifiP2pEnabled)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
- menu.add(Menu.NONE, MENU_ID_REMOVE_GROUP, 0, R.string.wifi_p2p_menu_remove_group)
- .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
- menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_p2p_menu_advanced)
+ menu.add(Menu.NONE, MENU_ID_RENAME, 0, R.string.wifi_p2p_menu_rename)
+ .setEnabled(mWifiP2pEnabled)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
super.onCreateOptionsMenu(menu, inflater);
}
@Override
+ public void onPrepareOptionsMenu(Menu menu) {
+ MenuItem searchMenu = menu.findItem(MENU_ID_SEARCH);
+ MenuItem renameMenu = menu.findItem(MENU_ID_RENAME);
+ if (mWifiP2pEnabled) {
+ searchMenu.setEnabled(true);
+ renameMenu.setEnabled(true);
+ } else {
+ searchMenu.setEnabled(false);
+ renameMenu.setEnabled(false);
+ }
+
+ if (mWifiP2pSearching) {
+ searchMenu.setTitle(R.string.wifi_p2p_menu_searching);
+ } else {
+ searchMenu.setTitle(R.string.wifi_p2p_menu_search);
+ }
+ }
+
+ @Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_ID_SEARCH:
- if (mWifiP2pManager != null) {
- mWifiP2pManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
- public void onSuccess() {
- Log.d(TAG, " discover success");
- }
- public void onFailure(int reason) {
- Log.d(TAG, " discover fail " + reason);
- }
- });
- }
+ startSearch();
return true;
- case MENU_ID_CREATE_GROUP:
- if (mWifiP2pManager != null) {
- mWifiP2pManager.createGroup(mChannel, new WifiP2pManager.ActionListener() {
- public void onSuccess() {
- Log.d(TAG, " create group success");
- }
- public void onFailure(int reason) {
- Log.d(TAG, " create group fail " + reason);
- }
- });
- }
- return true;
- case MENU_ID_REMOVE_GROUP:
- if (mWifiP2pManager != null) {
- mWifiP2pManager.removeGroup(mChannel, new WifiP2pManager.ActionListener() {
- public void onSuccess() {
- Log.d(TAG, " remove group success");
- }
- public void onFailure(int reason) {
- Log.d(TAG, " remove group fail " + reason);
- }
- });
- }
- return true;
- case MENU_ID_ADVANCED:
- //TODO: add advanced settings for p2p
+ case MENU_ID_RENAME:
+ showDialog(DIALOG_RENAME);
return true;
}
return super.onOptionsItemSelected(item);
@@ -263,8 +309,38 @@ public class WifiP2pSettings extends SettingsPreferenceFragment
mSelectedWifiPeer = (WifiP2pPeer) preference;
if (mSelectedWifiPeer.device.status == WifiP2pDevice.CONNECTED) {
showDialog(DIALOG_DISCONNECT);
+ } else if (mSelectedWifiPeer.device.status == WifiP2pDevice.INVITED) {
+ showDialog(DIALOG_CANCEL_CONNECT);
} else {
- showDialog(DIALOG_CONNECT);
+ WifiP2pConfig config = new WifiP2pConfig();
+ config.deviceAddress = mSelectedWifiPeer.device.deviceAddress;
+
+ int forceWps = SystemProperties.getInt("wifidirect.wps", -1);
+
+ if (forceWps != -1) {
+ config.wps.setup = forceWps;
+ } else {
+ if (mSelectedWifiPeer.device.wpsPbcSupported()) {
+ config.wps.setup = WpsInfo.PBC;
+ } else if (mSelectedWifiPeer.device.wpsKeypadSupported()) {
+ config.wps.setup = WpsInfo.KEYPAD;
+ } else {
+ config.wps.setup = WpsInfo.DISPLAY;
+ }
+ }
+
+ mWifiP2pManager.connect(mChannel, config,
+ new WifiP2pManager.ActionListener() {
+ public void onSuccess() {
+ if (DBG) Log.d(TAG, " connect success");
+ }
+ public void onFailure(int reason) {
+ Log.e(TAG, " connect fail " + reason);
+ Toast.makeText(getActivity(),
+ R.string.wifi_p2p_failed_connect_message,
+ Toast.LENGTH_SHORT).show();
+ }
+ });
}
}
return super.onPreferenceTreeClick(screen, preference);
@@ -272,50 +348,117 @@ public class WifiP2pSettings extends SettingsPreferenceFragment
@Override
public Dialog onCreateDialog(int id) {
- if (id == DIALOG_CONNECT) {
- mConnectDialog = new WifiP2pDialog(getActivity(), mConnectListener,
- mSelectedWifiPeer.device);
- return mConnectDialog;
- } else if (id == DIALOG_DISCONNECT) {
+ if (id == DIALOG_DISCONNECT) {
+ String deviceName = TextUtils.isEmpty(mSelectedWifiPeer.device.deviceName) ?
+ mSelectedWifiPeer.device.deviceAddress :
+ mSelectedWifiPeer.device.deviceName;
+ String msg;
+ if (mConnectedDevices > 1) {
+ msg = getActivity().getString(R.string.wifi_p2p_disconnect_multiple_message,
+ deviceName, mConnectedDevices - 1);
+ } else {
+ msg = getActivity().getString(R.string.wifi_p2p_disconnect_message, deviceName);
+ }
AlertDialog dialog = new AlertDialog.Builder(getActivity())
- .setTitle("Disconnect ?")
- .setMessage("Do you want to disconnect ?")
+ .setTitle(R.string.wifi_p2p_disconnect_title)
+ .setMessage(msg)
.setPositiveButton(getActivity().getString(R.string.dlg_ok), mDisconnectListener)
.setNegativeButton(getActivity().getString(R.string.dlg_cancel), null)
.create();
return dialog;
+ } else if (id == DIALOG_CANCEL_CONNECT) {
+ int stringId = R.string.wifi_p2p_cancel_connect_message;
+ String deviceName = TextUtils.isEmpty(mSelectedWifiPeer.device.deviceName) ?
+ mSelectedWifiPeer.device.deviceAddress :
+ mSelectedWifiPeer.device.deviceName;
+
+ AlertDialog dialog = new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.wifi_p2p_cancel_connect_title)
+ .setMessage(getActivity().getString(stringId, deviceName))
+ .setPositiveButton(getActivity().getString(R.string.dlg_ok), mCancelConnectListener)
+ .setNegativeButton(getActivity().getString(R.string.dlg_cancel), null)
+ .create();
+ return dialog;
+ } else if (id == DIALOG_RENAME) {
+ mDeviceNameText = new EditText(getActivity());
+ if (mSavedDeviceName != null) {
+ mDeviceNameText.setText(mSavedDeviceName);
+ mDeviceNameText.setSelection(mSavedDeviceName.length());
+ } else if (mThisDevice != null && !TextUtils.isEmpty(mThisDevice.deviceName)) {
+ mDeviceNameText.setText(mThisDevice.deviceName);
+ mDeviceNameText.setSelection(0, mThisDevice.deviceName.length());
+ }
+ mSavedDeviceName = null;
+ AlertDialog dialog = new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.wifi_p2p_menu_rename)
+ .setView(mDeviceNameText)
+ .setPositiveButton(getActivity().getString(R.string.dlg_ok), mRenameListener)
+ .setNegativeButton(getActivity().getString(R.string.dlg_cancel), null)
+ .create();
+ return dialog;
}
return null;
}
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ if (mSelectedWifiPeer != null) {
+ outState.putParcelable(SAVE_DIALOG_PEER, mSelectedWifiPeer.device);
+ }
+ if (mDeviceNameText != null) {
+ outState.putString(SAVE_DEVICE_NAME, mDeviceNameText.getText().toString());
+ }
+ }
+
public void onPeersAvailable(WifiP2pDeviceList peers) {
+ mPeersGroup.removeAll();
- final PreferenceScreen preferenceScreen = getPreferenceScreen();
- preferenceScreen.removeAll();
+ mPeers = peers;
+ mConnectedDevices = 0;
+ for (WifiP2pDevice peer: peers.getDeviceList()) {
+ if (DBG) Log.d(TAG, " peer " + peer);
+ mPeersGroup.addPreference(new WifiP2pPeer(getActivity(), peer));
+ if (peer.status == WifiP2pDevice.CONNECTED) mConnectedDevices++;
+ }
+ if (DBG) Log.d(TAG, " mConnectedDevices " + mConnectedDevices);
+ }
- preferenceScreen.setOrderingAsAdded(true);
+ private void handleP2pStateChanged() {
+ updateSearchMenu(false);
+ if (mWifiP2pEnabled) {
+ final PreferenceScreen preferenceScreen = getPreferenceScreen();
+ preferenceScreen.removeAll();
- if (mPeersGroup == null) {
- mPeersGroup = new PreferenceCategory(getActivity());
- } else {
- mPeersGroup.removeAll();
- }
+ preferenceScreen.setOrderingAsAdded(true);
+ preferenceScreen.addPreference(mThisDevicePref);
- preferenceScreen.addPreference(mThisDevicePref);
+ mPeersGroup.setEnabled(true);
+ preferenceScreen.addPreference(mPeersGroup);
- mPeersGroup.setTitle(R.string.wifi_p2p_available_devices);
- mPeersGroup.setEnabled(true);
- preferenceScreen.addPreference(mPeersGroup);
+ /* Request latest set of peers */
+ mWifiP2pManager.requestPeers(mChannel, WifiP2pSettings.this);
+ }
+ }
- mPeers = peers;
- for (WifiP2pDevice peer: peers.getDeviceList()) {
- mPeersGroup.addPreference(new WifiP2pPeer(getActivity(), peer));
+ private void updateSearchMenu(boolean searching) {
+ mWifiP2pSearching = searching;
+ Activity activity = getActivity();
+ if (activity != null) activity.invalidateOptionsMenu();
+ }
+
+ private void startSearch() {
+ if (mWifiP2pManager != null && !mWifiP2pSearching) {
+ mWifiP2pManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
+ public void onSuccess() {
+ }
+ public void onFailure(int reason) {
+ if (DBG) Log.d(TAG, " discover fail " + reason);
+ }
+ });
}
}
private void updateDevicePref() {
- mThisDevicePref = new Preference(getActivity());
-
if (mThisDevice != null) {
if (TextUtils.isEmpty(mThisDevice.deviceName)) {
mThisDevicePref.setTitle(mThisDevice.deviceAddress);
@@ -323,15 +466,9 @@ public class WifiP2pSettings extends SettingsPreferenceFragment
mThisDevicePref.setTitle(mThisDevice.deviceName);
}
- if (mThisDevice.status == WifiP2pDevice.CONNECTED) {
- String[] statusArray = getActivity().getResources().getStringArray(
- R.array.wifi_p2p_status);
- mThisDevicePref.setSummary(statusArray[mThisDevice.status]);
- }
mThisDevicePref.setPersistent(false);
mThisDevicePref.setEnabled(true);
mThisDevicePref.setSelectable(false);
}
- onPeersAvailable(mPeers); //update UI
}
}