blob: 31e49390b9b8af89e2c6f7f855b3365760609bad [file] [log] [blame]
/*
* 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.Manifest;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.media.AudioManager;
import android.util.Slog;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.shared.system.ActivityManagerWrapper;
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
* {@link TaskStackChangeListener} is registered to play a home sound effect when conditions
* documented at {@link #handleTaskStackChanged} apply.
*/
@SysUISingleton
public class HomeSoundEffectController extends SystemUI {
private static final String TAG = "HomeSoundEffectController";
private final AudioManager mAudioManager;
private final TaskStackChangeListeners mTaskStackChangeListeners;
private final ActivityManagerWrapper mActivityManagerWrapper;
private final PackageManager mPm;
private final boolean mPlayHomeSoundAfterAssistant;
private final boolean mPlayHomeSoundAfterDream;
// Initialize true because home sound should not be played when the system boots.
private boolean mIsLastTaskHome = true;
// mLastHomePackageName could go out of sync in rare circumstances if launcher changes,
// but it's cheaper than the alternative and potential impact is low
private String mLastHomePackageName;
private @WindowConfiguration.ActivityType int mLastActivityType;
private boolean mLastActivityHasNoHomeSound = false;
private int mLastTaskId;
@Inject
public HomeSoundEffectController(
Context context,
AudioManager audioManager,
TaskStackChangeListeners taskStackChangeListeners,
ActivityManagerWrapper activityManagerWrapper,
PackageManager packageManager) {
super(context);
mAudioManager = audioManager;
mTaskStackChangeListeners = taskStackChangeListeners;
mActivityManagerWrapper = activityManagerWrapper;
mPm = packageManager;
mPlayHomeSoundAfterAssistant = context.getResources().getBoolean(
R.bool.config_playHomeSoundAfterAssistant);
mPlayHomeSoundAfterDream = context.getResources().getBoolean(
R.bool.config_playHomeSoundAfterDream);
}
@Override
public void start() {
if (mAudioManager.isHomeSoundEffectEnabled()) {
mTaskStackChangeListeners.registerTaskStackListener(
new TaskStackChangeListener() {
@Override
public void onTaskStackChanged() {
ActivityManager.RunningTaskInfo currentTask =
mActivityManagerWrapper.getRunningTask();
if (currentTask == null || currentTask.topActivityInfo == null) {
return;
}
handleTaskStackChanged(currentTask);
}
});
}
}
private boolean hasFlagNoSound(ActivityInfo activityInfo) {
if ((activityInfo.privateFlags & ActivityInfo.PRIVATE_FLAG_HOME_TRANSITION_SOUND) == 0) {
// Only allow flag if app has permission
if (mPm.checkPermission(Manifest.permission.DISABLE_SYSTEM_SOUND_EFFECTS,
activityInfo.packageName) == PackageManager.PERMISSION_GRANTED) {
return true;
} else {
Slog.w(TAG,
"Activity has flag playHomeTransition set to false but doesn't hold "
+ "required permission "
+ Manifest.permission.DISABLE_SYSTEM_SOUND_EFFECTS);
return false;
}
}
return false;
}
/**
* The home sound is played if all of the following conditions are met:
* <ul>
* <li>The last task which moved to front was not home. This avoids playing the sound
* e.g. after FallbackHome transitions to home, another activity of the home app like a
* notification panel moved to front, or in case the home app crashed.</li>
* <li>The current activity which moved to front is home</li>
* <li>The topActivity of the last task has {@link android.R.attr#playHomeTransitionSound} set
* to <code>true</code>.</li>
* <li>The topActivity of the last task is not of type
* {@link WindowConfiguration#ACTIVITY_TYPE_ASSISTANT} if config_playHomeSoundAfterAssistant is
* set to <code>false</code> (default).</li>
* <li>The topActivity of the last task is not of type
* {@link WindowConfiguration#ACTIVITY_TYPE_DREAM} if config_playHomeSoundAfterDream is
* set to <code>false</code> (default).</li>
* </ul>
*/
private boolean shouldPlayHomeSoundForCurrentTransition(
ActivityManager.RunningTaskInfo currentTask) {
boolean isHomeActivity =
currentTask.topActivityType == WindowConfiguration.ACTIVITY_TYPE_HOME;
if (currentTask.taskId == mLastTaskId) {
return false;
}
if (mIsLastTaskHome || !isHomeActivity) {
return false;
}
if (mLastActivityHasNoHomeSound) {
return false;
}
if (mLastActivityType == WindowConfiguration.ACTIVITY_TYPE_ASSISTANT
&& !mPlayHomeSoundAfterAssistant) {
return false;
}
if (mLastActivityType == WindowConfiguration.ACTIVITY_TYPE_DREAM
&& !mPlayHomeSoundAfterDream) {
return false;
}
return true;
}
private void updateLastTaskInfo(ActivityManager.RunningTaskInfo currentTask) {
mLastTaskId = currentTask.taskId;
mLastActivityType = currentTask.topActivityType;
mLastActivityHasNoHomeSound = hasFlagNoSound(currentTask.topActivityInfo);
boolean isHomeActivity =
currentTask.topActivityType == WindowConfiguration.ACTIVITY_TYPE_HOME;
boolean isHomePackage = currentTask.topActivityInfo.packageName.equals(
mLastHomePackageName);
mIsLastTaskHome = isHomeActivity || isHomePackage;
if (isHomeActivity && !isHomePackage) {
mLastHomePackageName = currentTask.topActivityInfo.packageName;
}
}
private void handleTaskStackChanged(ActivityManager.RunningTaskInfo frontTask) {
if (shouldPlayHomeSoundForCurrentTransition(frontTask)) {
mAudioManager.playSoundEffect(AudioManager.FX_HOME);
}
updateLastTaskInfo(frontTask);
}
}