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

Commit 16fafae9 authored by Charlie Boutier's avatar Charlie Boutier
Browse files

Revert "Refactor WirelessChargingAnimation."

Revert submission 19754180-wirelessripplerefactor-master

Reason for revert: Build breakage b/244142057
Reverted Changes:
I2fbb8f0c6:Refactor WirelessChargingAnimation.
I3f0db106c:Refactor WirelessRippleAnimation

Change-Id: I7f093be2f4c4fc8679ebf93bee12f2c9c37e4b39
parent 77b4efa8
Loading
Loading
Loading
Loading
+254 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.systemui.charging;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.util.Slog;
import android.view.Gravity;
import android.view.WindowManager;

import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.ripple.RippleShader.RippleShape;

/**
 * A WirelessChargingAnimation is a view containing view + animation for wireless charging.
 * @hide
 */
public class WirelessChargingAnimation {
    public static final int UNKNOWN_BATTERY_LEVEL = -1;
    public static final long DURATION = 1500;
    private static final String TAG = "WirelessChargingView";
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

    private final WirelessChargingView mCurrentWirelessChargingView;
    private static WirelessChargingView mPreviousWirelessChargingView;

    public interface Callback {
        void onAnimationStarting();
        void onAnimationEnded();
    }

    /**
     * Constructs an empty WirelessChargingAnimation object.  If looper is null,
     * Looper.myLooper() is used.  Must set
     * {@link WirelessChargingAnimation#mCurrentWirelessChargingView}
     * before calling {@link #show} - can be done through {@link #makeWirelessChargingAnimation}.
     * @hide
     */
    private WirelessChargingAnimation(@NonNull Context context, @Nullable Looper looper,
            int transmittingBatteryLevel, int batteryLevel, Callback callback, boolean isDozing,
            RippleShape rippleShape, UiEventLogger uiEventLogger) {
        mCurrentWirelessChargingView = new WirelessChargingView(context, looper,
                transmittingBatteryLevel, batteryLevel, callback, isDozing,
                rippleShape, uiEventLogger);
    }

    /**
     * Creates a wireless charging animation object populated with next view.
     *
     * @hide
     */
    public static WirelessChargingAnimation makeWirelessChargingAnimation(@NonNull Context context,
            @Nullable Looper looper, int transmittingBatteryLevel, int batteryLevel,
            Callback callback, boolean isDozing, RippleShape rippleShape,
            UiEventLogger uiEventLogger) {
        return new WirelessChargingAnimation(context, looper, transmittingBatteryLevel,
                batteryLevel, callback, isDozing, rippleShape, uiEventLogger);
    }

    /**
     * Creates a charging animation object using mostly default values for non-dozing and unknown
     * battery level without charging number shown.
     */
    public static WirelessChargingAnimation makeChargingAnimationWithNoBatteryLevel(
            @NonNull Context context, RippleShape rippleShape, UiEventLogger uiEventLogger) {
        return makeWirelessChargingAnimation(context, null,
                UNKNOWN_BATTERY_LEVEL, UNKNOWN_BATTERY_LEVEL, null, false,
                rippleShape, uiEventLogger);
    }

    /**
     * Show the view for the specified duration.
     */
    public void show(long delay) {
        if (mCurrentWirelessChargingView == null ||
                mCurrentWirelessChargingView.mNextView == null) {
            throw new RuntimeException("setView must have been called");
        }

        if (mPreviousWirelessChargingView != null) {
            mPreviousWirelessChargingView.hide(0);
        }

        mPreviousWirelessChargingView = mCurrentWirelessChargingView;
        mCurrentWirelessChargingView.show(delay);
        mCurrentWirelessChargingView.hide(delay + DURATION);
    }

    private static class WirelessChargingView {
        private static final int SHOW = 0;
        private static final int HIDE = 1;

        private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
        private final Handler mHandler;
        private final UiEventLogger mUiEventLogger;

        private int mGravity;
        private WirelessChargingLayout mView;
        private WirelessChargingLayout mNextView;
        private WindowManager mWM;
        private Callback mCallback;

