From b8a6a2979cb9791da6d0b005195354dafc7344ba Mon Sep 17 00:00:00 2001 From: Philip Junker Date: Fri, 4 Dec 2020 16:51:39 +0100 Subject: [PATCH] Add support for a home sound effect Test: Manually tested on a device Test: atest frameworks/base/packages/SystemUI/tests/src/com/android/systemui/media/systemsounds/HomeSoundEffectControllerTest Bug: 167946828 Bug: 157407957 Change-Id: I2608737684f4f0af07ce1b12f8bf22cce9c1ebab --- .../SystemUI/res/values-television/config.xml | 1 + .../systemui/dagger/SystemUIBinder.java | 7 + .../HomeSoundEffectController.java | 82 +++++++++ .../HomeSoundEffectControllerTest.java | 158 ++++++++++++++++++ 4 files changed, 248 insertions(+) create mode 100644 packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java create mode 100644 packages/SystemUI/tests/src/com/android/systemui/media/systemsounds/HomeSoundEffectControllerTest.java diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml index 0c8c5c443c4ad..dec83d95f0be6 100644 --- a/packages/SystemUI/res/values-television/config.xml +++ b/packages/SystemUI/res/values-television/config.xml @@ -43,6 +43,7 @@ com.android.systemui.statusbar.notification.InstantAppNotifier com.android.systemui.toast.ToastUI com.android.systemui.wmshell.WMShell + com.android.systemui.media.systemsounds.HomeSoundEffectController diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java index 9f6c19bdf06fe..55359ea708735 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java @@ -27,6 +27,7 @@ import com.android.systemui.biometrics.AuthController; import com.android.systemui.globalactions.GlobalActionsComponent; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.dagger.KeyguardModule; +import com.android.systemui.media.systemsounds.HomeSoundEffectController; import com.android.systemui.power.PowerUI; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsModule; @@ -178,4 +179,10 @@ public abstract class SystemUIBinder { @IntoMap @ClassKey(WMShell.class) public abstract SystemUI bindWMShell(WMShell sysui); + + /** Inject into HomeSoundEffectController. */ + @Binds + @IntoMap + @ClassKey(HomeSoundEffectController.class) + public abstract SystemUI bindHomeSoundEffectController(HomeSoundEffectController sysui); } diff --git a/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java b/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java new file mode 100644 index 0000000000000..dd3d02a86672a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2020 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.media.systemsounds; + +import android.app.ActivityManager; +import android.app.WindowConfiguration; +import android.content.Context; +import android.media.AudioManager; + +import com.android.systemui.SystemUI; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.shared.system.TaskStackChangeListener; +import com.android.systemui.shared.system.TaskStackChangeListeners; + +import javax.inject.Inject; + +/** + * If a sound effect is defined for {@link android.media.AudioManager#FX_HOME}, a sound is played + * when the home task moves to front and the last task that moved to front was not the home task. + */ +@SysUISingleton +public class HomeSoundEffectController extends SystemUI { + + private final AudioManager mAudioManager; + private final TaskStackChangeListeners mTaskStackChangeListeners; + // Initialize true because home sound should not be played when the system boots. + private boolean mIsLastTaskHome = true; + + @Inject + public HomeSoundEffectController( + Context context, + AudioManager audioManager, + TaskStackChangeListeners taskStackChangeListeners) { + super(context); + mAudioManager = audioManager; + mTaskStackChangeListeners = taskStackChangeListeners; + } + + @Override + public void start() { + if (mAudioManager.isHomeSoundEffectEnabled()) { + mTaskStackChangeListeners.registerTaskStackListener( + new TaskStackChangeListener() { + @Override + public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) { + handleHomeTaskMovedToFront(taskInfo); + } + }); + } + } + + private boolean isHomeTask(ActivityManager.RunningTaskInfo taskInfo) { + return taskInfo.getActivityType() == WindowConfiguration.ACTIVITY_TYPE_HOME; + } + + /** + * To enable a home sound, check if the home app moves to front. + */ + private void handleHomeTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) { + boolean isCurrentTaskHome = isHomeTask(taskInfo); + // If the last task is home we don't want to play the home sound. This avoids playing + // the home sound after FallbackHome transitions to Home + if (!mIsLastTaskHome && isCurrentTaskHome) { + mAudioManager.playSoundEffect(AudioManager.FX_HOME); + } + mIsLastTaskHome = isCurrentTaskHome; + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/systemsounds/HomeSoundEffectControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/systemsounds/HomeSoundEffectControllerTest.java new file mode 100644 index 0000000000000..3a77f7eec7f91 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/systemsounds/HomeSoundEffectControllerTest.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2021 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.media.systemsounds; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.app.ActivityManager; +import android.app.WindowConfiguration; +import android.content.Context; +import android.media.AudioManager; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.shared.system.TaskStackChangeListener; +import com.android.systemui.shared.system.TaskStackChangeListeners; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class HomeSoundEffectControllerTest extends SysuiTestCase { + + private @Mock Context mContext; + private @Mock AudioManager mAudioManager; + private @Mock TaskStackChangeListeners mTaskStackChangeListeners; + private @Mock ActivityManager.RunningTaskInfo mStandardActivityTaskInfo; + private @Mock ActivityManager.RunningTaskInfo mHomeActivityTaskInfo; + + private HomeSoundEffectController mController; + private TaskStackChangeListener mTaskStackChangeListener; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + doReturn(WindowConfiguration.ACTIVITY_TYPE_STANDARD).when( + mStandardActivityTaskInfo).getActivityType(); + doReturn(WindowConfiguration.ACTIVITY_TYPE_HOME).when( + mHomeActivityTaskInfo).getActivityType(); + + mController = new HomeSoundEffectController(mContext, mAudioManager, + mTaskStackChangeListeners); + } + + @Test + public void testHomeSoundEffectNotPlayedWhenHomeFirstMovesToFront() { + // When HomeSoundEffectController is started and the home sound effect is enabled, + startController(true /* isHomeSoundEffectEnabled */); + + // And the home task moves to the front, + mTaskStackChangeListener.onTaskMovedToFront(mHomeActivityTaskInfo); + + // Then no home sound effect should be played. + verify(mAudioManager, never()).playSoundEffect(AudioManager.FX_HOME); + } + + @Test + public void testHomeSoundEffectPlayedWhenEnabled() { + // When HomeSoundEffectController is started and the home sound effect is enabled, + startController(true /* isHomeSoundEffectEnabled */); + + // And first a task different from the home task moves to front, + mTaskStackChangeListener.onTaskMovedToFront(mStandardActivityTaskInfo); + + // And the home task moves to the front, + mTaskStackChangeListener.onTaskMovedToFront(mHomeActivityTaskInfo); + + // Then the home sound effect should be played. + verify(mAudioManager).playSoundEffect(AudioManager.FX_HOME); + } + + @Test + public void testHomeSoundEffectNotPlayedTwiceInRow() { + // When HomeSoundEffectController is started and the home sound effect is enabled, + startController(true /* isHomeSoundEffectEnabled */); + + // And first a task different from the home task moves to front, + mTaskStackChangeListener.onTaskMovedToFront(mStandardActivityTaskInfo); + + // And the home task moves to the front, + mTaskStackChangeListener.onTaskMovedToFront(mHomeActivityTaskInfo); + + // Then the home sound effect should be played. + verify(mAudioManager).playSoundEffect(AudioManager.FX_HOME); + + // If the home task moves to front a second time in a row, + mTaskStackChangeListener.onTaskMovedToFront(mHomeActivityTaskInfo); + + // Then no home sound effect should be played. + verify(mAudioManager, times(1)).playSoundEffect(AudioManager.FX_HOME); + } + + @Test + public void testHomeSoundEffectNotPlayedWhenNonHomeTaskMovesToFront() { + // When HomeSoundEffectController is started and the home sound effect is enabled, + startController(true /* isHomeSoundEffectEnabled */); + + // And a standard, non-home task, moves to the front, + mTaskStackChangeListener.onTaskMovedToFront(mStandardActivityTaskInfo); + + // Then no home sound effect should be played. + verify(mAudioManager, never()).playSoundEffect(AudioManager.FX_HOME); + } + + @Test + public void testHomeSoundEffectDisabled() { + // When HomeSoundEffectController is started and the home sound effect is disabled, + startController(false /* isHomeSoundEffectEnabled */); + + // Then no TaskStackListener should be registered + verify(mTaskStackChangeListeners, never()).registerTaskStackListener( + any(TaskStackChangeListener.class)); + } + + /** + * Sets {@link AudioManager#isHomeSoundEffectEnabled()} and starts HomeSoundEffectController. + * If the home sound effect is enabled, the registered TaskStackChangeListener is extracted. + */ + private void startController(boolean isHomeSoundEffectEnabled) { + // Configure home sound effect to be enabled + doReturn(isHomeSoundEffectEnabled).when(mAudioManager).isHomeSoundEffectEnabled(); + + mController.start(); + + if (isHomeSoundEffectEnabled) { + // Construct controller. Save the TaskStackListener for injecting events. + final ArgumentCaptor listenerCaptor = + ArgumentCaptor.forClass(TaskStackChangeListener.class); + verify(mTaskStackChangeListeners).registerTaskStackListener(listenerCaptor.capture()); + mTaskStackChangeListener = listenerCaptor.getValue(); + } + } +}