Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 97ec3aa0 authored by Svetoslav Ganov's avatar Svetoslav Ganov
Browse files

Revert "Make the permission request dialog's layout robust"

This reverts commit 7b8c4681.

Change-Id: I86f8a5a75102350060c4fa3ba903260683e921c6
parent 7b8c4681
Loading
Loading
Loading
Loading
+55 −72
Original line number Diff line number Diff line
@@ -17,36 +17,25 @@
<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:clipChildren="false">

    android:layout_height="fill_parent" >
    <LinearLayout
        android:id="@+id/dialog_container"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:paddingTop="24dip"
        android:paddingBottom="8dip"
        android:paddingStart="22dip"
        android:paddingEnd="16dip"
        android:orientation="vertical">

        <FrameLayout
            android:id="@+id/desc_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingTop="24dip"
            android:paddingStart="22dip"
            android:paddingEnd="16dip"
            android:background="?android:attr/colorBackgroundFloating">
            android:layout_height="wrap_content" >
            <include
                layout="@layout/permission_description" />
        </FrameLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:paddingBottom="8dip"
            android:paddingStart="22dip"
            android:paddingEnd="16dip"
            android:background="?android:attr/colorBackgroundFloating">

        <CheckBox
            android:id="@+id/do_not_ask_checkbox"
            android:layout_width="fill_parent"
@@ -57,7 +46,7 @@
            android:visibility="gone">
        </CheckBox>

            <com.android.packageinstaller.permission.ui.ButtonBarLayout
        <com.android.internal.widget.ButtonBarLayout
            android:id="@+id/button_group"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
@@ -73,8 +62,8 @@
                android:paddingBottom="4dp"
                android:paddingEnd="12dp"
                android:singleLine="true"
                    style="?android:attr/textAppearanceSmall"
                    android:textColor="?android:attr/textColorSecondary"
                style="@android:style/TextAppearance.Material.Body2"
                android:textColor="@color/grant_permissions_progress_color"
                android:visibility="invisible">
            </TextView>

@@ -83,16 +72,14 @@
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_weight="1"
                    android:visibility="invisible" >
                </Space>
                android:visibility="invisible" />

            <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" >
                </Button>
                android:text="@string/grant_dialog_button_deny" />

            <Button
                android:id="@+id/permission_allow_button"
@@ -100,13 +87,9 @@
                android:layout_height="wrap_content"
                style="?android:attr/buttonBarButtonStyle"
                android:layout_marginStart="8dip"
                    android:text="@string/grant_dialog_button_allow" >
                </Button>
                android:text="@string/grant_dialog_button_allow" />

            </com.android.packageinstaller.permission.ui.ButtonBarLayout>
        </com.android.internal.widget.ButtonBarLayout>

    </LinearLayout>

    </LinearLayout>

</com.android.packageinstaller.permission.ui.ManualLayoutFrame>
+1 −6
Original line number Diff line number Diff line
@@ -22,10 +22,5 @@
    </style>

    <style name="GrantPermissions"
           parent="@android:style/Theme.DeviceDefault.Light.Dialog.NoActionBar">
        <item name="*android:windowFixedHeightMajor">100%</item>
        <item name="*android:windowFixedHeightMinor">100%</item>
        <item name="android:windowBackground">@android:color/transparent</item>
    </style>

            parent="@android:style/Theme.DeviceDefault.Light.Dialog.NoActionBar" />
</resources>
+1 −18
Original line number Diff line number Diff line
@@ -25,7 +25,6 @@ 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.Configuration;
import android.content.res.Resources;
import android.graphics.drawable.Icon;
import android.hardware.camera2.utils.ArrayUtils;
@@ -44,9 +43,8 @@ import com.android.packageinstaller.R;
import com.android.packageinstaller.permission.model.AppPermissionGroup;
import com.android.packageinstaller.permission.model.AppPermissions;
import com.android.packageinstaller.permission.model.Permission;
import com.android.packageinstaller.permission.ui.handheld.GrantPermissionsViewHandlerImpl;
import com.android.packageinstaller.permission.utils.SafetyNetLogger;
import libcore.util.EmptyArray;
import com.android.packageinstaller.permission.utils.Utils;

