diff options
author | Dan Pasanen <dan.pasanen@gmail.com> | 2017-04-05 07:26:34 -0500 |
---|---|---|
committer | Dan Pasanen <dan.pasanen@gmail.com> | 2017-04-05 07:26:34 -0500 |
commit | 4f8e6f18ca8790d99ad6a961d40749bb85401dac (patch) | |
tree | 82e25618e66133c4d5b7bcb92bb63318c32dfe80 /src/com | |
parent | 0fc5fe15f2571c7b117e87737b633ad9cc2aaf08 (diff) | |
parent | e1dbf0e2600ab6c7f5cc47621d85d0c38a27f5ff (diff) | |
download | android_packages_apps_PackageInstaller-staging/cm-14.1_android-7.1.2_r2.tar.gz android_packages_apps_PackageInstaller-staging/cm-14.1_android-7.1.2_r2.tar.bz2 android_packages_apps_PackageInstaller-staging/cm-14.1_android-7.1.2_r2.zip |
Merge tag 'android-7.1.2_r2' into cm-14.1staging/cm-14.1_android-7.1.2_r2
Android 7.1.2 Release 2 (N2G47E)
# gpg: Signature made Mon 03 Apr 2017 01:41:50 AM CDT
# gpg: using DSA key E8AD3F819AB10E78
# gpg: Can't check signature: No public key
Diffstat (limited to 'src/com')
19 files changed, 464 insertions, 1628 deletions
diff --git a/src/com/android/packageinstaller/PackageInstallerActivity.java b/src/com/android/packageinstaller/PackageInstallerActivity.java index 55a1f814..de685825 100644 --- a/src/com/android/packageinstaller/PackageInstallerActivity.java +++ b/src/com/android/packageinstaller/PackageInstallerActivity.java @@ -477,6 +477,7 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen final boolean requestFromUnknownSource = isInstallRequestFromUnknownSource(getIntent()); if (!requestFromUnknownSource) { initiateInstall(); + return; } // If the admin prohibits it, or we're running in a managed profile, just show error diff --git a/src/com/android/packageinstaller/permission/model/AppPermissionGroup.java b/src/com/android/packageinstaller/permission/model/AppPermissionGroup.java index a6601165..5a1a9d64 100644 --- a/src/com/android/packageinstaller/permission/model/AppPermissionGroup.java +++ b/src/com/android/packageinstaller/permission/model/AppPermissionGroup.java @@ -353,7 +353,7 @@ public final class AppPermissionGroup implements Comparable<AppPermissionGroup> // no longer has it fixed in a denied state. if (permission.isUserFixed() || permission.isUserSet()) { permission.setUserFixed(false); - permission.setUserSet(true); + permission.setUserSet(false); mPackageManager.updatePermissionFlags(permission.getName(), mPackageInfo.packageName, PackageManager.FLAG_PERMISSION_USER_FIXED @@ -538,11 +538,11 @@ public final class AppPermissionGroup implements Comparable<AppPermissionGroup> final int permissionCount = mPermissions.size(); for (int i = 0; i < permissionCount; i++) { Permission permission = mPermissions.valueAt(i); - if (!permission.isUserFixed()) { - return false; + if (permission.isUserFixed()) { + return true; } } - return true; + return false; } public boolean isPolicyFixed() { @@ -560,11 +560,11 @@ public final class AppPermissionGroup implements Comparable<AppPermissionGroup> final int permissionCount = mPermissions.size(); for (int i = 0; i < permissionCount; i++) { Permission permission = mPermissions.valueAt(i); - if (!permission.isUserSet()) { - return false; + if (permission.isUserSet()) { + return true; } } - return true; + return false; } public boolean isSystemFixed() { diff --git a/src/com/android/packageinstaller/permission/model/AppPermissions.java b/src/com/android/packageinstaller/permission/model/AppPermissions.java index e455ef13..099e5b07 100644 --- a/src/com/android/packageinstaller/permission/model/AppPermissions.java +++ b/src/com/android/packageinstaller/permission/model/AppPermissions.java @@ -48,11 +48,11 @@ public final class AppPermissions { private PackageInfo mPackageInfo; - public AppPermissions(Context context, PackageInfo packageInfo, String[] permissions, + public AppPermissions(Context context, PackageInfo packageInfo, String[] filterPermissions, boolean sortGroups, Runnable onErrorCallback) { mContext = context; mPackageInfo = packageInfo; - mFilterPermissions = permissions; + mFilterPermissions = filterPermissions; mAppLabel = BidiFormatter.getInstance().unicodeWrap( packageInfo.applicationInfo.loadSafeLabel( context.getPackageManager()).toString()); @@ -83,7 +83,9 @@ public final class AppPermissions { } public boolean isReviewRequired() { - if (!Build.PERMISSIONS_REVIEW_REQUIRED) { + if (!mContext.getResources().getBoolean( + com.android.internal.R.bool.config_permissionReviewRequired) + && !Build.PERMISSIONS_REVIEW_REQUIRED) { return false; } final int groupCount = mGroups.size(); @@ -120,34 +122,13 @@ public final class AppPermissions { if (!filterPermission.equals(requestedPerm)) { continue; } - - if (hasGroupForPermission(requestedPerm)) { - break; - } - - AppPermissionGroup group = AppPermissionGroup.create(mContext, - mPackageInfo, requestedPerm); - if (group == null) { - break; - } - - mGroups.add(group); + addPermissionGroupIfNeeded(requestedPerm); break; } } } else { for (String requestedPerm : mPackageInfo.requestedPermissions) { - if (hasGroupForPermission(requestedPerm)) { - continue; - } - - AppPermissionGroup group = AppPermissionGroup.create(mContext, - mPackageInfo, requestedPerm); - if (group == null) { - continue; - } - - mGroups.add(group); + addPermissionGroupIfNeeded(requestedPerm); } } @@ -161,6 +142,20 @@ public final class AppPermissions { } } + private void addPermissionGroupIfNeeded(String permission) { + if (hasGroupForPermission(permission)) { + return; + } + + AppPermissionGroup group = AppPermissionGroup.create(mContext, + mPackageInfo, permission); + if (group == null) { + return; + } + + mGroups.add(group); + } + private boolean hasGroupForPermission(String permission) { for (AppPermissionGroup group : mGroups) { if (group.hasPermission(permission)) { diff --git a/src/com/android/packageinstaller/permission/ui/GrantPermissionsWatchViewHandler.java b/src/com/android/packageinstaller/permission/ui/GrantPermissionsWatchViewHandler.java index 21042f00..82cfccb0 100644 --- a/src/com/android/packageinstaller/permission/ui/GrantPermissionsWatchViewHandler.java +++ b/src/com/android/packageinstaller/permission/ui/GrantPermissionsWatchViewHandler.java @@ -1,30 +1,51 @@ package com.android.packageinstaller.permission.ui; +import android.app.AlertDialog; +import android.app.Dialog; import android.content.Context; -import android.graphics.PixelFormat; +import android.content.DialogInterface; +import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; +import android.graphics.PixelFormat; +import android.graphics.PorterDuff; import android.os.Bundle; +import android.support.wearable.view.AcceptDenyDialog; +import android.support.wearable.view.WearableDialogHelper; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.style.ImageSpan; +import android.text.style.TextAppearanceSpan; +import android.text.TextUtils; import android.util.Log; +import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; +import android.widget.Space; import com.android.packageinstaller.R; -import com.android.packageinstaller.permission.ui.wear.ConfirmationViewHandler; /** * Watch-specific view handler for the grant permissions activity. */ -final class GrantPermissionsWatchViewHandler extends ConfirmationViewHandler - implements GrantPermissionsViewHandler { +final class GrantPermissionsWatchViewHandler implements GrantPermissionsViewHandler, + DialogInterface.OnClickListener { private static final String TAG = "GrantPermsWatchViewH"; - private static final String ARG_GROUP_NAME = "ARG_GROUP_NAME"; + private static final String WATCH_HANDLER_BUNDLE = "watch_handler_bundle"; + private static final String DIALOG_BUNDLE = "dialog_bundle"; + private static final String GROUP_NAME = "group_name"; + private static final String SHOW_DO_NOT_ASK = "show_do_not_ask"; + private static final String ICON = "icon"; + private static final String MESSAGE = "message"; + private static final String CURRENT_PAGE_TEXT = "current_page_text"; private final Context mContext; - + private ResultListener mResultListener; - + + private Dialog mDialog; + private String mGroupName; private boolean mShowDoNotAsk; @@ -33,7 +54,6 @@ final class GrantPermissionsWatchViewHandler extends ConfirmationViewHandler private Icon mIcon; GrantPermissionsWatchViewHandler(Context context) { - super(context); mContext = context; } @@ -45,13 +65,7 @@ final class GrantPermissionsWatchViewHandler extends ConfirmationViewHandler @Override public View createView() { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "createView()"); - } - - mShowDoNotAsk = false; - - return super.createView(); + return new Space(mContext); } @Override @@ -79,96 +93,117 @@ final class GrantPermissionsWatchViewHandler extends ConfirmationViewHandler mShowDoNotAsk = showDoNotAsk; mMessage = message; mIcon = icon; - mCurrentPageText = (groupCount > 1 ? - mContext.getString(R.string.current_permission_template, groupIndex + 1, groupCount) - : null); + mCurrentPageText = groupCount > 1 + ? mContext.getString(R.string.current_permission_template, + groupIndex + 1, groupCount) + : null; + showDialog(null); + } + + private void showDialog(Bundle savedInstanceState) { + TypedArray a = mContext.obtainStyledAttributes( + new int[] { android.R.attr.textColorPrimary }); + int color = a.getColor(0, mContext.getColor(android.R.color.white)); + a.recycle(); + Drawable drawable = mIcon == null ? null : mIcon.setTint(color).loadDrawable(mContext); + + SpannableStringBuilder ssb = new SpannableStringBuilder(); + if (!TextUtils.isEmpty(mCurrentPageText)) { + ssb.append(mCurrentPageText, new TextAppearanceSpan(mContext, R.style.BreadcrumbText), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + ssb.append('\n'); + } + if (!TextUtils.isEmpty(mMessage)) { + ssb.append(mMessage, new TextAppearanceSpan(mContext, R.style.TitleText), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } - invalidate(); + if (mDialog != null) { + mDialog.dismiss(); + mDialog = null; + } + + if (mShowDoNotAsk) { + AlertDialog alertDialog = new WearableDialogHelper.DialogBuilder(mContext) + .setPositiveIcon(R.drawable.confirm_button) + .setNeutralIcon(R.drawable.cancel_button) + .setNegativeIcon(R.drawable.deny_button) + .setTitle(ssb) + .setIcon(drawable) + .setPositiveButton(R.string.grant_dialog_button_allow, this) + .setNeutralButton(R.string.grant_dialog_button_deny, this) + .setNegativeButton(R.string.grant_dialog_button_deny_dont_ask_again, this) + .show(); + alertDialog.getButton(DialogInterface.BUTTON_POSITIVE) + .setId(R.id.permission_allow_button); + alertDialog.getButton(DialogInterface.BUTTON_NEUTRAL) + .setId(R.id.permission_deny_button); + alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE) + .setId(R.id.permission_deny_dont_ask_again_button); + + mDialog = alertDialog; + } else { + AcceptDenyDialog acceptDenyDialog = new AcceptDenyDialog(mContext); + acceptDenyDialog.setTitle(ssb); + acceptDenyDialog.setIcon(drawable); + acceptDenyDialog.setPositiveButton(this); + acceptDenyDialog.setNegativeButton(this); + acceptDenyDialog.show(); + acceptDenyDialog.getButton(DialogInterface.BUTTON_POSITIVE) + .setId(R.id.permission_allow_button); + acceptDenyDialog.getButton(DialogInterface.BUTTON_NEGATIVE) + .setId(R.id.permission_deny_button); + + mDialog = acceptDenyDialog; + } + mDialog.setCancelable(false); + + if (savedInstanceState != null) { + mDialog.onRestoreInstanceState(savedInstanceState); + } } @Override public void saveInstanceState(Bundle outState) { - outState.putString(ARG_GROUP_NAME, mGroupName); + Bundle b = new Bundle(); + b.putByte(SHOW_DO_NOT_ASK, (byte) (mShowDoNotAsk ? 1 : 0)); + b.putString(GROUP_NAME, mGroupName); + b.putBundle(DIALOG_BUNDLE, mDialog.onSaveInstanceState()); + + outState.putBundle(WATCH_HANDLER_BUNDLE, b); } @Override public void loadInstanceState(Bundle savedInstanceState) { - mGroupName = savedInstanceState.getString(ARG_GROUP_NAME); + Bundle b = savedInstanceState.getBundle(WATCH_HANDLER_BUNDLE); + mShowDoNotAsk = b.getByte(SHOW_DO_NOT_ASK) == 1; + mGroupName = b.getString(GROUP_NAME); + showDialog(b.getBundle(DIALOG_BUNDLE)); } @Override public void onBackPressed() { - if (mResultListener != null) { - mResultListener.onPermissionGrantResult(mGroupName, false, false); - } - } - - @Override // ConfirmationViewHandler - public void onButton1() { - onClick(true /* granted */, false /* doNotAskAgain */); - } - - @Override // ConfirmationViewHandler - public void onButton2() { - onClick(false /* granted */, false /* doNotAskAgain */); + notifyListener(false, false); } - @Override // ConfirmationViewHandler - public void onButton3() { - onClick(false /* granted */, true /* doNotAskAgain */); - } - - @Override // ConfirmationViewHandler - public CharSequence getCurrentPageText() { - return mCurrentPageText; - } - - @Override // ConfirmationViewHandler - public Icon getPermissionIcon() { - return mIcon; - } - - @Override // ConfirmationViewHandler - public CharSequence getMessage() { - return mMessage; - } - - @Override // ConfirmationViewHandler - public int getButtonBarMode() { - return mShowDoNotAsk ? MODE_VERTICAL_BUTTONS : MODE_HORIZONTAL_BUTTONS; - } - - @Override // ConfirmationViewHandler - public CharSequence getVerticalButton1Text() { - return mContext.getString(R.string.grant_dialog_button_allow); - } - - @Override // ConfirmationViewHandler - public CharSequence getVerticalButton2Text() { - return mContext.getString(R.string.grant_dialog_button_deny); - } - - @Override // ConfirmationViewHandler - public CharSequence getVerticalButton3Text() { - return mContext.getString(R.string.grant_dialog_button_deny_dont_ask_again); - } - - @Override // ConfirmationViewHandler - public Drawable getVerticalButton1Icon(){ - return mContext.getDrawable(R.drawable.confirm_button); - } - - @Override // ConfirmationViewHandler - public Drawable getVerticalButton2Icon(){ - return mContext.getDrawable(R.drawable.cancel_button); - } - - @Override // ConfirmationViewHandler - public Drawable getVerticalButton3Icon(){ - return mContext.getDrawable(R.drawable.deny_button); + @Override + public void onClick(DialogInterface dialog, int which) { + switch (which) { + case DialogInterface.BUTTON_POSITIVE: + notifyListener(true, false); + break; + case DialogInterface.BUTTON_NEUTRAL: + notifyListener(false, false); + break; + case DialogInterface.BUTTON_NEGATIVE: + notifyListener(false, + /* In AlertDialog, the negative button is also a don't ask again button. */ + dialog instanceof AlertDialog); + break; + } } - private void onClick(boolean granted, boolean doNotAskAgain) { + private void notifyListener(boolean granted, boolean doNotAskAgain) { if (mResultListener != null) { mResultListener.onPermissionGrantResult(mGroupName, granted, doNotAskAgain); } diff --git a/src/com/android/packageinstaller/permission/ui/television/AllAppPermissionsFragment.java b/src/com/android/packageinstaller/permission/ui/television/AllAppPermissionsFragment.java index 0f8cb5b1..d3f3e407 100644 --- a/src/com/android/packageinstaller/permission/ui/television/AllAppPermissionsFragment.java +++ b/src/com/android/packageinstaller/permission/ui/television/AllAppPermissionsFragment.java @@ -288,8 +288,10 @@ public final class AllAppPermissionsFragment extends SettingsWithHeader { return Utils.applyTint(context, icon, android.R.attr.colorControlNormal); } - private static boolean isMutableGranularPermission(String name) { - if (!Build.PERMISSIONS_REVIEW_REQUIRED) { + private boolean isMutableGranularPermission(String name) { + if (!getResources().getBoolean( + com.android.internal.R.bool.config_permissionReviewRequired) + && !Build.PERMISSIONS_REVIEW_REQUIRED) { return false; } switch (name) { diff --git a/src/com/android/packageinstaller/permission/ui/television/AppPermissionsFragment.java b/src/com/android/packageinstaller/permission/ui/television/AppPermissionsFragment.java index be7d159c..26467de9 100644 --- a/src/com/android/packageinstaller/permission/ui/television/AppPermissionsFragment.java +++ b/src/com/android/packageinstaller/permission/ui/television/AppPermissionsFragment.java @@ -33,7 +33,6 @@ import android.net.Uri; import android.os.Bundle; import android.provider.Settings; import android.support.v14.preference.SwitchPreference; -import android.support.v17.leanback.widget.VerticalGridView; import android.support.v7.preference.Preference; import android.support.v7.preference.Preference.OnPreferenceChangeListener; import android.support.v7.preference.Preference.OnPreferenceClickListener; @@ -48,7 +47,9 @@ import android.view.View; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; + import com.android.packageinstaller.R; + import com.android.packageinstaller.permission.model.AppPermissionGroup; import com.android.packageinstaller.permission.model.AppPermissions; import com.android.packageinstaller.permission.ui.ReviewPermissionsActivity; @@ -182,17 +183,6 @@ public final class AppPermissionsFragment extends SettingsWithHeader R.string.app_permissions_decor_title)); } - @Override - protected void updateHeader() { - - super.updateHeader(); - Preference headerLineTwo = getPreferenceScreen().findPreference(HEADER_PREFERENCE_KEY); - if (headerLineTwo != null) { - headerLineTwo.setTitle(mLabel); - headerLineTwo.setIcon(mIcon); - } - } - private void loadPreferences() { Context context = getPreferenceManager().getContext(); if (context == null) { @@ -201,25 +191,11 @@ public final class AppPermissionsFragment extends SettingsWithHeader PreferenceScreen screen = getPreferenceScreen(); screen.removeAll(); - - // Setting the second-line header that contains the app banner icon and its name. - // The styling used is the same as a leanback preference with a customized background color. - Preference headerLineTwo = new Preference(context) { - @Override - public void onBindViewHolder(PreferenceViewHolder holder) { - super.onBindViewHolder(holder); - holder.itemView.setBackgroundColor( - getResources().getColor(R.color.lb_header_banner_color)); - } - }; - headerLineTwo.setKey(HEADER_PREFERENCE_KEY); - headerLineTwo.setSelectable(false); - headerLineTwo.setTitle(mLabel); - headerLineTwo.setIcon(mIcon); - screen.addPreference(headerLineTwo); + screen.addPreference(createHeaderLineTwoPreference(context)); if (mExtraScreen != null) { mExtraScreen.removeAll(); + mExtraScreen = null; } final Preference extraPerms = new Preference(context); @@ -253,6 +229,7 @@ public final class AppPermissionsFragment extends SettingsWithHeader } else { if (mExtraScreen == null) { mExtraScreen = getPreferenceManager().createPreferenceScreen(context); + mExtraScreen.addPreference(createHeaderLineTwoPreference(context)); } mExtraScreen.addPreference(preference); } @@ -281,6 +258,30 @@ public final class AppPermissionsFragment extends SettingsWithHeader setLoading(false /* loading */, true /* animate */); } + /** + * Creates a heading below decor_title and above the rest of the preferences. This heading + * displays the app name and banner icon. It's used in both system and additional permissions + * fragments for each app. The styling used is the same as a leanback preference with a + * customized background color + * @param context The context the preferences created on + * @return The preference header to be inserted as the first preference in the list. + */ + private Preference createHeaderLineTwoPreference(Context context) { + Preference headerLineTwo = new Preference(context) { + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + holder.itemView.setBackgroundColor( + getResources().getColor(R.color.lb_header_banner_color)); + } + }; + headerLineTwo.setKey(HEADER_PREFERENCE_KEY); + headerLineTwo.setSelectable(false); + headerLineTwo.setTitle(mLabel); + headerLineTwo.setIcon(mIcon); + return headerLineTwo; + } + @Override public boolean onPreferenceChange(final Preference preference, Object newValue) { String groupName = preference.getKey(); @@ -390,8 +391,6 @@ public final class AppPermissionsFragment extends SettingsWithHeader public void onCreate(Bundle savedInstanceState) { mOuterFragment = (AppPermissionsFragment) getTargetFragment(); super.onCreate(savedInstanceState); - setHeader(mOuterFragment.mIcon, mOuterFragment.mLabel, mOuterFragment.mInfoIntent, - null); setHasOptionsMenu(true); } @@ -407,6 +406,22 @@ public final class AppPermissionsFragment extends SettingsWithHeader bindUi(this, getPackageInfo(getActivity(), packageName)); } + private static void bindUi(SettingsWithHeader fragment, PackageInfo packageInfo) { + Activity activity = fragment.getActivity(); + PackageManager pm = activity.getPackageManager(); + ApplicationInfo appInfo = packageInfo.applicationInfo; + Intent infoIntent = null; + if (!activity.getIntent().getBooleanExtra(EXTRA_HIDE_INFO_BUTTON, false)) { + infoIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) + .setData(Uri.fromParts("package", packageInfo.packageName, null)); + } + + Drawable icon = appInfo.loadIcon(pm); + CharSequence label = appInfo.loadLabel(pm); + fragment.setHeader(icon, label, infoIntent, fragment.getString( + R.string.additional_permissions_decor_title)); + } + @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { diff --git a/src/com/android/packageinstaller/permission/ui/television/PermissionAppsFragment.java b/src/com/android/packageinstaller/permission/ui/television/PermissionAppsFragment.java index ec320701..e6ad66da 100644 --- a/src/com/android/packageinstaller/permission/ui/television/PermissionAppsFragment.java +++ b/src/com/android/packageinstaller/permission/ui/television/PermissionAppsFragment.java @@ -421,6 +421,18 @@ public final class PermissionAppsFragment extends SettingsWithHeader implements bindUi(this, permissionApps); } + @Override + public void onResume() { + super.onResume(); + mOuterFragment.mPermissionApps.refresh(true); + } + + @Override + public void onDestroy() { + super.onDestroy(); + mOuterFragment.setOnPermissionsLoadedListener(null); + } + private static void bindUi(SettingsWithHeader fragment, PermissionApps permissionApps) { final CharSequence label = permissionApps.getLabel(); @@ -428,11 +440,9 @@ public final class PermissionAppsFragment extends SettingsWithHeader implements fragment.getString(R.string.system_apps_decor_title, label)); } - @Override public void onPermissionsLoaded(PermissionApps permissionApps) { setPreferenceScreen(); - mOuterFragment.setOnPermissionsLoadedListener(null); } private void setPreferenceScreen() { diff --git a/src/com/android/packageinstaller/permission/ui/wear/AppPermissionsFragmentWear.java b/src/com/android/packageinstaller/permission/ui/wear/AppPermissionsFragmentWear.java index db1c94d8..4ef27426 100644 --- a/src/com/android/packageinstaller/permission/ui/wear/AppPermissionsFragmentWear.java +++ b/src/com/android/packageinstaller/permission/ui/wear/AppPermissionsFragmentWear.java @@ -22,8 +22,14 @@ import android.app.Fragment; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PermissionInfo; import android.os.Bundle; -import android.support.wearable.view.WearableListView; +import android.os.UserHandle; +import android.preference.Preference; +import android.preference.PreferenceFragment; +import android.preference.PreferenceScreen; +import android.preference.SwitchPreference; +import android.support.wearable.view.WearableDialogHelper; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -33,25 +39,21 @@ import android.widget.Toast; import com.android.packageinstaller.R; import com.android.packageinstaller.permission.model.AppPermissionGroup; import com.android.packageinstaller.permission.model.AppPermissions; -import com.android.packageinstaller.permission.ui.wear.settings.PermissionsSettingsAdapter; -import com.android.packageinstaller.permission.ui.wear.settings.SettingsAdapter; +import com.android.packageinstaller.permission.model.Permission; import com.android.packageinstaller.permission.utils.LocationUtils; import com.android.packageinstaller.permission.utils.SafetyNetLogger; +import com.android.packageinstaller.permission.utils.ArrayUtils; import com.android.packageinstaller.permission.utils.Utils; +import com.android.settingslib.RestrictedLockUtils; +import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import java.util.ArrayList; import java.util.List; -public final class AppPermissionsFragmentWear extends TitledSettingsFragment { +public final class AppPermissionsFragmentWear extends PreferenceFragment { + private static final String LOG_TAG = "AppPermFragWear"; - private static final String LOG_TAG = "ManagePermsFragment"; - - private static final int WARNING_CONFIRMATION_REQUEST = 252; - private List<AppPermissionGroup> mToggledGroups; - private AppPermissions mAppPermissions; - private PermissionsSettingsAdapter mAdapter; - - private boolean mHasConfirmedRevoke; + private static final String KEY_NO_PERMISSIONS = "no_permissions"; public static AppPermissionsFragmentWear newInstance(String packageName) { return setPackageName(new AppPermissionsFragmentWear(), packageName); @@ -64,17 +66,53 @@ public final class AppPermissionsFragmentWear extends TitledSettingsFragment { return fragment; } + private PackageManager mPackageManager; + private List<AppPermissionGroup> mToggledGroups; + private AppPermissions mAppPermissions; + + private boolean mHasConfirmedRevoke; + + /** + * Provides click behavior for disabled preferences. + * We can't use {@link PreferenceFragment#onPreferenceTreeClick}, as the base + * {@link SwitchPreference} doesn't delegate to that method if the preference is disabled. + */ + private static class PermissionSwitchPreference extends SwitchPreference { + + private final Activity mActivity; + + public PermissionSwitchPreference(Activity activity) { + super(activity); + this.mActivity = activity; + } + + @Override + public void performClick(PreferenceScreen preferenceScreen) { + super.performClick(preferenceScreen); + if (!isEnabled()) { + // If setting the permission is disabled, it must have been locked + // by the device or profile owner. So get that info and pass it to + // the support details dialog. + EnforcedAdmin deviceOrProfileOwner = RestrictedLockUtils.getProfileOrDeviceOwner( + mActivity, UserHandle.myUserId()); + RestrictedLockUtils.sendShowAdminSupportDetailsIntent( + mActivity, deviceOrProfileOwner); + } + } + } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String packageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME); Activity activity = getActivity(); - PackageManager pm = activity.getPackageManager(); + mPackageManager = activity.getPackageManager(); + PackageInfo packageInfo; try { - packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS); + packageInfo = mPackageManager.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS); } catch (PackageManager.NameNotFoundException e) { Log.i(LOG_TAG, "No package:" + activity.getCallingPackage(), e); packageInfo = null; @@ -86,172 +124,180 @@ public final class AppPermissionsFragmentWear extends TitledSettingsFragment { return; } - mAppPermissions = new AppPermissions(activity, packageInfo, null, true, new Runnable() { - @Override - public void run() { - getActivity().finish(); - } - }); - - mAdapter = new PermissionsSettingsAdapter(getContext()); + mAppPermissions = new AppPermissions( + activity, packageInfo, null, true, () -> getActivity().finish()); + addPreferencesFromResource(R.xml.watch_permissions); initializePermissionGroupList(); } @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - return inflater.inflate(R.layout.settings, container, false); - } - - @Override public void onResume() { super.onResume(); mAppPermissions.refresh(); // Also refresh the UI - final int count = mAdapter.getItemCount(); - for (int i = 0; i < count; ++i) { - updatePermissionGroupSetting(i); + for (final AppPermissionGroup group : mAppPermissions.getPermissionGroups()) { + if (Utils.areGroupPermissionsIndividuallyControlled(getContext(), group.getName())) { + for (PermissionInfo perm : getPermissionInfosFromGroup(group)) { + setPreferenceCheckedIfPresent(perm.name, + group.areRuntimePermissionsGranted(new String[]{ perm.name })); + } + } else { + setPreferenceCheckedIfPresent(group.getName(), group.areRuntimePermissionsGranted()); + } } } @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - if (mAppPermissions != null) { - initializeLayout(mAdapter); - mHeader.setText(R.string.app_permissions); - mDetails.setText(R.string.no_permissions); - if (mAdapter.getItemCount() == 0) { - mDetails.setVisibility(View.VISIBLE); - mWheel.setVisibility(View.GONE); - } else { - mDetails.setVisibility(View.GONE); - mWheel.setVisibility(View.VISIBLE); - } - } + public void onPause() { + super.onPause(); + logAndClearToggledGroups(); } private void initializePermissionGroupList() { final String packageName = mAppPermissions.getPackageInfo().packageName; List<AppPermissionGroup> groups = mAppPermissions.getPermissionGroups(); - List<SettingsAdapter.Setting<AppPermissionGroup>> nonSystemGroups = new ArrayList<>(); + List<SwitchPreference> nonSystemPreferences = new ArrayList<>(); - final int count = groups.size(); - for (int i = 0; i < count; ++i) { - final AppPermissionGroup group = groups.get(i); + if (!groups.isEmpty()) { + getPreferenceScreen().removePreference(findPreference(KEY_NO_PERMISSIONS)); + } + + for (final AppPermissionGroup group : groups) { if (!Utils.shouldShowPermission(group, packageName)) { continue; } boolean isPlatform = group.getDeclaringPackage().equals(Utils.OS_PKG); - SettingsAdapter.Setting<AppPermissionGroup> setting = - new SettingsAdapter.Setting<AppPermissionGroup>( - group.getLabel(), - getPermissionGroupIcon(group), - i); - setting.data = group; - - // The UI shows System settings first, then non-system settings - if (isPlatform) { - mAdapter.addSetting(setting); + if (Utils.areGroupPermissionsIndividuallyControlled(getContext(), group.getName())) { + // If permission is controlled individually, we show all requested permission + // inside this group. + for (PermissionInfo perm : getPermissionInfosFromGroup(group)) { + final SwitchPreference pref = createSwitchPreferenceForPermission(group, perm); + showOrAddToNonSystemPreferences(pref, nonSystemPreferences, isPlatform); + } } else { - nonSystemGroups.add(setting); + final SwitchPreference pref = createSwitchPreferenceForGroup(group); + showOrAddToNonSystemPreferences(pref, nonSystemPreferences, isPlatform); } } // Now add the non-system settings to the end of the list - final int nonSystemCount = nonSystemGroups.size(); - for (int i = 0; i < nonSystemCount; ++i) { - final SettingsAdapter.Setting<AppPermissionGroup> setting = nonSystemGroups.get(i); - mAdapter.addSetting(setting); + for (SwitchPreference nonSystemPreference : nonSystemPreferences) { + getPreferenceScreen().addPreference(nonSystemPreference); } } - @Override - public void onPause() { - super.onPause(); - logAndClearToggledGroups(); + private void showOrAddToNonSystemPreferences(SwitchPreference pref, + List<SwitchPreference> nonSystemPreferences, // Mutate + boolean isPlatform) { + // The UI shows System settings first, then non-system settings + if (isPlatform) { + getPreferenceScreen().addPreference(pref); + } else { + nonSystemPreferences.add(pref); + } } - @Override - public void onClick(WearableListView.ViewHolder view) { - final int index = view.getPosition(); - SettingsAdapter.Setting<AppPermissionGroup> setting = mAdapter.get(index); - final AppPermissionGroup group = setting.data; + private SwitchPreference createSwitchPreferenceForPermission(AppPermissionGroup group, + PermissionInfo perm) { + final SwitchPreference pref = new PermissionSwitchPreference(getActivity()); + pref.setKey(perm.name); + pref.setTitle(perm.loadLabel(mPackageManager)); + pref.setChecked(group.areRuntimePermissionsGranted(new String[]{ perm.name })); + pref.setOnPreferenceChangeListener((p, newVal) -> { + if((Boolean) newVal) { + group.grantRuntimePermissions(false, new String[]{ perm.name }); + } else { + group.revokeRuntimePermissions(true, new String[]{ perm.name }); + } - if (group == null) { - Log.e(LOG_TAG, "Error: AppPermissionGroup is null"); - return; - } + if (Utils.areGroupPermissionsIndividuallyControlled(getContext(), group.getName()) + && group.hasRuntimePermission()) { + // As long as one permission is changed in individually controlled group + // permissions, we will set user_fixed for non-granted permissions in that group. + // This avoids the system to automatically grant runtime permissions based on the + // fact that one of dangerous permission in that group is already granted. + String[] revokedPermissionsToFix = null; + final int permissionCount = group.getPermissions().size(); + + for (int i = 0; i < permissionCount; i++) { + Permission current = group.getPermissions().get(i); + if (!current.isGranted() && !current.isUserFixed()) { + revokedPermissionsToFix = ArrayUtils.appendString( + revokedPermissionsToFix, current.getName()); + } + } - // The way WearableListView is designed, there is no way to avoid this click handler - // Since the policy is fixed, ignore the click as the user is not able to change the state - // of this permission group - if (group.isPolicyFixed()) { - return; - } + if (revokedPermissionsToFix != null) { + group.revokeRuntimePermissions(true, revokedPermissionsToFix); + } + } + return true; + }); + return pref; + } - addToggledGroup(group); + private SwitchPreference createSwitchPreferenceForGroup(AppPermissionGroup group) { + final SwitchPreference pref = new PermissionSwitchPreference(getActivity()); - if (LocationUtils.isLocationGroupAndProvider(group.getName(), group.getApp().packageName)) { - LocationUtils.showLocationDialog(getContext(), mAppPermissions.getAppLabel()); - return; - } + pref.setKey(group.getName()); + pref.setTitle(group.getLabel()); + pref.setChecked(group.areRuntimePermissionsGranted()); - if (!group.areRuntimePermissionsGranted()) { - group.grantRuntimePermissions(false); + if (group.isPolicyFixed()) { + pref.setEnabled(false); } else { - final boolean grantedByDefault = group.hasGrantedByDefaultPermission(); - if (grantedByDefault || (!group.hasRuntimePermission() && !mHasConfirmedRevoke)) { - Intent intent = new Intent(getActivity(), WarningConfirmationActivity.class); - intent.putExtra(WarningConfirmationActivity.EXTRA_WARNING_MESSAGE, - getString(grantedByDefault ? - R.string.system_warning : R.string.old_sdk_deny_warning)); - intent.putExtra(WarningConfirmationActivity.EXTRA_INDEX, index); - startActivityForResult(intent, WARNING_CONFIRMATION_REQUEST); - } else { - group.revokeRuntimePermissions(false); - } - } - - updatePermissionGroupSetting(index); - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == WARNING_CONFIRMATION_REQUEST) { - if (resultCode == Activity.RESULT_OK) { - int index = data.getIntExtra(WarningConfirmationActivity.EXTRA_INDEX, -1); - if (index == -1) { - Log.e(LOG_TAG, "Warning confirmation request came back with no index."); - return; + pref.setOnPreferenceChangeListener((p, newVal) -> { + if (LocationUtils.isLocationGroupAndProvider( + group.getName(), group.getApp().packageName)) { + LocationUtils.showLocationDialog( + getContext(), mAppPermissions.getAppLabel()); + return false; } - SettingsAdapter.Setting<AppPermissionGroup> setting = mAdapter.get(index); - final AppPermissionGroup group = setting.data; - group.revokeRuntimePermissions(false); - if (!group.hasGrantedByDefaultPermission()) { - mHasConfirmedRevoke = true; + if ((Boolean) newVal) { + setPermission(group, pref, true); + } else { + final boolean grantedByDefault = group.hasGrantedByDefaultPermission(); + if (grantedByDefault + || (!group.hasRuntimePermission() && !mHasConfirmedRevoke)) { + new WearableDialogHelper.DialogBuilder(getContext()) + .setNegativeIcon(R.drawable.confirm_button) + .setPositiveIcon(R.drawable.cancel_button) + .setNegativeButton(R.string.grant_dialog_button_deny_anyway, + (dialog, which) -> { + setPermission(group, pref, false); + if (!group.hasGrantedByDefaultPermission()) { + mHasConfirmedRevoke = true; + } + }) + .setPositiveButton(R.string.cancel, (dialog, which) -> {}) + .setMessage(grantedByDefault ? + R.string.system_warning : R.string.old_sdk_deny_warning) + .show(); + return false; + } else { + setPermission(group, pref, false); + } } - updatePermissionGroupSetting(index); - } - } else { - super.onActivityResult(requestCode, resultCode, data); + return true; + }); } + return pref; } - private void updatePermissionGroupSetting(int index) { - SettingsAdapter.Setting<AppPermissionGroup> setting = mAdapter.get(index); - AppPermissionGroup group = setting.data; - mAdapter.updateSetting( - index, - group.getLabel(), - getPermissionGroupIcon(group), - group); + private void setPermission(AppPermissionGroup group, SwitchPreference pref, boolean grant) { + if (grant) { + group.grantRuntimePermissions(false); + } else { + group.revokeRuntimePermissions(false); + } + addToggledGroup(group); + pref.setChecked(grant); } private void addToggledGroup(AppPermissionGroup group) { @@ -274,53 +320,22 @@ public final class AppPermissionsFragmentWear extends TitledSettingsFragment { } } - private int getPermissionGroupIcon(AppPermissionGroup group) { - String groupName = group.getName(); - boolean isEnabled = group.areRuntimePermissionsGranted(); - int resId; - - switch (groupName) { - case Manifest.permission_group.CALENDAR: - resId = isEnabled ? R.drawable.ic_permission_calendar - : R.drawable.ic_permission_calendardisable; - break; - case Manifest.permission_group.CAMERA: - resId = isEnabled ? R.drawable.ic_permission_camera - : R.drawable.ic_permission_cameradisable; - break; - case Manifest.permission_group.CONTACTS: - resId = isEnabled ? R.drawable.ic_permission_contact - : R.drawable.ic_permission_contactdisable; - break; - case Manifest.permission_group.LOCATION: - resId = isEnabled ? R.drawable.ic_permission_location - : R.drawable.ic_permission_locationdisable; - break; - case Manifest.permission_group.MICROPHONE: - resId = isEnabled ? R.drawable.ic_permission_mic - : R.drawable.ic_permission_micdisable; - break; - case Manifest.permission_group.PHONE: - resId = isEnabled ? R.drawable.ic_permission_call - : R.drawable.ic_permission_calldisable; - break; - case Manifest.permission_group.SENSORS: - resId = isEnabled ? R.drawable.ic_permission_sensor - : R.drawable.ic_permission_sensordisable; - break; - case Manifest.permission_group.SMS: - resId = isEnabled ? R.drawable.ic_permission_sms - : R.drawable.ic_permission_smsdisable; - break; - case Manifest.permission_group.STORAGE: - resId = isEnabled ? R.drawable.ic_permission_storage - : R.drawable.ic_permission_storagedisable; - break; - default: - resId = isEnabled ? R.drawable.ic_permission_shield - : R.drawable.ic_permission_shielddisable; + private List<PermissionInfo> getPermissionInfosFromGroup(AppPermissionGroup group) { + ArrayList<PermissionInfo> permInfos = new ArrayList<>(group.getPermissions().size()); + for(Permission perm : group.getPermissions()) { + try { + permInfos.add(mPackageManager.getPermissionInfo(perm.getName(), 0)); + } catch (PackageManager.NameNotFoundException e) { + Log.w(LOG_TAG, "No permission:" + perm.getName()); + } } + return permInfos; + } - return resId; + private void setPreferenceCheckedIfPresent(String preferenceKey, boolean checked) { + Preference pref = findPreference(preferenceKey); + if (pref instanceof SwitchPreference) { + ((SwitchPreference) pref).setChecked(checked); + } } } diff --git a/src/com/android/packageinstaller/permission/ui/wear/ConfirmationViewHandler.java b/src/com/android/packageinstaller/permission/ui/wear/ConfirmationViewHandler.java deleted file mode 100644 index 1c55e1bd..00000000 --- a/src/com/android/packageinstaller/permission/ui/wear/ConfirmationViewHandler.java +++ /dev/null @@ -1,381 +0,0 @@ -package com.android.packageinstaller.permission.ui.wear; - -import android.animation.ObjectAnimator; -import android.animation.PropertyValuesHolder; -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.Icon; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.text.TextUtils; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewTreeObserver; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; -import android.widget.Button; -import android.widget.ImageView; -import android.widget.ScrollView; -import android.widget.TextView; - -import com.android.packageinstaller.R; - -public abstract class ConfirmationViewHandler implements - Handler.Callback, - View.OnClickListener, - ViewTreeObserver.OnScrollChangedListener, - ViewTreeObserver.OnGlobalLayoutListener { - private static final String TAG = "ConfirmationViewHandler"; - - public static final int MODE_HORIZONTAL_BUTTONS = 0; - public static final int MODE_VERTICAL_BUTTONS = 1; - - private static final int MSG_SHOW_BUTTON_BAR = 1001; - private static final int MSG_HIDE_BUTTON_BAR = 1002; - private static final long HIDE_ANIM_DURATION = 500; - - private View mRoot; - private TextView mCurrentPageText; - private ImageView mIcon; - private TextView mMessage; - private ScrollView mScrollingContainer; - private ViewGroup mContent; - private ViewGroup mHorizontalButtonBar; - private ViewGroup mVerticalButtonBar; - private Button mVerticalButton1; - private Button mVerticalButton2; - private Button mVerticalButton3; - private View mButtonBarContainer; - - private Context mContext; - - private Handler mHideHandler; - private Interpolator mInterpolator; - private float mButtonBarFloatingHeight; - private ObjectAnimator mButtonBarAnimator; - private float mCurrentTranslation; - private boolean mHiddenBefore; - - // TODO: Move these into a builder - /** In the 2 button layout, this is allow button */ - public abstract void onButton1(); - /** In the 2 button layout, this is deny button */ - public abstract void onButton2(); - public abstract void onButton3(); - public abstract CharSequence getVerticalButton1Text(); - public abstract CharSequence getVerticalButton2Text(); - public abstract CharSequence getVerticalButton3Text(); - public abstract Drawable getVerticalButton1Icon(); - public abstract Drawable getVerticalButton2Icon(); - public abstract Drawable getVerticalButton3Icon(); - public abstract CharSequence getCurrentPageText(); - public abstract Icon getPermissionIcon(); - public abstract CharSequence getMessage(); - - public ConfirmationViewHandler(Context context) { - mContext = context; - } - - public View createView() { - mRoot = LayoutInflater.from(mContext).inflate(R.layout.confirmation_dialog, null); - - mMessage = (TextView) mRoot.findViewById(R.id.message); - mCurrentPageText = (TextView) mRoot.findViewById(R.id.current_page_text); - mIcon = (ImageView) mRoot.findViewById(R.id.icon); - mButtonBarContainer = mRoot.findViewById(R.id.button_bar_container); - mContent = (ViewGroup) mRoot.findViewById(R.id.content); - mScrollingContainer = (ScrollView) mRoot.findViewById(R.id.scrolling_container); - mHorizontalButtonBar = (ViewGroup) mRoot.findViewById(R.id.horizontal_button_bar); - mVerticalButtonBar = (ViewGroup) mRoot.findViewById(R.id.vertical_button_bar); - - Button horizontalAllow = (Button) mRoot.findViewById(R.id.permission_allow_button); - Button horizontalDeny = (Button) mRoot.findViewById(R.id.permission_deny_button); - horizontalAllow.setOnClickListener(this); - horizontalDeny.setOnClickListener(this); - - mVerticalButton1 = (Button) mRoot.findViewById(R.id.vertical_button1); - mVerticalButton2 = (Button) mRoot.findViewById(R.id.vertical_button2); - mVerticalButton3 = (Button) mRoot.findViewById(R.id.vertical_button3); - mVerticalButton1.setOnClickListener(this); - mVerticalButton2.setOnClickListener(this); - mVerticalButton3.setOnClickListener(this); - - mInterpolator = AnimationUtils.loadInterpolator(mContext, - android.R.interpolator.fast_out_slow_in); - mButtonBarFloatingHeight = mContext.getResources().getDimension( - R.dimen.conf_diag_floating_height); - mHideHandler = new Handler(Looper.getMainLooper(), this); - - mScrollingContainer.getViewTreeObserver().addOnScrollChangedListener(this); - mRoot.getViewTreeObserver().addOnGlobalLayoutListener(this); - - return mRoot; - } - - /** - * Child class should override this for other modes. Call invalidate() to update the UI to the - * new button mode. - * @return The current mode the layout should use for the buttons - */ - public int getButtonBarMode() { - return MODE_HORIZONTAL_BUTTONS; - } - - public void invalidate() { - CharSequence currentPageText = getCurrentPageText(); - if (!TextUtils.isEmpty(currentPageText)) { - mCurrentPageText.setText(currentPageText); - mCurrentPageText.setVisibility(View.VISIBLE); - } else { - mCurrentPageText.setVisibility(View.GONE); - } - - Icon icon = getPermissionIcon(); - if (icon != null) { - mIcon.setImageIcon(icon); - mIcon.setVisibility(View.VISIBLE); - } else { - mIcon.setVisibility(View.GONE); - } - mMessage.setText(getMessage()); - - switch (getButtonBarMode()) { - case MODE_HORIZONTAL_BUTTONS: - mHorizontalButtonBar.setVisibility(View.VISIBLE); - mVerticalButtonBar.setVisibility(View.GONE); - break; - case MODE_VERTICAL_BUTTONS: - mHorizontalButtonBar.setVisibility(View.GONE); - mVerticalButtonBar.setVisibility(View.VISIBLE); - - mVerticalButton1.setText(getVerticalButton1Text()); - mVerticalButton2.setText(getVerticalButton2Text()); - - mVerticalButton1.setCompoundDrawablesWithIntrinsicBounds( - getVerticalButton1Icon(), null, null, null); - mVerticalButton2.setCompoundDrawablesWithIntrinsicBounds( - getVerticalButton2Icon(), null, null, null); - - CharSequence verticalButton3Text = getVerticalButton3Text(); - if (TextUtils.isEmpty(verticalButton3Text)) { - mVerticalButton3.setVisibility(View.GONE); - } else { - mVerticalButton3.setText(getVerticalButton3Text()); - mVerticalButton3.setCompoundDrawablesWithIntrinsicBounds( - getVerticalButton3Icon(), null, null, null); - } - break; - } - - mScrollingContainer.scrollTo(0, 0); - - mHideHandler.removeMessages(MSG_HIDE_BUTTON_BAR); - mHideHandler.removeMessages(MSG_SHOW_BUTTON_BAR); - } - - @Override - public void onGlobalLayout() { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "onGlobalLayout"); - Log.d(TAG, " contentHeight: " + mContent.getHeight()); - } - - if (mButtonBarAnimator != null) { - mButtonBarAnimator.cancel(); - } - - // In order to fake the buttons peeking at the bottom, need to do set the - // padding properly. - if (mContent.getPaddingBottom() != mButtonBarContainer.getHeight()) { - mContent.setPadding(mContent.getPaddingLeft(), mContent.getPaddingTop(), - mContent.getPaddingRight(), mButtonBarContainer.getHeight()); - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, " set mContent.PaddingBottom: " + mButtonBarContainer.getHeight()); - } - } - - mButtonBarContainer.setTranslationY(mButtonBarContainer.getHeight()); - - // Give everything a chance to render - mHideHandler.removeMessages(MSG_HIDE_BUTTON_BAR); - mHideHandler.removeMessages(MSG_SHOW_BUTTON_BAR); - mHideHandler.sendEmptyMessageDelayed(MSG_SHOW_BUTTON_BAR, 50); - } - - @Override - public void onClick(View v) { - int id = v.getId(); - switch (id) { - case R.id.permission_allow_button: - case R.id.vertical_button1: - onButton1(); - break; - case R.id.permission_deny_button: - case R.id.vertical_button2: - onButton2(); - break; - case R.id.vertical_button3: - onButton3(); - break; - } - } - - @Override - public boolean handleMessage (Message msg) { - switch (msg.what) { - case MSG_SHOW_BUTTON_BAR: - showButtonBar(); - return true; - case MSG_HIDE_BUTTON_BAR: - hideButtonBar(); - return true; - } - return false; - } - - @Override - public void onScrollChanged () { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "onScrollChanged"); - } - mHideHandler.removeMessages(MSG_HIDE_BUTTON_BAR); - hideButtonBar(); - } - - private void showButtonBar() { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "showButtonBar"); - } - - // Setup Button animation. - // pop the button bar back to full height, stop all animation - if (mButtonBarAnimator != null) { - mButtonBarAnimator.cancel(); - } - - // stop any calls to hide the button bar in the future - mHideHandler.removeMessages(MSG_HIDE_BUTTON_BAR); - mHiddenBefore = false; - - // Evaluate the max height the button bar can go - final int screenHeight = mRoot.getHeight(); - final int halfScreenHeight = screenHeight / 2; - final int buttonBarHeight = mButtonBarContainer.getHeight(); - final int contentHeight = mContent.getHeight() - buttonBarHeight; - final int buttonBarMaxHeight = - Math.min(buttonBarHeight, halfScreenHeight); - - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, " screenHeight: " + screenHeight); - Log.d(TAG, " contentHeight: " + contentHeight); - Log.d(TAG, " buttonBarHeight: " + buttonBarHeight); - Log.d(TAG, " buttonBarMaxHeight: " + buttonBarMaxHeight); - } - - mButtonBarContainer.setTranslationZ(mButtonBarFloatingHeight); - - // Only hide the button bar if it is occluding the content or the button bar is bigger than - // half the screen - if (contentHeight > (screenHeight - buttonBarHeight) - || buttonBarHeight > halfScreenHeight) { - mHideHandler.sendEmptyMessageDelayed(MSG_HIDE_BUTTON_BAR, 3000); - } - - generateButtonBarAnimator(buttonBarHeight, - buttonBarHeight - buttonBarMaxHeight, 0, mButtonBarFloatingHeight, 1000); - } - - private void hideButtonBar() { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "hideButtonBar"); - } - - // The desired margin space between the button bar and the bottom of the dialog text - final int topMargin = mContext.getResources().getDimensionPixelSize( - R.dimen.conf_diag_button_container_top_margin); - final int contentHeight = mContent.getHeight() + topMargin; - final int screenHeight = mRoot.getHeight(); - final int buttonBarHeight = mButtonBarContainer.getHeight(); - - final int offset = screenHeight + buttonBarHeight - - contentHeight + Math.max(mScrollingContainer.getScrollY(), 0); - final int translationY = (offset > 0 ? - mButtonBarContainer.getHeight() - offset : mButtonBarContainer.getHeight()); - - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, " topMargin: " + topMargin); - Log.d(TAG, " contentHeight: " + contentHeight); - Log.d(TAG, " screenHeight: " + screenHeight); - Log.d(TAG, " offset: " + offset); - Log.d(TAG, " buttonBarHeight: " + buttonBarHeight); - Log.d(TAG, " mContent.getPaddingBottom(): " + mContent.getPaddingBottom()); - Log.d(TAG, " mScrollingContainer.getScrollY(): " + mScrollingContainer.getScrollY()); - Log.d(TAG, " translationY: " + translationY); - } - - if (!mHiddenBefore || mButtonBarAnimator == null) { - // Remove previous call to MSG_SHOW_BUTTON_BAR if the user scrolled or something before - // the animation got a chance to play - mHideHandler.removeMessages(MSG_SHOW_BUTTON_BAR); - - if(mButtonBarAnimator != null) { - mButtonBarAnimator.cancel(); // stop current animation if there is one playing - } - - // hasn't hidden the bar yet, just hide now to the right height - generateButtonBarAnimator( - mButtonBarContainer.getTranslationY(), translationY, - mButtonBarFloatingHeight, 0, HIDE_ANIM_DURATION); - } else if (mButtonBarAnimator.isRunning()) { - // we are animating the button bar closing, change to animate to the right place - if (Math.abs(mCurrentTranslation - translationY) > 1e-2f) { - mButtonBarAnimator.cancel(); // stop current animation - - if (Math.abs(mButtonBarContainer.getTranslationY() - translationY) > 1e-2f) { - long duration = Math.max((long) ( - (float) HIDE_ANIM_DURATION - * (translationY - mButtonBarContainer.getTranslationY()) - / mButtonBarContainer.getHeight()), 0); - - generateButtonBarAnimator( - mButtonBarContainer.getTranslationY(), translationY, - mButtonBarFloatingHeight, 0, duration); - } else { - mButtonBarContainer.setTranslationY(translationY); - mButtonBarContainer.setTranslationZ(0); - } - } - } else { - // not currently animating, have already hidden, snap to the right offset - mButtonBarContainer.setTranslationY(translationY); - mButtonBarContainer.setTranslationZ(0); - } - - mHiddenBefore = true; - } - - private void generateButtonBarAnimator( - float startY, float endY, float startZ, float endZ, long duration) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "generateButtonBarAnimator"); - Log.d(TAG, " startY: " + startY); - Log.d(TAG, " endY: " + endY); - Log.d(TAG, " startZ: " + startZ); - Log.d(TAG, " endZ: " + endZ); - Log.d(TAG, " duration: " + duration); - } - - mButtonBarAnimator = - ObjectAnimator.ofPropertyValuesHolder( - mButtonBarContainer, - PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, startY, endY), - PropertyValuesHolder.ofFloat(View.TRANSLATION_Z, startZ, endZ)); - mCurrentTranslation = endY; - mButtonBarAnimator.setDuration(duration); - mButtonBarAnimator.setInterpolator(mInterpolator); - mButtonBarAnimator.start(); - } -} diff --git a/src/com/android/packageinstaller/permission/ui/wear/TitledSettingsFragment.java b/src/com/android/packageinstaller/permission/ui/wear/TitledSettingsFragment.java deleted file mode 100644 index b673a498..00000000 --- a/src/com/android/packageinstaller/permission/ui/wear/TitledSettingsFragment.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * 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.packageinstaller.permission.ui.wear; - -import android.app.Fragment; -import android.content.Context; -import android.os.Bundle; -import android.support.v7.widget.RecyclerView; -import android.support.wearable.view.WearableListView; -import android.text.Editable; -import android.text.TextWatcher; -import android.util.TypedValue; -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowInsets; -import android.widget.LinearLayout; -import android.widget.TextView; - -import com.android.packageinstaller.permission.ui.wear.settings.ViewUtils; -import com.android.packageinstaller.R; - -/** - * Base settings Fragment that shows a title at the top of the page. - */ -public abstract class TitledSettingsFragment extends Fragment implements - View.OnLayoutChangeListener, WearableListView.ClickListener { - - private static final int ITEM_CHANGE_DURATION_MS = 120; - - private static final String TAG = "TitledSettingsFragment"; - private int mInitialHeaderHeight; - - protected TextView mHeader; - protected TextView mDetails; - protected WearableListView mWheel; - - private int mCharLimitShortTitle; - private int mCharLimitLine; - private int mChinOffset; - - private TextWatcher mHeaderTextWatcher = new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) {} - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) {} - - @Override - public void afterTextChanged(Editable editable) { - adjustHeaderSize(); - } - }; - - private void adjustHeaderTranslation() { - int translation = 0; - if (mWheel.getChildCount() > 0) { - translation = mWheel.getCentralViewTop() - mWheel.getChildAt(0).getTop(); - } - - float newTranslation = Math.min(Math.max(-mInitialHeaderHeight, -translation), 0); - - int position = mWheel.getChildAdapterPosition(mWheel.getChildAt(0)); - if (position == 0 || newTranslation < 0) { - mHeader.setTranslationY(newTranslation); - } - } - - @Override - public void onTopEmptyRegionClick() { - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mCharLimitShortTitle = getResources().getInteger(R.integer.short_title_length); - mCharLimitLine = getResources().getInteger(R.integer.char_limit_per_line); - } - - @Override - public void onLayoutChange(View view, int left, int top, int right, int bottom, int oldLeft, - int oldTop, int oldRight, int oldBottom) { - if (view == mHeader) { - mInitialHeaderHeight = bottom - top; - if (ViewUtils.getIsCircular(getContext())) { - // We are adding more margin on circular screens, so we need to account for it and use - // it for hiding the header. - mInitialHeaderHeight += - ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).topMargin; - } - } else if (view == mWheel) { - adjustHeaderTranslation(); - } - } - - protected void initializeLayout(RecyclerView.Adapter adapter) { - View v = getView(); - mWheel = (WearableListView) v.findViewById(R.id.wheel); - - mHeader = (TextView) v.findViewById(R.id.header); - mHeader.addOnLayoutChangeListener(this); - mHeader.addTextChangedListener(mHeaderTextWatcher); - - mDetails = (TextView) v.findViewById(R.id.details); - mDetails.addOnLayoutChangeListener(this); - - mWheel.setAdapter(adapter); - mWheel.addOnScrollListener(new RecyclerView.OnScrollListener() { - @Override - public void onScrollStateChanged(RecyclerView recyclerView, int newState) { - } - - @Override - public void onScrolled(RecyclerView recyclerView, int dx, int dy) { - adjustHeaderTranslation(); - } - }); - mWheel.setClickListener(this); - mWheel.addOnLayoutChangeListener(this); - - // Decrease item change animation duration to approximately half of the default duration. - RecyclerView.ItemAnimator itemAnimator = mWheel.getItemAnimator(); - itemAnimator.setChangeDuration(ITEM_CHANGE_DURATION_MS); - - adjustHeaderSize(); - - positionOnCircular(getContext(), mHeader, mDetails, mWheel); - } - - public void positionOnCircular(Context context, View header, View details, - final ViewGroup wheel) { - if (ViewUtils.getIsCircular(context)) { - LinearLayout.LayoutParams params = - (LinearLayout.LayoutParams) header.getLayoutParams(); - params.topMargin = (int) context.getResources().getDimension( - R.dimen.settings_header_top_margin_circular); - // Note that the margins are made symmetrical here. Since they're symmetrical we choose - // the smaller value to maximize usable width. - final int margin = (int) Math.min(context.getResources().getDimension( - R.dimen.round_content_padding_left), context.getResources().getDimension( - R.dimen.round_content_padding_right)); - params.leftMargin = margin; - params.rightMargin = margin; - params.gravity = Gravity.CENTER_HORIZONTAL; - header.setLayoutParams(params); - details.setLayoutParams(params); - - if (header instanceof TextView) { - ((TextView) header).setGravity(Gravity.CENTER); - } - if (details instanceof TextView) { - ((TextView) details).setGravity(Gravity.CENTER); - } - - final int leftPadding = (int) context.getResources().getDimension( - R.dimen.round_content_padding_left); - final int rightPadding = (int) context.getResources().getDimension( - R.dimen.round_content_padding_right); - final int topPadding = (int) context.getResources().getDimension( - R.dimen.settings_wearable_list_view_vertical_padding_round); - final int bottomPadding = (int) context.getResources().getDimension( - R.dimen.settings_wearable_list_view_vertical_padding_round); - wheel.setPadding(leftPadding, topPadding, rightPadding, mChinOffset + bottomPadding); - wheel.setClipToPadding(false); - - wheel.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { - @Override - public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { - mChinOffset = insets.getSystemWindowInsetBottom(); - wheel.setPadding(leftPadding, topPadding, rightPadding, - mChinOffset + bottomPadding); - // This listener is invoked after each time we navigate to SettingsActivity and - // it keeps adding padding. We need to disable it after the first update. - v.setOnApplyWindowInsetsListener(null); - return insets.consumeSystemWindowInsets(); - } - }); - } else { - int leftPadding = (int) context.getResources().getDimension( - R.dimen.content_padding_left); - wheel.setPadding(leftPadding, wheel.getPaddingTop(), wheel.getPaddingRight(), - wheel.getPaddingBottom()); - } - } - - private void adjustHeaderSize() { - int length = mHeader.length(); - - if (length <= mCharLimitShortTitle) { - mHeader.setTextSize(TypedValue.COMPLEX_UNIT_PX, - getResources().getDimensionPixelSize( - R.dimen.setting_short_header_text_size)); - } else { - mHeader.setTextSize(TypedValue.COMPLEX_UNIT_PX, - getResources().getDimensionPixelSize( - R.dimen.setting_long_header_text_size)); - } - - boolean singleLine = length <= mCharLimitLine; - - float height = getResources().getDimension(R.dimen.settings_header_base_height); - if (!singleLine) { - height += getResources().getDimension(R.dimen.setting_header_extra_line_height); - } - mHeader.setMinHeight((int) height); - - LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) mHeader.getLayoutParams(); - final Context context = getContext(); - if (!singleLine) { - // Make the top margin a little bit smaller so there is more space for the title. - if (ViewUtils.getIsCircular(context)) { - params.topMargin = getResources().getDimensionPixelSize( - R.dimen.settings_header_top_margin_circular_multiline); - } else { - params.topMargin = getResources().getDimensionPixelSize( - R.dimen.settings_header_top_margin_multiline); - } - } else { - if (ViewUtils.getIsCircular(context)) { - params.topMargin = getResources().getDimensionPixelSize( - R.dimen.settings_header_top_margin_circular); - } else { - params.topMargin = getResources().getDimensionPixelSize( - R.dimen.settings_header_top_margin); - } - } - mHeader.setLayoutParams(params); - } -} diff --git a/src/com/android/packageinstaller/permission/ui/wear/WarningConfirmationActivity.java b/src/com/android/packageinstaller/permission/ui/wear/WarningConfirmationActivity.java deleted file mode 100644 index 0800c14c..00000000 --- a/src/com/android/packageinstaller/permission/ui/wear/WarningConfirmationActivity.java +++ /dev/null @@ -1,118 +0,0 @@ -/* -* 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.packageinstaller.permission.ui.wear; - -import android.app.Activity; -import android.content.Intent; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.Icon; -import android.os.Bundle; - -import com.android.packageinstaller.R; - -public final class WarningConfirmationActivity extends Activity { - public final static String EXTRA_WARNING_MESSAGE = "EXTRA_WARNING_MESSAGE"; - // Saved index that will be returned in the onActivityResult() callback - public final static String EXTRA_INDEX = "EXTRA_INDEX"; - - private ConfirmationViewHandler mViewHandler; - private String mMessage; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mMessage = getIntent().getStringExtra(EXTRA_WARNING_MESSAGE); - - mViewHandler = new ConfirmationViewHandler(this) { - @Override // ConfirmationViewHandler - public int getButtonBarMode() { - return MODE_VERTICAL_BUTTONS; - } - - @Override - public void onButton1() { - setResultAndFinish(Activity.RESULT_CANCELED); - } - - @Override - public void onButton2() { - setResultAndFinish(Activity.RESULT_OK); - } - - @Override - public void onButton3() { - // no-op - } - - @Override - public CharSequence getVerticalButton1Text() { - return getString(R.string.cancel); - } - - @Override - public CharSequence getVerticalButton2Text() { - return getString(R.string.grant_dialog_button_deny_anyway); - } - - @Override - public CharSequence getVerticalButton3Text() { - return null; - } - - @Override - public Drawable getVerticalButton1Icon() { - return getDrawable(R.drawable.cancel_button); - } - - @Override - public Drawable getVerticalButton2Icon() { - return getDrawable(R.drawable.confirm_button); - } - - @Override - public Drawable getVerticalButton3Icon() { - return null; - } - - @Override - public CharSequence getCurrentPageText() { - return null; - } - - @Override - public Icon getPermissionIcon() { - return null; - } - - @Override - public CharSequence getMessage() { - return mMessage; - } - }; - - setContentView(mViewHandler.createView()); - mViewHandler.invalidate(); - } - - private void setResultAndFinish(int result) { - Intent intent = new Intent(); - intent.putExtra(EXTRA_INDEX, getIntent().getIntExtra(EXTRA_INDEX, -1)); - setResult(result, intent); - finish(); - } -} diff --git a/src/com/android/packageinstaller/permission/ui/wear/settings/ExtendedOnCenterProximityListener.java b/src/com/android/packageinstaller/permission/ui/wear/settings/ExtendedOnCenterProximityListener.java deleted file mode 100644 index 02c203b3..00000000 --- a/src/com/android/packageinstaller/permission/ui/wear/settings/ExtendedOnCenterProximityListener.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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.packageinstaller.permission.ui.wear.settings; - -import android.support.wearable.view.WearableListView; - -public interface ExtendedOnCenterProximityListener - extends WearableListView.OnCenterProximityListener { - float getProximityMinValue(); - - float getProximityMaxValue(); - - float getCurrentProximityValue(); - - void setScalingAnimatorValue(float value); -} diff --git a/src/com/android/packageinstaller/permission/ui/wear/settings/ExtendedViewHolder.java b/src/com/android/packageinstaller/permission/ui/wear/settings/ExtendedViewHolder.java deleted file mode 100644 index 6b725419..00000000 --- a/src/com/android/packageinstaller/permission/ui/wear/settings/ExtendedViewHolder.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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.packageinstaller.permission.ui.wear.settings; - -import android.animation.ObjectAnimator; -import android.support.wearable.view.WearableListView; -import android.view.View; - - -public class ExtendedViewHolder extends WearableListView.ViewHolder { - public static final long DEFAULT_ANIMATION_DURATION = 150; - - private ObjectAnimator mScalingUpAnimator; - - private ObjectAnimator mScalingDownAnimator; - - private float mMinValue; - - private float mMaxValue; - - public ExtendedViewHolder(View itemView) { - super(itemView); - if (itemView instanceof ExtendedOnCenterProximityListener) { - ExtendedOnCenterProximityListener item = - (ExtendedOnCenterProximityListener) itemView; - mMinValue = item.getProximityMinValue(); - item.setScalingAnimatorValue(mMinValue); - mMaxValue = item.getProximityMaxValue(); - mScalingUpAnimator = ObjectAnimator.ofFloat(item, "scalingAnimatorValue", mMinValue, - mMaxValue); - mScalingUpAnimator.setDuration(DEFAULT_ANIMATION_DURATION); - mScalingDownAnimator = ObjectAnimator.ofFloat(item, "scalingAnimatorValue", - mMaxValue, mMinValue); - mScalingDownAnimator.setDuration(DEFAULT_ANIMATION_DURATION); - } - } - - public void onCenterProximity(boolean isCentralItem, boolean animate) { - if (!(itemView instanceof ExtendedOnCenterProximityListener)) { - return; - } - ExtendedOnCenterProximityListener item = (ExtendedOnCenterProximityListener) itemView; - if (isCentralItem) { - if (animate) { - mScalingDownAnimator.cancel(); - if (!mScalingUpAnimator.isRunning()) { - mScalingUpAnimator.setFloatValues(item.getCurrentProximityValue(), - mMaxValue); - mScalingUpAnimator.start(); - } - } else { - mScalingUpAnimator.cancel(); - item.setScalingAnimatorValue(item.getProximityMaxValue()); - } - } else { - mScalingUpAnimator.cancel(); - if (animate) { - if (!mScalingDownAnimator.isRunning()) { - mScalingDownAnimator.setFloatValues(item.getCurrentProximityValue(), - mMinValue); - mScalingDownAnimator.start(); - } - } else { - mScalingDownAnimator.cancel(); - item.setScalingAnimatorValue(item.getProximityMinValue()); - } - } - super.onCenterProximity(isCentralItem, animate); - } -} diff --git a/src/com/android/packageinstaller/permission/ui/wear/settings/PermissionsSettingsAdapter.java b/src/com/android/packageinstaller/permission/ui/wear/settings/PermissionsSettingsAdapter.java deleted file mode 100644 index 0e0adcbb..00000000 --- a/src/com/android/packageinstaller/permission/ui/wear/settings/PermissionsSettingsAdapter.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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.packageinstaller.permission.ui.wear.settings; - -import android.content.Context; -import android.content.res.Resources; -import android.support.wearable.view.WearableListView; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import com.android.packageinstaller.R; -import com.android.packageinstaller.permission.model.AppPermissionGroup; - -public final class PermissionsSettingsAdapter extends SettingsAdapter<AppPermissionGroup> { - private Resources mRes; - - public PermissionsSettingsAdapter(Context context) { - super(context, R.layout.permissions_settings_item); - mRes = context.getResources(); - } - - @Override - public WearableListView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - return new PermissionsViewHolder(new PermissionsSettingsItem(parent.getContext())); - } - - @Override - public void onBindViewHolder(WearableListView.ViewHolder holder, int position) { - super.onBindViewHolder(holder, position); - PermissionsViewHolder viewHolder = (PermissionsViewHolder) holder; - AppPermissionGroup group = get(position).data; - - if (group.isPolicyFixed()) { - viewHolder.imageView.setEnabled(false); - viewHolder.textView.setEnabled(false); - viewHolder.state.setEnabled(false); - viewHolder.state.setText( - mRes.getString(R.string.permission_summary_enforced_by_policy)); - } else { - viewHolder.imageView.setEnabled(true); - viewHolder.textView.setEnabled(true); - viewHolder.state.setEnabled(true); - - if (group.areRuntimePermissionsGranted()) { - viewHolder.state.setText(R.string.generic_enabled); - } else { - viewHolder.state.setText(R.string.generic_disabled); - } - } - } - - private static final class PermissionsViewHolder extends SettingsAdapter.SettingsItemHolder { - public final TextView state; - - public PermissionsViewHolder(View view) { - super(view); - state = (TextView) view.findViewById(R.id.state); - } - } - - private class PermissionsSettingsItem extends SettingsItem { - private final TextView mState; - private final float mCenteredAlpha = 1.0f; - private final float mNonCenteredAlpha = 0.5f; - - public PermissionsSettingsItem (Context context) { - super(context); - mState = (TextView) findViewById(R.id.state); - } - - @Override - public void onCenterPosition(boolean animate) { - mImage.setAlpha(mImage.isEnabled() ? mCenteredAlpha : mNonCenteredAlpha); - mText.setAlpha(mText.isEnabled() ? mCenteredAlpha : mNonCenteredAlpha); - mState.setAlpha(mState.isEnabled() ? mCenteredAlpha : mNonCenteredAlpha); - } - - @Override - public void onNonCenterPosition(boolean animate) { - mImage.setAlpha(mNonCenteredAlpha); - mText.setAlpha(mNonCenteredAlpha); - mState.setAlpha(mNonCenteredAlpha); - } - } -} - diff --git a/src/com/android/packageinstaller/permission/ui/wear/settings/SettingsAdapter.java b/src/com/android/packageinstaller/permission/ui/wear/settings/SettingsAdapter.java deleted file mode 100644 index baf1a2b4..00000000 --- a/src/com/android/packageinstaller/permission/ui/wear/settings/SettingsAdapter.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * 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.packageinstaller.permission.ui.wear.settings; - -import android.content.Context; -import android.support.wearable.view.CircledImageView; -import android.support.wearable.view.WearableListView; -import android.util.Log; -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; -import android.widget.TextView; - -import com.android.packageinstaller.R; - -import java.util.ArrayList; - -/** - * Common adapter for settings views. Maintains a list of 'Settings', consisting of a name, - * icon and optional activity-specific data. - */ -public class SettingsAdapter<T> extends WearableListView.Adapter { - private static final String TAG = "SettingsAdapter"; - private final Context mContext; - - public static final class Setting<S> { - public static final int ID_INVALID = -1; - - public final int id; - public int nameResourceId; - public CharSequence name; - public int iconResource; - public boolean inProgress; - public S data; - - public Setting(CharSequence name, int iconResource, S data) { - this(name, iconResource, data, ID_INVALID); - } - - public Setting(CharSequence name, int iconResource, S data, int id) { - this.name = name; - this.iconResource = iconResource; - this.data = data; - this.inProgress = false; - this.id = id; - } - - public Setting(int nameResource, int iconResource, S data, int id) { - this.nameResourceId = nameResource; - this.iconResource = iconResource; - this.data = data; - this.inProgress = false; - this.id = id; - } - - public Setting(int nameResource, int iconResource, int id) { - this.nameResourceId = nameResource; - this.iconResource = iconResource; - this.data = null; - this.inProgress = false; - this.id = id; - } - - public Setting(CharSequence name, int iconResource, int id) { - this(name, iconResource, null, id); - } - - } - - private final int mItemLayoutId; - private final float mDefaultCircleRadiusPercent; - private final float mSelectedCircleRadiusPercent; - - protected ArrayList<Setting<T>> mSettings = new ArrayList<Setting<T>>(); - - public SettingsAdapter(Context context, int itemLayoutId) { - mContext = context; - mItemLayoutId = itemLayoutId; - mDefaultCircleRadiusPercent = context.getResources().getFraction( - R.dimen.default_settings_circle_radius_percent, 1, 1); - mSelectedCircleRadiusPercent = context.getResources().getFraction( - R.dimen.selected_settings_circle_radius_percent, 1, 1); - } - - @Override - public WearableListView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - return new SettingsItemHolder(new SettingsItem(parent.getContext())); - } - - @Override - public void onBindViewHolder(WearableListView.ViewHolder holder, int position) { - Setting<T> setting = mSettings.get(position); - if (setting.iconResource == -1) { - ((SettingsItemHolder) holder).imageView.setVisibility(View.GONE); - } else { - ((SettingsItemHolder) holder).imageView.setVisibility(View.VISIBLE); - ((SettingsItemHolder) holder).imageView.setImageResource( - mSettings.get(position).iconResource); - } - Log.d(TAG, "onBindViewHolder " + setting.name + " " + setting.id + " " + setting - .nameResourceId); - if (setting.name == null && setting.nameResourceId != 0) { - setting.name = mContext.getString(setting.nameResourceId); - } - ((SettingsItemHolder) holder).textView.setText(setting.name); - } - - @Override - public int getItemCount() { - return mSettings.size(); - } - - public void addSetting(CharSequence name, int iconResource) { - addSetting(name, iconResource, null); - } - - public void addSetting(CharSequence name, int iconResource, T intent) { - addSetting(mSettings.size(), name, iconResource, intent); - } - - public void addSetting(int index, CharSequence name, int iconResource, T intent) { - addSetting(Setting.ID_INVALID, index, name, iconResource, intent); - } - - public void addSetting(int id, int index, CharSequence name, int iconResource, T intent) { - mSettings.add(index, new Setting<T>(name, iconResource, intent, id)); - notifyItemInserted(index); - } - - public void addSettingDontNotify(Setting<T> setting) { - mSettings.add(setting); - } - - public void addSetting(Setting<T> setting) { - mSettings.add(setting); - notifyItemInserted(mSettings.size() - 1); - } - - public void addSetting(int index, Setting<T> setting) { - mSettings.add(index, setting); - notifyItemInserted(index); - } - - /** - * Returns the index of the setting in the adapter based on the ID supplied when it was - * originally added. - * @param id the setting's id - * @return index in the adapter of the setting. -1 if not found. - */ - public int findSetting(int id) { - for (int i = mSettings.size() - 1; i >= 0; --i) { - Setting setting = mSettings.get(i); - - if (setting.id == id) { - return i; - } - } - - return -1; - } - - /** - * Removes a setting at the given index. - * @param index the index of the setting to be removed - */ - public void removeSetting(int index) { - mSettings.remove(index); - notifyDataSetChanged(); - } - - public void clearSettings() { - mSettings.clear(); - notifyDataSetChanged(); - } - - /** - * Updates a setting in place. - * @param index the index of the setting - * @param name the updated setting name - * @param iconResource the update setting icon - * @param intent the updated intent for the setting - */ - public void updateSetting(int index, CharSequence name, int iconResource, T intent) { - Setting<T> setting = mSettings.get(index); - setting.iconResource = iconResource; - setting.name = name; - setting.data = intent; - notifyItemChanged(index); - } - - public Setting<T> get(int position) { - return mSettings.get(position); - } - - protected static class SettingsItemHolder extends ExtendedViewHolder { - public final CircledImageView imageView; - public final TextView textView; - - public SettingsItemHolder(View itemView) { - super(itemView); - - imageView = ((CircledImageView) itemView.findViewById(R.id.image)); - textView = ((TextView) itemView.findViewById(R.id.text)); - } - } - - protected class SettingsItem extends FrameLayout implements ExtendedOnCenterProximityListener { - - protected final CircledImageView mImage; - protected final TextView mText; - - public SettingsItem(Context context) { - super(context); - View view = View.inflate(context, mItemLayoutId, null); - FrameLayout.LayoutParams params = new FrameLayout.LayoutParams( - FrameLayout.LayoutParams.MATCH_PARENT, - FrameLayout.LayoutParams.MATCH_PARENT); - params.gravity = Gravity.CENTER_VERTICAL; - addView(view, params); - mImage = (CircledImageView) findViewById(R.id.image); - mText = (TextView) findViewById(R.id.text); - } - - @Override - public float getProximityMinValue() { - return mDefaultCircleRadiusPercent; - } - - @Override - public float getProximityMaxValue() { - return mSelectedCircleRadiusPercent; - } - - @Override - public float getCurrentProximityValue() { - return mImage.getCircleRadiusPressedPercent(); - } - - @Override - public void setScalingAnimatorValue(float value) { - mImage.setCircleRadiusPercent(value); - mImage.setCircleRadiusPressedPercent(value); - } - - @Override - public void onCenterPosition(boolean animate) { - mImage.setAlpha(1f); - mText.setAlpha(1f); - } - - @Override - public void onNonCenterPosition(boolean animate) { - mImage.setAlpha(0.5f); - mText.setAlpha(0.5f); - } - - TextView getTextView() { - return mText; - } - } -} diff --git a/src/com/android/packageinstaller/permission/ui/wear/settings/ViewUtils.java b/src/com/android/packageinstaller/permission/ui/wear/settings/ViewUtils.java deleted file mode 100644 index cf1c0fd0..00000000 --- a/src/com/android/packageinstaller/permission/ui/wear/settings/ViewUtils.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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.packageinstaller.permission.ui.wear.settings; - -import android.content.Context; -import android.view.View; -import android.view.ViewGroup; - -/** - * Utility to determine screen shape - */ -public class ViewUtils { - - public static boolean getIsCircular(Context context) { - return context.getResources().getConfiguration().isScreenRound(); - } - - /** - * Set the given {@code view} and all descendants to the given {@code enabled} state. - * - * @param view the parent view of a subtree of components whose enabled state must be set - * @param enabled the new enabled state of the subtree of components - */ - public static void setEnabled(View view, boolean enabled) { - view.setEnabled(enabled); - - if (view instanceof ViewGroup) { - final ViewGroup viewGroup = (ViewGroup) view; - for (int i = 0; i < viewGroup.getChildCount(); i++) { - setEnabled(viewGroup.getChildAt(i), enabled); - } - } - } -} diff --git a/src/com/android/packageinstaller/permission/utils/ArrayUtils.java b/src/com/android/packageinstaller/permission/utils/ArrayUtils.java index 4b7a3947..2af641bc 100644 --- a/src/com/android/packageinstaller/permission/utils/ArrayUtils.java +++ b/src/com/android/packageinstaller/permission/utils/ArrayUtils.java @@ -16,6 +16,8 @@ package com.android.packageinstaller.permission.utils; +import android.text.TextUtils; + import java.util.Objects; public final class ArrayUtils { @@ -42,4 +44,20 @@ public final class ArrayUtils { } return -1; } + + public static String[] appendString(String[] cur, String val) { + if (cur == null) { + return new String[] { val }; + } + final int N = cur.length; + for (int i = 0; i < N; i++) { + if (TextUtils.equals(cur[i], val)) { + return cur; + } + } + String[] ret = new String[N + 1]; + System.arraycopy(cur, 0, ret, 0, N); + ret[N] = val; + return ret; + } } diff --git a/src/com/android/packageinstaller/permission/utils/Utils.java b/src/com/android/packageinstaller/permission/utils/Utils.java index 22663e13..c2da5824 100644 --- a/src/com/android/packageinstaller/permission/utils/Utils.java +++ b/src/com/android/packageinstaller/permission/utils/Utils.java @@ -145,4 +145,14 @@ public final class Utils { return info.isSystemApp() && (info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0 && !launcherPkgs.contains(info.packageName); } + + public static boolean areGroupPermissionsIndividuallyControlled(Context context, String group) { + if (!context.getResources().getBoolean( + com.android.internal.R.bool.config_permissionReviewRequired)) { + return false; + } + return Manifest.permission_group.SMS.equals(group) + || Manifest.permission_group.PHONE.equals(group) + || Manifest.permission_group.CONTACTS.equals(group); + } } diff --git a/src/com/android/packageinstaller/wear/WearPackageInstallerService.java b/src/com/android/packageinstaller/wear/WearPackageInstallerService.java index 2d4744c2..6f4e2bcf 100644 --- a/src/com/android/packageinstaller/wear/WearPackageInstallerService.java +++ b/src/com/android/packageinstaller/wear/WearPackageInstallerService.java @@ -298,16 +298,6 @@ public class WearPackageInstallerService extends Service { } } - // Check permissions on both the new wearable package and also on the already installed - // wearable package. - // If the app is targeting API level 23, we will also start a service in ClockworkHome - // which will ultimately prompt the user to accept/reject permissions. - if (checkPerms && !checkPermissions(pkg, companionSdkVersion, companionDeviceVersion, - permUri, wearablePerms, tempFile)) { - Log.w(TAG, "Wearable does not have enough permissions."); - return; - } - // Check that the wearable has all the features. boolean hasAllFeatures = true; if (pkg.reqFeatures != null) { @@ -325,6 +315,16 @@ public class WearPackageInstallerService extends Service { return; } + // Check permissions on both the new wearable package and also on the already installed + // wearable package. + // If the app is targeting API level 23, we will also start a service in ClockworkHome + // which will ultimately prompt the user to accept/reject permissions. + if (checkPerms && !checkPermissions(pkg, companionSdkVersion, companionDeviceVersion, + permUri, wearablePerms, tempFile)) { + Log.w(TAG, "Wearable does not have enough permissions."); + return; + } + // Finally install the package. ParcelFileDescriptor fd = getContentResolver().openFileDescriptor(assetUri, "r"); PackageInstallerFactory.getPackageInstaller(this).install(packageName, fd, @@ -355,11 +355,27 @@ public class WearPackageInstallerService extends Service { final String packageName = WearPackageArgs.getPackageName(argsBundle); final PackageManager pm = getPackageManager(); - PowerManager.WakeLock lock = getLock(this.getApplicationContext()); - pm.deletePackage(packageName, new PackageDeleteObserver(lock, startId), - PackageManager.DELETE_ALL_USERS); - startPermsServiceForUninstall(packageName); - Log.i(TAG, "Sent delete request for " + packageName); + try { + // Result ignored. + pm.getPackageInfo(packageName, 0); + + // Found package, send uninstall request. + PowerManager.WakeLock lock = getLock(this.getApplicationContext()); + + try { + pm.deletePackage(packageName, new PackageDeleteObserver(lock, startId), + PackageManager.DELETE_ALL_USERS); + } catch (IllegalArgumentException e) { + // Couldn't find the package, no need to call uninstall. + Log.w(TAG, "Could not find package, not deleting " + packageName, e); + } + + startPermsServiceForUninstall(packageName); + Log.i(TAG, "Sent delete request for " + packageName); + } catch (PackageManager.NameNotFoundException e) { + // Couldn't find the package, no need to call uninstall. + Log.w(TAG, "Could not find package, not deleting " + packageName, e); + } } private boolean checkPermissions(PackageParser.Package pkg, int companionSdkVersion, |