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

Commit ab272fa2 authored by HrX03's avatar HrX03 Committed by Bruno Martins
Browse files

VolumeDialog: Convert CustomDialog to a View and configure ViewTreeObserver

* This lets us pass touch events to the parent view while
  using match_parent width to avoid jumpy animations.

Thanks to Alex (treydev) for the ViewTreeInspector animation trick!

Change-Id: Ie3c9af4d9f45b688c6a82ec37ae1c7d1a116ac8d
parent 7a4627c5
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -27,7 +27,7 @@
    <FrameLayout
        android:id="@+id/volume_dialog"
        android:minWidth="@dimen/volume_dialog_panel_width"
        android:layout_width="wrap_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="right"
        android:layout_gravity="right"
+1 −1
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@
    <LinearLayout
        android:id="@+id/volume_dialog"
        android:minWidth="@dimen/volume_dialog_panel_width"
        android:layout_width="wrap_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="right"
        android:layout_gravity="right"
+95 −84
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Region;
import android.graphics.drawable.ColorDrawable;
import android.media.AudioManager;
import android.media.AudioSystem;
@@ -70,12 +71,16 @@ import android.util.Slog;
import android.util.SparseBooleanArray;
import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.AccessibilityDelegate;
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
import android.view.ViewStub;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.InternalInsetsInfo;
import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
import android.view.Window;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
@@ -136,12 +141,13 @@ public class VolumeDialogImpl implements VolumeDialog,
    static final int DIALOG_HIDE_ANIMATION_DURATION = 250;

    private final Context mContext;
    private WindowManager mWindowManager;
    private WindowManager.LayoutParams mWindowParams;
    private final H mHandler = new H();
    private final VolumeDialogController mController;
    private final DeviceProvisionedController mDeviceProvisionedController;

    private Window mWindow;
    private CustomDialog mDialog;
    private View mDialog;
    private ViewGroup mDialogView;
    private ViewGroup mDialogRowsView;
    private ViewGroup mRinger;
@@ -189,6 +195,7 @@ public class VolumeDialogImpl implements VolumeDialog,
        mController = Dependency.get(VolumeDialogController.class);
        mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
        mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        mAccessibilityMgr = Dependency.get(AccessibilityManagerWrapper.class);
        mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
        mShowActiveStreamOnly = showActiveStreamOnly();
