diff options
7 files changed, 535 insertions, 223 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 0e5b5306..dedf0c48 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -67,7 +67,7 @@ <activity android:name=".permission.ui.GrantPermissionsActivity" android:configChanges="orientation|keyboardHidden|screenSize" android:excludeFromRecents="true" - android:theme="@android:style/Theme.DeviceDefault.Dialog.NoActionBar"> + android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.NoActionBar"> <intent-filter> <action android:name="android.content.pm.action.REQUEST_PERMISSIONS" /> <category android:name="android.intent.category.DEFAULT" /> diff --git a/res/layout/grant_permissions.xml b/res/layout/grant_permissions.xml index a39ee253..2a5f7a93 100644 --- a/res/layout/grant_permissions.xml +++ b/res/layout/grant_permissions.xml @@ -14,81 +14,75 @@ limitations under the License. --> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" +<com.android.packageinstaller.permission.ui.ManualLayoutFrame + xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" - android:layout_height="fill_parent" - android:paddingTop="24dip" - android:paddingBottom="8dip" - android:paddingStart="22dip" - android:paddingEnd="16dip" - android:filterTouchesWhenObscured="true"> - - <ImageView - android:id="@+id/permission_icon" - android:layout_width="36dip" - android:layout_height="36dip" - android:tint="?android:attr/colorAccent" - android:scaleType="fitCenter"> - </ImageView> - - <TextView - android:id="@+id/permission_message" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/permission_icon" - android:lineSpacingMultiplier="1.024" - android:fontFamily="@*android:string/font_family_body_2_material" - android:paddingStart="16dip" - android:paddingEnd="8dip" - style="?android:attr/textAppearanceMedium"> - </TextView> - - <CheckBox - android:id="@+id/do_not_ask_checkbox" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_below="@id/permission_message" - android:layout_marginTop="16dip" - android:text="@string/never_ask_again" - style="?android:attr/textAppearanceSmall" - android:visibility="gone"> - </CheckBox> - + android:layout_height="fill_parent" > <LinearLayout + android:id="@+id/dialog_container" android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_below="@id/do_not_ask_checkbox" - android:orientation="horizontal" - android:paddingStart="2dip" - android:paddingTop="16dip"> + android:layout_height="fill_parent" + android:paddingTop="24dip" + android:paddingBottom="8dip" + android:paddingStart="22dip" + android:paddingEnd="16dip" + android:filterTouchesWhenObscured="true" + android:orientation="vertical"> - <TextView - android:id="@+id/current_page_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_weight="1" - style="@android:style/TextAppearance.Material.Body2" - android:textColor="@color/grant_permissions_progress_color" - android:visibility="invisible"> - </TextView> + <FrameLayout + android:id="@+id/desc_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" > + <include + layout="@layout/permission_description" /> + </FrameLayout> - <Button - android:id="@+id/permission_deny_button" + <CheckBox + android:id="@+id/do_not_ask_checkbox" android:layout_width="wrap_content" android:layout_height="wrap_content" - style="?android:attr/buttonBarButtonStyle" - android:text="@string/grant_dialog_button_deny" - android:layout_marginEnd="8dip"> - </Button> + android:layout_marginTop="16dip" + android:text="@string/never_ask_again" + style="?android:attr/textAppearanceSmall" + android:visibility="gone"> + </CheckBox> - <Button - android:id="@+id/permission_allow_button" - android:layout_width="wrap_content" + <LinearLayout + android:id="@+id/button_group" + android:layout_width="fill_parent" android:layout_height="wrap_content" - style="?android:attr/buttonBarButtonStyle" - android:text="@string/grant_dialog_button_allow"> - </Button> + android:orientation="horizontal" + android:paddingStart="2dip" + android:paddingTop="16dip"> - </LinearLayout> + <TextView + android:id="@+id/current_page_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + style="@android:style/TextAppearance.Material.Body2" + android:textColor="@color/grant_permissions_progress_color" + android:visibility="invisible"> + </TextView> + + <Button + android:id="@+id/permission_deny_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + style="?android:attr/buttonBarButtonStyle" + android:text="@string/grant_dialog_button_deny" + android:layout_marginEnd="8dip"> + </Button> -</RelativeLayout> + <Button + android:id="@+id/permission_allow_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + style="?android:attr/buttonBarButtonStyle" + android:text="@string/grant_dialog_button_allow"> + </Button> + + </LinearLayout> + + </LinearLayout> +</com.android.packageinstaller.permission.ui.ManualLayoutFrame> diff --git a/res/layout/permission_description.xml b/res/layout/permission_description.xml new file mode 100644 index 00000000..3f1cf434 --- /dev/null +++ b/res/layout/permission_description.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/perm_desc_root" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <ImageView + android:id="@+id/permission_icon" + android:layout_width="36dip" + android:layout_height="36dip" + android:layout_marginTop="3dp" + android:tint="?android:attr/colorAccent" + android:scaleType="fitCenter" /> + + <TextView + android:id="@+id/permission_message" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toRightOf="@id/permission_icon" + android:lineSpacingMultiplier="1.024" + android:fontFamily="@*android:string/font_family_body_2_material" + android:paddingStart="16dip" + android:paddingEnd="8dip" + style="?android:attr/textAppearanceMedium"> + </TextView> + +</LinearLayout> diff --git a/src/com/android/packageinstaller/permission/ui/GrantPermissionFragment.java b/src/com/android/packageinstaller/permission/ui/GrantPermissionFragment.java deleted file mode 100644 index 0458f9b9..00000000 --- a/src/com/android/packageinstaller/permission/ui/GrantPermissionFragment.java +++ /dev/null @@ -1,129 +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; - -import android.app.Activity; -import android.app.DialogFragment; -import android.content.DialogInterface; -import android.graphics.drawable.Drawable; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; - -import com.android.packageinstaller.R; -import com.android.packageinstaller.permission.utils.Utils; - -public final class GrantPermissionFragment extends DialogFragment { - public static final String ARG_GROUP_NAME = "ARG_GROUP_NAME"; - public static final String ARG_GROUP_COUNT = "ARG_GROUP_COUNT"; - public static final String ARG_GROUP_INDEX = "ARG_GROUP_INDEX"; - public static final String ARG_GROUP_ICON_RES_ID = "ARG_GROUP_ICON"; - public static final String ARG_GROUP_ICON_PKG = "ARG_GROUP_ICON_PKG"; - public static final String ARG_GROUP_MESSAGE = "ARG_GROUP_MESSAGE"; - - public interface OnRequestGrantPermissionGroupResult { - public void onRequestGrantPermissionGroupResult(String name, boolean granted); - } - - public static GrantPermissionFragment newInstance(String groupName, int groupCount, - int groupIndex, String iconPkg, int iconResId, CharSequence message) { - GrantPermissionFragment instance = new GrantPermissionFragment(); - instance.setStyle(STYLE_NORMAL, - android.R.style.Theme_DeviceDefault_Light_Dialog_NoActionBar); - - Bundle arguments = new Bundle(); - arguments.putString(ARG_GROUP_NAME, groupName); - arguments.putInt(ARG_GROUP_COUNT, groupCount); - arguments.putInt(ARG_GROUP_INDEX, groupIndex); - arguments.putInt(ARG_GROUP_ICON_RES_ID, iconResId); - arguments.putString(ARG_GROUP_ICON_PKG, iconPkg); - arguments.putCharSequence(ARG_GROUP_MESSAGE, message); - instance.setArguments(arguments); - - return instance; - } - - private String mGroupName; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View content = inflater.inflate(R.layout.grant_permissions, container, false); - - mGroupName = getArguments().getString(ARG_GROUP_NAME); - final CharSequence message = getArguments().getCharSequence(ARG_GROUP_MESSAGE); - final String iconPkg = getArguments().getString(ARG_GROUP_ICON_PKG); - final int iconResId = getArguments().getInt(ARG_GROUP_ICON_RES_ID); - final int groupCount = getArguments().getInt(ARG_GROUP_COUNT); - final int groupIndex = getArguments().getInt(ARG_GROUP_INDEX); - - final ImageView iconView = (ImageView) content.findViewById(R.id.permission_icon); - final View allowButton = content.findViewById(R.id.permission_allow_button); - final View denyButton = content.findViewById(R.id.permission_deny_button); - final View doNotAskCheckbox = content.findViewById(R.id.do_not_ask_checkbox); - final TextView currentGroupView = (TextView) content.findViewById(R.id.current_page_text); - final TextView messageView = (TextView) content.findViewById(R.id.permission_message); - - OnClickListener clickListener = new OnClickListener() { - @Override - public void onClick(View view) { - if (view == allowButton) { - ((OnRequestGrantPermissionGroupResult) getActivity()) - .onRequestGrantPermissionGroupResult(mGroupName, true); - } else if (view == denyButton) { - ((OnRequestGrantPermissionGroupResult) getActivity()) - .onRequestGrantPermissionGroupResult(mGroupName, false); - } else if (view == doNotAskCheckbox) { - //TODO: Implement me. - } - } - }; - - Drawable icon = Utils.loadDrawable(getActivity().getPackageManager(), iconPkg, - iconResId); - iconView.setImageDrawable(icon); - - messageView.setText(message); - - allowButton.setOnClickListener(clickListener); - denyButton.setOnClickListener(clickListener); - doNotAskCheckbox.setOnClickListener(clickListener); - - if (groupCount > 1) { - currentGroupView.setVisibility(View.VISIBLE); - currentGroupView.setText(getString(R.string.current_permission_template, - groupIndex + 1, groupCount)); - } else { - currentGroupView.setVisibility(View.INVISIBLE); - } - - return content; - } - - @Override - public void onDismiss(DialogInterface dialog) { - Activity activity = getActivity(); - if (activity != null) { - ((OnRequestGrantPermissionGroupResult) getActivity()) - .onRequestGrantPermissionGroupResult(mGroupName, false); - } - } -} diff --git a/src/com/android/packageinstaller/permission/ui/GrantPermissionViewHandler.java b/src/com/android/packageinstaller/permission/ui/GrantPermissionViewHandler.java new file mode 100644 index 00000000..dba459c2 --- /dev/null +++ b/src/com/android/packageinstaller/permission/ui/GrantPermissionViewHandler.java @@ -0,0 +1,334 @@ +/* + * 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; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.content.Context; +import android.graphics.drawable.Icon; +import android.os.Bundle; +import android.util.SparseArray; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnLayoutChangeListener; +import android.view.ViewGroup; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.packageinstaller.R; + +public final class GrantPermissionViewHandler implements OnClickListener { + + public static final String ARG_GROUP_NAME = "ARG_GROUP_NAME"; + public static final String ARG_GROUP_COUNT = "ARG_GROUP_COUNT"; + public static final String ARG_GROUP_INDEX = "ARG_GROUP_INDEX"; + public static final String ARG_GROUP_ICON = "ARG_GROUP_ICON"; + public static final String ARG_GROUP_MESSAGE = "ARG_GROUP_MESSAGE"; + + // Animation parameters. + private static final long SIZE_START_DELAY = 300; + private static final long SIZE_START_LENGTH = 233; + private static final long FADE_OUT_START_DELAY = 300; + private static final long FADE_OUT_START_LENGTH = 217; + private static final long TRANSLATE_START_DELAY = 367; + private static final long TRANSLATE_LENGTH = 317; + private static final long GROUP_UPDATE_DELAY = 400; + + private final OnRequestGrantPermissionGroupResult mResultListener; + private final Context mContext; + + private String mGroupName; + private int mGroupCount; + private int mGroupIndex; + private Icon mGroupIcon; + private CharSequence mGroupMessage; + + private ImageView mIconView; + private TextView mCurrentGroupView; + private TextView mMessageView; + + private ViewHeightController mRootViewHeightController; + private ManualLayoutFrame mRootView; + + // Needed for animation + private ViewGroup mDescContainer; + private ViewGroup mCurrentDesc; + private ViewGroup mNextDesc; + + private ViewGroup mDialogContainer; + + private final Runnable mUpdateGroup = new Runnable() { + @Override + public void run() { + updateGroup(); + } + }; + + public interface OnRequestGrantPermissionGroupResult { + public void onRequestGrantPermissionGroupResult(String name, boolean granted); + } + + public GrantPermissionViewHandler(OnRequestGrantPermissionGroupResult resultListener, + Context context) { + mResultListener = resultListener; + mContext = context; + } + + public void onSaveInstanceState(Bundle arguments) { + arguments.putString(ARG_GROUP_NAME, mGroupName); + arguments.putInt(ARG_GROUP_COUNT, mGroupCount); + arguments.putInt(ARG_GROUP_INDEX, mGroupIndex); + arguments.putParcelable(ARG_GROUP_ICON, mGroupIcon); + arguments.putCharSequence(ARG_GROUP_MESSAGE, mGroupMessage); + } + + public void loadSavedInstance(Bundle savedInstanceState) { + mGroupName = savedInstanceState.getString(ARG_GROUP_NAME); + mGroupMessage = savedInstanceState.getCharSequence(ARG_GROUP_MESSAGE); + mGroupIcon = savedInstanceState.getParcelable(ARG_GROUP_ICON); + mGroupCount = savedInstanceState.getInt(ARG_GROUP_COUNT); + mGroupIndex = savedInstanceState.getInt(ARG_GROUP_INDEX); + } + + public void showPermission(String groupName, int groupCount, int groupIndex, Icon icon, + CharSequence message) { + mGroupName = groupName; + mGroupCount = groupCount; + mGroupIndex = groupIndex; + mGroupIcon = icon; + mGroupMessage = message; + // If this is a second (or later) permission and the views exist, then animate. + if (mIconView != null) { + if (mGroupIndex > 0) { + animateToPermission(); + } else { + updateDescription(); + updateGroup(); + } + } + } + + private void animateToPermission() { + if (mRootViewHeightController == null) { + // Allow height control of the real root view, not the root of what we inflate. + // Need to do it on the root view so that the background drawable of the dialog + // moves with the animation. + View realRootView = mRootView.getViewRootImpl().getView(); + mRootViewHeightController = new ViewHeightController(realRootView); + mRootViewHeightController.setHeight(realRootView.getHeight()); + } + + // Grab the current height/y positions, then wait for the layout to change, + // so we can get the end height/y positions. + final SparseArray<Float> startPositions = getViewPositions(); + final int startHeight = mRootView.getLayoutHeight(); + mRootView.addOnLayoutChangeListener(new OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, + int oldTop, int oldRight, int oldBottom) { + mRootView.removeOnLayoutChangeListener(this); + SparseArray<Float> endPositions = getViewPositions(); + int endHeight = mRootView.getLayoutHeight(); + animateYPos(startPositions, endPositions, endHeight - startHeight); + } + }); + + // Fade out old description group and scale out the icon for it. + Interpolator interpolator = AnimationUtils.loadInterpolator(mContext, + android.R.interpolator.fast_out_linear_in); + mIconView.animate() + .scaleX(0) + .scaleY(0) + .setStartDelay(FADE_OUT_START_DELAY) + .setDuration(FADE_OUT_START_LENGTH) + .setInterpolator(interpolator) + .start(); + mCurrentDesc.animate() + .alpha(0) + .setStartDelay(FADE_OUT_START_DELAY) + .setDuration(FADE_OUT_START_LENGTH) + .setInterpolator(interpolator) + .setListener(null) + .start(); + + // Update the index of the permission after the animations have started. + mCurrentGroupView.getHandler().postDelayed(mUpdateGroup, GROUP_UPDATE_DELAY); + + // Add the new description and translate it in. + mNextDesc = (ViewGroup) LayoutInflater.from(mContext).inflate( + R.layout.permission_description, mDescContainer, false); + + mMessageView = (TextView) mNextDesc.findViewById(R.id.permission_message); + mIconView = (ImageView) mNextDesc.findViewById(R.id.permission_icon); + updateDescription(); + + int width = mDescContainer.getRootView().getWidth(); + mDescContainer.addView(mNextDesc); + mNextDesc.setTranslationX(width); + final View oldDesc = mCurrentDesc; + mCurrentDesc = mNextDesc; + mNextDesc.animate() + .translationX(0) + .setStartDelay(TRANSLATE_START_DELAY) + .setDuration(TRANSLATE_LENGTH) + .setInterpolator(AnimationUtils.loadInterpolator(mContext, + android.R.interpolator.linear_out_slow_in)) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + // This is the longest animation, when it finishes, we are done. + mDescContainer.removeView(oldDesc); + } + }) + .start(); + } + + private SparseArray<Float> getViewPositions() { + SparseArray<Float> locMap = new SparseArray<>(); + final int N = mDialogContainer.getChildCount(); + for (int i = 0; i < N; i++) { + View child = mDialogContainer.getChildAt(i); + if (child.getId() <= 0) { + // Only track views with ids. + continue; + } + locMap.put(child.getId(), child.getY()); + } + return locMap; + } + + private void animateYPos(SparseArray<Float> startPositions, SparseArray<Float> endPositions, + int heightDiff) { + final int N = startPositions.size(); + for (int i = 0; i < N; i++) { + int key = startPositions.keyAt(i); + float start = startPositions.get(key); + float end = endPositions.get(key); + if (start != end) { + final View child = mDialogContainer.findViewById(key); + child.setTranslationY(start - end); + child.animate() + .setStartDelay(SIZE_START_DELAY) + .setDuration(SIZE_START_LENGTH) + .translationY(0) + .start(); + } + } + mRootViewHeightController.animateAddHeight(heightDiff); + } + + public View creatView() { + mRootView = (ManualLayoutFrame) LayoutInflater.from(mContext) + .inflate(R.layout.grant_permissions, null); + + mDialogContainer = (ViewGroup) mRootView.findViewById(R.id.dialog_container); + mMessageView = (TextView) mRootView.findViewById(R.id.permission_message); + mIconView = (ImageView) mRootView.findViewById(R.id.permission_icon); + mCurrentGroupView = (TextView) mRootView.findViewById(R.id.current_page_text); + + mDescContainer = (ViewGroup) mRootView.findViewById(R.id.desc_container); + mCurrentDesc = (ViewGroup) mRootView.findViewById(R.id.perm_desc_root); + + mRootView.findViewById(R.id.permission_allow_button).setOnClickListener(this); + mRootView.findViewById(R.id.permission_deny_button).setOnClickListener(this); + mRootView.findViewById(R.id.do_not_ask_checkbox).setOnClickListener(this); + + if (mGroupName != null) { + updateDescription(); + updateGroup(); + } + + return mRootView; + } + + private void updateDescription() { + mIconView.setImageDrawable(mGroupIcon.loadDrawable(mContext)); + mMessageView.setText(mGroupMessage); + } + + private void updateGroup() { + if (mGroupCount > 1) { + mCurrentGroupView.setVisibility(View.VISIBLE); + mCurrentGroupView.setText(mContext.getString(R.string.current_permission_template, + mGroupIndex + 1, mGroupCount)); + } else { + mCurrentGroupView.setVisibility(View.INVISIBLE); + } + } + + @Override + public void onClick(View view) { + switch (view.getId()) { + case R.id.permission_allow_button: + mResultListener.onRequestGrantPermissionGroupResult(mGroupName, true); + break; + case R.id.permission_deny_button: + mResultListener.onRequestGrantPermissionGroupResult(mGroupName, false); + break; + case R.id.do_not_ask_checkbox: + //TODO: Implement me. + break; + + } + } + + /** + * Manually controls the height of a view through getBottom/setTop. Also listens + * for layout changes and sets the height again to be sure it doesn't change. + */ + public static final class ViewHeightController implements OnLayoutChangeListener { + private final View mView; + private int mHeight; + + public ViewHeightController(View view) { + mView = view; + mView.addOnLayoutChangeListener(this); + } + + public void animateAddHeight(int heightDiff) { + if (heightDiff != 0) { + final int startHeight = mHeight; + final int endHeight = startHeight + heightDiff; + ObjectAnimator animator = ObjectAnimator.ofInt(this, "height", + startHeight, endHeight); + animator.setStartDelay(SIZE_START_DELAY); + animator.setDuration(SIZE_START_LENGTH); + animator.start(); + } + } + + public void setHeight(int height) { + mHeight = height; + updateTop(); + } + + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, + int oldTop, int oldRight, int oldBottom) { + // Ensure that the height never changes. + updateTop(); + } + + private void updateTop() { + mView.setTop(mView.getBottom() - mHeight); + } + } +} diff --git a/src/com/android/packageinstaller/permission/ui/GrantPermissionsActivity.java b/src/com/android/packageinstaller/permission/ui/GrantPermissionsActivity.java index 02951e9f..159a043f 100644 --- a/src/com/android/packageinstaller/permission/ui/GrantPermissionsActivity.java +++ b/src/com/android/packageinstaller/permission/ui/GrantPermissionsActivity.java @@ -17,14 +17,13 @@ package com.android.packageinstaller.permission.ui; import android.app.Activity; -import android.app.DialogFragment; -import android.app.Fragment; -import android.app.FragmentTransaction; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PermissionInfo; +import android.content.res.Resources; +import android.graphics.drawable.Icon; import android.hardware.camera2.utils.ArrayUtils; import android.os.Bundle; import android.text.SpannableString; @@ -39,12 +38,9 @@ import com.android.packageinstaller.permission.model.Permission; import com.android.packageinstaller.permission.model.PermissionGroup; public class GrantPermissionsActivity extends Activity implements - GrantPermissionFragment.OnRequestGrantPermissionGroupResult { + GrantPermissionViewHandler.OnRequestGrantPermissionGroupResult { private static final String LOG_TAG = "GrantPermissionsActivity"; - private static final String TAG_GRANT_PERMISSION_GROUP_FRAGMENT = - "TAG_GRANT_PERMISSION_GROUP_FRAGMENT"; - private static final int PERMISSION_GRANTED = 1; private static final int PERMISSION_DENIED = 2; private static final int PERMISSION_DENIED_RUNTIME = 3; @@ -55,11 +51,14 @@ public class GrantPermissionsActivity extends Activity implements private ArrayMap<String, GroupState> mRequestGrantPermissionGroups = new ArrayMap<>(); + private final GrantPermissionViewHandler mViewHandler = + new GrantPermissionViewHandler(this, this); private AppPermissions mAppPermissions; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); + setFinishOnTouchOutside(false); mRequestedPermissions = getIntent().getStringArrayExtra( PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES); @@ -94,6 +93,19 @@ public class GrantPermissionsActivity extends Activity implements if (!showNextPermissionGroupFragment()) { setResultAndFinish(); } + setContentView(mViewHandler.creatView()); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mViewHandler.onSaveInstanceState(outState); + } + + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + mViewHandler.loadSavedInstance(savedInstanceState); } private boolean showNextPermissionGroupFragment() { @@ -103,19 +115,6 @@ public class GrantPermissionsActivity extends Activity implements GroupState groupState = mRequestGrantPermissionGroups.valueAt(i); if (!groupState.mGroup.areRuntimePermissionsGranted() && groupState.mState == GroupState.STATE_UNKNOWN) { - // Make sure adding the fragment we will remove is not in flight. - getFragmentManager().executePendingTransactions(); - - // Remove old grant fragment if such exists. - FragmentTransaction transaction = getFragmentManager().beginTransaction(); - Fragment oldFragment = getFragmentManager().findFragmentByTag( - TAG_GRANT_PERMISSION_GROUP_FRAGMENT); - if (oldFragment != null) { - transaction.remove(oldFragment); - } - transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE); - transaction.commit(); - CharSequence appLabel = mAppPermissions.getAppLabel(); SpannableString message = new SpannableString(getString( R.string.permission_warning_template, appLabel, @@ -127,15 +126,20 @@ public class GrantPermissionsActivity extends Activity implements message.setSpan(new ForegroundColorSpan(color), appLabelStart, appLabelStart + appLabelLength, 0); - // Add the new grant fragment. + // Set the new grant view // TODO: Use a real message for the action. We need group action APIs - String pkg = groupState.mGroup.getIconPkg(); + Resources resources; + try { + resources = getPackageManager().getResourcesForApplication( + groupState.mGroup.getIconPkg()); + } catch (NameNotFoundException e) { + // Fallback to system. + resources = Resources.getSystem(); + } int icon = groupState.mGroup.getIconResId(); - DialogFragment newFragment = GrantPermissionFragment - .newInstance(groupState.mGroup.getName(), groupCount, i, - pkg, icon, message); - newFragment.show(getFragmentManager(), TAG_GRANT_PERMISSION_GROUP_FRAGMENT); + mViewHandler.showPermission(groupState.mGroup.getName(), groupCount, i, + Icon.createWithResource(resources, icon), message); return true; } } diff --git a/src/com/android/packageinstaller/permission/ui/ManualLayoutFrame.java b/src/com/android/packageinstaller/permission/ui/ManualLayoutFrame.java new file mode 100644 index 00000000..97049df8 --- /dev/null +++ b/src/com/android/packageinstaller/permission/ui/ManualLayoutFrame.java @@ -0,0 +1,66 @@ +/* + * 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; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.FrameLayout; + +/** + * Allows one standard layout pass, but afterwards holds getMeasuredHeight constant, + * however still allows drawing larger at the size needed by its children. This allows + * a dialog to tell the window the height is constant (with keeps its position constant) + * but allows the view to grow downwards for animation. + */ +public class ManualLayoutFrame extends FrameLayout { + + private int mDesiredHeight; + private int mHeight; + + public ManualLayoutFrame(Context context, AttributeSet attrs) { + super(context, attrs); + setClipChildren(false); + setClipToPadding(false); + } + + public int getLayoutHeight() { + return mDesiredHeight; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + mDesiredHeight = getMeasuredHeight(); + if (mHeight == 0 && mDesiredHeight != 0) { + // Record the first non-zero height, this will be the height henceforth. + mHeight = mDesiredHeight; + } + if (mHeight != 0) { + // Always report the same height + setMeasuredDimension(getMeasuredWidth(), mHeight); + } + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + if (mDesiredHeight != 0) { + // Draw at height we expect to be. + setBottom(getTop() + mDesiredHeight); + bottom = top + mDesiredHeight; + } + super.onLayout(changed, left, top, right, bottom); + } +} |