import java.util.ArrayList;
import java.util.LinkedHashMap;
@@ -174,21 +172,6 @@ public class GrantPermissionsActivity extends OverlayTouchActivity
        }
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        // We need to relayout the window as dialog width may be
        // different in landscape vs portrait which affect the min
        // window height needed to show all content. We have to
        // re-add the window to force it to be resized if needed.
        View decor = getWindow().getDecorView();
        getWindowManager().removeViewImmediate(decor);
        getWindowManager().addView(decor, decor.getLayoutParams());
        if (mViewHandler instanceof GrantPermissionsViewHandlerImpl) {
            ((GrantPermissionsViewHandlerImpl) mViewHandler).onConfigurationChanged();
        }
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        View rootView = getWindow().getDecorView();
+214 −106
Original line number Diff line number Diff line
@@ -16,14 +16,23 @@

package com.android.packageinstaller.permission.ui;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Resources;
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.ViewParent;
import android.view.ViewRootImpl;
import android.view.WindowManager.LayoutParams;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.Button;
@@ -34,7 +43,9 @@ import android.widget.TextView;
import com.android.internal.widget.ButtonBarLayout;
import com.android.packageinstaller.R;

public final class GrantPermissionsViewHandlerImpl
import java.util.ArrayList;

final class GrantPermissionsDefaultViewHandler
        implements GrantPermissionsViewHandler, OnClickListener {

    public static final String ARG_GROUP_NAME = "ARG_GROUP_NAME";
@@ -46,8 +57,14 @@ public final class GrantPermissionsViewHandlerImpl
    public static final String ARG_GROUP_DO_NOT_ASK_CHECKED = "ARG_GROUP_DO_NOT_ASK_CHECKED";

    // Animation parameters.
    private static final long OUT_DURATION = 200;
    private static final long IN_DURATION = 300;
    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 static final long DO_NOT_ASK_CHECK_DELAY = 450;

    private final Context mContext;

@@ -67,13 +84,22 @@ public final class GrantPermissionsViewHandlerImpl
    private CheckBox mDoNotAskCheckbox;
    private Button mAllowButton;

    private ArrayList<ViewHeightController> mHeightControllers;
    private ManualLayoutFrame mRootView;

    // Needed for animation
    private ViewGroup mDescContainer;
    private ViewGroup mCurrentDesc;
    private ViewGroup mNextDesc;

    private ViewGroup mDialogContainer;
    private ButtonBarLayout mButtonBar;

    private final Runnable mUpdateGroup = new Runnable() {
        @Override
        public void run() {
            updateGroup();
        }
    };

    GrantPermissionsDefaultViewHandler(Context context) {
        mContext = context;
@@ -132,148 +158,173 @@ public final class GrantPermissionsViewHandlerImpl
        }
    }

    public void onConfigurationChanged() {
        mRootView.onConfigurationChanged();
    private void animateToPermission() {
        if (mHeightControllers == null) {
            // We need to manually control the height of any views heigher than the root that
            // we inflate.  Find all the views up to the root and create ViewHeightControllers for
            // them.
            mHeightControllers = new ArrayList<>();
            ViewRootImpl viewRoot = mRootView.getViewRootImpl();
            ViewParent v = mRootView.getParent();
            addHeightController(mDialogContainer);
            addHeightController(mRootView);
            while (v != viewRoot) {
                addHeightController((View) v);
                v = v.getParent();
            }
            // On the heighest level view, we want to setTop rather than setBottom to control the
            // height, this way the dialog will grow up rather than down.
            ViewHeightController realRootView =
                    mHeightControllers.get(mHeightControllers.size() - 1);
            realRootView.setControlTop(true);
        }

        // 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();
                if (startPositions.get(R.id.do_not_ask_checkbox) == 0
                        && endPositions.get(R.id.do_not_ask_checkbox) != 0) {
                    // If the checkbox didn't have a position before but has one now then set
                    // the start position to the end position because it just became visible.
                    startPositions.put(R.id.do_not_ask_checkbox,
                            endPositions.get(R.id.do_not_ask_checkbox));
                }
                animateYPos(startPositions, endPositions, endHeight - startHeight);
            }
        });

    private void animateOldContent(Runnable callback) {
        // Fade out old description group and scale out the icon for it.
        Interpolator interpolator = AnimationUtils.loadInterpolator(mContext,
                android.R.interpolator.fast_out_linear_in);

        // Icon scale to zero
        mIconView.animate()
                .scaleX(0)
                .scaleY(0)
                .setDuration(OUT_DURATION)
                .setStartDelay(FADE_OUT_START_DELAY)
                .setDuration(FADE_OUT_START_LENGTH)
                .setInterpolator(interpolator)
                .start();

        // Description fade out
        mCurrentDesc.animate()
                .alpha(0)
                .setDuration(OUT_DURATION)
                .setStartDelay(FADE_OUT_START_DELAY)
                .setDuration(FADE_OUT_START_LENGTH)
                .setInterpolator(interpolator)
                .withEndAction(callback)
                .setListener(null)
                .start();

        // Checkbox fade out if needed
        if (!mShowDonNotAsk && mDoNotAskCheckbox.getVisibility() == View.VISIBLE) {
            mDoNotAskCheckbox.animate()
                    .alpha(0)
                    .setDuration(OUT_DURATION)
                    .setInterpolator(interpolator)
                    .start();
        }
    }
        // Update the index of the permission after the animations have started.
        mCurrentGroupView.getHandler().postDelayed(mUpdateGroup, GROUP_UPDATE_DELAY);

    private void attachNewContent(final Runnable callback) {
        mCurrentDesc = (ViewGroup) LayoutInflater.from(mContext).inflate(
        // Add the new description and translate it in.
        mNextDesc = (ViewGroup) LayoutInflater.from(mContext).inflate(
                R.layout.permission_description, mDescContainer, false);
        mDescContainer.removeAllViews();
        mDescContainer.addView(mCurrentDesc);

        mDialogContainer.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
                @Override
                public void onLayoutChange(View v, int left, int top, int right, int bottom,
                        int oldLeft, int oldTop, int oldRight, int oldBottom) {
                    mDialogContainer.removeOnLayoutChangeListener(this);

                    // Prepare new content to the right to be moved in
                    final int containerWidth = mDescContainer.getWidth();
                    mCurrentDesc.setTranslationX(containerWidth);

                    // How much scale for the dialog to appear the same?
                    final int oldDynamicHeight = oldBottom - oldTop - mButtonBar.getHeight();
                    final float scaleY = (float) oldDynamicHeight / mDescContainer.getHeight();

                    // How much to translate for the dialog to appear the same?
                    final int translationCompensatingScale = (int) (scaleY
                            * mDescContainer.getHeight() - mDescContainer.getHeight()) / 2;
                    final int translationY = (oldTop - top) + translationCompensatingScale;

                    // Animate to the current layout
                    mDescContainer.setScaleY(scaleY);
                    mDescContainer.setTranslationY(translationY);
                    mDescContainer.animate()
                            .translationY(0)
                            .scaleY(1.0f)
                            .setInterpolator(AnimationUtils.loadInterpolator(mContext,
                                    android.R.interpolator.linear_out_slow_in))
                            .setDuration(IN_DURATION)
                            .withEndAction(callback)
                            .start();
                }
            }
        );

        mMessageView = (TextView) mCurrentDesc.findViewById(R.id.permission_message);
        mIconView = (ImageView) mCurrentDesc.findViewById(R.id.permission_icon);

        final boolean doNotAskWasShown = mDoNotAskCheckbox.getVisibility() == View.VISIBLE;

        mMessageView = (TextView) mNextDesc.findViewById(R.id.permission_message);
        mIconView = (ImageView) mNextDesc.findViewById(R.id.permission_icon);
        updateDescription();
        updateGroup();
        updateDoNotAskCheckBox();

        if (!doNotAskWasShown && mShowDonNotAsk) {
            mDoNotAskCheckbox.setAlpha(0);
        }
    }
        int width = mDescContainer.getRootView().getWidth();
        mDescContainer.addView(mNextDesc);
        mNextDesc.setTranslationX(width);

    private void animateNewContent() {
        Interpolator interpolator = AnimationUtils.loadInterpolator(mContext,
                android.R.interpolator.linear_out_slow_in);
        final View oldDesc = mCurrentDesc;
        // Remove the old view from the description, so that we can shrink if necessary.
        mDescContainer.removeView(oldDesc);
        oldDesc.setPadding(mDescContainer.getLeft(), mDescContainer.getTop(),
                mRootView.getRight() - mDescContainer.getRight(), 0);
        mRootView.addView(oldDesc);

        // Description slide in
        mCurrentDesc.animate()
        mCurrentDesc = mNextDesc;
        mNextDesc.animate()
                .translationX(0)
                .setDuration(IN_DURATION)
                .setInterpolator(interpolator)
                .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.
                        mRootView.removeView(oldDesc);
                    }
                })
                .start();

        // Checkbox fade in if needed
        if (mShowDonNotAsk && mDoNotAskCheckbox.getVisibility() == View.VISIBLE
                && mDoNotAskCheckbox.getAlpha() < 1.0f) {
            mDoNotAskCheckbox.setAlpha(0);
            mDoNotAskCheckbox.animate()
                    .alpha(1.0f)
                    .setDuration(IN_DURATION)
                    .setInterpolator(interpolator)
        boolean visibleBefore = mDoNotAskCheckbox.getVisibility() == View.VISIBLE;
        updateDoNotAskCheckBox();
        boolean visibleAfter = mDoNotAskCheckbox.getVisibility() == View.VISIBLE;
        if (visibleBefore != visibleAfter) {
            Animation anim = AnimationUtils.loadAnimation(mContext,
                    visibleAfter ? android.R.anim.fade_in : android.R.anim.fade_out);
            anim.setStartOffset(visibleAfter ? DO_NOT_ASK_CHECK_DELAY : 0);
            mDoNotAskCheckbox.startAnimation(anim);
        }
    }

    private void addHeightController(View v) {
        ViewHeightController heightController = new ViewHeightController(v);
        heightController.setHeight(v.getHeight());
        mHeightControllers.add(heightController);
    }

    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();
            }
        }

    private void animateToPermission() {
        // Remove the old content
        animateOldContent(new Runnable() {
            @Override
            public void run() {
                // Add the new content
                attachNewContent(new Runnable() {
                    @Override
                    public void run() {
                        // Animate the new content
                        animateNewContent();
        for (int i = 0; i < mHeightControllers.size(); i++) {
            mHeightControllers.get(i).animateAddHeight(heightDiff);
        }
                });
            }
        });
    }

    @Override
    public View createView() {
        mRootView = (ManualLayoutFrame) LayoutInflater.from(mContext)
                .inflate(R.layout.grant_permissions, null);
        mButtonBar = (ButtonBarLayout) mRootView.findViewById(R.id.button_group);
        mButtonBar.setAllowStacking(true);
        ((ButtonBarLayout) mRootView.findViewById(R.id.button_group)).setAllowStacking(
                Resources.getSystem().getBoolean(
                        com.android.internal.R.bool.allow_stacked_button_bar));

        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);
        mDoNotAskCheckbox = (CheckBox) mRootView.findViewById(R.id.do_not_ask_checkbox);
        mAllowButton = (Button) mRootView.findViewById(R.id.permission_allow_button);

        mDialogContainer = (ViewGroup) mRootView.findViewById(R.id.dialog_container);
        mDescContainer = (ViewGroup) mRootView.findViewById(R.id.desc_container);
        mCurrentDesc = (ViewGroup) mRootView.findViewById(R.id.perm_desc_root);

@@ -351,4 +402,61 @@ public final class GrantPermissionsViewHandlerImpl
            mResultListener.onPermissionGrantResult(mGroupName, false, doNotAskAgain);
        }
    }

    /**
     * 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.
     */
    private static final class ViewHeightController implements OnLayoutChangeListener {
        private final View mView;
        private int mHeight;
        private int mNextHeight;
        private boolean mControlTop;
        private ObjectAnimator mAnimator;

        public ViewHeightController(View view) {
            mView = view;
            mView.addOnLayoutChangeListener(this);
        }

        public void setControlTop(boolean controlTop) {
            mControlTop = controlTop;
        }

        public void animateAddHeight(int heightDiff) {
            if (heightDiff != 0) {
                if (mNextHeight == 0) {
                    mNextHeight = mHeight;
                }
                mNextHeight += heightDiff;
                if (mAnimator != null) {
                    mAnimator.cancel();
                }
                mAnimator = ObjectAnimator.ofInt(this, "height", mHeight, mNextHeight);
                mAnimator.setStartDelay(SIZE_START_DELAY);
                mAnimator.setDuration(SIZE_START_LENGTH);
                mAnimator.start();
            }
        }

        public void setHeight(int height) {
            mHeight = height;
            updateHeight();
        }

        @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.
            updateHeight();
        }

        private void updateHeight() {
            if (mControlTop) {
                mView.setTop(mView.getBottom() - mHeight);
            } else {
                mView.setBottom(mView.getTop() + mHeight);
            }
        }
    }
}
+31 −29

File changed.

Preview size limit exceeded, changes collapsed.