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

Commit 943b1c87 authored by Johannes Gallmann's avatar Johannes Gallmann Committed by Android (Google) Code Review
Browse files

Merge "Fix privacy chip overlapping with statusbar icons" into udc-qpr-dev

parents c57dc987 01572146
Loading
Loading
Loading
Loading
+38 −15
Original line number Diff line number Diff line
@@ -14,9 +14,6 @@

package com.android.systemui.statusbar.phone.fragment;

import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.IDLE;
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.SHOWING_PERSISTENT_DOT;

import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.Fragment;
@@ -39,6 +36,7 @@ import androidx.annotation.VisibleForTesting;
import androidx.core.animation.Animator;

import com.android.app.animation.Interpolators;
import com.android.app.animation.InterpolatorsAndroidX;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
@@ -77,6 +75,8 @@ import com.android.systemui.util.CarrierConfigTracker.CarrierConfigChangedListen
import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener;
import com.android.systemui.util.settings.SecureSettings;

import kotlin.Unit;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -99,12 +99,16 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
    private static final String EXTRA_PANEL_STATE = "panel_state";
    public static final String STATUS_BAR_ICON_MANAGER_TAG = "status_bar_icon_manager";
    public static final int FADE_IN_DURATION = 320;
    public static final int FADE_OUT_DURATION = 160;
    public static final int FADE_IN_DELAY = 50;
    private static final int SOURCE_SYSTEM_EVENT_ANIMATOR = 1;
    private static final int SOURCE_OTHER = 2;
    private StatusBarFragmentComponent mStatusBarFragmentComponent;
    private PhoneStatusBarView mStatusBar;
    private final StatusBarStateController mStatusBarStateController;
    private final KeyguardStateController mKeyguardStateController;
    private final ShadeViewController mShadeViewController;
    private MultiSourceMinAlphaController mEndSideAlphaController;
    private LinearLayout mEndSideContent;
    private View mClockView;
    private View mOngoingCallChip;
@@ -149,7 +153,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
        }
    };
    private OperatorNameViewController mOperatorNameViewController;
    private StatusBarSystemEventAnimator mSystemEventAnimator;
    private StatusBarSystemEventDefaultAnimator mSystemEventAnimator;

    private final CarrierConfigChangedListener mCarrierConfigCallback =
            new CarrierConfigChangedListener() {
@@ -297,14 +301,14 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
        updateBlockedIcons();
        mStatusBarIconController.addIconGroup(mDarkIconManager);
        mEndSideContent = mStatusBar.findViewById(R.id.status_bar_end_side_content);
        mEndSideAlphaController = new MultiSourceMinAlphaController(mEndSideContent);
        mClockView = mStatusBar.findViewById(R.id.clock);
        mOngoingCallChip = mStatusBar.findViewById(R.id.ongoing_call_chip);
        showEndSideContent(false);
        showClock(false);
        initOperatorName();
        initNotificationIconArea();
        mSystemEventAnimator =
                new StatusBarSystemEventAnimator(mEndSideContent, getResources());
        mSystemEventAnimator = getSystemEventAnimator();
        mCarrierConfigTracker.addCallback(mCarrierConfigCallback);
        mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener);

