diff options
author | Jason Monk <jmonk@google.com> | 2015-02-24 11:35:29 -0500 |
---|---|---|
committer | Jason Monk <jmonk@google.com> | 2015-03-02 10:21:19 -0500 |
commit | cd91128a2de5d111c59fe442c72b764d9a9acb3a (patch) | |
tree | dd1f7740f23a433b0ff6f3a95088d97d438caab5 /src/com/android/settings/applications | |
parent | a330b1a093869194203bf4d40ea81676c7f6dbb3 (diff) | |
download | packages_apps_Settings-cd91128a2de5d111c59fe442c72b764d9a9acb3a.zip packages_apps_Settings-cd91128a2de5d111c59fe442c72b764d9a9acb3a.tar.gz packages_apps_Settings-cd91128a2de5d111c59fe442c72b764d9a9acb3a.tar.bz2 |
Split app info into several screens
The root screen now only has the uninstall/force stop/disable buttons
and the rest has moved to sub screens listed in a preference list.
The root screen as UI approximate to the new mocks, but the separated
screens (storage, launch by default, etc.) have yet to receive their
visual overhaul.
Bug: 19511439
Change-Id: I4e01fbaefc69e0652edea2429d9e9b028c78e825
Diffstat (limited to 'src/com/android/settings/applications')
7 files changed, 1423 insertions, 1035 deletions
diff --git a/src/com/android/settings/applications/AppInfoBase.java b/src/com/android/settings/applications/AppInfoBase.java new file mode 100644 index 0000000..2203a21 --- /dev/null +++ b/src/com/android/settings/applications/AppInfoBase.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.applications; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.app.admin.DevicePolicyManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.hardware.usb.IUsbManager; +import android.os.Bundle; +import android.os.IBinder; +import android.os.ServiceManager; +import android.os.UserManager; +import android.preference.PreferenceFragment; +import android.util.Log; + +import com.android.settings.SettingsActivity; +import com.android.settings.applications.ApplicationsState.AppEntry; + +import java.util.ArrayList; + +public abstract class AppInfoBase extends PreferenceFragment + implements ApplicationsState.Callbacks { + + public static final String ARG_PACKAGE_NAME = "package"; + + protected static final String TAG = AppInfoBase.class.getSimpleName(); + protected static final boolean localLOGV = false; + + protected boolean mAppControlRestricted = false; + + protected ApplicationsState mState; + private ApplicationsState.Session mSession; + protected ApplicationsState.AppEntry mAppEntry; + protected PackageInfo mPackageInfo; + protected String mPackageName; + + protected IUsbManager mUsbManager; + protected DevicePolicyManager mDpm; + protected UserManager mUserManager; + protected PackageManager mPm; + + // Dialog identifiers used in showDialog + protected static final int DLG_BASE = 0; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mState = ApplicationsState.getInstance(getActivity().getApplication()); + mSession = mState.newSession(this); + Context context = getActivity(); + mDpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); + mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); + mPm = context.getPackageManager(); + IBinder b = ServiceManager.getService(Context.USB_SERVICE); + mUsbManager = IUsbManager.Stub.asInterface(b); + + // Need to make sure we have loaded applications at this point. + mSession.resume(); + + retrieveAppEntry(); + } + + @Override + public void onResume() { + super.onResume(); + mAppControlRestricted = mUserManager.hasUserRestriction(UserManager.DISALLOW_APPS_CONTROL); + mSession.resume(); + + if (!refreshUi()) { + setIntentAndFinish(true, true); + } + } + + @Override + public void onPause() { + super.onPause(); + mSession.pause(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + mSession.release(); + } + + protected String retrieveAppEntry() { + final Bundle args = getArguments(); + mPackageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null; + if (mPackageName == null) { + Intent intent = (args == null) ? + getActivity().getIntent() : (Intent) args.getParcelable("intent"); + if (intent != null) { + mPackageName = intent.getData().getSchemeSpecificPart(); + } + } + mAppEntry = mState.getEntry(mPackageName); + if (mAppEntry != null) { + // Get application info again to refresh changed properties of application + try { + mPackageInfo = mPm.getPackageInfo(mAppEntry.info.packageName, + PackageManager.GET_DISABLED_COMPONENTS | + PackageManager.GET_UNINSTALLED_PACKAGES | + PackageManager.GET_SIGNATURES); + } catch (NameNotFoundException e) { + Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e); + } + } else { + Log.w(TAG, "Missing AppEntry; maybe reinstalling?"); + mPackageInfo = null; + } + + return mPackageName; + } + + protected void setIntentAndFinish(boolean finish, boolean appChanged) { + if (localLOGV) Log.i(TAG, "appChanged="+appChanged); + Intent intent = new Intent(); + intent.putExtra(ManageApplications.APP_CHG, appChanged); + SettingsActivity sa = (SettingsActivity)getActivity(); + sa.finishPreferencePanel(this, Activity.RESULT_OK, intent); + } + + protected void showDialogInner(int id, int moveErrorCode) { + DialogFragment newFragment = new MyAlertDialogFragment(id, moveErrorCode); + newFragment.setTargetFragment(this, 0); + newFragment.show(getFragmentManager(), "dialog " + id); + } + + protected abstract boolean refreshUi(); + protected abstract AlertDialog createDialog(int id, int errorCode); + + @Override + public void onRunningStateChanged(boolean running) { } + @Override + public void onRebuildComplete(ArrayList<AppEntry> apps) { } + @Override + public void onPackageIconChanged() { } + @Override + public void onPackageSizeChanged(String packageName) { } + @Override + public void onAllSizesComputed() { } + + @Override + public void onPackageListChanged() { + refreshUi(); + } + + public class MyAlertDialogFragment extends DialogFragment { + public MyAlertDialogFragment(int id, int errorCode) { + Bundle args = new Bundle(); + args.putInt("id", id); + args.putInt("moveError", errorCode); + setArguments(args); + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + int id = getArguments().getInt("id"); + int errorCode = getArguments().getInt("moveError"); + Dialog dialog = createDialog(id, errorCode); + if (dialog == null) { + throw new IllegalArgumentException("unknown id " + id); + } + return dialog; + } + } +} diff --git a/src/com/android/settings/applications/AppInfoWithHeader.java b/src/com/android/settings/applications/AppInfoWithHeader.java new file mode 100644 index 0000000..f7546f2 --- /dev/null +++ b/src/com/android/settings/applications/AppInfoWithHeader.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.applications; + +import android.os.Bundle; +import android.util.Log; + +import com.android.settings.AppHeader; + +public abstract class AppInfoWithHeader extends AppInfoBase { + + private boolean mCreated; + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + if (mCreated) { + Log.w(TAG, "onActivityCreated: ignoring duplicate call"); + return; + } + mCreated = true; + if (mPackageInfo == null) return; + AppHeader.createAppHeader(getActivity(), mPackageInfo.applicationInfo.loadIcon(mPm), + mPackageInfo.applicationInfo.loadLabel(mPm), null); + } +} diff --git a/src/com/android/settings/applications/AppLaunchSettings.java b/src/com/android/settings/applications/AppLaunchSettings.java new file mode 100644 index 0000000..6379102 --- /dev/null +++ b/src/com/android/settings/applications/AppLaunchSettings.java @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.applications; + +import android.app.AlertDialog; +import android.appwidget.AppWidgetManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.hardware.usb.IUsbManager; +import android.os.Bundle; +import android.os.RemoteException; +import android.os.UserHandle; +import android.text.SpannableString; +import android.text.TextUtils; +import android.text.style.BulletSpan; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; + +import com.android.settings.R; +import com.android.settings.Utils; +import com.android.settings.applications.ApplicationsState.AppEntry; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class AppLaunchSettings extends AppInfoWithHeader implements OnClickListener { + + private Button mActivitiesButton; + private AppWidgetManager mAppWidgetManager; + + private View mRootView; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mAppWidgetManager = AppWidgetManager.getInstance(getActivity()); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.app_preferred_settings, container, false); + + final ViewGroup allDetails = (ViewGroup) view.findViewById(R.id.all_details); + Utils.forceCustomPadding(allDetails, true /* additive padding */); + + mActivitiesButton = (Button) view.findViewById(R.id.clear_activities_button); + mRootView = view; + + return view; + } + + @Override + protected boolean refreshUi() { + retrieveAppEntry(); + boolean hasBindAppWidgetPermission = + mAppWidgetManager.hasBindAppWidgetPermission(mAppEntry.info.packageName); + + TextView autoLaunchTitleView = (TextView) mRootView.findViewById(R.id.auto_launch_title); + TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch); + boolean autoLaunchEnabled = hasPreferredActivities(mPm, mPackageName) + || hasUsbDefaults(mUsbManager, mPackageName); + if (!autoLaunchEnabled && !hasBindAppWidgetPermission) { + resetLaunchDefaultsUi(autoLaunchTitleView, autoLaunchView); + } else { + boolean useBullets = hasBindAppWidgetPermission && autoLaunchEnabled; + + if (hasBindAppWidgetPermission) { + autoLaunchTitleView.setText(R.string.auto_launch_label_generic); + } else { + autoLaunchTitleView.setText(R.string.auto_launch_label); + } + + CharSequence text = null; + int bulletIndent = getResources() + .getDimensionPixelSize(R.dimen.installed_app_details_bullet_offset); + if (autoLaunchEnabled) { + CharSequence autoLaunchEnableText = getText(R.string.auto_launch_enable_text); + SpannableString s = new SpannableString(autoLaunchEnableText); + if (useBullets) { + s.setSpan(new BulletSpan(bulletIndent), 0, autoLaunchEnableText.length(), 0); + } + text = (text == null) ? + TextUtils.concat(s, "\n") : TextUtils.concat(text, "\n", s, "\n"); + } + if (hasBindAppWidgetPermission) { + CharSequence alwaysAllowBindAppWidgetsText = + getText(R.string.always_allow_bind_appwidgets_text); + SpannableString s = new SpannableString(alwaysAllowBindAppWidgetsText); + if (useBullets) { + s.setSpan(new BulletSpan(bulletIndent), + 0, alwaysAllowBindAppWidgetsText.length(), 0); + } + text = (text == null) ? + TextUtils.concat(s, "\n") : TextUtils.concat(text, "\n", s, "\n"); + } + autoLaunchView.setText(text); + mActivitiesButton.setEnabled(true); + mActivitiesButton.setOnClickListener(this); + } + return true; + } + + private void resetLaunchDefaultsUi(TextView title, TextView autoLaunchView) { + title.setText(R.string.auto_launch_label); + autoLaunchView.setText(R.string.auto_launch_disable_text); + // Disable clear activities button + mActivitiesButton.setEnabled(false); + } + + @Override + protected AlertDialog createDialog(int id, int errorCode) { + // No dialogs for preferred launch settings. + return null; + } + + @Override + public void onClick(View v) { + if (v == mActivitiesButton) { + if (mUsbManager != null) { + mPm.clearPackagePreferredActivities(mPackageName); + try { + mUsbManager.clearDefaults(mPackageName, UserHandle.myUserId()); + } catch (RemoteException e) { + Log.e(TAG, "mUsbManager.clearDefaults", e); + } + mAppWidgetManager.setBindAppWidgetPermission(mPackageName, false); + TextView autoLaunchTitleView = + (TextView) mRootView.findViewById(R.id.auto_launch_title); + TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch); + resetLaunchDefaultsUi(autoLaunchTitleView, autoLaunchView); + } + } + } + + private static boolean hasUsbDefaults(IUsbManager usbManager, String packageName) { + try { + if (usbManager != null) { + return usbManager.hasDefaults(packageName, UserHandle.myUserId()); + } + } catch (RemoteException e) { + Log.e(TAG, "mUsbManager.hasDefaults", e); + } + return false; + } + + private static boolean hasPreferredActivities(PackageManager pm, String packageName) { + // Get list of preferred activities + List<ComponentName> prefActList = Collections.emptyList(); + // Intent list cannot be null. so pass empty list + List<IntentFilter> intentList = Collections.emptyList(); + pm.getPreferredActivities(intentList, prefActList, packageName); + if (localLOGV) { + Log.i(TAG, "Have " + prefActList.size() + " number of activities in preferred list"); + } + return prefActList.size() > 0; + } + + public static CharSequence getSummary(AppEntry appEntry, IUsbManager usbManager, + PackageManager pm, Context context) { + String packageName = appEntry.info.packageName; + boolean hasPreferred = hasPreferredActivities(pm, packageName) + || hasUsbDefaults(usbManager, packageName); + return context.getString(hasPreferred + ? R.string.launch_defaults_some + : R.string.launch_defaults_none); + } + +} diff --git a/src/com/android/settings/applications/AppPermissionSettings.java b/src/com/android/settings/applications/AppPermissionSettings.java new file mode 100644 index 0000000..a5b4895 --- /dev/null +++ b/src/com/android/settings/applications/AppPermissionSettings.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.applications; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.os.Bundle; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AppSecurityPermissions; +import android.widget.ArrayAdapter; +import android.widget.LinearLayout; +import android.widget.Spinner; +import android.widget.TextView; + +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; + +import java.util.ArrayList; + +public class AppPermissionSettings extends AppInfoWithHeader { + + private ISms mSmsManager; + private View mRootView; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mSmsManager = ISms.Stub.asInterface(ServiceManager.getService("isms")); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.permission_settings, container, false); + + final ViewGroup allDetails = (ViewGroup) view.findViewById(R.id.all_details); + Utils.forceCustomPadding(allDetails, true /* additive padding */); + + mRootView = view; + return view; + } + + @Override + protected boolean refreshUi() { + retrieveAppEntry(); + // Security permissions section + LinearLayout permsView = (LinearLayout) mRootView.findViewById(R.id.permissions_section); + AppSecurityPermissions asp = new AppSecurityPermissions(getActivity(), mPackageName); + int premiumSmsPermission = getPremiumSmsPermission(mPackageName); + // 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( + mPackageName, 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); + securityList.removeAllViews(); + securityList.addView(asp.getPermissionsViewWithRevokeButtons()); + // If this app is running under a shared user ID with other apps, + // update the description to explain this. + String[] packages = mPm.getPackagesForUid(mPackageInfo.applicationInfo.uid); + if (packages != null && packages.length > 1) { + ArrayList<CharSequence> pnames = new ArrayList<CharSequence>(); + for (int i=0; i<packages.length; i++) { + String pkg = packages[i]; + if (mPackageInfo.packageName.equals(pkg)) { + continue; + } + try { + ApplicationInfo ainfo = mPm.getApplicationInfo(pkg, 0); + pnames.add(ainfo.loadLabel(mPm)); + } catch (PackageManager.NameNotFoundException e) { + } + } + final int N = pnames.size(); + if (N > 0) { + final Resources res = getActivity().getResources(); + String appListStr; + if (N == 1) { + appListStr = pnames.get(0).toString(); + } else if (N == 2) { + appListStr = res.getString(R.string.join_two_items, pnames.get(0), + pnames.get(1)); + } else { + appListStr = pnames.get(N-2).toString(); + for (int i=N-3; i>=0; i--) { + appListStr = res.getString(i == 0 ? R.string.join_many_items_first + : R.string.join_many_items_middle, pnames.get(i), appListStr); + } + appListStr = res.getString(R.string.join_many_items_last, + appListStr, pnames.get(N-1)); + } + TextView descr = (TextView) mRootView.findViewById( + R.id.security_settings_desc); + descr.setText(res.getString(R.string.security_settings_desc_multi, + mPackageInfo.applicationInfo.loadLabel(mPm), appListStr)); + } + } + } + return true; + } + + private int getPremiumSmsPermission(String packageName) { + try { + if (mSmsManager != null) { + return mSmsManager.getPremiumSmsPermission(packageName); + } + } catch (RemoteException ex) { + // ignored + } + return SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN; + } + + @Override + protected AlertDialog createDialog(int id, int errorCode) { + // No dialogs for Permissions screen. + return null; + } + + public static CharSequence getSummary(AppEntry appEntry, Context context) { + AppSecurityPermissions asp = new AppSecurityPermissions(context, + appEntry.info.packageName); + int count = asp.getPermissionCount(); + return context.getResources().getQuantityString(R.plurals.permissions_summary, + count, count); + } + + 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) { + if (localLOGV) 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 + } + } + } + +}
\ No newline at end of file diff --git a/src/com/android/settings/applications/AppStorageSettings.java b/src/com/android/settings/applications/AppStorageSettings.java new file mode 100644 index 0000000..5ae5e8f --- /dev/null +++ b/src/com/android/settings/applications/AppStorageSettings.java @@ -0,0 +1,501 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.applications; + +import android.app.ActivityManager; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageDataObserver; +import android.content.pm.IPackageMoveObserver; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.os.Environment; +import android.os.Handler; +import android.os.Message; +import android.os.RemoteException; +import android.text.format.Formatter; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; + +import com.android.settings.R; +import com.android.settings.Utils; +import com.android.settings.applications.ApplicationsState.AppEntry; +import com.android.settings.applications.ApplicationsState.Callbacks; + +public class AppStorageSettings extends AppInfoWithHeader implements OnClickListener, Callbacks { + private static final String TAG = "AppStorageSettings"; + + //internal constants used in Handler + private static final int OP_SUCCESSFUL = 1; + private static final int OP_FAILED = 2; + private static final int MSG_CLEAR_USER_DATA = 1; + private static final int MSG_CLEAR_CACHE = 3; + private static final int MSG_PACKAGE_MOVE = 4; + + // invalid size value used initially and also when size retrieval through PackageManager + // fails for whatever reason + private static final int SIZE_INVALID = -1; + + // Result code identifiers + public static final int REQUEST_MANAGE_SPACE = 2; + + private static final int DLG_CLEAR_DATA = DLG_BASE + 1; + private static final int DLG_CANNOT_CLEAR_DATA = DLG_BASE + 2; + private static final int DLG_MOVE_FAILED = DLG_BASE + 3; + + private CanBeOnSdCardChecker mCanBeOnSdCardChecker; + private TextView mTotalSize; + private TextView mAppSize; + private TextView mDataSize; + private TextView mExternalCodeSize; + private TextView mExternalDataSize; + + // Views related to cache info + private TextView mCacheSize; + private Button mClearDataButton; + private Button mClearCacheButton; + private Button mMoveAppButton; + private boolean mMoveInProgress = false; + + private boolean mCanClearData = true; + private boolean mHaveSizes = false; + + private long mLastCodeSize = -1; + private long mLastDataSize = -1; + private long mLastExternalCodeSize = -1; + private long mLastExternalDataSize = -1; + private long mLastCacheSize = -1; + private long mLastTotalSize = -1; + + private ClearCacheObserver mClearCacheObserver; + private ClearUserDataObserver mClearDataObserver; + private PackageMoveObserver mPackageMoveObserver; + + // Resource strings + private CharSequence mInvalidSizeStr; + private CharSequence mComputingStr; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mCanBeOnSdCardChecker = new CanBeOnSdCardChecker(); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.storage_settings, container, false); + + final ViewGroup allDetails = (ViewGroup)view.findViewById(R.id.all_details); + Utils.forceCustomPadding(allDetails, true /* additive padding */); + mComputingStr = getActivity().getText(R.string.computing_size); + mInvalidSizeStr = getActivity().getText(R.string.invalid_size_value); + + // Set default values on sizes + mTotalSize = (TextView)view.findViewById(R.id.total_size_text); + mAppSize = (TextView)view.findViewById(R.id.application_size_text); + mDataSize = (TextView)view.findViewById(R.id.data_size_text); + mExternalCodeSize = (TextView)view.findViewById(R.id.external_code_size_text); + mExternalDataSize = (TextView)view.findViewById(R.id.external_data_size_text); + + if (Environment.isExternalStorageEmulated()) { + ((View)mExternalCodeSize.getParent()).setVisibility(View.GONE); + ((View)mExternalDataSize.getParent()).setVisibility(View.GONE); + } + + // Initialize clear data and move install location buttons + View data_buttons_panel = view.findViewById(R.id.data_buttons_panel); + mMoveAppButton = (Button) data_buttons_panel.findViewById(R.id.left_button); + + // Cache section + mCacheSize = (TextView)view.findViewById(R.id.cache_size_text); + mClearCacheButton = (Button)view.findViewById(R.id.clear_cache_button); + mClearDataButton = (Button) data_buttons_panel.findViewById(R.id.right_button); + + return view; + } + + @Override + public void onClick(View v) { + if (v == mClearCacheButton) { + // Lazy initialization of observer + if (mClearCacheObserver == null) { + mClearCacheObserver = new ClearCacheObserver(); + } + mPm.deleteApplicationCacheFiles(mPackageName, mClearCacheObserver); + } else if(v == mClearDataButton) { + if (mAppEntry.info.manageSpaceActivityName != null) { + if (!Utils.isMonkeyRunning()) { + Intent intent = new Intent(Intent.ACTION_DEFAULT); + intent.setClassName(mAppEntry.info.packageName, + mAppEntry.info.manageSpaceActivityName); + startActivityForResult(intent, REQUEST_MANAGE_SPACE); + } + } else { + showDialogInner(DLG_CLEAR_DATA, 0); + } + } else if (v == mMoveAppButton) { + if (mPackageMoveObserver == null) { + mPackageMoveObserver = new PackageMoveObserver(); + } + int moveFlags = (mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0 ? + PackageManager.MOVE_INTERNAL : PackageManager.MOVE_EXTERNAL_MEDIA; + mMoveInProgress = true; + refreshButtons(); + mPm.movePackage(mAppEntry.info.packageName, mPackageMoveObserver, moveFlags); + } + } + + private String getSizeStr(long size) { + if (size == SIZE_INVALID) { + return mInvalidSizeStr.toString(); + } + return Formatter.formatFileSize(getActivity(), size); + } + + private void refreshSizeInfo() { + if (mAppEntry.size == ApplicationsState.SIZE_INVALID + || mAppEntry.size == ApplicationsState.SIZE_UNKNOWN) { + mLastCodeSize = mLastDataSize = mLastCacheSize = mLastTotalSize = -1; + if (!mHaveSizes) { + mAppSize.setText(mComputingStr); + mDataSize.setText(mComputingStr); + mCacheSize.setText(mComputingStr); + mTotalSize.setText(mComputingStr); + } + mClearDataButton.setEnabled(false); + mClearCacheButton.setEnabled(false); + + } else { + mHaveSizes = true; + long codeSize = mAppEntry.codeSize; + long dataSize = mAppEntry.dataSize; + if (Environment.isExternalStorageEmulated()) { + codeSize += mAppEntry.externalCodeSize; + dataSize += mAppEntry.externalDataSize; + } else { + if (mLastExternalCodeSize != mAppEntry.externalCodeSize) { + mLastExternalCodeSize = mAppEntry.externalCodeSize; + mExternalCodeSize.setText(getSizeStr(mAppEntry.externalCodeSize)); + } + if (mLastExternalDataSize != mAppEntry.externalDataSize) { + mLastExternalDataSize = mAppEntry.externalDataSize; + mExternalDataSize.setText(getSizeStr( mAppEntry.externalDataSize)); + } + } + if (mLastCodeSize != codeSize) { + mLastCodeSize = codeSize; + mAppSize.setText(getSizeStr(codeSize)); + } + if (mLastDataSize != dataSize) { + mLastDataSize = dataSize; + mDataSize.setText(getSizeStr(dataSize)); + } + long cacheSize = mAppEntry.cacheSize + mAppEntry.externalCacheSize; + if (mLastCacheSize != cacheSize) { + mLastCacheSize = cacheSize; + mCacheSize.setText(getSizeStr(cacheSize)); + } + if (mLastTotalSize != mAppEntry.size) { + mLastTotalSize = mAppEntry.size; + mTotalSize.setText(getSizeStr(mAppEntry.size)); + } + + if ((mAppEntry.dataSize+ mAppEntry.externalDataSize) <= 0 || !mCanClearData) { + mClearDataButton.setEnabled(false); + } else { + mClearDataButton.setEnabled(true); + mClearDataButton.setOnClickListener(this); + } + if (cacheSize <= 0) { + mClearCacheButton.setEnabled(false); + } else { + mClearCacheButton.setEnabled(true); + mClearCacheButton.setOnClickListener(this); + } + } + if (mAppControlRestricted) { + mClearCacheButton.setEnabled(false); + mClearDataButton.setEnabled(false); + } + } + + @Override + protected boolean refreshUi() { + retrieveAppEntry(); + refreshButtons(); + refreshSizeInfo(); + return true; + } + + private void refreshButtons() { + if (!mMoveInProgress) { + initMoveButton(); + initDataButtons(); + } else { + mMoveAppButton.setText(R.string.moving); + mMoveAppButton.setEnabled(false); + } + } + + private void initDataButtons() { + // If the app doesn't have its own space management UI + // And it's a system app that doesn't allow clearing user data or is an active admin + // Then disable the Clear Data button. + if (mAppEntry.info.manageSpaceActivityName == null + && ((mAppEntry.info.flags&(ApplicationInfo.FLAG_SYSTEM + | ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA)) + == ApplicationInfo.FLAG_SYSTEM + || mDpm.packageHasActiveAdmins(mPackageName))) { + mClearDataButton.setText(R.string.clear_user_data_text); + mClearDataButton.setEnabled(false); + mCanClearData = false; + } else { + if (mAppEntry.info.manageSpaceActivityName != null) { + mClearDataButton.setText(R.string.manage_space_text); + } else { + mClearDataButton.setText(R.string.clear_user_data_text); + } + mClearDataButton.setOnClickListener(this); + } + + if (mAppControlRestricted) { + mClearDataButton.setEnabled(false); + } + } + + private void initMoveButton() { + if (Environment.isExternalStorageEmulated()) { + mMoveAppButton.setVisibility(View.INVISIBLE); + return; + } + boolean dataOnly = (mPackageInfo == null) && (mAppEntry != null); + boolean moveDisable = true; + if (dataOnly) { + mMoveAppButton.setText(R.string.move_app); + } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) { + mMoveAppButton.setText(R.string.move_app_to_internal); + // Always let apps move to internal storage from sdcard. + moveDisable = false; + } else { + mMoveAppButton.setText(R.string.move_app_to_sdcard); + mCanBeOnSdCardChecker.init(); + moveDisable = !mCanBeOnSdCardChecker.check(mAppEntry.info); + } + if (moveDisable || mAppControlRestricted) { + mMoveAppButton.setEnabled(false); + } else { + mMoveAppButton.setOnClickListener(this); + mMoveAppButton.setEnabled(true); + } + } + + /* + * Private method to initiate clearing user data when the user clicks the clear data + * button for a system package + */ + private void initiateClearUserData() { + mClearDataButton.setEnabled(false); + // Invoke uninstall or clear user data based on sysPackage + String packageName = mAppEntry.info.packageName; + Log.i(TAG, "Clearing user data for package : " + packageName); + if (mClearDataObserver == null) { + mClearDataObserver = new ClearUserDataObserver(); + } + ActivityManager am = (ActivityManager) + getActivity().getSystemService(Context.ACTIVITY_SERVICE); + boolean res = am.clearApplicationUserData(packageName, mClearDataObserver); + if (!res) { + // Clearing data failed for some obscure reason. Just log error for now + Log.i(TAG, "Couldnt clear application user data for package:"+packageName); + showDialogInner(DLG_CANNOT_CLEAR_DATA, 0); + } else { + mClearDataButton.setText(R.string.recompute_size); + } + } + + private void processMoveMsg(Message msg) { + int result = msg.arg1; + String packageName = mAppEntry.info.packageName; + // Refresh the button attributes. + mMoveInProgress = false; + if (result == PackageManager.MOVE_SUCCEEDED) { + Log.i(TAG, "Moved resources for " + packageName); + // Refresh size information again. + mState.requestSize(mAppEntry.info.packageName); + } else { + showDialogInner(DLG_MOVE_FAILED, result); + } + refreshUi(); + } + + /* + * Private method to handle clear message notification from observer when + * the async operation from PackageManager is complete + */ + private void processClearMsg(Message msg) { + int result = msg.arg1; + String packageName = mAppEntry.info.packageName; + mClearDataButton.setText(R.string.clear_user_data_text); + if(result == OP_SUCCESSFUL) { + Log.i(TAG, "Cleared user data for package : "+packageName); + mState.requestSize(mAppEntry.info.packageName); + } else { + mClearDataButton.setEnabled(true); + } + } + + private CharSequence getMoveErrMsg(int errCode) { + switch (errCode) { + case PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE: + return getActivity().getString(R.string.insufficient_storage); + case PackageManager.MOVE_FAILED_DOESNT_EXIST: + return getActivity().getString(R.string.does_not_exist); + case PackageManager.MOVE_FAILED_FORWARD_LOCKED: + return getActivity().getString(R.string.app_forward_locked); + case PackageManager.MOVE_FAILED_INVALID_LOCATION: + return getActivity().getString(R.string.invalid_location); + case PackageManager.MOVE_FAILED_SYSTEM_PACKAGE: + return getActivity().getString(R.string.system_package); + case PackageManager.MOVE_FAILED_INTERNAL_ERROR: + return ""; + } + return ""; + } + + @Override + protected AlertDialog createDialog(int id, int errorCode) { + switch (id) { + case DLG_CLEAR_DATA: + return new AlertDialog.Builder(getActivity()) + .setTitle(getActivity().getText(R.string.clear_data_dlg_title)) + .setMessage(getActivity().getText(R.string.clear_data_dlg_text)) + .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + // Clear user data here + initiateClearUserData(); + } + }) + .setNegativeButton(R.string.dlg_cancel, null) + .create(); + case DLG_CANNOT_CLEAR_DATA: + return new AlertDialog.Builder(getActivity()) + .setTitle(getActivity().getText(R.string.clear_failed_dlg_title)) + .setMessage(getActivity().getText(R.string.clear_failed_dlg_text)) + .setNeutralButton(R.string.dlg_ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + mClearDataButton.setEnabled(false); + //force to recompute changed value + setIntentAndFinish(false, false); + } + }) + .create(); + case DLG_MOVE_FAILED: + CharSequence msg = getActivity().getString(R.string.move_app_failed_dlg_text, + getMoveErrMsg(errorCode)); + return new AlertDialog.Builder(getActivity()) + .setTitle(getActivity().getText(R.string.move_app_failed_dlg_title)) + .setMessage(msg) + .setNeutralButton(R.string.dlg_ok, null) + .create(); + } + return null; + } + + @Override + public void onPackageSizeChanged(String packageName) { + if (packageName.equals(mAppEntry.info.packageName)) { + refreshSizeInfo(); + } + } + + private final Handler mHandler = new Handler() { + public void handleMessage(Message msg) { + if (getView() == null) { + return; + } + switch (msg.what) { + case MSG_CLEAR_USER_DATA: + processClearMsg(msg); + break; + case MSG_CLEAR_CACHE: + // Refresh size info + mState.requestSize(mPackageName); + break; + case MSG_PACKAGE_MOVE: + processMoveMsg(msg); + break; + } + } + }; + + public static CharSequence getSummary(AppEntry appEntry, Context context) { + if (appEntry.size == ApplicationsState.SIZE_INVALID + || appEntry.size == ApplicationsState.SIZE_UNKNOWN) { + return context.getText(R.string.computing_size); + } else { + CharSequence storageType = context.getString( + (appEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0 + ? R.string.storage_type_external + : R.string.storage_type_internal); + return context.getString(R.string.storage_summary_format, + getSize(appEntry, context), storageType); + } + } + + private static CharSequence getSize(AppEntry appEntry, Context context) { + long size = appEntry.size; + if (size == SIZE_INVALID) { + return context.getText(R.string.invalid_size_value); + } + return Formatter.formatFileSize(context, size); + } + + class ClearCacheObserver extends IPackageDataObserver.Stub { + public void onRemoveCompleted(final String packageName, final boolean succeeded) { + final Message msg = mHandler.obtainMessage(MSG_CLEAR_CACHE); + msg.arg1 = succeeded ? OP_SUCCESSFUL : OP_FAILED; + mHandler.sendMessage(msg); + } + } + + class ClearUserDataObserver extends IPackageDataObserver.Stub { + public void onRemoveCompleted(final String packageName, final boolean succeeded) { + final Message msg = mHandler.obtainMessage(MSG_CLEAR_USER_DATA); + msg.arg1 = succeeded?OP_SUCCESSFUL:OP_FAILED; + mHandler.sendMessage(msg); + } + } + + class PackageMoveObserver extends IPackageMoveObserver.Stub { + public void packageMoved(String packageName, int returnCode) throws RemoteException { + final Message msg = mHandler.obtainMessage(MSG_PACKAGE_MOVE); + msg.arg1 = returnCode; + mHandler.sendMessage(msg); + } + } + +} diff --git a/src/com/android/settings/applications/HeaderPreference.java b/src/com/android/settings/applications/HeaderPreference.java new file mode 100644 index 0000000..57fe9f3 --- /dev/null +++ b/src/com/android/settings/applications/HeaderPreference.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.applications; + +import android.content.Context; +import android.content.res.TypedArray; +import android.preference.Preference; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.android.settings.R; +import com.android.settings.Utils; + +public class HeaderPreference extends Preference { + + private View mRootView; + + public HeaderPreference(Context context, AttributeSet attrs) { + super(context, attrs); + final TypedArray a = context.obtainStyledAttributes( + attrs, com.android.internal.R.styleable.Preference, 0, 0); + int layoutResource = a.getResourceId(com.android.internal.R.styleable.Preference_layout, + 0); + if (layoutResource == 0) { + throw new IllegalArgumentException("HeaderPreference requires a layout to be defined"); + } + // Need to create view now so that findViewById can be called immediately. + final View view = LayoutInflater.from(getContext()) + .inflate(layoutResource, null, false); + + final ViewGroup allDetails = (ViewGroup) view.findViewById(R.id.all_details); + Utils.forceCustomPadding(allDetails, true /* additive padding */); + mRootView = view; + } + + @Override + protected View onCreateView(ViewGroup parent) { + return mRootView; + } + + public View findViewById(int id) { + return mRootView.findViewById(id); + } + +} diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java index 3aa948d..d77d63f 100755 --- a/src/com/android/settings/applications/InstalledAppDetails.java +++ b/src/com/android/settings/applications/InstalledAppDetails.java @@ -16,75 +16,47 @@ package com.android.settings.applications; -import com.android.internal.telephony.ISms; -import com.android.internal.telephony.SmsUsageMonitor; -import com.android.settings.R; -import com.android.settings.SettingsActivity; -import com.android.settings.Utils; -import com.android.settings.applications.ApplicationsState.AppEntry; - import android.app.Activity; import android.app.ActivityManager; import android.app.AlertDialog; -import android.app.Dialog; -import android.app.DialogFragment; -import android.app.Fragment; -import android.app.INotificationManager; -import android.app.admin.DevicePolicyManager; -import android.appwidget.AppWidgetManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.ApplicationInfo; -import android.content.pm.IPackageDataObserver; -import android.content.pm.IPackageMoveObserver; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; import android.content.pm.PackageManager.NameNotFoundException; -import android.content.res.Resources; -import android.hardware.usb.IUsbManager; +import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; -import android.os.Environment; -import android.os.Handler; -import android.os.IBinder; -import android.os.Message; -import android.os.RemoteException; -import android.os.ServiceManager; import android.os.UserHandle; -import android.os.UserManager; -import android.text.SpannableString; -import android.text.TextUtils; -import android.text.format.Formatter; -import android.text.style.BulletSpan; -import android.util.Log; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import android.view.LayoutInflater; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceClickListener; +import android.provider.Settings; 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; +import com.android.settings.R; +import com.android.settings.SettingsActivity; +import com.android.settings.Utils; +import com.android.settings.applications.ApplicationsState.AppEntry; +import com.android.settings.notification.NotificationAppList; +import com.android.settings.notification.NotificationAppList.AppRow; +import com.android.settings.notification.NotificationAppList.Backend; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + /** * Activity to display application information from Settings. This activity presents * extended information associated with a package like code, data, total size, permissions @@ -94,227 +66,48 @@ import android.widget.TextView; * For non-system applications, there is no option to clear data. Instead there is an option to * uninstall the application. */ -public class InstalledAppDetails extends Fragment - implements View.OnClickListener, CompoundButton.OnCheckedChangeListener, - ApplicationsState.Callbacks { - private static final String TAG="InstalledAppDetails"; - private static final boolean localLOGV = false; - - public static final String ARG_PACKAGE_NAME = "package"; +public class InstalledAppDetails extends AppInfoBase + implements View.OnClickListener, OnPreferenceClickListener { + + // Menu identifiers + public static final int UNINSTALL_ALL_USERS_MENU = 1; + + // Result code identifiers + public static final int REQUEST_UNINSTALL = 0; + private static final int SUB_INFO_FRAGMENT = 1; + + private static final int DLG_FORCE_STOP = DLG_BASE + 1; + private static final int DLG_DISABLE = DLG_BASE + 2; + private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 3; + private static final int DLG_FACTORY_RESET = DLG_BASE + 4; + + private static final String KEY_HEADER = "header_view"; + private static final String KEY_NOTIFICATION = "notification_settings"; + private static final String KEY_STORAGE = "storage_settings"; + private static final String KEY_PERMISSION = "permission_settings"; + private static final String KEY_DATA = "data_settings"; + private static final String KEY_LAUNCH = "preferred_settings"; + + private final HashSet<String> mHomePackages = new HashSet<String>(); - 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; private boolean mInitialized; private boolean mShowUninstalled; - private PackageInfo mPackageInfo; - private CanBeOnSdCardChecker mCanBeOnSdCardChecker; - private View mRootView; + private HeaderPreference mHeader; private Button mUninstallButton; private View mMoreControlButtons; private Button mSpecialDisableButton; - private boolean mMoveInProgress = false; private boolean mUpdatedSysApp = false; - private Button mActivitiesButton; - private View mScreenCompatSection; - private CheckBox mAskCompatibilityCB; - private CheckBox mEnableCompatibilityCB; - private boolean mCanClearData = true; - private boolean mAppControlRestricted = false; private TextView mAppVersion; - private TextView mTotalSize; - private TextView mAppSize; - private TextView mDataSize; - private TextView mExternalCodeSize; - private TextView mExternalDataSize; - private ClearUserDataObserver mClearDataObserver; - // Views related to cache info - private TextView mCacheSize; - private Button mClearCacheButton; - private ClearCacheObserver mClearCacheObserver; private Button mForceStopButton; - private Button mClearDataButton; - private Button mMoveAppButton; - private CompoundButton mNotificationSwitch; - - private PackageMoveObserver mPackageMoveObserver; - - private final HashSet<String> mHomePackages = new HashSet<String>(); + private Preference mNotificationPreference; + private Preference mStoragePreference; + private Preference mPermissionsPreference; + private Preference mLaunchPreference; + private Preference mDataPreference; private boolean mDisableAfterUninstall; - - private boolean mHaveSizes = false; - private long mLastCodeSize = -1; - private long mLastDataSize = -1; - private long mLastExternalCodeSize = -1; - private long mLastExternalDataSize = -1; - private long mLastCacheSize = -1; - private long mLastTotalSize = -1; - - //internal constants used in Handler - private static final int OP_SUCCESSFUL = 1; - private static final int OP_FAILED = 2; - private static final int CLEAR_USER_DATA = 1; - private static final int CLEAR_CACHE = 3; - private static final int PACKAGE_MOVE = 4; - - // invalid size value used initially and also when size retrieval through PackageManager - // fails for whatever reason - private static final int SIZE_INVALID = -1; - - // Resource strings - private CharSequence mInvalidSizeStr; - private CharSequence mComputingStr; - - // Dialog identifiers used in showDialog - private static final int DLG_BASE = 0; - private static final int DLG_CLEAR_DATA = DLG_BASE + 1; - private static final int DLG_FACTORY_RESET = DLG_BASE + 2; - private static final int DLG_APP_NOT_FOUND = DLG_BASE + 3; - private static final int DLG_CANNOT_CLEAR_DATA = DLG_BASE + 4; - private static final int DLG_FORCE_STOP = DLG_BASE + 5; - private static final int DLG_MOVE_FAILED = DLG_BASE + 6; - private static final int DLG_DISABLE = DLG_BASE + 7; - private static final int DLG_DISABLE_NOTIFICATIONS = DLG_BASE + 8; - private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 9; - - // 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. - if (getView() == null) { - return; - } - switch (msg.what) { - case CLEAR_USER_DATA: - processClearMsg(msg); - break; - case CLEAR_CACHE: - // Refresh size info - mState.requestSize(mAppEntry.info.packageName); - break; - case PACKAGE_MOVE: - processMoveMsg(msg); - break; - default: - break; - } - } - }; - - class ClearUserDataObserver extends IPackageDataObserver.Stub { - public void onRemoveCompleted(final String packageName, final boolean succeeded) { - final Message msg = mHandler.obtainMessage(CLEAR_USER_DATA); - msg.arg1 = succeeded?OP_SUCCESSFUL:OP_FAILED; - mHandler.sendMessage(msg); - } - } - - class ClearCacheObserver extends IPackageDataObserver.Stub { - public void onRemoveCompleted(final String packageName, final boolean succeeded) { - final Message msg = mHandler.obtainMessage(CLEAR_CACHE); - msg.arg1 = succeeded ? OP_SUCCESSFUL:OP_FAILED; - mHandler.sendMessage(msg); - } - } - - class PackageMoveObserver extends IPackageMoveObserver.Stub { - public void packageMoved(String packageName, int returnCode) throws RemoteException { - final Message msg = mHandler.obtainMessage(PACKAGE_MOVE); - msg.arg1 = returnCode; - mHandler.sendMessage(msg); - } - } - - private String getSizeStr(long size) { - if (size == SIZE_INVALID) { - return mInvalidSizeStr.toString(); - } - return Formatter.formatFileSize(getActivity(), size); - } - - private void initDataButtons() { - // If the app doesn't have its own space management UI - // And it's a system app that doesn't allow clearing user data or is an active admin - // Then disable the Clear Data button. - if (mAppEntry.info.manageSpaceActivityName == null - && ((mAppEntry.info.flags&(ApplicationInfo.FLAG_SYSTEM - | ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA)) - == ApplicationInfo.FLAG_SYSTEM - || mDpm.packageHasActiveAdmins(mPackageInfo.packageName))) { - mClearDataButton.setText(R.string.clear_user_data_text); - mClearDataButton.setEnabled(false); - mCanClearData = false; - } else { - if (mAppEntry.info.manageSpaceActivityName != null) { - mClearDataButton.setText(R.string.manage_space_text); - } else { - mClearDataButton.setText(R.string.clear_user_data_text); - } - mClearDataButton.setOnClickListener(this); - } - - if (mAppControlRestricted) { - mClearDataButton.setEnabled(false); - } - } - - private CharSequence getMoveErrMsg(int errCode) { - switch (errCode) { - case PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE: - return getActivity().getString(R.string.insufficient_storage); - case PackageManager.MOVE_FAILED_DOESNT_EXIST: - return getActivity().getString(R.string.does_not_exist); - case PackageManager.MOVE_FAILED_FORWARD_LOCKED: - return getActivity().getString(R.string.app_forward_locked); - case PackageManager.MOVE_FAILED_INVALID_LOCATION: - return getActivity().getString(R.string.invalid_location); - case PackageManager.MOVE_FAILED_SYSTEM_PACKAGE: - return getActivity().getString(R.string.system_package); - case PackageManager.MOVE_FAILED_INTERNAL_ERROR: - return ""; - } - return ""; - } - - private void initMoveButton() { - if (Environment.isExternalStorageEmulated()) { - mMoveAppButton.setVisibility(View.INVISIBLE); - return; - } - boolean dataOnly = false; - dataOnly = (mPackageInfo == null) && (mAppEntry != null); - boolean moveDisable = true; - if (dataOnly) { - mMoveAppButton.setText(R.string.move_app); - } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) { - mMoveAppButton.setText(R.string.move_app_to_internal); - // Always let apps move to internal storage from sdcard. - moveDisable = false; - } else { - mMoveAppButton.setText(R.string.move_app_to_sdcard); - mCanBeOnSdCardChecker.init(); - moveDisable = !mCanBeOnSdCardChecker.check(mAppEntry.info); - } - if (moveDisable || mAppControlRestricted) { - mMoveAppButton.setEnabled(false); - } else { - mMoveAppButton.setOnClickListener(this); - mMoveAppButton.setEnabled(true); - } - } + // Used for updating notification preference. + private final Backend mBackend = new Backend(); private boolean handleDisableable(Button button) { boolean disableable = false; @@ -407,108 +200,48 @@ public class InstalledAppDetails extends Fragment } } - private void initNotificationButton() { - INotificationManager nm = INotificationManager.Stub.asInterface( - ServiceManager.getService(Context.NOTIFICATION_SERVICE)); - boolean enabled = true; // default on - try { - enabled = nm.areNotificationsEnabledForPackage(mAppEntry.info.packageName, - mAppEntry.info.uid); - } catch (android.os.RemoteException ex) { - // this does not bode well - } - mNotificationSwitch.setChecked(enabled); - if (Utils.isSystemPackage(mPm, mPackageInfo)) { - mNotificationSwitch.setEnabled(false); - } else if ((mPackageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { - // App is not installed on the current user - mNotificationSwitch.setEnabled(false); - } else { - mNotificationSwitch.setEnabled(true); - mNotificationSwitch.setOnCheckedChangeListener(this); - } - } - /** Called when the activity is first created. */ @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); - mState = ApplicationsState.getInstance(getActivity().getApplication()); - mSession = mState.newSession(this); - mPm = getActivity().getPackageManager(); - 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(); - - // Need to make sure we have loaded applications at this point. - mSession.resume(); - - retrieveAppEntry(); - setHasOptionsMenu(true); + addPreferencesFromResource(R.xml.installed_app_details); } - @Override - public View onCreateView( - LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final View view = inflater.inflate(R.layout.installed_app_details, container, false); + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + handleHeader(); - final ViewGroup allDetails = (ViewGroup) view.findViewById(R.id.all_details); - Utils.forceCustomPadding(allDetails, true /* additive padding */); - - mRootView = view; - mComputingStr = getActivity().getText(R.string.computing_size); - - // Set default values on sizes - mTotalSize = (TextView) view.findViewById(R.id.total_size_text); - mAppSize = (TextView) view.findViewById(R.id.application_size_text); - mDataSize = (TextView) view.findViewById(R.id.data_size_text); - mExternalCodeSize = (TextView) view.findViewById(R.id.external_code_size_text); - mExternalDataSize = (TextView) view.findViewById(R.id.external_data_size_text); + mNotificationPreference = findPreference(KEY_NOTIFICATION); + mNotificationPreference.setOnPreferenceClickListener(this); + mStoragePreference = findPreference(KEY_STORAGE); + mStoragePreference.setOnPreferenceClickListener(this); + mPermissionsPreference = findPreference(KEY_PERMISSION); + mPermissionsPreference.setOnPreferenceClickListener(this); + mLaunchPreference = findPreference(KEY_LAUNCH); + mLaunchPreference.setOnPreferenceClickListener(this); + mDataPreference = findPreference(KEY_DATA); + mDataPreference.setOnPreferenceClickListener(this); + // Data isn't ready, lets just pull it for now. + getPreferenceScreen().removePreference(mDataPreference); + } - if (Environment.isExternalStorageEmulated()) { - ((View)mExternalCodeSize.getParent()).setVisibility(View.GONE); - ((View)mExternalDataSize.getParent()).setVisibility(View.GONE); - } + private void handleHeader() { + mHeader = (HeaderPreference) findPreference(KEY_HEADER); // Get Control button panel - View btnPanel = view.findViewById(R.id.control_buttons_panel); - mForceStopButton = (Button) btnPanel.findViewById(R.id.left_button); + View btnPanel = mHeader.findViewById(R.id.control_buttons_panel); + mForceStopButton = (Button) btnPanel.findViewById(R.id.right_button); mForceStopButton.setText(R.string.force_stop); - mUninstallButton = (Button) btnPanel.findViewById(R.id.right_button); + mUninstallButton = (Button) btnPanel.findViewById(R.id.left_button); mForceStopButton.setEnabled(false); - + // Get More Control button panel - mMoreControlButtons = view.findViewById(R.id.more_control_buttons_panel); - mMoreControlButtons.findViewById(R.id.left_button).setVisibility(View.INVISIBLE); - mSpecialDisableButton = (Button) mMoreControlButtons.findViewById(R.id.right_button); + mMoreControlButtons = mHeader.findViewById(R.id.more_control_buttons_panel); + mMoreControlButtons.findViewById(R.id.right_button).setVisibility(View.INVISIBLE); + mSpecialDisableButton = (Button) mMoreControlButtons.findViewById(R.id.left_button); mMoreControlButtons.setVisibility(View.GONE); - - // Initialize clear data and move install location buttons - View data_buttons_panel = view.findViewById(R.id.data_buttons_panel); - mClearDataButton = (Button) data_buttons_panel.findViewById(R.id.right_button); - mMoveAppButton = (Button) data_buttons_panel.findViewById(R.id.left_button); - - // Cache section - mCacheSize = (TextView) view.findViewById(R.id.cache_size_text); - mClearCacheButton = (Button) view.findViewById(R.id.clear_cache_button); - - mActivitiesButton = (Button) view.findViewById(R.id.clear_activities_button); - - // Screen compatibility control - mScreenCompatSection = view.findViewById(R.id.screen_compatibility_section); - mAskCompatibilityCB = (CheckBox) view.findViewById(R.id.ask_compatibility_cb); - mEnableCompatibilityCB = (CheckBox) view.findViewById(R.id.enable_compatibility_cb); - - mNotificationSwitch = (CompoundButton) view.findViewById(R.id.notification_switch); - - return view; } @Override @@ -572,8 +305,9 @@ public class InstalledAppDetails extends Fragment // Utility method to set application label and icon. private void setAppLabelAndIcon(PackageInfo pkgInfo) { - final View appSnippet = mRootView.findViewById(R.id.app_snippet); - appSnippet.setPaddingRelative(0, appSnippet.getPaddingTop(), 0, appSnippet.getPaddingBottom()); + final View appSnippet = mHeader.findViewById(R.id.app_snippet); + appSnippet.setPaddingRelative(0, appSnippet.getPaddingTop(), 0, + appSnippet.getPaddingBottom()); ImageView icon = (ImageView) appSnippet.findViewById(R.id.app_icon); mState.ensureIcon(mAppEntry); @@ -593,86 +327,6 @@ public class InstalledAppDetails extends Fragment } } - @Override - public void onResume() { - super.onResume(); - - mAppControlRestricted = mUserManager.hasUserRestriction(UserManager.DISALLOW_APPS_CONTROL); - mSession.resume(); - if (!refreshUi()) { - setIntentAndFinish(true, true); - } - } - - @Override - public void onPause() { - super.onPause(); - mSession.pause(); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - mSession.release(); - } - - @Override - public void onAllSizesComputed() { - } - - @Override - public void onPackageIconChanged() { - } - - @Override - public void onPackageListChanged() { - refreshUi(); - } - - @Override - public void onRebuildComplete(ArrayList<AppEntry> apps) { - } - - @Override - public void onPackageSizeChanged(String packageName) { - if (packageName.equals(mAppEntry.info.packageName)) { - refreshSizeInfo(); - } - } - - @Override - public void onRunningStateChanged(boolean running) { - } - - private String retrieveAppEntry() { - final Bundle args = getArguments(); - String packageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null; - if (packageName == null) { - Intent intent = (args == null) ? - getActivity().getIntent() : (Intent) args.getParcelable("intent"); - if (intent != null) { - packageName = intent.getData().getSchemeSpecificPart(); - } - } - mAppEntry = mState.getEntry(packageName); - if (mAppEntry != null) { - // Get application info again to refresh changed properties of application - try { - mPackageInfo = mPm.getPackageInfo(mAppEntry.info.packageName, - PackageManager.GET_DISABLED_COMPONENTS | - PackageManager.GET_UNINSTALLED_PACKAGES | - PackageManager.GET_SIGNATURES); - } catch (NameNotFoundException e) { - Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e); - } - } else { - Log.w(TAG, "Missing AppEntry; maybe reinstalling?"); - mPackageInfo = null; - } - - return packageName; - } - private boolean signaturesMatch(String pkg1, String pkg2) { if (pkg1 != null && pkg2 != null) { try { @@ -688,12 +342,9 @@ public class InstalledAppDetails extends Fragment return false; } - private boolean refreshUi() { - if (mMoveInProgress) { - return true; - } - final String packageName = retrieveAppEntry(); - + @Override + protected boolean refreshUi() { + retrieveAppEntry(); if (mAppEntry == null) { return false; // onCreate must have failed, make sure to exit } @@ -721,175 +372,18 @@ public class InstalledAppDetails extends Fragment } } - // Get list of preferred activities - List<ComponentName> prefActList = new ArrayList<ComponentName>(); - - // Intent list cannot be null. so pass empty list - List<IntentFilter> intentList = new ArrayList<IntentFilter>(); - mPm.getPreferredActivities(intentList, prefActList, packageName); - if (localLOGV) - Log.i(TAG, "Have " + prefActList.size() + " number of activities in preferred list"); - boolean hasUsbDefaults = false; - try { - if (mUsbManager != null) { - hasUsbDefaults = mUsbManager.hasDefaults(packageName, UserHandle.myUserId()); - } - } catch (RemoteException e) { - Log.e(TAG, "mUsbManager.hasDefaults", e); - } - boolean hasBindAppWidgetPermission = - mAppWidgetManager.hasBindAppWidgetPermission(mAppEntry.info.packageName); - - TextView autoLaunchTitleView = (TextView) mRootView.findViewById(R.id.auto_launch_title); - TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch); - boolean autoLaunchEnabled = prefActList.size() > 0 || hasUsbDefaults; - if (!autoLaunchEnabled && !hasBindAppWidgetPermission) { - resetLaunchDefaultsUi(autoLaunchTitleView, autoLaunchView); - } else { - boolean useBullets = hasBindAppWidgetPermission && autoLaunchEnabled; - - if (hasBindAppWidgetPermission) { - autoLaunchTitleView.setText(R.string.auto_launch_label_generic); - } else { - autoLaunchTitleView.setText(R.string.auto_launch_label); - } - - CharSequence text = null; - int bulletIndent = getResources() - .getDimensionPixelSize(R.dimen.installed_app_details_bullet_offset); - if (autoLaunchEnabled) { - CharSequence autoLaunchEnableText = getText(R.string.auto_launch_enable_text); - SpannableString s = new SpannableString(autoLaunchEnableText); - if (useBullets) { - s.setSpan(new BulletSpan(bulletIndent), 0, autoLaunchEnableText.length(), 0); - } - text = (text == null) ? - TextUtils.concat(s, "\n") : TextUtils.concat(text, "\n", s, "\n"); - } - if (hasBindAppWidgetPermission) { - CharSequence alwaysAllowBindAppWidgetsText = - getText(R.string.always_allow_bind_appwidgets_text); - SpannableString s = new SpannableString(alwaysAllowBindAppWidgetsText); - if (useBullets) { - s.setSpan(new BulletSpan(bulletIndent), - 0, alwaysAllowBindAppWidgetsText.length(), 0); - } - text = (text == null) ? - TextUtils.concat(s, "\n") : TextUtils.concat(text, "\n", s, "\n"); - } - autoLaunchView.setText(text); - mActivitiesButton.setEnabled(true); - mActivitiesButton.setOnClickListener(this); - } - - // Screen compatibility section. - ActivityManager am = (ActivityManager) - getActivity().getSystemService(Context.ACTIVITY_SERVICE); - int compatMode = am.getPackageScreenCompatMode(packageName); - // For now these are always off; this is the old UI model which we - // are no longer using. - if (false && (compatMode == ActivityManager.COMPAT_MODE_DISABLED - || compatMode == ActivityManager.COMPAT_MODE_ENABLED)) { - mScreenCompatSection.setVisibility(View.VISIBLE); - mAskCompatibilityCB.setChecked(am.getPackageAskScreenCompat(packageName)); - mAskCompatibilityCB.setOnCheckedChangeListener(this); - mEnableCompatibilityCB.setChecked(compatMode == ActivityManager.COMPAT_MODE_ENABLED); - mEnableCompatibilityCB.setOnCheckedChangeListener(this); - } else { - mScreenCompatSection.setVisibility(View.GONE); - } - - // Security permissions section - LinearLayout permsView = (LinearLayout) mRootView.findViewById(R.id.permissions_section); - AppSecurityPermissions asp = new AppSecurityPermissions(getActivity(), packageName); - 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); - securityList.removeAllViews(); - securityList.addView(asp.getPermissionsViewWithRevokeButtons()); - // If this app is running under a shared user ID with other apps, - // update the description to explain this. - String[] packages = mPm.getPackagesForUid(mPackageInfo.applicationInfo.uid); - if (packages != null && packages.length > 1) { - ArrayList<CharSequence> pnames = new ArrayList<CharSequence>(); - for (int i=0; i<packages.length; i++) { - String pkg = packages[i]; - if (mPackageInfo.packageName.equals(pkg)) { - continue; - } - try { - ApplicationInfo ainfo = mPm.getApplicationInfo(pkg, 0); - pnames.add(ainfo.loadLabel(mPm)); - } catch (PackageManager.NameNotFoundException e) { - } - } - final int N = pnames.size(); - if (N > 0) { - final Resources res = getActivity().getResources(); - String appListStr; - if (N == 1) { - appListStr = pnames.get(0).toString(); - } else if (N == 2) { - appListStr = res.getString(R.string.join_two_items, pnames.get(0), - pnames.get(1)); - } else { - appListStr = pnames.get(N-2).toString(); - for (int i=N-3; i>=0; i--) { - appListStr = res.getString(i == 0 ? R.string.join_many_items_first - : R.string.join_many_items_middle, pnames.get(i), appListStr); - } - appListStr = res.getString(R.string.join_many_items_last, - appListStr, pnames.get(N-1)); - } - TextView descr = (TextView) mRootView.findViewById( - R.id.security_settings_desc); - descr.setText(res.getString(R.string.security_settings_desc_multi, - mPackageInfo.applicationInfo.loadLabel(mPm), appListStr)); - } - } - } - checkForceStop(); setAppLabelAndIcon(mPackageInfo); - refreshButtons(); - refreshSizeInfo(); + initUninstallButtons(); + + // Update the preference summaries. + Activity context = getActivity(); + mStoragePreference.setSummary(AppStorageSettings.getSummary(mAppEntry, context)); + mPermissionsPreference.setSummary(AppPermissionSettings.getSummary(mAppEntry, context)); + mLaunchPreference.setSummary(AppLaunchSettings.getSummary(mAppEntry, mUsbManager, + mPm, context)); + mNotificationPreference.setSummary(getNotificationSummary(mAppEntry, context, + mBackend)); if (!mInitialized) { // First time init: are we displaying an uninstalled app? @@ -899,7 +393,7 @@ public class InstalledAppDetails extends Fragment // All other times: if the app no longer exists then we want // to go away. try { - ApplicationInfo ainfo = getActivity().getPackageManager().getApplicationInfo( + ApplicationInfo ainfo = context.getPackageManager().getApplicationInfo( mAppEntry.info.packageName, PackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_DISABLED_COMPONENTS); if (!mShowUninstalled) { @@ -916,344 +410,63 @@ public class InstalledAppDetails extends Fragment 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); - autoLaunchView.setText(R.string.auto_launch_disable_text); - // Disable clear activities button - mActivitiesButton.setEnabled(false); - } - - private void setIntentAndFinish(boolean finish, boolean appChanged) { - if(localLOGV) Log.i(TAG, "appChanged="+appChanged); - Intent intent = new Intent(); - intent.putExtra(ManageApplications.APP_CHG, appChanged); - SettingsActivity sa = (SettingsActivity)getActivity(); - sa.finishPreferencePanel(this, Activity.RESULT_OK, intent); - } - - private void refreshSizeInfo() { - if (mAppEntry.size == ApplicationsState.SIZE_INVALID - || mAppEntry.size == ApplicationsState.SIZE_UNKNOWN) { - mLastCodeSize = mLastDataSize = mLastCacheSize = mLastTotalSize = -1; - if (!mHaveSizes) { - mAppSize.setText(mComputingStr); - mDataSize.setText(mComputingStr); - mCacheSize.setText(mComputingStr); - mTotalSize.setText(mComputingStr); - } - mClearDataButton.setEnabled(false); - mClearCacheButton.setEnabled(false); - - } else { - mHaveSizes = true; - long codeSize = mAppEntry.codeSize; - long dataSize = mAppEntry.dataSize; - if (Environment.isExternalStorageEmulated()) { - codeSize += mAppEntry.externalCodeSize; - dataSize += mAppEntry.externalDataSize; - } else { - if (mLastExternalCodeSize != mAppEntry.externalCodeSize) { - mLastExternalCodeSize = mAppEntry.externalCodeSize; - mExternalCodeSize.setText(getSizeStr(mAppEntry.externalCodeSize)); - } - if (mLastExternalDataSize != mAppEntry.externalDataSize) { - mLastExternalDataSize = mAppEntry.externalDataSize; - mExternalDataSize.setText(getSizeStr( mAppEntry.externalDataSize)); - } - } - if (mLastCodeSize != codeSize) { - mLastCodeSize = codeSize; - mAppSize.setText(getSizeStr(codeSize)); - } - if (mLastDataSize != dataSize) { - mLastDataSize = dataSize; - mDataSize.setText(getSizeStr(dataSize)); - } - long cacheSize = mAppEntry.cacheSize + mAppEntry.externalCacheSize; - if (mLastCacheSize != cacheSize) { - mLastCacheSize = cacheSize; - mCacheSize.setText(getSizeStr(cacheSize)); - } - if (mLastTotalSize != mAppEntry.size) { - mLastTotalSize = mAppEntry.size; - mTotalSize.setText(getSizeStr(mAppEntry.size)); - } - - if ((mAppEntry.dataSize+ mAppEntry.externalDataSize) <= 0 || !mCanClearData) { - mClearDataButton.setEnabled(false); - } else { - mClearDataButton.setEnabled(true); - mClearDataButton.setOnClickListener(this); - } - if (cacheSize <= 0) { - mClearCacheButton.setEnabled(false); - } else { - mClearCacheButton.setEnabled(true); - mClearCacheButton.setOnClickListener(this); - } - } - if (mAppControlRestricted) { - mClearCacheButton.setEnabled(false); - mClearDataButton.setEnabled(false); - } - } - - /* - * Private method to handle clear message notification from observer when - * the async operation from PackageManager is complete - */ - private void processClearMsg(Message msg) { - int result = msg.arg1; - String packageName = mAppEntry.info.packageName; - mClearDataButton.setText(R.string.clear_user_data_text); - if(result == OP_SUCCESSFUL) { - Log.i(TAG, "Cleared user data for package : "+packageName); - mState.requestSize(mAppEntry.info.packageName); - } else { - mClearDataButton.setEnabled(true); - } - checkForceStop(); - } - - private void refreshButtons() { - if (!mMoveInProgress) { - initUninstallButtons(); - initDataButtons(); - initMoveButton(); - initNotificationButton(); - } else { - mMoveAppButton.setText(R.string.moving); - mMoveAppButton.setEnabled(false); - mUninstallButton.setEnabled(false); - mSpecialDisableButton.setEnabled(false); - } - } - - private void processMoveMsg(Message msg) { - int result = msg.arg1; - String packageName = mAppEntry.info.packageName; - // Refresh the button attributes. - mMoveInProgress = false; - if (result == PackageManager.MOVE_SUCCEEDED) { - Log.i(TAG, "Moved resources for " + packageName); - // Refresh size information again. - mState.requestSize(mAppEntry.info.packageName); - } else { - showDialogInner(DLG_MOVE_FAILED, result); - } - refreshUi(); - } - - /* - * Private method to initiate clearing user data when the user clicks the clear data - * button for a system package - */ - private void initiateClearUserData() { - mClearDataButton.setEnabled(false); - // Invoke uninstall or clear user data based on sysPackage - String packageName = mAppEntry.info.packageName; - Log.i(TAG, "Clearing user data for package : " + packageName); - if (mClearDataObserver == null) { - mClearDataObserver = new ClearUserDataObserver(); - } - ActivityManager am = (ActivityManager) - getActivity().getSystemService(Context.ACTIVITY_SERVICE); - boolean res = am.clearApplicationUserData(packageName, mClearDataObserver); - if (!res) { - // Clearing data failed for some obscure reason. Just log error for now - Log.i(TAG, "Couldnt clear application user data for package:"+packageName); - showDialogInner(DLG_CANNOT_CLEAR_DATA, 0); - } else { - mClearDataButton.setText(R.string.recompute_size); - } - } - - private void showDialogInner(int id, int moveErrorCode) { - DialogFragment newFragment = MyAlertDialogFragment.newInstance(id, moveErrorCode); - newFragment.setTargetFragment(this, 0); - newFragment.show(getFragmentManager(), "dialog " + id); - } - - public static class MyAlertDialogFragment extends DialogFragment { - - public static MyAlertDialogFragment newInstance(int id, int moveErrorCode) { - MyAlertDialogFragment frag = new MyAlertDialogFragment(); - Bundle args = new Bundle(); - args.putInt("id", id); - args.putInt("moveError", moveErrorCode); - frag.setArguments(args); - return frag; - } - - InstalledAppDetails getOwner() { - return (InstalledAppDetails)getTargetFragment(); - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - int id = getArguments().getInt("id"); - int moveErrorCode = getArguments().getInt("moveError"); - switch (id) { - case DLG_CLEAR_DATA: - return new AlertDialog.Builder(getActivity()) - .setTitle(getActivity().getText(R.string.clear_data_dlg_title)) - .setMessage(getActivity().getText(R.string.clear_data_dlg_text)) - .setPositiveButton(R.string.dlg_ok, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - // Clear user data here - getOwner().initiateClearUserData(); - } - }) - .setNegativeButton(R.string.dlg_cancel, null) - .create(); - case DLG_FACTORY_RESET: - return new AlertDialog.Builder(getActivity()) - .setTitle(getActivity().getText(R.string.app_factory_reset_dlg_title)) - .setMessage(getActivity().getText(R.string.app_factory_reset_dlg_text)) - .setPositiveButton(R.string.dlg_ok, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - // Clear user data here - getOwner().uninstallPkg(getOwner().mAppEntry.info.packageName, - false, false); - } - }) - .setNegativeButton(R.string.dlg_cancel, null) - .create(); - case DLG_APP_NOT_FOUND: - return new AlertDialog.Builder(getActivity()) - .setTitle(getActivity().getText(R.string.app_not_found_dlg_title)) - .setMessage(getActivity().getText(R.string.app_not_found_dlg_title)) - .setNeutralButton(getActivity().getText(R.string.dlg_ok), - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - //force to recompute changed value - getOwner().setIntentAndFinish(true, true); - } - }) - .create(); - case DLG_CANNOT_CLEAR_DATA: - return new AlertDialog.Builder(getActivity()) - .setTitle(getActivity().getText(R.string.clear_failed_dlg_title)) - .setMessage(getActivity().getText(R.string.clear_failed_dlg_text)) - .setNeutralButton(R.string.dlg_ok, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - getOwner().mClearDataButton.setEnabled(false); - //force to recompute changed value - getOwner().setIntentAndFinish(false, false); - } - }) - .create(); - case DLG_FORCE_STOP: - return new AlertDialog.Builder(getActivity()) - .setTitle(getActivity().getText(R.string.force_stop_dlg_title)) - .setMessage(getActivity().getText(R.string.force_stop_dlg_text)) - .setPositiveButton(R.string.dlg_ok, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - // Force stop - getOwner().forceStopPackage(getOwner().mAppEntry.info.packageName); - } - }) - .setNegativeButton(R.string.dlg_cancel, null) - .create(); - case DLG_MOVE_FAILED: - CharSequence msg = getActivity().getString(R.string.move_app_failed_dlg_text, - getOwner().getMoveErrMsg(moveErrorCode)); - return new AlertDialog.Builder(getActivity()) - .setTitle(getActivity().getText(R.string.move_app_failed_dlg_title)) - .setMessage(msg) - .setNeutralButton(R.string.dlg_ok, null) - .create(); - case DLG_DISABLE: - return new AlertDialog.Builder(getActivity()) - .setTitle(getActivity().getText(R.string.app_disable_dlg_title)) - .setMessage(getActivity().getText(R.string.app_disable_dlg_text)) - .setPositiveButton(R.string.dlg_ok, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - // Disable the app - new DisableChanger(getOwner(), getOwner().mAppEntry.info, - PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) - .execute((Object)null); - } - }) - .setNegativeButton(R.string.dlg_cancel, null) - .create(); - case DLG_DISABLE_NOTIFICATIONS: - return new AlertDialog.Builder(getActivity()) - .setTitle(getActivity().getText(R.string.app_disable_notifications_dlg_title)) - .setMessage(getActivity().getText(R.string.app_disable_notifications_dlg_text)) - .setPositiveButton(R.string.dlg_ok, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - // Disable the package's notifications - getOwner().setNotificationsEnabled(false); - } - }) - .setNegativeButton(R.string.dlg_cancel, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - // Re-enable the checkbox - getOwner().mNotificationSwitch.setChecked(true); - } - }) - .create(); - case DLG_SPECIAL_DISABLE: - return new AlertDialog.Builder(getActivity()) - .setTitle(getActivity().getText(R.string.app_special_disable_dlg_title)) - .setMessage(getActivity().getText(R.string.app_special_disable_dlg_text)) - .setPositiveButton(R.string.dlg_ok, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - // Clear user data here - getOwner().uninstallPkg(getOwner().mAppEntry.info.packageName, - false, true); - } - }) - .setNegativeButton(R.string.dlg_cancel, null) - .create(); - } - throw new IllegalArgumentException("unknown id " + id); - } + @Override + protected AlertDialog createDialog(int id, int errorCode) { + switch (id) { + case DLG_DISABLE: + return new AlertDialog.Builder(getActivity()) + .setTitle(getActivity().getText(R.string.app_disable_dlg_title)) + .setMessage(getActivity().getText(R.string.app_disable_dlg_text)) + .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + // Disable the app + new DisableChanger(InstalledAppDetails.this, mAppEntry.info, + PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) + .execute((Object)null); + } + }) + .setNegativeButton(R.string.dlg_cancel, null) + .create(); + case DLG_SPECIAL_DISABLE: + return new AlertDialog.Builder(getActivity()) + .setTitle(getActivity().getText(R.string.app_special_disable_dlg_title)) + .setMessage(getActivity().getText(R.string.app_special_disable_dlg_text)) + .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + // Clear user data here + uninstallPkg(mAppEntry.info.packageName, + false, true); + } + }) + .setNegativeButton(R.string.dlg_cancel, null) + .create(); + case DLG_FORCE_STOP: + return new AlertDialog.Builder(getActivity()) + .setTitle(getActivity().getText(R.string.force_stop_dlg_title)) + .setMessage(getActivity().getText(R.string.force_stop_dlg_text)) + .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + // Force stop + forceStopPackage(mAppEntry.info.packageName); + } + }) + .setNegativeButton(R.string.dlg_cancel, null) + .create(); + case DLG_FACTORY_RESET: + return new AlertDialog.Builder(getActivity()) + .setTitle(getActivity().getText(R.string.app_factory_reset_dlg_title)) + .setMessage(getActivity().getText(R.string.app_factory_reset_dlg_text)) + .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + // Clear user data here + uninstallPkg(mAppEntry.info.packageName, + false, false); + } + }) + .setNegativeButton(R.string.dlg_cancel, null) + .create(); + } + return null; } private void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) { @@ -1277,13 +490,6 @@ public class InstalledAppDetails extends Fragment checkForceStop(); } - private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - updateForceStopButton(getResultCode() != Activity.RESULT_CANCELED); - } - }; - private void updateForceStopButton(boolean enabled) { if (mAppControlRestricted) { mForceStopButton.setEnabled(false); @@ -1292,7 +498,7 @@ public class InstalledAppDetails extends Fragment mForceStopButton.setOnClickListener(InstalledAppDetails.this); } } - + private void checkForceStop() { if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { // User can't force stop device admin. @@ -1312,47 +518,21 @@ public class InstalledAppDetails extends Fragment } } - static class DisableChanger extends AsyncTask<Object, Object, Object> { - final PackageManager mPm; - final WeakReference<InstalledAppDetails> mActivity; - final ApplicationInfo mInfo; - final int mState; + private void startAppInfoFragment(Class<? extends AppInfoBase> fragment, CharSequence title) { + // start new fragment to display extended information + Bundle args = new Bundle(); + args.putString(InstalledAppDetails.ARG_PACKAGE_NAME, mAppEntry.info.packageName); - DisableChanger(InstalledAppDetails activity, ApplicationInfo info, int state) { - mPm = activity.mPm; - mActivity = new WeakReference<InstalledAppDetails>(activity); - mInfo = info; - mState = state; - } - - @Override - protected Object doInBackground(Object... params) { - mPm.setApplicationEnabledSetting(mInfo.packageName, mState, 0); - return null; - } + SettingsActivity sa = (SettingsActivity) getActivity(); + sa.startPreferencePanel(fragment.getName(), args, -1, title, this, SUB_INFO_FRAGMENT); } - private void setNotificationsEnabled(boolean enabled) { - String packageName = mAppEntry.info.packageName; - INotificationManager nm = INotificationManager.Stub.asInterface( - ServiceManager.getService(Context.NOTIFICATION_SERVICE)); - try { - final boolean enable = mNotificationSwitch.isChecked(); - nm.setNotificationsEnabledForPackage(packageName, mAppEntry.info.uid, enabled); - } catch (android.os.RemoteException ex) { - mNotificationSwitch.setChecked(!enabled); // revert - } - } - - private int getPremiumSmsPermission(String packageName) { - try { - if (mSmsManager != null) { - return mSmsManager.getPremiumSmsPermission(packageName); - } - } catch (RemoteException ex) { - // ignored - } - return SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN; + private void startNotifications() { + // start new fragment to display extended information + getActivity().startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + .putExtra(Settings.EXTRA_APP_PACKAGE, mAppEntry.info.packageName) + .putExtra(Settings.EXTRA_APP_UID, mAppEntry.info.uid)); } /* @@ -1381,69 +561,77 @@ public class InstalledAppDetails extends Fragment } } else if(v == mSpecialDisableButton) { showDialogInner(DLG_SPECIAL_DISABLE, 0); - } else if(v == mActivitiesButton) { - if (mUsbManager != null) { - mPm.clearPackagePreferredActivities(packageName); - try { - mUsbManager.clearDefaults(packageName, UserHandle.myUserId()); - } catch (RemoteException e) { - Log.e(TAG, "mUsbManager.clearDefaults", e); - } - mAppWidgetManager.setBindAppWidgetPermission(packageName, false); - TextView autoLaunchTitleView = - (TextView) mRootView.findViewById(R.id.auto_launch_title); - TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch); - resetLaunchDefaultsUi(autoLaunchTitleView, autoLaunchView); - } - } else if(v == mClearDataButton) { - if (mAppEntry.info.manageSpaceActivityName != null) { - if (!Utils.isMonkeyRunning()) { - Intent intent = new Intent(Intent.ACTION_DEFAULT); - intent.setClassName(mAppEntry.info.packageName, - mAppEntry.info.manageSpaceActivityName); - startActivityForResult(intent, REQUEST_MANAGE_SPACE); - } - } else { - showDialogInner(DLG_CLEAR_DATA, 0); - } - } else if (v == mClearCacheButton) { - // Lazy initialization of observer - if (mClearCacheObserver == null) { - mClearCacheObserver = new ClearCacheObserver(); - } - mPm.deleteApplicationCacheFiles(packageName, mClearCacheObserver); } else if (v == mForceStopButton) { showDialogInner(DLG_FORCE_STOP, 0); //forceStopPackage(mAppInfo.packageName); - } else if (v == mMoveAppButton) { - if (mPackageMoveObserver == null) { - mPackageMoveObserver = new PackageMoveObserver(); - } - int moveFlags = (mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0 ? - PackageManager.MOVE_INTERNAL : PackageManager.MOVE_EXTERNAL_MEDIA; - mMoveInProgress = true; - refreshButtons(); - mPm.movePackage(mAppEntry.info.packageName, mPackageMoveObserver, moveFlags); } } @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - String packageName = mAppEntry.info.packageName; - ActivityManager am = (ActivityManager) - getActivity().getSystemService(Context.ACTIVITY_SERVICE); - if (buttonView == mAskCompatibilityCB) { - am.setPackageAskScreenCompat(packageName, isChecked); - } else if (buttonView == mEnableCompatibilityCB) { - am.setPackageScreenCompatMode(packageName, isChecked ? - ActivityManager.COMPAT_MODE_ENABLED : ActivityManager.COMPAT_MODE_DISABLED); - } else if (buttonView == mNotificationSwitch) { - if (!isChecked) { - showDialogInner(DLG_DISABLE_NOTIFICATIONS, 0); - } else { - setNotificationsEnabled(true); + public boolean onPreferenceClick(Preference preference) { + if (preference == mStoragePreference) { + startAppInfoFragment(AppStorageSettings.class, mStoragePreference.getTitle()); + } else if (preference == mNotificationPreference) { + startNotifications(); + } else if (preference == mPermissionsPreference) { + startAppInfoFragment(AppPermissionSettings.class, mPermissionsPreference.getTitle()); + } else if (preference == mLaunchPreference) { + startAppInfoFragment(AppLaunchSettings.class, mLaunchPreference.getTitle()); + } else if (preference == mDataPreference) { + // Not yet. + } else { + return false; + } + return true; + } + + public static CharSequence getNotificationSummary(AppEntry appEntry, Context context) { + return getNotificationSummary(appEntry, context, new Backend()); + } + + public static CharSequence getNotificationSummary(AppEntry appEntry, Context context, + Backend backend) { + AppRow appRow = NotificationAppList.loadAppRow(context.getPackageManager(), appEntry.info, + backend); + if (appRow.banned) { + return context.getString(R.string.notifications_disabled); + } else if (appRow.priority) { + if (appRow.sensitive) { + return context.getString(R.string.notifications_priority_sensitive); } + return context.getString(R.string.notifications_priority); + } else if (appRow.sensitive) { + return context.getString(R.string.notifications_sensitive); + } + return context.getString(R.string.notifications_enabled); + } + + static class DisableChanger extends AsyncTask<Object, Object, Object> { + final PackageManager mPm; + final WeakReference<InstalledAppDetails> mActivity; + final ApplicationInfo mInfo; + final int mState; + + DisableChanger(InstalledAppDetails activity, ApplicationInfo info, int state) { + mPm = activity.mPm; + mActivity = new WeakReference<InstalledAppDetails>(activity); + mInfo = info; + mState = state; + } + + @Override + protected Object doInBackground(Object... params) { + mPm.setApplicationEnabledSetting(mInfo.packageName, mState, 0); + return null; } } + + private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + updateForceStopButton(getResultCode() != Activity.RESULT_CANCELED); + } + }; } + |