@@ -221,8 +228,6 @@ public class VolumeDialogImpl implements VolumeDialog,
    }

    private void initDialog() {
        mDialog = new CustomDialog(mContext);

        // Gravitate various views left/right depending on panel placement setting.
        final int panelGravity = mVolumePanelOnLeft ? Gravity.LEFT : Gravity.RIGHT;

@@ -230,50 +235,36 @@ public class VolumeDialogImpl implements VolumeDialog,
        mHovering = false;
        mShowing = false;
        mExpanded = false;
        mWindow = mDialog.getWindow();
        mWindow.requestFeature(Window.FEATURE_NO_TITLE);
        mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
                | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
        mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
        mWindowParams = new WindowManager.LayoutParams();
        mWindowParams.flags &= ~WindowManager.LayoutParams.FLAG_DIM_BEHIND;
        mWindowParams.flags &= ~WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
        mWindowParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
        mWindow.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
        mWindow.setWindowAnimations(com.android.internal.R.style.Animation_Toast);
        WindowManager.LayoutParams lp = mWindow.getAttributes();
        lp.format = PixelFormat.TRANSLUCENT;
        lp.setTitle(VolumeDialogImpl.class.getSimpleName());
        lp.windowAnimations = -1;
        lp.gravity = panelGravity | Gravity.CENTER_VERTICAL;
        mWindow.setAttributes(lp);
        mWindow.setLayout(WRAP_CONTENT, WRAP_CONTENT);

        mDialog.setContentView(R.layout.volume_dialog);
        mDialogView = mDialog.findViewById(R.id.volume_dialog);
        mDialogView.setAlpha(0);
        mDialog.setCanceledOnTouchOutside(true);
        mDialog.setOnShowListener(dialog -> {
            mDialogView.setTranslationX(getAnimatorX());
            mDialogView.setAlpha(0);
            mDialogView.animate()
                    .alpha(1)
                    .translationX(0)
                    .setDuration(DIALOG_SHOW_ANIMATION_DURATION)
                    .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
                    .withEndAction(() -> {
                        if (!Prefs.getBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, false)) {
                            if (mRingerIcon != null) {
                                mRingerIcon.postOnAnimationDelayed(
                                        getSinglePressFor(mRingerIcon), 1500);
                | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
        mWindowParams.type = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
        mWindowParams.format = PixelFormat.TRANSLUCENT;
        mWindowParams.windowAnimations = -1;
        mDialog = LayoutInflater.from(mContext).inflate(R.layout.volume_dialog,
                (ViewGroup) null, false);

        mDialog.setOnTouchListener((v, event) -> {
            if (mShowing) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_OUTSIDE:
                    case MotionEvent.ACTION_DOWN:
                        dismissH(Events.DISMISS_REASON_TOUCH_OUTSIDE);
                        return true;
                }
            }
                    })
                    .start();
            return false;
        });

        mDialogView = mDialog.findViewById(R.id.volume_dialog);
        mDialogView.setAlpha(0);

        mDialogView.setOnHoverListener((v, event) -> {
            int action = event.getActionMasked();
            mHovering = (action == MotionEvent.ACTION_HOVER_ENTER)
@@ -282,6 +273,11 @@ public class VolumeDialogImpl implements VolumeDialog,
            return true;
        });

        FrameLayout.LayoutParams dialogViewLP =
                (FrameLayout.LayoutParams) mDialogView.getLayoutParams();
        dialogViewLP.gravity = Gravity.CENTER_VERTICAL;
        mDialogView.setLayoutParams(dialogViewLP);

        mDialogRowsView = mDialog.findViewById(R.id.volume_dialog_rows);
        mRinger = mDialog.findViewById(R.id.ringer);
        if (mRinger != null) {
@@ -343,6 +339,22 @@ public class VolumeDialogImpl implements VolumeDialog,
        initODICaptionsH();
    }

    private final OnComputeInternalInsetsListener mInsetsListener = internalInsetsInfo -> {
        internalInsetsInfo.touchableRegion.setEmpty();
        internalInsetsInfo.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
        View main = mDialog.findViewById(R.id.main);
        int[] mainLocation = new int[2];
        main.getLocationOnScreen(mainLocation);
        int[] dialogLocation = new int[2];
        mDialogView.getLocationOnScreen(dialogLocation);
        internalInsetsInfo.touchableRegion.set(new Region(
                mainLocation[0],
                dialogLocation[1],
                mainLocation[0] + main.getWidth(),
                dialogLocation[1] + mDialogView.getHeight()
        ));
    };

    // Helper to set layout gravity.
    // Particular useful when the ViewGroup in question
    // is different for portait vs landscape.
@@ -478,7 +490,7 @@ public class VolumeDialogImpl implements VolumeDialog,
        row.iconMuteRes = iconMuteRes;
        row.important = important;
        row.defaultStream = defaultStream;
        row.view = mDialog.getLayoutInflater().inflate(R.layout.volume_dialog_row, null);
        row.view = LayoutInflater.from(mContext).inflate(R.layout.volume_dialog_row, null);
        row.view.setId(row.stream);
        row.view.setTag(row);
        row.header = row.view.findViewById(R.id.volume_row_header);
@@ -496,6 +508,7 @@ public class VolumeDialogImpl implements VolumeDialog,
        row.icon.setImageResource(iconRes);
        if (row.stream != AudioSystem.STREAM_ACCESSIBILITY) {
            row.icon.setOnClickListener(v -> {
                rescheduleTimeoutH();
                Events.writeEvent(mContext, Events.EVENT_ICON_CLICK, row.stream, row.iconState);
                mController.setActiveStream(row.stream);
                if (row.stream == AudioManager.STREAM_RING) {
@@ -542,6 +555,7 @@ public class VolumeDialogImpl implements VolumeDialog,
        }
        if (mMediaOutputIcon != null) {
            mMediaOutputIcon.setOnClickListener(v -> {
                rescheduleTimeoutH();
                Events.writeEvent(mContext, Events.EVENT_SETTINGS_CLICK);
                Intent intent = new Intent(ACTION_MEDIA_OUTPUT);
                dismissH(DISMISS_REASON_SETTINGS_CLICKED);
@@ -558,6 +572,7 @@ public class VolumeDialogImpl implements VolumeDialog,
        }
        if (mExpandRows != null) {
            mExpandRows.setOnClickListener(v -> {
                rescheduleTimeoutH();
                Util.setVisOrGone(findRow(AudioManager.STREAM_RING).view, !mExpanded);
                Util.setVisOrGone(findRow(STREAM_ALARM).view, !mExpanded);

@@ -572,6 +587,7 @@ public class VolumeDialogImpl implements VolumeDialog,
        if (mRingerIcon != null) {
            mRingerIcon.setAccessibilityLiveRegion(ACCESSIBILITY_LIVE_REGION_POLITE);
            mRingerIcon.setOnClickListener(v -> {
                rescheduleTimeoutH();
                Prefs.putBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, true);
                final StreamState ss = mState.states.get(AudioManager.STREAM_RING);
                if (ss == null) {
@@ -631,11 +647,11 @@ public class VolumeDialogImpl implements VolumeDialog,
        if (!mHasSeenODICaptionsTooltip && mODICaptionsTooltipViewStub != null) {
            mODICaptionsTooltipView = mODICaptionsTooltipViewStub.inflate();
            mODICaptionsTooltipView.findViewById(R.id.dismiss).setOnClickListener(v -> {
                rescheduleTimeoutH();
                hideCaptionsTooltip();
                Events.writeEvent(mContext, Events.EVENT_ODI_CAPTIONS_TOOLTIP_CLICK);
            });
            mODICaptionsTooltipViewStub = null;
            rescheduleTimeoutH();
        }

        if (mODICaptionsTooltipView != null) {
@@ -783,8 +799,36 @@ public class VolumeDialogImpl implements VolumeDialog,
        }

        initSettingsH();
        mDialog.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsListener);

        if (!mShowing && !mDialog.isShown()) {
            if (!isLandscape()) {
                mDialogView.setTranslationX(
                        (mVolumePanelOnLeft ? -1 : 1) * mDialogView.getWidth() / 2.0f);
            }
            mDialogView.setAlpha(0);
            mDialogView.animate()
                    .alpha(1)
                    .translationX(0)
                    .setDuration(DIALOG_SHOW_ANIMATION_DURATION)
                    .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
                    .withStartAction(() -> {
                        if (!mDialog.isShown()) {
                            mWindowManager.addView(mDialog, mWindowParams);
                        }
                    })
                    .withEndAction(() -> {
                        if (!Prefs.getBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, false)) {
                            if (mRingerIcon != null) {
                                mRingerIcon.postOnAnimationDelayed(
                                        getSinglePressFor(mRingerIcon), 1500);
                            }
                        }
                        mShowing = true;
        mDialog.show();
                    })
                    .start();
        }

        Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
        mController.notifyVisible(true);
        mController.getCaptionsComponentState(false);
@@ -845,10 +889,12 @@ public class VolumeDialogImpl implements VolumeDialog,
                .setDuration(DIALOG_HIDE_ANIMATION_DURATION)
                .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
                .withEndAction(() -> mHandler.postDelayed(() -> {
                    mDialog.dismiss();
                    tryToRemoveCaptionsTooltip();
                    if (mDialog.isShown()){
                        mWindowManager.removeViewImmediate(mDialog);
                    }
                    mExpanded = false;
                    mExpandRows.setExpanded(mExpanded);
                    tryToRemoveCaptionsTooltip();
                    mController.notifyVisible(false);
                }, 50));
        animator.translationX(getAnimatorX());
@@ -1065,7 +1111,6 @@ public class VolumeDialogImpl implements VolumeDialog,
            updateVolumeRowH(row);
        }
        updateRingerH();
        mWindow.setTitle(composeWindowTitle());
    }

    CharSequence composeWindowTitle() {
@@ -1385,7 +1430,7 @@ public class VolumeDialogImpl implements VolumeDialog,

        @Override
        public void onConfigurationChanged() {
            mDialog.dismiss();
            if (mDialog.isShown()) mWindowManager.removeViewImmediate(mDialog);
            mConfigChanged = true;
        }

@@ -1454,41 +1499,6 @@ public class VolumeDialogImpl implements VolumeDialog,
        }
    }

    private final class CustomDialog extends Dialog implements DialogInterface {
        public CustomDialog(Context context) {
            super(context, R.style.qs_theme);
        }

        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            rescheduleTimeoutH();
            return super.dispatchTouchEvent(ev);
        }

        @Override
        protected void onStart() {
            super.setCanceledOnTouchOutside(true);
            super.onStart();
        }

        @Override
        protected void onStop() {
            super.onStop();
            mHandler.sendEmptyMessage(H.RECHECK_ALL);
        }

        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (mShowing) {
                if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
                    dismissH(Events.DISMISS_REASON_TOUCH_OUTSIDE);
                    return true;
                }
            }
            return false;
        }
    }

    private final class VolumeSeekBarChangeListener implements OnSeekBarChangeListener {
        private final VolumeRow mRow;

@@ -1498,6 +1508,7 @@ public class VolumeDialogImpl implements VolumeDialog,

        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            rescheduleTimeoutH();
            if (mRow.ss == null) return;
            if (D.BUG) Log.d(TAG, AudioSystem.streamToString(mRow.stream)
                    + " onProgressChanged " + progress + " fromUser=" + fromUser);