@@ -593,18 +597,27 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
    }

    private void hideEndSideContent(boolean animate) {
        animateHide(mEndSideContent, animate);
        if (!animate) {
            mEndSideAlphaController.setAlpha(/*alpha*/ 0f, SOURCE_OTHER);
        } else {
            mEndSideAlphaController.animateToAlpha(/*alpha*/ 0f, SOURCE_OTHER, FADE_OUT_DURATION,
                    InterpolatorsAndroidX.ALPHA_OUT, /*startDelay*/ 0);
        }
    }

    private void showEndSideContent(boolean animate) {
        // Only show the system icon area if we are not currently animating
        int state = mAnimationScheduler.getAnimationState();
        if (state == IDLE || state == SHOWING_PERSISTENT_DOT) {
            animateShow(mEndSideContent, animate);
        if (!animate) {
            mEndSideAlphaController.setAlpha(1f, SOURCE_OTHER);
            return;
        }
        if (mKeyguardStateController.isKeyguardFadingAway()) {
            mEndSideAlphaController.animateToAlpha(/*alpha*/ 1f, SOURCE_OTHER,
                    mKeyguardStateController.getKeyguardFadingAwayDuration(),
                    InterpolatorsAndroidX.LINEAR_OUT_SLOW_IN,
                    mKeyguardStateController.getKeyguardFadingAwayDelay());
        } else {
            // We are in the middle of a system status event animation, which will animate the
            // alpha (but not the visibility). Allow the view to become visible again
            mEndSideContent.setVisibility(View.VISIBLE);
            mEndSideAlphaController.animateToAlpha(/*alpha*/ 1f, SOURCE_OTHER, FADE_IN_DURATION,
                    InterpolatorsAndroidX.ALPHA_IN, FADE_IN_DELAY);
        }
    }

@@ -671,7 +684,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue

        v.animate()
                .alpha(0f)
                .setDuration(160)
                .setDuration(FADE_OUT_DURATION)
                .setStartDelay(0)
                .setInterpolator(Interpolators.ALPHA_OUT)
                .withEndAction(() -> v.setVisibility(state));
@@ -754,6 +767,16 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
        return mSystemEventAnimator.onSystemEventAnimationFinish(hasPersistentDot);
    }

    private StatusBarSystemEventDefaultAnimator getSystemEventAnimator() {
        return new StatusBarSystemEventDefaultAnimator(getResources(), (alpha) -> {
            mEndSideAlphaController.setAlpha(alpha, SOURCE_SYSTEM_EVENT_ANIMATOR);
            return Unit.INSTANCE;
        }, (translationX) -> {
            mEndSideContent.setTranslationX(translationX);
            return Unit.INSTANCE;
        }, /*isAnimationRunning*/ false);
    }

    private void updateStatusBarLocation(int left, int right) {
        int leftMargin = left - mStatusBar.getLeft();
        int rightMargin = mStatusBar.getRight() - right;
+82 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.statusbar.phone.fragment

import android.view.View
import androidx.core.animation.Interpolator
import androidx.core.animation.ValueAnimator
import com.android.app.animation.InterpolatorsAndroidX

/**
 * A controller that keeps track of multiple sources applying alpha value changes to a view. It will
 * always apply the minimum alpha value of all sources.
 */
internal class MultiSourceMinAlphaController
@JvmOverloads
constructor(private val view: View, private val initialAlpha: Float = 1f) {

    private val alphas = mutableMapOf<Int, Float>()
    private val animators = mutableMapOf<Int, ValueAnimator>()

    /**
     * Sets the alpha of the provided source and applies it to the view (if no other source has set
     * a lower alpha currently). If an animator of the same source is still running (i.e.
     * [animateToAlpha] was called before), that animator is cancelled.
     */
    fun setAlpha(alpha: Float, sourceId: Int) {
        animators[sourceId]?.cancel()
        updateAlpha(alpha, sourceId)
    }

    /** Animates to the alpha of the provided source. */
    fun animateToAlpha(
        alpha: Float,
        sourceId: Int,
        duration: Long,
        interpolator: Interpolator = InterpolatorsAndroidX.ALPHA_IN,
        startDelay: Long = 0
    ) {
        animators[sourceId]?.cancel()
        val animator = ValueAnimator.ofFloat(getMinAlpha(), alpha)
        animator.duration = duration
        animator.startDelay = startDelay
        animator.interpolator = interpolator
        animator.addUpdateListener { updateAlpha(animator.animatedValue as Float, sourceId) }
        animator.start()
        animators[sourceId] = animator
    }

    fun reset() {
        alphas.clear()
        animators.forEach { it.value.cancel() }
        animators.clear()
        applyAlphaToView()
    }

    private fun updateAlpha(alpha: Float, sourceId: Int) {
        alphas[sourceId] = alpha
        applyAlphaToView()
    }

    private fun applyAlphaToView() {
        val minAlpha = getMinAlpha()
        view.visibility = if (minAlpha != 0f) View.VISIBLE else View.INVISIBLE
        view.alpha = minAlpha
    }

    private fun getMinAlpha() = alphas.minOfOrNull { it.value } ?: initialAlpha
}
+34 −44
Original line number Diff line number Diff line
@@ -18,10 +18,6 @@ import static android.view.Display.DEFAULT_DISPLAY;

import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED;
import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_OPEN;
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN;
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT;
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.IDLE;
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.RUNNING_CHIP_ANIM;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -49,7 +45,7 @@ import android.view.View;
import android.view.ViewPropertyAnimator;
import android.widget.FrameLayout;

import androidx.core.animation.Animator;
import androidx.core.animation.AnimatorTestRule;
import androidx.test.filters.SmallTest;

import com.android.keyguard.KeyguardUpdateMonitor;
@@ -85,6 +81,7 @@ import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;

import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -139,6 +136,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
    private StatusBarWindowStateController mStatusBarWindowStateController;
    @Mock
    private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
    @ClassRule
    public static AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule();

    private List<StatusBarWindowStateListener> mStatusBarWindowStateListeners = new ArrayList<>();

@@ -172,7 +171,6 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {

    @Test
    public void testDisableSystemInfo_systemAnimationIdle_doesHide() {
        when(mAnimationScheduler.getAnimationState()).thenReturn(IDLE);
        CollapsedStatusBarFragment fragment = resumeAndGetFragment();

        fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
@@ -192,24 +190,26 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
    public void testSystemStatusAnimation_startedDisabled_finishedWithAnimator_showsSystemInfo() {
        // GIVEN the status bar hides the system info via disable flags, while there is no event
        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
        when(mAnimationScheduler.getAnimationState()).thenReturn(IDLE);
        fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
        assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());

        // WHEN the system event animation starts
        fragment.onSystemEventAnimationBegin().start();

        // THEN the view remains invisible during the animation
        assertEquals(0f, getEndSideContentView().getAlpha(), 0.01);
        mAnimatorTestRule.advanceTimeBy(500);
        assertEquals(0f, getEndSideContentView().getAlpha(), 0.01);

        // WHEN the disable flags are cleared during a system event animation
        when(mAnimationScheduler.getAnimationState()).thenReturn(RUNNING_CHIP_ANIM);
        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);

        // THEN the view is made visible again, but still low alpha
        assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
        // THEN the view remains invisible
        assertEquals(0, getEndSideContentView().getAlpha(), 0.01);

        // WHEN the system event animation finishes
        when(mAnimationScheduler.getAnimationState()).thenReturn(ANIMATING_OUT);
        Animator anim = fragment.onSystemEventAnimationFinish(false);
        anim.start();
        processAllMessages();
        anim.end();
        fragment.onSystemEventAnimationFinish(false).start();
        mAnimatorTestRule.advanceTimeBy(500);

        // THEN the system info is full alpha
        assertEquals(1, getEndSideContentView().getAlpha(), 0.01);
@@ -219,20 +219,15 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
    public void testSystemStatusAnimation_systemInfoDisabled_staysInvisible() {
        // GIVEN the status bar hides the system info via disable flags, while there is no event
        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
        when(mAnimationScheduler.getAnimationState()).thenReturn(IDLE);
        fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
        assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());

        // WHEN the system event animation finishes
        when(mAnimationScheduler.getAnimationState()).thenReturn(ANIMATING_OUT);
        Animator anim = fragment.onSystemEventAnimationFinish(false);
        anim.start();
        processAllMessages();
        anim.end();
        fragment.onSystemEventAnimationFinish(false).start();
        mAnimatorTestRule.advanceTimeBy(500);

        // THEN the system info is at full alpha, but still INVISIBLE (since the disable flag is
        // still set)
        assertEquals(1, getEndSideContentView().getAlpha(), 0.01);
        // THEN the system info remains invisible (since the disable flag is still set)
        assertEquals(0, getEndSideContentView().getAlpha(), 0.01);
        assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
    }

@@ -241,15 +236,14 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
    public void testSystemStatusAnimation_notDisabled_animatesAlphaZero() {
        // GIVEN the status bar is not disabled
        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
        when(mAnimationScheduler.getAnimationState()).thenReturn(ANIMATING_IN);
        assertEquals(1, getEndSideContentView().getAlpha(), 0.01);

        // WHEN the system event animation begins
        Animator anim = fragment.onSystemEventAnimationBegin();
        anim.start();
        processAllMessages();
        anim.end();
        fragment.onSystemEventAnimationBegin().start();
        mAnimatorTestRule.advanceTimeBy(500);

        // THEN the system info is visible but alpha 0
        assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
        // THEN the system info is invisible
        assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
        assertEquals(0, getEndSideContentView().getAlpha(), 0.01);
    }

@@ -257,25 +251,21 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
    public void testSystemStatusAnimation_notDisabled_animatesBackToAlphaOne() {
        // GIVEN the status bar is not disabled
        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
        when(mAnimationScheduler.getAnimationState()).thenReturn(ANIMATING_IN);
        assertEquals(1, getEndSideContentView().getAlpha(), 0.01);

        // WHEN the system event animation begins
        Animator anim = fragment.onSystemEventAnimationBegin();
        anim.start();
        processAllMessages();
        anim.end();
        fragment.onSystemEventAnimationBegin().start();
        mAnimatorTestRule.advanceTimeBy(500);

        // THEN the system info is visible but alpha 0
        assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
        // THEN the system info is invisible
        assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
        assertEquals(0, getEndSideContentView().getAlpha(), 0.01);

        // WHEN the system event animation finishes
        when(mAnimationScheduler.getAnimationState()).thenReturn(ANIMATING_OUT);
        anim = fragment.onSystemEventAnimationFinish(false);
        anim.start();
        processAllMessages();
        anim.end();
        fragment.onSystemEventAnimationFinish(false).start();
        mAnimatorTestRule.advanceTimeBy(500);

        // THEN the syste info is full alpha and VISIBLE
        // THEN the system info is full alpha and VISIBLE
        assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
        assertEquals(1, getEndSideContentView().getAlpha(), 0.01);
    }
+106 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.statusbar.phone.fragment

import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
import androidx.core.animation.AnimatorTestRule
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import junit.framework.Assert.assertEquals
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

private const val TEST_SOURCE_1 = 1
private const val TEST_SOURCE_2 = 2
private const val TEST_ANIMATION_DURATION = 100L
private const val INITIAL_ALPHA = 1f

@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
class MultiSourceMinAlphaControllerTest : SysuiTestCase() {

    private val view = View(context)
    private val multiSourceMinAlphaController =
        MultiSourceMinAlphaController(view, initialAlpha = INITIAL_ALPHA)

    @get:Rule val animatorTestRule = AnimatorTestRule()

    @Before
    fun setup() {
        multiSourceMinAlphaController.reset()
    }

    @Test
    fun testSetAlpha() {
        multiSourceMinAlphaController.setAlpha(alpha = 0.5f, sourceId = TEST_SOURCE_1)
        assertEquals(0.5f, view.alpha)
    }

    @Test
    fun testAnimateToAlpha() {
        multiSourceMinAlphaController.animateToAlpha(
            alpha = 0.5f,
            sourceId = TEST_SOURCE_1,
            duration = TEST_ANIMATION_DURATION
        )
        animatorTestRule.advanceTimeBy(TEST_ANIMATION_DURATION)
        assertEquals(0.5f, view.alpha)
    }

    @Test
    fun testReset() {
        multiSourceMinAlphaController.animateToAlpha(
            alpha = 0.5f,
            sourceId = TEST_SOURCE_1,
            duration = TEST_ANIMATION_DURATION
        )
        multiSourceMinAlphaController.setAlpha(alpha = 0.7f, sourceId = TEST_SOURCE_2)
        multiSourceMinAlphaController.reset()
        // advance time to ensure that animators are cancelled when the controller is reset
        animatorTestRule.advanceTimeBy(TEST_ANIMATION_DURATION)
        assertEquals(INITIAL_ALPHA, view.alpha)
    }

    @Test
    fun testMinOfTwoSourcesIsApplied() {
        multiSourceMinAlphaController.setAlpha(alpha = 0f, sourceId = TEST_SOURCE_1)
        multiSourceMinAlphaController.setAlpha(alpha = 0.5f, sourceId = TEST_SOURCE_2)
        assertEquals(0f, view.alpha)
        multiSourceMinAlphaController.setAlpha(alpha = 1f, sourceId = TEST_SOURCE_1)
        assertEquals(0.5f, view.alpha)
    }

    @Test
    fun testSetAlphaForSameSourceCancelsAnimator() {
        multiSourceMinAlphaController.animateToAlpha(
            alpha = 0f,
            sourceId = TEST_SOURCE_1,
            duration = TEST_ANIMATION_DURATION
        )
        animatorTestRule.advanceTimeBy(TEST_ANIMATION_DURATION / 2)
        multiSourceMinAlphaController.setAlpha(alpha = 1f, sourceId = TEST_SOURCE_1)
        animatorTestRule.advanceTimeBy(TEST_ANIMATION_DURATION / 2)
        // verify that animation was cancelled and the setAlpha call overrides the alpha value of
        // the animation
        assertEquals(1f, view.alpha)
    }
}