        public WirelessChargingView(Context context, @Nullable Looper looper,
                int transmittingBatteryLevel, int batteryLevel, Callback callback,
                boolean isDozing, RippleShape rippleShape, UiEventLogger uiEventLogger) {
            mCallback = callback;
            mNextView = new WirelessChargingLayout(context, transmittingBatteryLevel, batteryLevel,
                    isDozing, rippleShape);
            mGravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER;
            mUiEventLogger = uiEventLogger;

            final WindowManager.LayoutParams params = mParams;
            params.height = WindowManager.LayoutParams.MATCH_PARENT;
            params.width = WindowManager.LayoutParams.MATCH_PARENT;
            params.format = PixelFormat.TRANSLUCENT;
            params.type = WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
            params.setTitle("Charging Animation");
            params.layoutInDisplayCutoutMode =
                    WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
            params.setFitInsetsTypes(0 /* ignore all system bar insets */);
            params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                    | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
            params.setTrustedOverlay();

            if (looper == null) {
                // Use Looper.myLooper() if looper is not specified.
                looper = Looper.myLooper();
                if (looper == null) {
                    throw new RuntimeException(
                            "Can't display wireless animation on a thread that has not called "
                                    + "Looper.prepare()");
                }
            }

            mHandler = new Handler(looper, null) {
                @Override
                public void handleMessage(Message msg) {
                    switch (msg.what) {
                        case SHOW: {
                            handleShow();
                            break;
                        }
                        case HIDE: {
                            handleHide();
                            // Don't do this in handleHide() because it is also invoked by
                            // handleShow()
                            mNextView = null;
                            break;
                        }
                    }
                }
            };
        }

        public void show(long delay) {
            if (DEBUG) Slog.d(TAG, "SHOW: " + this);
            mHandler.sendMessageDelayed(Message.obtain(mHandler, SHOW), delay);
        }

        public void hide(long duration) {
            mHandler.removeMessages(HIDE);

            if (DEBUG) Slog.d(TAG, "HIDE: " + this);
            mHandler.sendMessageDelayed(Message.obtain(mHandler, HIDE), duration);
        }

        private void handleShow() {
            if (DEBUG) {
                Slog.d(TAG, "HANDLE SHOW: " + this + " mView=" + mView + " mNextView="
                        + mNextView);
            }

            if (mView != mNextView) {
                // remove the old view if necessary
                handleHide();
                mView = mNextView;
                Context context = mView.getContext().getApplicationContext();
                String packageName = mView.getContext().getOpPackageName();
                if (context == null) {
                    context = mView.getContext();
                }
                mWM = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
                mParams.packageName = packageName;
                mParams.hideTimeoutMilliseconds = DURATION;

                if (mView.getParent() != null) {
                    if (DEBUG) Slog.d(TAG, "REMOVE! " + mView + " in " + this);
                    mWM.removeView(mView);
                }
                if (DEBUG) Slog.d(TAG, "ADD! " + mView + " in " + this);

                try {
                    if (mCallback != null) {
                        mCallback.onAnimationStarting();
                    }
                    mWM.addView(mView, mParams);
                    mUiEventLogger.log(WirelessChargingRippleEvent.WIRELESS_RIPPLE_PLAYED);
                } catch (WindowManager.BadTokenException e) {
                    Slog.d(TAG, "Unable to add wireless charging view. " + e);
                }
            }
        }

        private void handleHide() {
            if (DEBUG) Slog.d(TAG, "HANDLE HIDE: " + this + " mView=" + mView);
            if (mView != null) {
                if (mView.getParent() != null) {
                    if (DEBUG) Slog.d(TAG, "REMOVE! " + mView + " in " + this);
                    if (mCallback != null) {
                        mCallback.onAnimationEnded();
                    }
                    mWM.removeViewImmediate(mView);
                }

                mView = null;
            }
        }

        enum WirelessChargingRippleEvent implements UiEventLogger.UiEventEnum {
            @UiEvent(doc = "Wireless charging ripple effect played")
            WIRELESS_RIPPLE_PLAYED(830);

