diff options
5 files changed, 159 insertions, 69 deletions
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/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 class StorageMeasurement { } public static class MeasurementDetails { + public long totalSize; + public long availSize; + /** * Total apps disk usage. * <p> @@ -111,6 +114,11 @@ public class StorageMeasurement { 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 @@ public class StorageMeasurement { } 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 @@ public class StorageMeasurement { 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 @@ public class StorageVolumePreferenceCategory extends PreferenceCategory { 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 @@ public class StorageVolumePreferenceCategory extends PreferenceCategory { 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/users/ProfileUpdateReceiver.java b/src/com/android/settings/users/ProfileUpdateReceiver.java new file mode 100644 index 0000000..5513608 --- /dev/null +++ b/src/com/android/settings/users/ProfileUpdateReceiver.java @@ -0,0 +1,82 @@ +/* + * 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.pm.UserInfo; +import android.net.Uri; +import android.os.ParcelFileDescriptor; +import android.os.UserHandle; +import android.os.UserManager; +import android.provider.ContactsContract.Contacts; +import android.provider.ContactsContract.Profile; +import android.util.Log; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * Watches for changes to Me Profile in Contacts and writes the photo to the User Manager. + */ +public class ProfileUpdateReceiver extends BroadcastReceiver { + + @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() { + copyProfilePhoto(context, null); + } + }.start(); + } + + /* Used by UserSettings as well. Call this on a non-ui thread. */ + static boolean copyProfilePhoto(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; + } +} diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java index fe1bd90..28fe4c1 100644 --- a/src/com/android/settings/users/UserSettings.java +++ b/src/com/android/settings/users/UserSettings.java @@ -31,6 +31,7 @@ import android.graphics.BitmapFactory; 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; @@ -98,10 +99,10 @@ public class UserSettings extends SettingsPreferenceFragment private EditTextPreference mNicknamePreference; private int mRemovingUserId = -1; private boolean mAddingUser; + private boolean mProfileExists; private final Object mUserLock = new Object(); private UserManager mUserManager; - private boolean mProfileChanged; private Handler mHandler = new Handler() { @Override @@ -114,13 +115,6 @@ public class UserSettings extends SettingsPreferenceFragment } }; - private ContentObserver mProfileObserver = new ContentObserver(mHandler) { - @Override - public void onChange(boolean selfChange) { - mProfileChanged = true; - } - }; - private BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() { @Override @@ -143,11 +137,8 @@ public class UserSettings extends SettingsPreferenceFragment mNicknamePreference = (EditTextPreference) findPreference(KEY_USER_NICKNAME); mNicknamePreference.setOnPreferenceChangeListener(this); mNicknamePreference.setSummary(mUserManager.getUserInfo(UserHandle.myUserId()).name); - loadProfile(false); + 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 +146,13 @@ public class UserSettings extends SettingsPreferenceFragment @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 +185,29 @@ public class UserSettings extends SettingsPreferenceFragment } } - 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() { @@ -343,37 +346,10 @@ public class UserSettings extends SettingsPreferenceFragment 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 (!ProfileUpdateReceiver.copyProfilePhoto(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() { @@ -387,6 +363,7 @@ public class UserSettings extends SettingsPreferenceFragment try { if (cursor.moveToFirst()) { + mProfileExists = true; return cursor.getString(cursor.getColumnIndex(Phone.DISPLAY_NAME)); } } finally { @@ -421,8 +398,14 @@ public class UserSettings extends SettingsPreferenceFragment @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); |