Merge "Revert "Import translations. DO NOT MERGE"" into jb-mr1-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 24fc8b0..b1dd657 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -59,6 +59,7 @@
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.COPY_PROTECTED_DATA" />
<uses-permission android:name="android.permission.MANAGE_USERS" />
+ <uses-permission android:name="android.permission.READ_PROFILE" />
<application android:label="@string/settings_label"
android:icon="@mipmap/ic_launcher_settings"
@@ -1489,5 +1490,12 @@
</intent-filter>
</receiver>
+ <!-- Watch for ContactsContract.Profile changes and update the user's photo. -->
+ <receiver android:name=".users.ProfileUpdateReceiver">
+ <intent-filter>
+ <action android:name="android.provider.Contacts.PROFILE_CHANGED" />
+ </intent-filter>
+ </receiver>
+
</application>
</manifest>
diff --git a/res/layout/installed_app_details.xml b/res/layout/installed_app_details.xml
index 9e1eeb5..df4f718 100644
--- a/res/layout/installed_app_details.xml
+++ b/res/layout/installed_app_details.xml
@@ -393,6 +393,27 @@
style="?android:attr/listSeparatorTextViewStyle"
android:layout_marginTop="8dip"
android:text="@string/permissions_label" />
+ <TextView android:id="@+id/security_settings_billing_desc"
+ android:text="@string/security_settings_billing_desc"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:paddingTop="6dip"
+ android:paddingStart="6dip"
+ android:paddingBottom="6dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ <LinearLayout
+ android:id="@+id/security_settings_billing_list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <TextView
+ android:text="@string/security_settings_premium_sms_desc" />
+ <Spinner
+ android:id="@+id/security_settings_premium_sms_list"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:spinnerMode="dropdown" />
+ </LinearLayout>
<TextView android:id="@+id/security_settings_desc"
android:text="@string/security_settings_desc"
android:textAppearance="?android:attr/textAppearanceSmall"
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index e699dc2..6b1c3f9a 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -746,4 +746,14 @@
<item>2</item>
<item>1</item>
</string-array>
+
+ <!-- Values for premium SMS permission selector [CHAR LIMIT=30] -->
+ <string-array name="security_settings_premium_sms_values">
+ <!-- Ask user before sending to premium SMS short code. -->
+ <item>Ask</item>
+ <!-- Never allow app to send to premium SMS short code. -->
+ <item>Never allow</item>
+ <!-- Always allow app to send to premium SMS short code. -->
+ <item>Always allow</item>
+ </string-array>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f1b4a5e..18e2f61 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -794,6 +794,12 @@
<!-- Title for PreferenceScreen to launch picker for security method when there is none [CHAR LIMIT=22] -->
<string name="unlock_set_unlock_launch_picker_title">Screen lock</string>
+ <!-- Title for PreferenceScreen to launch picker for a user-selected widget that will live on lock screen [CHAR LIMIT=22] -->
+ <string name="choose_user_selected_lockscreen_widget_picker_title">Widget</string>
+
+ <!-- String to display if there is no user-selected widget on lock screen [CHAR LIMIT=22] -->
+ <string name="widget_none">None</string>
+
<!-- Title for PreferenceScreen to change security method: None/Pattern/PIN/Password [CHAR LIMIT=22] -->
<string name="unlock_set_unlock_launch_picker_change_title">Change lock screen</string>
@@ -2342,6 +2348,8 @@
<string name="lockpattern_too_many_failed_confirmation_attempts_header">Too many incorrect attempts!</string>
<!-- Security & location settings screen, change unlock pattern screen countdown hint on bottom of screen after too many incorrect attempts -->
<string name="lockpattern_too_many_failed_confirmation_attempts_footer">Try again in <xliff:g id="number">%d</xliff:g> seconds.</string>
+ <!-- Displayed when user launches a widget configuration activity that was uninstalled -->
+ <string name="activity_not_found">Application is not installed on your phone.</string>
<!-- ChooseLockPatternTutorial --> <skip />
<!-- ChooseLockPatternTutorial, button labels: This is to cancel the tutorial -->
@@ -2387,7 +2395,7 @@
damage to your phone or loss of data that may result from using
these apps.</string>
<!-- Applications settings screen, setting check box title. If checked, the system will send package verification requests to package verifiers on the device who will ultimately allow or reject the installation of applications. [CHAR LIMIT=30] -->
- <string name="verify_applications">App check</string>
+ <string name="verify_applications">Verify apps</string>
<!-- Applications settings screen, setting check box summary. This is the summary for "App Check" checkbox. [CHAR LIMIT=none] -->
<string name="verify_applications_summary">Disallow or warn before installation of apps that may cause harm</string>
<!-- Applications settings screen, setting check box title. If checked, applications show more settings options. -->
@@ -2430,6 +2438,8 @@
<string name="external_data_size_label" product="default">SD card</string>
<!-- Manage applications, individual application info screen, button label under Storage heading. Button to remove the application from the system. -->
<string name="uninstall_text">Uninstall</string>
+ <!-- Manage applications, individual application info screen, menu item to uninstall an application for all users. -->
+ <string name="uninstall_all_users_text">Uninstall for all users</string>
<!-- [CHAR LIMIT=NONE] Manage applications, individual application info screen, button label under Storage heading. Button to install an application for the user. -->
<string name="install_text">Install</string>
<!-- [CHAR LIMIT=25] Manage applications, individual application info screen, button label under Storage heading. Button to disable an existing application. -->
@@ -2501,6 +2511,8 @@
<string name="filter_apps_onsdcard" product="default">On SD card</string>
<!-- [CHAR LIMIT=25] Manage applications, text telling using an application is disabled. -->
<string name="disabled">Disabled</string>
+ <!-- [CHAR LIMIT=25] Manage applications, text telling using an application is not installed. -->
+ <string name="not_installed">Not installed</string>
<!-- [CHAR LIMIT=25] Text shown when there are no applications to display. -->
<string name="no_applications">No apps.</string>
<!-- [CHAR LIMIT=15] Manage applications, label for chart showing internal storage use. -->
@@ -2559,6 +2571,10 @@
<string name="join_many_items_first"><xliff:g id="first_item">%1$s</xliff:g>, <xliff:g id="all_but_first_and_last_item">%2$s</xliff:g></string>
<!-- [CHAR_LIMIT=NONE] Format to put the middle items together in a series of 4 or more items in a list -->
<string name="join_many_items_middle"><xliff:g id="added_item">%1$s</xliff:g>, <xliff:g id="rest_of_items">%2$s</xliff:g></string>
+ <!-- Manage applications, individual application info screen, text that appears under the "Permissions" heading after the app has tried to send to a premium SMS. [CHAR LIMIT=50] -->
+ <string name="security_settings_billing_desc">This app may charge you money:</string>
+ <!-- Manage applications, text for permission to send to premium SMS short codes. [CHAR LIMIT=40] -->
+ <string name="security_settings_premium_sms_desc">Send premium SMS</string>
<string name="computing_size">Computing\u2026</string>
<string name="invalid_size_value">Couldn\'t compute package size.</string>
<!-- String displayed when list is empty -->
@@ -2989,18 +3005,17 @@
<string name="accessibility_screen_magnification_title">Screen magnification</string>
<!-- Summary for the accessibility preference screen to enable screen magnification. [CHAR LIMIT=35] -->
<string name="accessibility_screen_magnification_summary">
- When screen magnification is on, you can:\n
- \n
- Temporary zoom-in: Triple-tap & hold.\n
- Toggle permanent zoom state: Triple-tap & release.\n
- \n
- Adjust zoomed area: Triple-tap & hold to zoom, then drag your finger across the screen.\n
- Pan when zoomed-in: Drag two or more fingers across the screen.\n
- \n
- Adjust zoom level when zoomed-out: Triple-tap & hold to zoom, then drag one or more fingers.\n
- Adjust zoom level when zoomed-in: Pinch with two or more fingers.\n
- </string>
+ When screen magnification is on, you can\:\n\n
+ Zoom: Triple-tap & hold.\n
+ Zoom & pan: Triple-tap & hold, then drag your finger.\n
+ Toggle zoom in or out: Triple tap & release.\n\n
+
+ While you\'re zoomed in, you can:\n
+
+ Pan: Drag two or more fingers across the screen.\n
+ Adjust zoom level: Pinch or expand using two or more fingers.
+ </string>
<!-- Title for the accessibility preference to enable large text. [CHAR LIMIT=35] -->
<string name="accessibility_toggle_large_text_preference_title">Large text</string>
<!-- Title for the accessibility preference to enable screen magnification. [CHAR LIMIT=35] -->
@@ -4176,6 +4191,8 @@
<string name="user_summary_active">Active</string>
<!-- User summary to indicate that user is currently inactive in the background [CHAR LIMIT=100] -->
<string name="user_summary_inactive">Not active</string>
+ <!-- User summary to indicate that user is currently not set up [CHAR LIMIT=100] -->
+ <string name="user_summary_not_set_up">Not set up</string>
<!-- User information string to represent the owner of the device [CHAR LIMIT=25] -->
<string name="user_owner">Owner</string>
<!-- Title for the preference to enter the nickname of the userto display in the user switcher [CHAR LIMIT=25]-->
@@ -4183,14 +4200,26 @@
<!-- Title for add user confirmation dialog [CHAR LIMIT=30] -->
<string name="user_add_user_title">Add new user</string>
<!-- Message for add user confirmation dialog [CHAR LIMIT=none] -->
- <string name="user_add_user_message">Adding a user to this device will take them through guided setup.\n\nEach user will have their own apps and personal space on this device.\nUsers can accept permissions for updates on behalf of other users of this device.\nYou can switch between users on the lock screen.</string>
+ <string name="user_add_user_message">Each user you add can go to their own space from the lock screen and perform most routine tasks such as installing, updating, or uninstalling apps. After you create a new user, that person needs to go through a setup process.</string>
+ <!-- Title of dialog to setup a new user [CHAR LIMIT=30] -->
+ <string name="user_setup_dialog_title">Set up user now?</string>
+ <!-- Message in dialog to setup a new user after creation [CHAR LIMIT=none] -->
+ <string name="user_setup_dialog_message">Make sure the person is available to take the tablet and set up their space</string>
+ <!-- Button text to setup the new user now [CHAR LIMIT=25] -->
+ <string name="user_setup_button_setup_now">Set up now</string>
+ <!-- Button text to setup the new user later [CHAR LIMIT=25] -->
+ <string name="user_setup_button_setup_later">Not now</string>
<!-- User details remove user menu [CHAR LIMIT=20] -->
<string name="user_remove_user_menu">Remove user</string>
<!-- User details new user name [CHAR LIMIT=30] -->
<string name="user_new_user_name">New user</string>
+ <!-- User (self) removal confirmation title [CHAR LIMIT=30] -->
+ <string name="user_confirm_remove_self_title">Delete yourself?</string>
<!-- User removal confirmation title [CHAR LIMIT=25] -->
<string name="user_confirm_remove_title">Remove user?</string>
+ <!-- User (self) removal confirmation message [CHAR LIMIT=none] -->
+ <string name="user_confirm_remove_self_message">You will lose your space and data on this delete. Are you sure you want to remove the user and all associated data from the device?</string>
<!-- User removal confirmation message [CHAR LIMIT=none] -->
<string name="user_confirm_remove_message">Are you sure you want to remove the user and all associated data from the device?</string>
<!-- Setting label to show that a new user is being added [CHAR LIMIT=30] -->
diff --git a/res/xml/security_settings_chooser.xml b/res/xml/security_settings_chooser.xml
index 60d3a9f..98422ba 100644
--- a/res/xml/security_settings_chooser.xml
+++ b/res/xml/security_settings_chooser.xml
@@ -27,6 +27,12 @@
android:persistent="false"/>
<PreferenceScreen
+ android:key="choose_user_selected_lockscreen_widget"
+ android:title="@string/choose_user_selected_lockscreen_widget_picker_title"
+ android:summary=""
+ android:persistent="false"/>
+
+ <PreferenceScreen
android:fragment="com.android.settings.OwnerInfoSettings"
android:key="owner_info_settings"
android:title="@string/owner_info_settings_title"
diff --git a/res/xml/security_settings_lockscreen.xml b/res/xml/security_settings_lockscreen.xml
index fbdee60..06ec934 100644
--- a/res/xml/security_settings_lockscreen.xml
+++ b/res/xml/security_settings_lockscreen.xml
@@ -25,7 +25,11 @@
android:title="@string/unlock_set_unlock_launch_picker_title"
android:summary="@string/unlock_set_unlock_mode_off"
android:persistent="false"/>
-
+ <PreferenceScreen
+ android:fragment="com.android.settings.OwnerInfoSettings"
+ android:key="owner_info_settings"
+ android:title="@string/owner_info_settings_title"
+ android:summary="@string/owner_info_settings_summary"/>
</PreferenceCategory>
</PreferenceScreen>
diff --git a/res/xml/security_settings_password.xml b/res/xml/security_settings_password.xml
index 0e9c71d..4fb2606 100644
--- a/res/xml/security_settings_password.xml
+++ b/res/xml/security_settings_password.xml
@@ -26,6 +26,12 @@
android:summary="@string/unlock_set_unlock_mode_password"
android:persistent="false"/>
+ <PreferenceScreen
+ android:key="choose_user_selected_lockscreen_widget"
+ android:title="@string/choose_user_selected_lockscreen_widget_picker_title"
+ android:summary=""
+ android:persistent="false"/>
+
<ListPreference
android:key="lock_after_timeout"
android:title="@string/lock_after_timeout"
diff --git a/res/xml/security_settings_pattern.xml b/res/xml/security_settings_pattern.xml
index b91b2b6..ebc9c41 100644
--- a/res/xml/security_settings_pattern.xml
+++ b/res/xml/security_settings_pattern.xml
@@ -25,6 +25,11 @@
android:title="@string/unlock_set_unlock_launch_picker_title"
android:summary="@string/unlock_set_unlock_mode_pattern"
android:persistent="false"/>
+ <PreferenceScreen
+ android:key="choose_user_selected_lockscreen_widget"
+ android:title="@string/choose_user_selected_lockscreen_widget_picker_title"
+ android:summary=""
+ android:persistent="false"/>
<CheckBoxPreference
android:key="visiblepattern"
diff --git a/res/xml/security_settings_pin.xml b/res/xml/security_settings_pin.xml
index 4562a9a..b908140 100644
--- a/res/xml/security_settings_pin.xml
+++ b/res/xml/security_settings_pin.xml
@@ -25,7 +25,11 @@
android:title="@string/unlock_set_unlock_launch_picker_title"
android:summary="@string/unlock_set_unlock_mode_pin"
android:persistent="false"/>
-
+ <PreferenceScreen
+ android:key="choose_user_selected_lockscreen_widget"
+ android:title="@string/choose_user_selected_lockscreen_widget_picker_title"
+ android:summary=""
+ android:persistent="false"/>
<ListPreference
android:key="lock_after_timeout"
android:title="@string/lock_after_timeout"
diff --git a/src/com/android/settings/AppWidgetPickActivity.java b/src/com/android/settings/AppWidgetPickActivity.java
index 176ac80..f573edf 100644
--- a/src/com/android/settings/AppWidgetPickActivity.java
+++ b/src/com/android/settings/AppWidgetPickActivity.java
@@ -74,7 +74,7 @@
finish();
}
}
-
+
/**
* Create list entries for any custom widgets requested through
* {@link AppWidgetManager#EXTRA_CUSTOM_INFO}.
@@ -129,7 +129,7 @@
}
if (LOGD) Log.d(TAG, "Using " + customInfo.size() + " custom items");
- putAppWidgetItems(customInfo, customExtras, items);
+ putAppWidgetItems(customInfo, customExtras, items, 0, 0, true);
}
/**
@@ -166,12 +166,13 @@
* inserting extras if provided.
*/
void putAppWidgetItems(List<AppWidgetProviderInfo> appWidgets,
- List<Bundle> customExtras, List<PickAdapter.Item> items) {
+ List<Bundle> customExtras, List<PickAdapter.Item> items, int categoryFilter,
+ int featuresFilter, boolean ignoreFilters) {
if (appWidgets == null) return;
final int size = appWidgets.size();
for (int i = 0; i < size; i++) {
AppWidgetProviderInfo info = appWidgets.get(i);
-
+
CharSequence label = info.label;
Drawable icon = null;
@@ -184,18 +185,28 @@
}
PickAdapter.Item item = new PickAdapter.Item(this, label, icon);
-
+
item.packageName = info.provider.getPackageName();
item.className = info.provider.getClassName();
if (customExtras != null) {
item.extras = customExtras.get(i);
}
-
+
+ // We remove any widgets whose category isn't included in the filter
+ if (!ignoreFilters && (info.widgetCategory & categoryFilter) == 0) {
+ continue;
+ }
+
+ // We remove any widgets who don't have all the features in the features filter
+ if (!ignoreFilters && (info.widgetFeatures & featuresFilter) != featuresFilter) {
+ continue;
+ }
+
items.add(item);
}
}
-
+
/**
* Build and return list of items to be shown in dialog. This will mix both
* installed {@link AppWidgetProviderInfo} and those provided through
@@ -203,28 +214,50 @@
*/
@Override
protected List<PickAdapter.Item> getItems() {
- List<PickAdapter.Item> items = new ArrayList<PickAdapter.Item>();
-
- putInstalledAppWidgets(items);
- putCustomAppWidgets(items);
-
- // Sort all items together by label
- Collections.sort(items, new Comparator<PickAdapter.Item>() {
- Collator mCollator = Collator.getInstance();
- public int compare(PickAdapter.Item lhs, PickAdapter.Item rhs) {
- return mCollator.compare(lhs.label, rhs.label);
- }
- });
+ final Intent intent = getIntent();
+ boolean sortCustomAppWidgets =
+ intent.getBooleanExtra(AppWidgetManager.EXTRA_CUSTOM_SORT, true);
+ List<PickAdapter.Item> items = new ArrayList<PickAdapter.Item>();
+
+ int categoryFilter = AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN;
+ if (intent.getExtras().containsKey(AppWidgetManager.EXTRA_CATEGORY_FILTER)) {
+ categoryFilter = intent.getExtras().getInt(AppWidgetManager.EXTRA_CATEGORY_FILTER);
+ }
+
+ // If not specified, we don't filter on any specific
+ int featuresFilter = AppWidgetProviderInfo.WIDGET_FEATURES_NONE;
+ if (intent.getExtras().containsKey(AppWidgetManager.EXTRA_FEATURES_FILTER)) {
+ featuresFilter = intent.getExtras().getInt(AppWidgetManager.EXTRA_CATEGORY_FILTER);
+ }
+
+ putInstalledAppWidgets(items, categoryFilter, featuresFilter);
+
+ // Sort all items together by label
+ if (sortCustomAppWidgets) {
+ putCustomAppWidgets(items);
+ }
+ Collections.sort(items, new Comparator<PickAdapter.Item>() {
+ Collator mCollator = Collator.getInstance();
+
+ public int compare(PickAdapter.Item lhs, PickAdapter.Item rhs) {
+ return mCollator.compare(lhs.label, rhs.label);
+ }
+ });
+ if (!sortCustomAppWidgets) {
+ List<PickAdapter.Item> customItems = new ArrayList<PickAdapter.Item>();
+ putCustomAppWidgets(customItems);
+ items.addAll(0, customItems);
+ }
return items;
}
/**
* Create list entries for installed {@link AppWidgetProviderInfo} widgets.
*/
- void putInstalledAppWidgets(List<PickAdapter.Item> items) {
+ void putInstalledAppWidgets(List<PickAdapter.Item> items, int categoryFilter, int featuresFilter) {
List<AppWidgetProviderInfo> installed = mAppWidgetManager.getInstalledProviders();
- putAppWidgetItems(installed, null, items);
+ putAppWidgetItems(installed, null, items, categoryFilter, featuresFilter, false);
}
/**
diff --git a/src/com/android/settings/CredentialStorage.java b/src/com/android/settings/CredentialStorage.java
index 07b1776..c737c7d 100644
--- a/src/com/android/settings/CredentialStorage.java
+++ b/src/com/android/settings/CredentialStorage.java
@@ -25,6 +25,7 @@
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.security.Credentials;
import android.security.KeyChain.KeyChainConnection;
import android.security.KeyChain;
@@ -104,17 +105,24 @@
*/
private int mRetriesRemaining = -1;
- @Override protected void onResume() {
+ @Override
+ protected void onResume() {
super.onResume();
+ if (UserHandle.myUserId() != UserHandle.USER_OWNER) {
+ Log.i(TAG, "Cannot install to CredentialStorage as non-primary user");
+ finish();
+ return;
+ }
+
Intent intent = getIntent();
String action = intent.getAction();
if (ACTION_RESET.equals(action)) {
new ResetDialog();
} else {
- if (ACTION_INSTALL.equals(action) &&
- "com.android.certinstaller".equals(getCallingPackage())) {
+ if (ACTION_INSTALL.equals(action)
+ && "com.android.certinstaller".equals(getCallingPackage())) {
mInstallBundle = intent.getExtras();
}
// ACTION_UNLOCK also handled here in addition to ACTION_INSTALL
diff --git a/src/com/android/settings/DataUsageSummary.java b/src/com/android/settings/DataUsageSummary.java
index 9837193..7c1832f6 100644
--- a/src/com/android/settings/DataUsageSummary.java
+++ b/src/com/android/settings/DataUsageSummary.java
@@ -48,6 +48,7 @@
import static com.android.settings.Utils.prepareCustomPreferencesList;
import android.animation.LayoutTransition;
+import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
@@ -127,7 +128,6 @@
import android.widget.TabWidget;
import android.widget.TextView;
-import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.settings.drawable.InsetBoundsDrawable;
import com.android.settings.net.ChartData;
@@ -447,20 +447,24 @@
public void onPrepareOptionsMenu(Menu menu) {
final Context context = getActivity();
final boolean appDetailMode = isAppDetailMode();
+ final boolean isOwner = ActivityManager.getCurrentUser() == UserHandle.USER_OWNER;
mMenuDataRoaming = menu.findItem(R.id.data_usage_menu_roaming);
mMenuDataRoaming.setVisible(hasReadyMobileRadio(context) && !appDetailMode);
mMenuDataRoaming.setChecked(getDataRoaming());
+ mMenuDataRoaming.setVisible(isOwner);
mMenuRestrictBackground = menu.findItem(R.id.data_usage_menu_restrict_background);
mMenuRestrictBackground.setVisible(hasReadyMobileRadio(context) && !appDetailMode);
mMenuRestrictBackground.setChecked(mPolicyManager.getRestrictBackground());
+ mMenuRestrictBackground.setVisible(isOwner);
mMenuAutoSync = menu.findItem(R.id.data_usage_menu_auto_sync);
mMenuAutoSync.setChecked(ContentResolver.getMasterSyncAutomatically());
+ mMenuAutoSync.setVisible(isOwner && !appDetailMode);
final MenuItem split4g = menu.findItem(R.id.data_usage_menu_split_4g);
- split4g.setVisible(hasReadyMobile4gRadio(context) && !appDetailMode);
+ split4g.setVisible(hasReadyMobile4gRadio(context) && isOwner && !appDetailMode);
split4g.setChecked(isMobilePolicySplit());
final MenuItem showWifi = menu.findItem(R.id.data_usage_menu_show_wifi);
@@ -481,7 +485,7 @@
final MenuItem metered = menu.findItem(R.id.data_usage_menu_metered);
if (hasReadyMobileRadio(context) || hasWifiRadio(context)) {
- metered.setVisible(!appDetailMode);
+ metered.setVisible(isOwner && !appDetailMode);
} else {
metered.setVisible(false);
}
@@ -681,6 +685,7 @@
final Context context = getActivity();
final String currentTab = mTabHost.getCurrentTabTag();
+ final boolean isOwner = ActivityManager.getCurrentUser() == UserHandle.USER_OWNER;
if (currentTab == null) {
Log.w(TAG, "no tab selected; hiding body");
@@ -695,7 +700,7 @@
if (LOGD) Log.d(TAG, "updateBody() with currentTab=" + currentTab);
- mDataEnabledView.setVisibility(View.VISIBLE);
+ mDataEnabledView.setVisibility(isOwner ? View.VISIBLE : View.GONE);
// TODO: remove mobile tabs when SIM isn't ready
final TelephonyManager tele = TelephonyManager.from(context);
@@ -774,8 +779,8 @@
mChart.bindNetworkPolicy(null);
// show icon and all labels appearing under this app
- final int appId = mCurrentApp.appId;
- final UidDetail detail = mUidDetailProvider.getUidDetail(appId, true);
+ final int uid = mCurrentApp.key;
+ final UidDetail detail = mUidDetailProvider.getUidDetail(uid, true);
mAppIcon.setImageDrawable(detail.icon);
mAppTitles.removeAllViews();
@@ -788,14 +793,21 @@
}
// enable settings button when package provides it
- // TODO: target torwards entire UID instead of just first package
- final String[] packageNames = pm.getPackagesForUid(appId);
+ final String[] packageNames = pm.getPackagesForUid(uid);
if (packageNames != null && packageNames.length > 0) {
mAppSettingsIntent = new Intent(Intent.ACTION_MANAGE_NETWORK_USAGE);
- mAppSettingsIntent.setPackage(packageNames[0]);
mAppSettingsIntent.addCategory(Intent.CATEGORY_DEFAULT);
- final boolean matchFound = pm.resolveActivity(mAppSettingsIntent, 0) != null;
+ // Search for match across all packages
+ boolean matchFound = false;
+ for (String packageName : packageNames) {
+ mAppSettingsIntent.setPackage(packageName);
+ if (pm.resolveActivity(mAppSettingsIntent, 0) != null) {
+ matchFound = true;
+ break;
+ }
+ }
+
mAppSettings.setEnabled(matchFound);
mAppSettings.setVisibility(View.VISIBLE);
@@ -806,7 +818,7 @@
updateDetailData();
- if (UserHandle.isApp(appId) && !mPolicyManager.getRestrictBackground()
+ if (UserHandle.isApp(uid) && !mPolicyManager.getRestrictBackground()
&& isBandwidthControlEnabled() && hasReadyMobileRadio(context)) {
setPreferenceTitle(mAppRestrictView, R.string.data_usage_app_restrict_background);
setPreferenceSummary(mAppRestrictView,
@@ -855,7 +867,8 @@
}
private boolean isNetworkPolicyModifiable(NetworkPolicy policy) {
- return policy != null && isBandwidthControlEnabled() && mDataEnabled.isChecked();
+ return policy != null && isBandwidthControlEnabled() && mDataEnabled.isChecked()
+ && ActivityManager.getCurrentUser() == UserHandle.USER_OWNER;
}
private boolean isBandwidthControlEnabled() {
@@ -886,16 +899,16 @@
}
private boolean getAppRestrictBackground() {
- final int appId = mCurrentApp.appId;
- final int uidPolicy = mPolicyManager.getAppPolicy(appId);
+ final int uid = mCurrentApp.key;
+ final int uidPolicy = mPolicyManager.getUidPolicy(uid);
return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
}
private void setAppRestrictBackground(boolean restrictBackground) {
if (LOGD) Log.d(TAG, "setAppRestrictBackground()");
- final int appId = mCurrentApp.appId;
- mPolicyManager.setAppPolicy(appId,
- restrictBackground ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE);
+ final int uid = mCurrentApp.key;
+ mPolicyManager.setUidPolicy(
+ uid, restrictBackground ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE);
mAppRestrict.setChecked(restrictBackground);
}
@@ -1080,7 +1093,7 @@
// TODO: sigh, remove this hack once we understand 6450986
if (mUidDetailProvider == null || app == null) return;
- final UidDetail detail = mUidDetailProvider.getUidDetail(app.appId, true);
+ final UidDetail detail = mUidDetailProvider.getUidDetail(app.key, true);
AppDetailsFragment.show(DataUsageSummary.this, app, detail.label);
}
};
@@ -1224,9 +1237,9 @@
@Override
public void onLoadFinished(Loader<NetworkStats> loader, NetworkStats data) {
- final int[] restrictedAppIds = mPolicyManager.getAppsWithPolicy(
+ final int[] restrictedUids = mPolicyManager.getUidsWithPolicy(
POLICY_REJECT_METERED_BACKGROUND);
- mAdapter.bindStats(data, restrictedAppIds);
+ mAdapter.bindStats(data, restrictedUids);
updateEmptyVisible();
}
@@ -1408,17 +1421,17 @@
}
public static class AppItem implements Comparable<AppItem>, Parcelable {
- public final int appId;
+ public final int key;
public boolean restricted;
public SparseBooleanArray uids = new SparseBooleanArray();
public long total;
- public AppItem(int appId) {
- this.appId = appId;
+ public AppItem(int key) {
+ this.key = key;
}
public AppItem(Parcel parcel) {
- appId = parcel.readInt();
+ key = parcel.readInt();
uids = parcel.readSparseBooleanArray();
total = parcel.readLong();
}
@@ -1429,7 +1442,7 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(appId);
+ dest.writeInt(key);
dest.writeSparseBooleanArray(uids);
dest.writeLong(total);
}
@@ -1475,49 +1488,56 @@
/**
* Bind the given {@link NetworkStats}, or {@code null} to clear list.
*/
- public void bindStats(NetworkStats stats, int[] restrictedAppIds) {
+ public void bindStats(NetworkStats stats, int[] restrictedUids) {
mItems.clear();
- final AppItem systemItem = new AppItem(android.os.Process.SYSTEM_UID);
- final SparseArray<AppItem> knownUids = new SparseArray<AppItem>();
+ final int currentUserId = ActivityManager.getCurrentUser();
+ final SparseArray<AppItem> knownItems = 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 boolean isApp = UserHandle.isApp(entry.uid);
- final int appId = isApp ? UserHandle.getAppId(entry.uid) : entry.uid;
- if (isApp || appId == UID_REMOVED || appId == UID_TETHERING) {
- AppItem item = knownUids.get(appId);
- if (item == null) {
- item = new AppItem(appId);
- knownUids.put(appId, item);
- mItems.add(item);
+ // Decide how to collapse items together
+ final int uid = entry.uid;
+ final int collapseKey;
+ if (UserHandle.isApp(uid)) {
+ if (UserHandle.getUserId(uid) == currentUserId) {
+ collapseKey = uid;
+ } else {
+ collapseKey = UidDetailProvider.buildKeyForUser(UserHandle.getUserId(uid));
}
-
- item.total += entry.rxBytes + entry.txBytes;
- item.addUid(entry.uid);
+ } else if (uid == UID_REMOVED || uid == UID_TETHERING) {
+ collapseKey = uid;
} else {
- systemItem.total += entry.rxBytes + entry.txBytes;
- systemItem.addUid(entry.uid);
+ collapseKey = android.os.Process.SYSTEM_UID;
}
+
+ AppItem item = knownItems.get(collapseKey);
+ if (item == null) {
+ item = new AppItem(collapseKey);
+ mItems.add(item);
+ knownItems.put(item.key, item);
+ }
+ item.addUid(uid);
+ item.total += entry.rxBytes + entry.txBytes;
}
- for (int appId : restrictedAppIds) {
- AppItem item = knownUids.get(appId);
+ for (int uid : restrictedUids) {
+ // Only splice in restricted state for current user
+ if (UserHandle.getUserId(uid) != currentUserId) continue;
+
+ AppItem item = knownItems.get(uid);
if (item == null) {
- item = new AppItem(appId);
+ item = new AppItem(uid);
item.total = -1;
mItems.add(item);
+ knownItems.put(item.key, item);
}
item.restricted = true;
}
- if (systemItem.total > 0) {
- mItems.add(systemItem);
- }
-
Collections.sort(mItems);
mLargest = (mItems.size() > 0) ? mItems.get(0).total : 0;
notifyDataSetChanged();
@@ -1535,7 +1555,7 @@
@Override
public long getItemId(int position) {
- return mItems.get(position).appId;
+ return mItems.get(position).key;
}
@Override
@@ -2126,7 +2146,7 @@
existing.cancel(false);
}
- final UidDetail cachedDetail = provider.getUidDetail(item.appId, false);
+ final UidDetail cachedDetail = provider.getUidDetail(item.key, false);
if (cachedDetail != null) {
bindView(cachedDetail, target);
} else {
@@ -2155,7 +2175,7 @@
@Override
protected UidDetail doInBackground(Void... params) {
- return mProvider.getUidDetail(mItem.appId, true);
+ return mProvider.getUidDetail(mItem.key, true);
}
@Override
diff --git a/src/com/android/settings/SecuritySettings.java b/src/com/android/settings/SecuritySettings.java
index e309c37..d896a68 100644
--- a/src/com/android/settings/SecuritySettings.java
+++ b/src/com/android/settings/SecuritySettings.java
@@ -21,13 +21,18 @@
import android.app.Activity;
import android.app.AlertDialog;
+import android.app.KeyguardManager;
import android.app.admin.DevicePolicyManager;
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.net.Uri;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.Vibrator;
@@ -41,8 +46,8 @@
import android.security.KeyStore;
import android.telephony.TelephonyManager;
import android.util.Log;
+import android.widget.Toast;
-import com.android.internal.telephony.Phone;
import com.android.internal.widget.LockPatternUtils;
import java.util.ArrayList;
@@ -53,9 +58,12 @@
*/
public class SecuritySettings extends SettingsPreferenceFragment
implements OnPreferenceChangeListener, DialogInterface.OnClickListener {
+ static final String TAG = "SecuritySettings";
// Lock Settings
private static final String KEY_UNLOCK_SET_OR_CHANGE = "unlock_set_or_change";
+ private static final String KEY_CHOOSE_USER_SELECTED_LOCKSCREEN_WIDGET =
+ "choose_user_selected_lockscreen_widget";
private static final String KEY_BIOMETRIC_WEAK_IMPROVE_MATCHING =
"biometric_weak_improve_matching";
private static final String KEY_BIOMETRIC_WEAK_LIVELINESS = "biometric_weak_liveliness";
@@ -64,9 +72,12 @@
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 String EXTRA_NO_WIDGET = "com.android.settings.NO_WIDGET";
private static final int SET_OR_CHANGE_LOCK_METHOD_REQUEST = 123;
private static final int CONFIRM_EXISTING_FOR_BIOMETRIC_WEAK_IMPROVE_REQUEST = 124;
private static final int CONFIRM_EXISTING_FOR_BIOMETRIC_WEAK_LIVELINESS_OFF = 125;
+ private static final int REQUEST_PICK_APPWIDGET = 126;
+ private static final int REQUEST_CREATE_APPWIDGET = 127;
// Misc Settings
private static final String KEY_SIM_LOCK = "sim_lock";
@@ -80,6 +91,7 @@
DevicePolicyManager mDPM;
private ChooseLockSettingsHelper mChooseLockSettingsHelper;
+ private Preference mUserSelectedWidget;
private LockPatternUtils mLockPatternUtils;
private ListPreference mLockAfter;
@@ -233,12 +245,33 @@
mToggleAppInstallation.setChecked(isNonMarketAppsAllowed());
// Package verification
- mToggleVerifyApps = (CheckBoxPreference) findPreference(KEY_TOGGLE_VERIFY_APPLICATIONS);
- if (isVerifierInstalled()) {
- mToggleVerifyApps.setChecked(isVerifyAppsEnabled());
- } else {
- mToggleVerifyApps.setChecked(false);
- mToggleVerifyApps.setEnabled(false);
+ if (showVerifierSetting()) {
+ mToggleVerifyApps = (CheckBoxPreference) findPreference(KEY_TOGGLE_VERIFY_APPLICATIONS);
+ if (isVerifierInstalled()) {
+ mToggleVerifyApps.setChecked(isVerifyAppsEnabled());
+ } else {
+ mToggleVerifyApps.setChecked(false);
+ mToggleVerifyApps.setEnabled(false);
+ }
+ }
+
+ mUserSelectedWidget = root.findPreference(KEY_CHOOSE_USER_SELECTED_LOCKSCREEN_WIDGET);
+ if (mUserSelectedWidget != null) {
+ AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getActivity());
+ int appWidgetId = -1;
+ String appWidgetIdString = Settings.Secure.getString(
+ getContentResolver(), Settings.Secure.LOCK_SCREEN_USER_SELECTED_APPWIDGET_ID);
+ if (appWidgetIdString != null) {;
+ appWidgetId = (int) Integer.decode(appWidgetIdString);
+ }
+ if (appWidgetId == -1) {
+ mUserSelectedWidget.setSummary(getResources().getString(R.string.widget_none));
+ } else {
+ AppWidgetProviderInfo appWidget = appWidgetManager.getAppWidgetInfo(appWidgetId);
+ if (appWidget != null) {
+ mUserSelectedWidget.setSummary(appWidget.label);
+ }
+ }
}
return root;
@@ -269,6 +302,11 @@
return (receivers.size() > 0) ? true : false;
}
+ private boolean showVerifierSetting() {
+ return Settings.Global.getInt(getContentResolver(),
+ Settings.Global.PACKAGE_VERIFIER_SETTING_VISIBLE, 1) > 0;
+ }
+
private void warnAppInstallation() {
// TODO: DialogFragment?
mWarnInstallApps = new AlertDialog.Builder(getActivity()).setTitle(
@@ -393,6 +431,17 @@
}
}
+ void startActivityForResultSafely(Intent intent, int requestCode) {
+ try {
+ startActivityForResult(intent, requestCode);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(getActivity(), R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+ } catch (SecurityException e) {
+ Toast.makeText(getActivity(), R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+ Log.e(TAG, "Settings does not have the permission to launch " + intent, e);
+ }
+ }
+
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
final String key = preference.getKey();
@@ -401,6 +450,38 @@
if (KEY_UNLOCK_SET_OR_CHANGE.equals(key)) {
startFragment(this, "com.android.settings.ChooseLockGeneric$ChooseLockGenericFragment",
SET_OR_CHANGE_LOCK_METHOD_REQUEST, null);
+ } else if (KEY_CHOOSE_USER_SELECTED_LOCKSCREEN_WIDGET.equals(key)) {
+ // Create intent to pick widget
+ Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
+ // Found in KeyguardHostView.java
+ final int KEYGUARD_HOST_ID = 0x4B455947;
+ int appWidgetId = AppWidgetHost.allocateAppWidgetIdForHost(
+ "com.android.internal.policy.impl.keyguard", KEYGUARD_HOST_ID);
+ if (appWidgetId != -1) {
+ pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+ pickIntent.putExtra(AppWidgetManager.EXTRA_CUSTOM_SORT, false);
+ pickIntent.putExtra(AppWidgetManager.EXTRA_CATEGORY_FILTER,
+ AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD);
+
+ // Add an entry for "none" to let someone select no widget
+ AppWidgetProviderInfo noneInfo = new AppWidgetProviderInfo();
+ ArrayList<AppWidgetProviderInfo> extraInfos = new ArrayList<AppWidgetProviderInfo>();
+ noneInfo.label = getResources().getString(R.string.widget_none);
+ noneInfo.provider = new ComponentName("", "");
+ extraInfos.add(noneInfo);
+
+ ArrayList<Bundle> extraExtras = new ArrayList<Bundle>();
+ Bundle b = new Bundle();
+ b.putBoolean(EXTRA_NO_WIDGET, true);
+ extraExtras.add(b);
+
+ // Launch the widget picker
+ pickIntent.putExtra(AppWidgetManager.EXTRA_CUSTOM_INFO, extraInfos);
+ pickIntent.putExtra(AppWidgetManager.EXTRA_CUSTOM_EXTRAS, extraExtras);
+ startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET);
+ } else {
+ Log.e(TAG, "Unable to allocate an AppWidget id in lock screen");
+ }
} else if (KEY_BIOMETRIC_WEAK_IMPROVE_MATCHING.equals(key)) {
ChooseLockSettingsHelper helper =
new ChooseLockSettingsHelper(this.getActivity(), this);
@@ -479,10 +560,45 @@
resultCode == Activity.RESULT_OK) {
final LockPatternUtils lockPatternUtils = mChooseLockSettingsHelper.utils();
lockPatternUtils.setBiometricWeakLivelinessEnabled(false);
- // Setting the mBiometricWeakLiveliness checked value to false is handled when onResume
- // is called by grabbing the value from lockPatternUtils. We can't set it here
- // because mBiometricWeakLiveliness could be null
+ // Setting the mBiometricWeakLiveliness checked value to false is handled when onResume
+ // is called by grabbing the value from lockPatternUtils. We can't set it here
+ // because mBiometricWeakLiveliness could be null
return;
+ } else if (requestCode == REQUEST_PICK_APPWIDGET ||
+ requestCode == REQUEST_CREATE_APPWIDGET) {
+ int appWidgetId = (data == null) ? -1 : data.getIntExtra(
+ AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
+ if (requestCode == REQUEST_PICK_APPWIDGET && resultCode == Activity.RESULT_OK) {
+ AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getActivity());
+ boolean noWidget = data.getBooleanExtra(EXTRA_NO_WIDGET, false);
+
+ AppWidgetProviderInfo appWidget = null;
+ if (!noWidget) {
+ appWidget = appWidgetManager.getAppWidgetInfo(appWidgetId);
+ }
+
+ if (!noWidget && appWidget.configure != null) {
+ // Launch over to configure widget, if needed
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
+ intent.setComponent(appWidget.configure);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+
+ startActivityForResultSafely(intent, REQUEST_CREATE_APPWIDGET);
+ } else {
+ // Otherwise just add it
+ if (noWidget) {
+ data.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
+ }
+ onActivityResult(REQUEST_CREATE_APPWIDGET, Activity.RESULT_OK, data);
+ }
+ } else if (requestCode == REQUEST_CREATE_APPWIDGET && resultCode == Activity.RESULT_OK) {
+ Settings.Secure.putString(getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_USER_SELECTED_APPWIDGET_ID,
+ Integer.toString(appWidgetId));
+
+ } else {
+ AppWidgetHost.deleteAppWidgetIdForHost(appWidgetId);
+ }
}
createPreferenceHierarchy();
}
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 88b3e87..bf31386 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -100,6 +100,7 @@
R.id.wireless_section,
R.id.wifi_settings,
R.id.bluetooth_settings,
+ R.id.data_usage_settings,
R.id.device_section,
R.id.sound_settings,
R.id.display_settings,
diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java
index f6c1734..df44e91 100644
--- a/src/com/android/settings/TetherSettings.java
+++ b/src/com/android/settings/TetherSettings.java
@@ -48,6 +48,7 @@
import java.io.InputStream;
import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.Locale;
/*
@@ -78,7 +79,7 @@
private String[] mWifiRegexs;
private String[] mBluetoothRegexs;
- private BluetoothPan mBluetoothPan;
+ private AtomicReference<BluetoothPan> mBluetoothPan = new AtomicReference<BluetoothPan>();
private static final String WIFI_AP_SSID_AND_SECURITY = "wifi_ap_ssid_and_security";
private static final int CONFIG_SUBTEXT = R.string.wifi_tether_configure_subtext;
@@ -151,7 +152,8 @@
if (!bluetoothAvailable) {
getPreferenceScreen().removePreference(mBluetoothTether);
} else {
- if (mBluetoothPan != null && mBluetoothPan.isTetheringOn()) {
+ BluetoothPan pan = mBluetoothPan.get();
+ if (pan != null && pan.isTetheringOn()) {
mBluetoothTether.setChecked(true);
} else {
mBluetoothTether.setChecked(false);
@@ -188,10 +190,10 @@
private BluetoothProfile.ServiceListener mProfileServiceListener =
new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
- mBluetoothPan = (BluetoothPan) proxy;
+ mBluetoothPan.set((BluetoothPan) proxy);
}
public void onServiceDisconnected(int profile) {
- mBluetoothPan = null;
+ mBluetoothPan.set(null);
}
};
@@ -235,8 +237,9 @@
switch (intent
.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) {
case BluetoothAdapter.STATE_ON:
- if(mBluetoothPan != null) {
- mBluetoothPan.setBluetoothTethering(true);
+ BluetoothPan bluetoothPan = mBluetoothPan.get();
+ if (bluetoothPan != null) {
+ bluetoothPan.setBluetoothTethering(true);
mBluetoothEnableForTether = false;
}
break;
@@ -389,26 +392,31 @@
} else if (btState == BluetoothAdapter.STATE_TURNING_ON) {
mBluetoothTether.setEnabled(false);
mBluetoothTether.setSummary(R.string.bluetooth_turning_on);
- } else if (btState == BluetoothAdapter.STATE_ON &&
- mBluetoothPan != null && mBluetoothPan.isTetheringOn()) {
- mBluetoothTether.setChecked(true);
- mBluetoothTether.setEnabled(true);
- int bluetoothTethered = mBluetoothPan.getConnectedDevices().size();
- if (bluetoothTethered > 1) {
- String summary = getString(
- R.string.bluetooth_tethering_devices_connected_subtext, bluetoothTethered);
- mBluetoothTether.setSummary(summary);
- } else if (bluetoothTethered == 1) {
- mBluetoothTether.setSummary(R.string.bluetooth_tethering_device_connected_subtext);
- } else if (bluetoothErrored) {
- mBluetoothTether.setSummary(R.string.bluetooth_tethering_errored_subtext);
- } else {
- mBluetoothTether.setSummary(R.string.bluetooth_tethering_available_subtext);
- }
} else {
- mBluetoothTether.setEnabled(true);
- mBluetoothTether.setChecked(false);
- mBluetoothTether.setSummary(R.string.bluetooth_tethering_off_subtext);
+ BluetoothPan bluetoothPan = mBluetoothPan.get();
+ if (btState == BluetoothAdapter.STATE_ON && bluetoothPan != null &&
+ bluetoothPan.isTetheringOn()) {
+ mBluetoothTether.setChecked(true);
+ mBluetoothTether.setEnabled(true);
+ int bluetoothTethered = bluetoothPan.getConnectedDevices().size();
+ if (bluetoothTethered > 1) {
+ String summary = getString(
+ R.string.bluetooth_tethering_devices_connected_subtext,
+ bluetoothTethered);
+ mBluetoothTether.setSummary(summary);
+ } else if (bluetoothTethered == 1) {
+ mBluetoothTether.setSummary(
+ R.string.bluetooth_tethering_device_connected_subtext);
+ } else if (bluetoothErrored) {
+ mBluetoothTether.setSummary(R.string.bluetooth_tethering_errored_subtext);
+ } else {
+ mBluetoothTether.setSummary(R.string.bluetooth_tethering_available_subtext);
+ }
+ } else {
+ mBluetoothTether.setEnabled(true);
+ mBluetoothTether.setChecked(false);
+ mBluetoothTether.setSummary(R.string.bluetooth_tethering_off_subtext);
+ }
}
}
@@ -476,7 +484,8 @@
mBluetoothTether.setSummary(R.string.bluetooth_turning_on);
mBluetoothTether.setEnabled(false);
} else {
- mBluetoothPan.setBluetoothTethering(true);
+ BluetoothPan bluetoothPan = mBluetoothPan.get();
+ if (bluetoothPan != null) bluetoothPan.setBluetoothTethering(true);
mBluetoothTether.setSummary(R.string.bluetooth_tethering_available_subtext);
}
break;
@@ -528,7 +537,8 @@
errored = true;
}
- mBluetoothPan.setBluetoothTethering(false);
+ BluetoothPan bluetoothPan = mBluetoothPan.get();
+ if (bluetoothPan != null) bluetoothPan.setBluetoothTethering(false);
if (errored) {
mBluetoothTether.setSummary(R.string.bluetooth_tethering_errored_subtext);
} else {
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 777a712..ee1bfee 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -23,25 +23,41 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
+import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
import android.net.LinkProperties;
+import android.net.Uri;
import android.os.BatteryManager;
import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.preference.Preference;
import android.preference.PreferenceActivity.Header;
import android.preference.PreferenceFrameLayout;
import android.preference.PreferenceGroup;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Profile;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import android.widget.TabWidget;
+import com.android.settings.users.ProfileUpdateReceiver;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
import java.net.InetAddress;
import java.util.Iterator;
import java.util.List;
@@ -445,4 +461,55 @@
return R.string.tether_settings_title_bluetooth;
}
}
+
+ /* Used by UserSettings as well. Call this on a non-ui thread. */
+ public static boolean copyMeProfilePhoto(Context context, UserInfo user) {
+ Uri contactUri = Profile.CONTENT_URI;
+
+ InputStream avatarDataStream = Contacts.openContactPhotoInputStream(
+ context.getContentResolver(),
+ contactUri, true);
+ // If there's no profile photo, assign a default avatar
+ if (avatarDataStream == null) {
+ return false;
+ }
+ int userId = user != null ? user.id : UserHandle.myUserId();
+ UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ ParcelFileDescriptor fd = um.setUserIcon(userId);
+ FileOutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
+ byte[] buffer = new byte[4096];
+ int readSize;
+ try {
+ while ((readSize = avatarDataStream.read(buffer)) > 0) {
+ os.write(buffer, 0, readSize);
+ }
+ return true;
+ } catch (IOException ioe) {
+ Log.e("copyProfilePhoto", "Error copying profile photo " + ioe);
+ } finally {
+ try {
+ os.close();
+ avatarDataStream.close();
+ } catch (IOException ioe) { }
+ }
+ return false;
+ }
+
+ public static String getMeProfileName(Context context) {
+ Cursor cursor = context.getContentResolver().query(
+ Profile.CONTENT_URI, new String[] {Phone._ID, Phone.DISPLAY_NAME},
+ null, null, null);
+ if (cursor == null) {
+ return null;
+ }
+
+ try {
+ if (cursor.moveToFirst()) {
+ return cursor.getString(cursor.getColumnIndex(Phone.DISPLAY_NAME));
+ }
+ } finally {
+ cursor.close();
+ }
+ return null;
+ }
}
diff --git a/src/com/android/settings/applications/ApplicationsState.java b/src/com/android/settings/applications/ApplicationsState.java
index cca9086..2ce6779 100644
--- a/src/com/android/settings/applications/ApplicationsState.java
+++ b/src/com/android/settings/applications/ApplicationsState.java
@@ -163,8 +163,12 @@
private final Collator sCollator = Collator.getInstance();
@Override
public int compare(AppEntry object1, AppEntry object2) {
- if (object1.info.enabled != object2.info.enabled) {
- return object1.info.enabled ? -1 : 1;
+ final boolean normal1 = object1.info.enabled
+ && (object1.info.flags&ApplicationInfo.FLAG_INSTALLED) != 0;
+ final boolean normal2 = object2.info.enabled
+ && (object2.info.flags&ApplicationInfo.FLAG_INSTALLED) != 0;
+ if (normal1 != normal2) {
+ return normal1 ? -1 : 1;
}
return sCollator.compare(object1.label, object2.label);
}
diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java
index 2c1944d..907bf53 100644
--- a/src/com/android/settings/applications/InstalledAppDetails.java
+++ b/src/com/android/settings/applications/InstalledAppDetails.java
@@ -16,6 +16,8 @@
package com.android.settings.applications;
+import com.android.internal.telephony.ISms;
+import com.android.internal.telephony.SmsUsageMonitor;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.applications.ApplicationsState.AppEntry;
@@ -53,6 +55,8 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.preference.PreferenceActivity;
import android.text.SpannableString;
import android.text.TextUtils;
@@ -64,14 +68,20 @@
import java.util.ArrayList;
import java.util.List;
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.AppSecurityPermissions;
+import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.Spinner;
import android.widget.TextView;
/**
@@ -87,15 +97,16 @@
implements View.OnClickListener, CompoundButton.OnCheckedChangeListener,
ApplicationsState.Callbacks {
private static final String TAG="InstalledAppDetails";
- static final boolean SUPPORT_DISABLE_APPS = true;
private static final boolean localLOGV = false;
public static final String ARG_PACKAGE_NAME = "package";
private PackageManager mPm;
+ private UserManager mUserManager;
private IUsbManager mUsbManager;
private AppWidgetManager mAppWidgetManager;
private DevicePolicyManager mDpm;
+ private ISms mSmsManager;
private ApplicationsState mState;
private ApplicationsState.Session mSession;
private ApplicationsState.AppEntry mAppEntry;
@@ -161,7 +172,14 @@
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;
-
+
+ // Menu identifiers
+ public static final int UNINSTALL_ALL_USERS_MENU = 1;
+
+ // Result code identifiers
+ public static final int REQUEST_UNINSTALL = 1;
+ public static final int REQUEST_MANAGE_SPACE = 2;
+
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
// If the fragment is gone, don't process any more messages.
@@ -297,30 +315,28 @@
} else {
if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
enabled = false;
- if (SUPPORT_DISABLE_APPS) {
- try {
- // Try to prevent the user from bricking their phone
- // by not allowing disabling of apps signed with the
- // system cert and any launcher app in the system.
- PackageInfo sys = mPm.getPackageInfo("android",
- PackageManager.GET_SIGNATURES);
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.addCategory(Intent.CATEGORY_HOME);
- intent.setPackage(mAppEntry.info.packageName);
- List<ResolveInfo> homes = mPm.queryIntentActivities(intent, 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) {
- mUninstallButton.setText(R.string.disable_text);
- enabled = true;
- } else {
- mUninstallButton.setText(R.string.enable_text);
- enabled = true;
- }
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Unable to get package info", e);
+ try {
+ // Try to prevent the user from bricking their phone
+ // by not allowing disabling of apps signed with the
+ // system cert and any launcher app in the system.
+ PackageInfo sys = mPm.getPackageInfo("android",
+ PackageManager.GET_SIGNATURES);
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_HOME);
+ intent.setPackage(mAppEntry.info.packageName);
+ List<ResolveInfo> homes = mPm.queryIntentActivities(intent, 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) {
+ mUninstallButton.setText(R.string.disable_text);
+ enabled = true;
+ } else {
+ mUninstallButton.setText(R.string.enable_text);
+ enabled = true;
}
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Unable to get package info", e);
}
} else if ((mPackageInfo.applicationInfo.flags
& ApplicationInfo.FLAG_INSTALLED) == 0) {
@@ -364,13 +380,17 @@
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
+ setHasOptionsMenu(true);
+
mState = ApplicationsState.getInstance(getActivity().getApplication());
mSession = mState.newSession(this);
mPm = getActivity().getPackageManager();
+ mUserManager = (UserManager)getActivity().getSystemService(Context.USER_SERVICE);
IBinder b = ServiceManager.getService(Context.USB_SERVICE);
mUsbManager = IUsbManager.Stub.asInterface(b);
mAppWidgetManager = AppWidgetManager.getInstance(getActivity());
mDpm = (DevicePolicyManager)getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
+ mSmsManager = ISms.Stub.asInterface(ServiceManager.getService("isms"));
mCanBeOnSdCardChecker = new CanBeOnSdCardChecker();
}
@@ -421,6 +441,49 @@
return view;
}
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ menu.add(0, UNINSTALL_ALL_USERS_MENU, 1, R.string.uninstall_all_users_text)
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+ }
+
+ @Override
+ public void onPrepareOptionsMenu(Menu menu) {
+ boolean showIt = true;
+ if (mUpdatedSysApp) {
+ showIt = false;
+ } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ showIt = false;
+ } else if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
+ showIt = false;
+ } else if (UserHandle.myUserId() != 0) {
+ showIt = false;
+ } else if (mUserManager.getUsers().size() < 1) {
+ showIt = false;
+ }
+ menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(showIt);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ int menuId = item.getItemId();
+ if (menuId == UNINSTALL_ALL_USERS_MENU) {
+ uninstallPkg(mAppEntry.info.packageName, true);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == REQUEST_UNINSTALL) {
+ if (!refreshUi()) {
+ setIntentAndFinish(true, true);
+ }
+ }
+ }
+
// Utility method to set applicaiton label and icon.
private void setAppLabelAndIcon(PackageInfo pkgInfo) {
View appSnippet = mRootView.findViewById(R.id.app_snippet);
@@ -595,8 +658,43 @@
// Security permissions section
LinearLayout permsView = (LinearLayout) mRootView.findViewById(R.id.permissions_section);
AppSecurityPermissions asp = new AppSecurityPermissions(getActivity(), packageName);
- if (asp.getPermissionCount() > 0) {
+ int premiumSmsPermission = getPremiumSmsPermission(packageName);
+ // Premium SMS permission implies the app also has SEND_SMS permission, so the original
+ // application permissions list doesn't have to be shown/hidden separately. The premium
+ // SMS subsection should only be visible if the app has tried to send to a premium SMS.
+ if (asp.getPermissionCount() > 0
+ || premiumSmsPermission != SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) {
permsView.setVisibility(View.VISIBLE);
+ } else {
+ permsView.setVisibility(View.GONE);
+ }
+ // Premium SMS permission subsection
+ TextView securityBillingDesc = (TextView) permsView.findViewById(
+ R.id.security_settings_billing_desc);
+ LinearLayout securityBillingList = (LinearLayout) permsView.findViewById(
+ R.id.security_settings_billing_list);
+ if (premiumSmsPermission != SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) {
+ // Show the premium SMS permission selector
+ securityBillingDesc.setVisibility(View.VISIBLE);
+ securityBillingList.setVisibility(View.VISIBLE);
+ Spinner spinner = (Spinner) permsView.findViewById(
+ R.id.security_settings_premium_sms_list);
+ ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(getActivity(),
+ R.array.security_settings_premium_sms_values,
+ android.R.layout.simple_spinner_item);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ spinner.setAdapter(adapter);
+ // List items are in the same order as SmsUsageMonitor constants, offset by 1.
+ spinner.setSelection(premiumSmsPermission - 1);
+ spinner.setOnItemSelectedListener(new PremiumSmsSelectionListener(
+ packageName, mSmsManager));
+ } else {
+ // Hide the premium SMS permission selector
+ securityBillingDesc.setVisibility(View.GONE);
+ securityBillingList.setVisibility(View.GONE);
+ }
+ // App permissions subsection
+ if (asp.getPermissionCount() > 0) {
// Make the security sections header visible
LinearLayout securityList = (LinearLayout) permsView.findViewById(
R.id.security_settings_list);
@@ -642,8 +740,6 @@
mPackageInfo.applicationInfo.loadLabel(mPm), appListStr));
}
}
- } else {
- permsView.setVisibility(View.GONE);
}
checkForceStop();
@@ -652,6 +748,42 @@
refreshSizeInfo();
return true;
}
+
+ private static class PremiumSmsSelectionListener implements AdapterView.OnItemSelectedListener {
+ private final String mPackageName;
+ private final ISms mSmsManager;
+
+ PremiumSmsSelectionListener(String packageName, ISms smsManager) {
+ mPackageName = packageName;
+ mSmsManager = smsManager;
+ }
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position,
+ long id) {
+ if (position >= 0 && position < 3) {
+ Log.d(TAG, "Selected premium SMS policy " + position);
+ setPremiumSmsPermission(mPackageName, (position + 1));
+ } else {
+ Log.e(TAG, "Error: unknown premium SMS policy " + position);
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ // Ignored
+ }
+
+ private void setPremiumSmsPermission(String packageName, int permission) {
+ try {
+ if (mSmsManager != null) {
+ mSmsManager.setPremiumSmsPermission(packageName, permission);
+ }
+ } catch (RemoteException ex) {
+ // ignored
+ }
+ }
+ }
private void resetLaunchDefaultsUi(TextView title, TextView autoLaunchView) {
title.setText(R.string.auto_launch_label);
@@ -849,7 +981,7 @@
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Clear user data here
- getOwner().uninstallPkg(getOwner().mAppEntry.info.packageName);
+ getOwner().uninstallPkg(getOwner().mAppEntry.info.packageName, false);
}
})
.setNegativeButton(R.string.dlg_cancel, null)
@@ -945,12 +1077,12 @@
}
}
- private void uninstallPkg(String packageName) {
+ private void uninstallPkg(String packageName, boolean allUsers) {
// Create new intent to launch Uninstaller activity
Uri packageURI = Uri.parse("package:"+packageName);
- Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, packageURI);
- startActivity(uninstallIntent);
- setIntentAndFinish(true, true);
+ Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI);
+ uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers);
+ startActivityForResult(uninstallIntent, REQUEST_UNINSTALL);
}
private void forceStopPackage(String pkgName) {
@@ -1027,6 +1159,17 @@
}
}
+ private int getPremiumSmsPermission(String packageName) {
+ try {
+ if (mSmsManager != null) {
+ return mSmsManager.getPremiumSmsPermission(packageName);
+ }
+ } catch (RemoteException ex) {
+ // ignored
+ }
+ return SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN;
+ }
+
/*
* Method implementing functionality of buttons clicked
* @see android.view.View.OnClickListener#onClick(android.view.View)
@@ -1052,7 +1195,7 @@
} catch (NameNotFoundException e) {
}
} else {
- uninstallPkg(packageName);
+ uninstallPkg(packageName, false);
}
}
} else if(v == mActivitiesButton) {
@@ -1073,7 +1216,7 @@
Intent intent = new Intent(Intent.ACTION_DEFAULT);
intent.setClassName(mAppEntry.info.packageName,
mAppEntry.info.manageSpaceActivityName);
- startActivityForResult(intent, -1);
+ startActivityForResult(intent, REQUEST_MANAGE_SPACE);
}
} else {
showDialogInner(DLG_CLEAR_DATA, 0);
diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java
index 1240d43..b94d944 100644
--- a/src/com/android/settings/applications/ManageApplications.java
+++ b/src/com/android/settings/applications/ManageApplications.java
@@ -20,6 +20,7 @@
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
import android.app.Activity;
+import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Fragment;
import android.app.INotificationManager;
@@ -41,6 +42,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.preference.PreferenceActivity;
import android.preference.PreferenceFrameLayout;
import android.provider.Settings;
@@ -772,8 +774,12 @@
holder.appIcon.setImageDrawable(entry.icon);
}
holder.updateSizeText(mTab.mInvalidSizeStr, mWhichSize);
- if (InstalledAppDetails.SUPPORT_DISABLE_APPS) {
- holder.disabled.setVisibility(entry.info.enabled ? View.GONE : View.VISIBLE);
+ if ((entry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
+ holder.disabled.setVisibility(View.VISIBLE);
+ holder.disabled.setText(R.string.not_installed);
+ } else if (!entry.info.enabled) {
+ holder.disabled.setVisibility(View.VISIBLE);
+ holder.disabled.setText(R.string.disabled);
} else {
holder.disabled.setVisibility(View.GONE);
}
@@ -995,7 +1001,6 @@
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- Log.i(TAG, "onCreateOptionsMenu in " + this + ": " + menu);
mOptionsMenu = menu;
// note: icons removed for now because the cause the new action
// bar UI to be very confusing.
@@ -1119,11 +1124,15 @@
+ prefActivities.get(i).getPackageName());
pm.clearPackagePreferredActivities(prefActivities.get(i).getPackageName());
}
- final int[] restrictedAppIds = npm.getAppsWithPolicy(
+ final int[] restrictedUids = npm.getUidsWithPolicy(
POLICY_REJECT_METERED_BACKGROUND);
- for (int i : restrictedAppIds) {
- if (DEBUG) Log.v(TAG, "Clearing data policy: " + i);
- npm.setAppPolicy(i, POLICY_NONE);
+ final int currentUserId = ActivityManager.getCurrentUser();
+ for (int uid : restrictedUids) {
+ // Only reset for current user
+ if (UserHandle.getUserId(uid) == currentUserId) {
+ if (DEBUG) Log.v(TAG, "Clearing data policy: " + uid);
+ npm.setUidPolicy(uid, POLICY_NONE);
+ }
}
handler.post(new Runnable() {
@Override public void run() {
diff --git a/src/com/android/settings/deviceinfo/StorageMeasurement.java b/src/com/android/settings/deviceinfo/StorageMeasurement.java
index 50238f3..772ac0d 100644
--- a/src/com/android/settings/deviceinfo/StorageMeasurement.java
+++ b/src/com/android/settings/deviceinfo/StorageMeasurement.java
@@ -96,6 +96,9 @@
}
public static class MeasurementDetails {
+ public long totalSize;
+ public long availSize;
+
/**
* Total apps disk usage.
* <p>
@@ -111,6 +114,11 @@
public long appsSize;
/**
+ * Total cache disk usage by apps.
+ */
+ public long cacheSize;
+
+ /**
* Total media disk usage, categorized by types such as
* {@link Environment#DIRECTORY_MUSIC}.
* <p>
@@ -237,34 +245,36 @@
}
private void addStatsLocked(PackageStats stats) {
- final long externalSize = stats.externalCodeSize + stats.externalDataSize
- + stats.externalCacheSize + stats.externalMediaSize;
-
if (mIsInternal) {
- final long codeSize;
- final long dataSize;
+ long codeSize = stats.codeSize;
+ long dataSize = stats.dataSize;
+ long cacheSize = stats.cacheSize;
if (Environment.isExternalStorageEmulated()) {
- // OBB is shared on emulated storage, so count once as code,
- // and data includes emulated storage.
- codeSize = stats.codeSize + stats.externalObbSize;
- dataSize = stats.dataSize + externalSize;
- } else {
- codeSize = stats.codeSize;
- dataSize = stats.dataSize;
+ // Include emulated storage when measuring internal. OBB is
+ // shared on emulated storage, so treat as code.
+ codeSize += stats.externalCodeSize + stats.externalObbSize;
+ dataSize += stats.externalDataSize + stats.externalMediaSize;
+ cacheSize += stats.externalCacheSize;
}
- // Include code and combined data for current user
+ // Count code and data for current user
if (stats.userHandle == mCurrentUser) {
mDetails.appsSize += codeSize;
mDetails.appsSize += dataSize;
}
- // Include combined data for user summary
+ // User summary only includes data (code is only counted once
+ // for the current user)
addValue(mDetails.usersSize, stats.userHandle, dataSize);
+ // Include cache for all users
+ mDetails.cacheSize += cacheSize;
+
} else {
// Physical storage; only count external sizes
- mDetails.appsSize += externalSize + stats.externalObbSize;
+ mDetails.appsSize += stats.externalCodeSize + stats.externalDataSize
+ + stats.externalMediaSize + stats.externalObbSize;
+ mDetails.cacheSize += stats.externalCacheSize;
}
}
}
@@ -389,6 +399,9 @@
final MeasurementDetails details = new MeasurementDetails();
final Message finished = obtainMessage(MSG_COMPLETED, details);
+ details.totalSize = mTotalSize;
+ details.availSize = mAvailSize;
+
final UserManager userManager = (UserManager) context.getSystemService(
Context.USER_SERVICE);
final List<UserInfo> users = userManager.getUsers();
diff --git a/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java b/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java
index 469dbc7..44d40a0 100644
--- a/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java
+++ b/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java
@@ -312,6 +312,10 @@
final boolean showDetails = mVolume == null || mVolume.isPrimary();
if (!showDetails) return;
+ // Count caches as available space, since system manages them
+ mItemTotal.setSummary(formatSize(details.totalSize));
+ mItemAvailable.setSummary(formatSize(details.availSize + details.cacheSize));
+
mUsageBarPreference.clear();
updatePreference(mItemApps, details.appsSize);
@@ -326,7 +330,7 @@
updatePreference(mItemMusic, musicSize);
final long downloadsSize = totalValues(details.mediaSize, Environment.DIRECTORY_DOWNLOADS);
- updatePreference(mItemDownloads, musicSize);
+ updatePreference(mItemDownloads, downloadsSize);
updatePreference(mItemMisc, details.miscSize);
diff --git a/src/com/android/settings/net/UidDetailProvider.java b/src/com/android/settings/net/UidDetailProvider.java
index dd2b8c0..37b99dd 100644
--- a/src/com/android/settings/net/UidDetailProvider.java
+++ b/src/com/android/settings/net/UidDetailProvider.java
@@ -21,20 +21,30 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
import android.net.TrafficStats;
+import android.os.UserManager;
import android.text.TextUtils;
import android.util.SparseArray;
import com.android.settings.R;
import com.android.settings.Utils;
+/**
+ * Return details about a specific UID, handling special cases like
+ * {@link TrafficStats#UID_TETHERING} and {@link UserInfo}.
+ */
public class UidDetailProvider {
private final Context mContext;
private final SparseArray<UidDetail> mUidDetailCache;
+ public static int buildKeyForUser(int userHandle) {
+ return -(2000 + userHandle);
+ }
+
public UidDetailProvider(Context context) {
mContext = context.getApplicationContext();
mUidDetailCache = new SparseArray<UidDetail>();
@@ -101,10 +111,21 @@
return detail;
}
+ // Handle keys that are actually user handles
+ if (uid <= -2000) {
+ final int userHandle = (-uid) - 2000;
+ final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ final UserInfo info = um.getUserInfo(userHandle);
+ if (info != null) {
+ detail.label = res.getString(R.string.running_process_item_user_label, info.name);
+ detail.icon = Drawable.createFromPath(info.iconPath);
+ return detail;
+ }
+ }
+
// otherwise fall back to using packagemanager labels
final String[] packageNames = pm.getPackagesForUid(uid);
final int length = packageNames != null ? packageNames.length : 0;
-
try {
if (length == 1) {
final ApplicationInfo info = pm.getApplicationInfo(packageNames[0], 0);
diff --git a/src/com/android/settings/users/ProfileUpdateReceiver.java b/src/com/android/settings/users/ProfileUpdateReceiver.java
new file mode 100644
index 0000000..88daa58
--- /dev/null
+++ b/src/com/android/settings/users/ProfileUpdateReceiver.java
@@ -0,0 +1,63 @@
+/*
+ * 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.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+
+import com.android.settings.Utils;
+
+
+/**
+ * Watches for changes to Me Profile in Contacts and writes the photo to the User Manager.
+ */
+public class ProfileUpdateReceiver extends BroadcastReceiver {
+
+ private static final String KEY_PROFILE_NAME_COPIED_ONCE = "name_copied_once";
+
+ @Override
+ public void onReceive(final Context context, Intent intent) {
+ // Profile changed, lets get the photo and write to user manager
+ new Thread() {
+ public void run() {
+ Utils.copyMeProfilePhoto(context, null);
+ copyProfileName(context);
+ }
+ }.start();
+ }
+
+ static void copyProfileName(Context context) {
+ SharedPreferences prefs = context.getSharedPreferences("profile", Context.MODE_PRIVATE);
+ if (prefs.contains(KEY_PROFILE_NAME_COPIED_ONCE)) {
+ return;
+ }
+
+ int userId = UserHandle.myUserId();
+ UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ String profileName = Utils.getMeProfileName(context);
+ if (profileName != null && profileName.length() > 0) {
+ um.setUserName(userId, profileName);
+ // Flag that we've written the profile one time at least. No need to do it in the future.
+ prefs.edit().putBoolean(KEY_PROFILE_NAME_COPIED_ONCE, true).commit();
+ }
+ }
+}
diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java
index fe1bd90..11948b4 100644
--- a/src/com/android/settings/users/UserSettings.java
+++ b/src/com/android/settings/users/UserSettings.java
@@ -16,6 +16,7 @@
package com.android.settings.users;
+import android.app.ActivityManagerNative;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.BroadcastReceiver;
@@ -31,10 +32,12 @@
import android.graphics.Bitmap.CompressFormat;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.preference.EditTextPreference;
@@ -45,6 +48,7 @@
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Profile;
import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.text.InputType;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
@@ -53,8 +57,10 @@
import android.view.View.OnClickListener;
import android.widget.Toast;
+import com.android.internal.telephony.MccTable;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.Utils;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -76,8 +82,10 @@
private static final int DIALOG_CONFIRM_REMOVE = 1;
private static final int DIALOG_ADD_USER = 2;
+ private static final int DIALOG_SETUP_USER = 3;
private static final int MESSAGE_UPDATE_LIST = 1;
+ private static final int MESSAGE_SETUP_USER = 2;
private static final int[] USER_DRAWABLES = {
R.drawable.ic_user,
@@ -97,11 +105,12 @@
private Preference mMePreference;
private EditTextPreference mNicknamePreference;
private int mRemovingUserId = -1;
+ private int mAddedUserId = 0;
private boolean mAddingUser;
+ private boolean mProfileExists;
private final Object mUserLock = new Object();
private UserManager mUserManager;
- private boolean mProfileChanged;
private Handler mHandler = new Handler() {
@Override
@@ -110,17 +119,13 @@
case MESSAGE_UPDATE_LIST:
updateUserList();
break;
+ case MESSAGE_SETUP_USER:
+ onUserCreated(msg.arg1);
+ break;
}
}
};
- private ContentObserver mProfileObserver = new ContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange) {
- mProfileChanged = true;
- }
- };
-
private BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() {
@Override
@@ -142,12 +147,10 @@
}
mNicknamePreference = (EditTextPreference) findPreference(KEY_USER_NICKNAME);
mNicknamePreference.setOnPreferenceChangeListener(this);
- mNicknamePreference.setSummary(mUserManager.getUserInfo(UserHandle.myUserId()).name);
- loadProfile(false);
+ mNicknamePreference.getEditText().setInputType(
+ InputType.TYPE_TEXT_VARIATION_NORMAL | InputType.TYPE_TEXT_FLAG_CAP_WORDS);
+ loadProfile();
setHasOptionsMenu(true);
- // Register to watch for profile changes
- getActivity().getContentResolver().registerContentObserver(
- ContactsContract.Profile.CONTENT_URI, false, mProfileObserver);
getActivity().registerReceiver(mUserChangeReceiver,
new IntentFilter(Intent.ACTION_USER_REMOVED));
}
@@ -155,17 +158,13 @@
@Override
public void onResume() {
super.onResume();
- if (mProfileChanged) {
- loadProfile(true);
- mProfileChanged = false;
- }
+ loadProfile();
updateUserList();
}
@Override
public void onDestroy() {
super.onDestroy();
- getActivity().getContentResolver().unregisterContentObserver(mProfileObserver);
getActivity().unregisterReceiver(mUserChangeReceiver);
}
@@ -198,13 +197,29 @@
}
}
- private void loadProfile(boolean force) {
- UserInfo user = mUserManager.getUserInfo(UserHandle.myUserId());
- if (force || user.iconPath == null || user.iconPath.equals("")) {
- assignProfilePhoto(user);
- }
- String profileName = getProfileName();
+ private void loadProfile() {
+ mProfileExists = false;
+ new AsyncTask<Void, Void, String>() {
+ @Override
+ protected void onPostExecute(String result) {
+ finishLoadProfile(result);
+ }
+
+ @Override
+ protected String doInBackground(Void... values) {
+ UserInfo user = mUserManager.getUserInfo(UserHandle.myUserId());
+ if (user.iconPath == null || user.iconPath.equals("")) {
+ assignProfilePhoto(user);
+ }
+ String profileName = getProfileName();
+ return profileName;
+ }
+ }.execute();
+ }
+
+ private void finishLoadProfile(String profileName) {
mMePreference.setTitle(profileName);
+ setPhotoId(mMePreference, mUserManager.getUserInfo(UserHandle.myUserId()));
}
private void onAddUserClicked() {
@@ -226,13 +241,22 @@
}
}
+ private void onUserCreated(int userId) {
+ mAddedUserId = userId;
+ showDialog(DIALOG_SETUP_USER);
+ }
+
@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)
+ .setTitle(UserHandle.myUserId() == mRemovingUserId
+ ? R.string.user_confirm_remove_self_title
+ : R.string.user_confirm_remove_title)
+ .setMessage(UserHandle.myUserId() == mRemovingUserId
+ ? R.string.user_confirm_remove_self_message
+ : R.string.user_confirm_remove_message)
.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
@@ -253,6 +277,19 @@
})
.setNegativeButton(android.R.string.cancel, null)
.create();
+ case DIALOG_SETUP_USER:
+ return new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.user_setup_dialog_title)
+ .setMessage(R.string.user_setup_dialog_message)
+ .setPositiveButton(R.string.user_setup_button_setup_now,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ switchUserNow(mAddedUserId);
+ }
+ })
+ .setNegativeButton(R.string.user_setup_button_setup_later, null)
+ .create();
+
default:
return null;
}
@@ -276,11 +313,12 @@
}
private void removeThisUser() {
- // TODO:
- Toast.makeText(getActivity(), "Not implemented yet!", Toast.LENGTH_SHORT).show();
-
- synchronized (mUserLock) {
- mRemovingUserId = -1;
+ try {
+ ActivityManagerNative.getDefault().switchUser(UserHandle.USER_OWNER);
+ ((UserManager) getActivity().getSystemService(Context.USER_SERVICE))
+ .removeUser(UserHandle.myUserId());
+ } catch (RemoteException re) {
+ Log.e(TAG, "Unable to remove self user");
}
}
@@ -299,12 +337,22 @@
synchronized (mUserLock) {
mAddingUser = false;
mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST);
+ mHandler.sendMessage(mHandler.obtainMessage(
+ MESSAGE_SETUP_USER, user.id, user.serialNumber));
}
}
}.start();
}
}
+ private void switchUserNow(int userId) {
+ try {
+ ActivityManagerNative.getDefault().switchUser(userId);
+ } catch (RemoteException re) {
+ // Nothing to do
+ }
+ }
+
private void updateUserList() {
List<UserInfo> users = mUserManager.getUsers();
@@ -315,6 +363,8 @@
Preference pref;
if (user.id == UserHandle.myUserId()) {
pref = mMePreference;
+ mNicknamePreference.setText(user.name);
+ mNicknamePreference.setSummary(user.name);
} else {
pref = new UserPreference(getActivity(), null, user.id,
UserHandle.myUserId() == UserHandle.USER_OWNER, this);
@@ -343,56 +393,18 @@
getActivity().invalidateOptionsMenu();
}
- /* TODO: Put this in an AsyncTask */
private void assignProfilePhoto(final UserInfo user) {
- // If the contact is "me", then use my local profile photo. Otherwise, build a
- // uri to get the avatar of the contact.
- Uri contactUri = Profile.CONTENT_URI;
-
- InputStream avatarDataStream = Contacts.openContactPhotoInputStream(
- getActivity().getContentResolver(),
- contactUri, true);
- // If there's no profile photo, assign a default avatar
- if (avatarDataStream == null) {
+ if (!Utils.copyMeProfilePhoto(getActivity(), user)) {
assignDefaultPhoto(user);
- setPhotoId(mMePreference, user);
- return;
}
-
- ParcelFileDescriptor fd = mUserManager.setUserIcon(user.id);
- FileOutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
- byte[] buffer = new byte[4096];
- int readSize;
- try {
- while ((readSize = avatarDataStream.read(buffer)) > 0) {
- os.write(buffer, 0, readSize);
- }
- os.close();
- avatarDataStream.close();
- } catch (IOException ioe) {
- Log.e(TAG, "Error copying profile photo " + ioe);
- }
-
- setPhotoId(mMePreference, user);
}
private String getProfileName() {
- Cursor cursor = getActivity().getContentResolver().query(
- Profile.CONTENT_URI, CONTACT_PROJECTION, null, null, null);
- if (cursor == null) {
- Log.w(TAG, "getProfileName() returned NULL cursor!"
- + " contact uri used " + Profile.CONTENT_URI);
- return null;
+ String name = Utils.getMeProfileName(getActivity());
+ if (name != null) {
+ mProfileExists = true;
}
-
- try {
- if (cursor.moveToFirst()) {
- return cursor.getString(cursor.getColumnIndex(Phone.DISPLAY_NAME));
- }
- } finally {
- cursor.close();
- }
- return null;
+ return name;
}
private void assignDefaultPhoto(UserInfo user) {
@@ -421,8 +433,14 @@
@Override
public boolean onPreferenceClick(Preference pref) {
if (pref == mMePreference) {
- Intent editProfile = new Intent(Intent.ACTION_EDIT);
- editProfile.setData(ContactsContract.Profile.CONTENT_URI);
+ Intent editProfile;
+ if (!mProfileExists) {
+ editProfile = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
+ // TODO: Make this a proper API
+ editProfile.putExtra("newLocalProfile", true);
+ } else {
+ editProfile = new Intent(Intent.ACTION_EDIT, ContactsContract.Profile.CONTENT_URI);
+ }
// To make sure that it returns back here when done
// TODO: Make this a proper API
editProfile.putExtra("finishActivityOnSaveCompleted", true);