            private final int mInt;
            WirelessChargingRippleEvent(int id) {
                mInt = id;
            }

            @Override public int getId() {
                return mInt;
            }
        }
    }
}
+22 −24
Original line number Diff line number Diff line
@@ -16,9 +16,6 @@

package com.android.systemui.charging;

import static com.android.systemui.charging.WirelessChargingRippleControllerKt.DEFAULT_DURATION;
import static com.android.systemui.charging.WirelessChargingRippleControllerKt.UNKNOWN_BATTERY_LEVEL;

import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
@@ -44,43 +41,39 @@ import com.android.systemui.ripple.RippleView;
import java.text.NumberFormat;

/**
 * Layout that is used for wireless charging.
 *
 * <p>Wireless charging layout has {@link RippleView} and text view to show the battery level.
 * @hide
 */
final class WirelessChargingLayout extends FrameLayout {
    private static final long CIRCLE_RIPPLE_ANIMATION_DURATION = 1500;
    private static final long ROUNDED_BOX_RIPPLE_ANIMATION_DURATION = 1750;
    private static final int SCRIM_COLOR = 0x4C000000;
    private static final int SCRIM_FADE_DURATION = 300;
    private RippleView mRippleView;

    WirelessChargingLayout(Context context, int transmittingBatteryLevel, int batteryLevel,
            boolean isDozing, RippleShape rippleShape, long duration) {
            boolean isDozing, RippleShape rippleShape) {
        super(context);
        init(context, null, transmittingBatteryLevel, batteryLevel, isDozing, rippleShape,
                duration);
        init(context, null, transmittingBatteryLevel, batteryLevel, isDozing, rippleShape);
    }

    private WirelessChargingLayout(Context context) {
        super(context);
        init(context, null, /* isDozing= */ false, RippleShape.CIRCLE,
                DEFAULT_DURATION);
        init(context, null, /* isDozing= */ false, RippleShape.CIRCLE);
    }

    private WirelessChargingLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, /* isDozing= */false, RippleShape.CIRCLE,
                DEFAULT_DURATION);
        init(context, attrs, /* isDozing= */false, RippleShape.CIRCLE);
    }

    private void init(Context c, AttributeSet attrs, boolean isDozing, RippleShape rippleShape,
            long duration) {
        init(c, attrs, -1, -1, isDozing, rippleShape, duration);
    private void init(Context c, AttributeSet attrs, boolean isDozing, RippleShape rippleShape) {
        init(c, attrs, -1, -1, isDozing, rippleShape);
    }

    private void init(Context context, AttributeSet attrs, int transmittingBatteryLevel,
            int batteryLevel, boolean isDozing, RippleShape rippleShape, long duration) {
            int batteryLevel, boolean isDozing, RippleShape rippleShape) {
        final boolean showTransmittingBatteryLevel =
                (transmittingBatteryLevel != UNKNOWN_BATTERY_LEVEL);
                (transmittingBatteryLevel != WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL);

        // set style based on background
        int style = R.style.ChargingAnim_WallpaperBackground;
@@ -93,7 +86,7 @@ final class WirelessChargingLayout extends FrameLayout {
        // amount of battery:
        final TextView percentage = findViewById(R.id.wireless_charging_percentage);

        if (batteryLevel != UNKNOWN_BATTERY_LEVEL) {
        if (batteryLevel != WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL) {
            percentage.setText(NumberFormat.getPercentInstance().format(batteryLevel / 100f));
            percentage.setAlpha(0);
        }
@@ -132,6 +125,7 @@ final class WirelessChargingLayout extends FrameLayout {
        // play all animations together
        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playTogether(textSizeAnimator, textOpacityAnimator, textFadeAnimator);

        ValueAnimator scrimFadeInAnimator = ObjectAnimator.ofArgb(this,
                "backgroundColor", Color.TRANSPARENT, SCRIM_COLOR);
        scrimFadeInAnimator.setDuration(SCRIM_FADE_DURATION);
@@ -140,21 +134,25 @@ final class WirelessChargingLayout extends FrameLayout {
                "backgroundColor", SCRIM_COLOR, Color.TRANSPARENT);
        scrimFadeOutAnimator.setDuration(SCRIM_FADE_DURATION);
        scrimFadeOutAnimator.setInterpolator(Interpolators.LINEAR);
        scrimFadeOutAnimator.setStartDelay(duration - SCRIM_FADE_DURATION);
        scrimFadeOutAnimator.setStartDelay((rippleShape == RippleShape.CIRCLE
                ? CIRCLE_RIPPLE_ANIMATION_DURATION : ROUNDED_BOX_RIPPLE_ANIMATION_DURATION)
                - SCRIM_FADE_DURATION);
        AnimatorSet animatorSetScrim = new AnimatorSet();
        animatorSetScrim.playTogether(scrimFadeInAnimator, scrimFadeOutAnimator);
        animatorSetScrim.start();

        mRippleView = findViewById(R.id.wireless_charging_ripple);
        mRippleView.setupShader(rippleShape);
        mRippleView.setDuration(duration);
        int color = Utils.getColorAttr(mRippleView.getContext(),
                android.R.attr.colorAccent).getDefaultColor();
        if (mRippleView.getRippleShape() == RippleShape.ROUNDED_BOX) {
            mRippleView.setDuration(ROUNDED_BOX_RIPPLE_ANIMATION_DURATION);
            mRippleView.setSparkleStrength(0.22f);
            int color = Utils.getColorAttr(mRippleView.getContext(),
                    android.R.attr.colorAccent).getDefaultColor();
            mRippleView.setColor(ColorUtils.setAlphaComponent(color, 28));
        } else {
            mRippleView.setColor(ColorUtils.setAlphaComponent(color, 45));
            mRippleView.setDuration(CIRCLE_RIPPLE_ANIMATION_DURATION);
            mRippleView.setColor(Utils.getColorAttr(mRippleView.getContext(),
                    android.R.attr.colorAccent).getDefaultColor());
        }

        OnAttachStateChangeListener listener = new OnAttachStateChangeListener() {
+0 −116
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.systemui.charging

import android.content.Context
import android.view.WindowManager
import androidx.annotation.VisibleForTesting
import com.android.internal.logging.UiEventLogger
import com.android.systemui.charging.WirelessChargingView.WirelessChargingRippleEvent
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel
import com.android.systemui.log.dagger.ChargingLog
import com.android.systemui.util.concurrency.DelayableExecutor
import javax.inject.Inject

const val UNKNOWN_BATTERY_LEVEL = -1
const val DEFAULT_DURATION: Long = 1500

/**
 * Controls the wireless charging animation.
 */
@SysUISingleton
class WirelessChargingRippleController @Inject constructor(
        context: Context,
        private val uiEventLogger: UiEventLogger,
        @Main private val delayableExecutor: DelayableExecutor,
        @ChargingLog private val logBuffer: LogBuffer
) {
    private val windowManager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE)
            as WindowManager

    @VisibleForTesting
    var wirelessChargingView: WirelessChargingView? = null
    private var callback: Callback? = null

    companion object {
        private const val TAG = "WirelessChargingRippleController"
    }

    /**
     * Shows the wireless charging view with the given delay.
     *
     * If there's already the animation is playing, the new request will be disregarded.
     * @param wirelessChargingView WirelessChargingView to display.
     * @param delay the start delay of the WirelessChargingView.
     * @param callback optional callback that is triggered on animations start and end.
     */
    fun show(wirelessChargingView: WirelessChargingView, delay: Long, callback: Callback? = null) {
        // Avoid multiple animation getting triggered.
        if (this.wirelessChargingView != null) {
            logBuffer.log(TAG, LogLevel.INFO, "Already playing animation, disregard " +
                    "$wirelessChargingView")
            return
        }

        this.wirelessChargingView = wirelessChargingView
        this.callback = callback

        logBuffer.log(TAG, LogLevel.DEBUG, "SHOW: $wirelessChargingView")
        delayableExecutor.executeDelayed({ showInternal() }, delay)

        logBuffer.log(TAG, LogLevel.DEBUG, "HIDE: $wirelessChargingView")
        delayableExecutor.executeDelayed({ hideInternal() }, delay + wirelessChargingView.duration)
    }

    private fun showInternal() {
        if (wirelessChargingView == null) {
            return
        }

        val chargingLayout = wirelessChargingView!!.getWirelessChargingLayout()
        try {
            callback?.onAnimationStarting()
            windowManager.addView(chargingLayout, wirelessChargingView!!.wmLayoutParams)
            uiEventLogger.log(WirelessChargingRippleEvent.WIRELESS_RIPPLE_PLAYED)
        } catch (e: WindowManager.BadTokenException) {
            logBuffer.log(TAG, LogLevel.ERROR, "Unable to add wireless charging view. $e")
        }
    }

    private fun hideInternal() {
        wirelessChargingView?.getWirelessChargingLayout().let {
            callback?.onAnimationEnded()
            if (it?.parent != null) {
                windowManager.removeViewImmediate(it)
            }
        }
        wirelessChargingView = null
        callback = null
    }

    /**
     * Callbacks that are triggered on animation events.
     */
    interface Callback {
        /** Triggered when the animation starts playing. */
        fun onAnimationStarting()
        /** Triggered when the animation ends playing. */
        fun onAnimationEnded()
    }
}
+0 −91
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.systemui.charging

import android.content.Context
import android.graphics.PixelFormat
import android.view.View
import android.view.WindowManager
import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger.UiEventEnum
import com.android.systemui.ripple.RippleShader.RippleShape

/**
 * WirelessChargingView that encapsulates the current and next [WirelessChargingLayout]s.
 */
class WirelessChargingView (
        context: Context,
        transmittingBatteryLevel: Int,
        batteryLevel: Int,
        isDozing: Boolean,
        rippleShape: RippleShape,
        val duration: Long,
) {
    companion object {
        @JvmStatic
        fun create(
                context: Context,
                transmittingBatteryLevel: Int,
                batteryLevel: Int,
                isDozing: Boolean,
                rippleShape: RippleShape,
                duration: Long = DEFAULT_DURATION
        ): WirelessChargingView {
            return WirelessChargingView(context, transmittingBatteryLevel, batteryLevel, isDozing,
                    rippleShape, duration)
        }

        @JvmStatic
        fun createWithNoBatteryLevel(
                context: Context,
                rippleShape: RippleShape,
                duration: Long = DEFAULT_DURATION
        ): WirelessChargingView {
            return create(context,
                    UNKNOWN_BATTERY_LEVEL, UNKNOWN_BATTERY_LEVEL, false, rippleShape,
                    duration)
        }
    }

    val wmLayoutParams = WindowManager.LayoutParams().apply {
        height = WindowManager.LayoutParams.MATCH_PARENT
        width = WindowManager.LayoutParams.MATCH_PARENT
        format = PixelFormat.TRANSLUCENT
        type = WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
        title = "Charging Animation"
        layoutInDisplayCutoutMode =
                WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
        fitInsetsTypes = 0
        flags = (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
        packageName = context.applicationContext.opPackageName
        setTrustedOverlay()
    }

    private val wirelessChargingLayout: WirelessChargingLayout =
            WirelessChargingLayout(context, transmittingBatteryLevel, batteryLevel, isDozing,
                    rippleShape, duration)
    fun getWirelessChargingLayout(): View = wirelessChargingLayout

    internal enum class WirelessChargingRippleEvent(private val mInt: Int) : UiEventEnum {
        @UiEvent(doc = "Wireless charging ripple effect played")
        WIRELESS_RIPPLE_PLAYED(830);

        override fun getId(): Int {
            return mInt
        }
    }
}
+0 −35
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.systemui.log.dagger;

import static java.lang.annotation.RetentionPolicy.RUNTIME;

import com.android.systemui.log.LogBuffer;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;

import javax.inject.Qualifier;

/**
 * A {@link LogBuffer} for {@link com.android.systemui.charging}
 */
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface ChargingLog {
}
Loading