Loading packages/SystemUI/res/values/config.xml +3 −0 Original line number Diff line number Diff line Loading @@ -917,4 +917,7 @@ "$packageName" part that will be replaced by the code with the package name of the target app. --> <string name="config_appStoreAppLinkTemplate" translatable="false"></string> <!-- Flag controlling whether visual query attention detection has been enabled. --> <bool name="config_enableVisualQueryAttentionDetection">false</bool> </resources> packages/SystemUI/src/com/android/systemui/assist/AssistManager.java +76 −2 Original line number Diff line number Diff line Loading @@ -23,6 +23,8 @@ import android.service.voice.VoiceInteractionSession; import android.util.Log; import com.android.internal.app.AssistUtils; import com.android.internal.app.IVisualQueryDetectionAttentionListener; import com.android.internal.app.IVisualQueryRecognitionStatusListener; import com.android.internal.app.IVoiceInteractionSessionListener; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; Loading @@ -39,10 +41,13 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.util.settings.SecureSettings; import javax.inject.Inject; import dagger.Lazy; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; /** * Class to manage everything related to assist in SystemUI. */ Loading Loading @@ -78,6 +83,18 @@ public class AssistManager { void hide(); } /** * An interface for a listener that receives notification that visual query attention has * either been gained or lost. */ public interface VisualQueryAttentionListener { /** Called when visual query attention has been gained. */ void onAttentionGained(); /** Called when visual query attention has been lost. */ void onAttentionLost(); } private static final String TAG = "AssistManager"; // Note that VERBOSE logging may leak PII (e.g. transcription contents). Loading Loading @@ -127,6 +144,23 @@ public class AssistManager { private final SecureSettings mSecureSettings; private final DeviceProvisionedController mDeviceProvisionedController; private final List<VisualQueryAttentionListener> mVisualQueryAttentionListeners = new ArrayList<>(); private final IVisualQueryDetectionAttentionListener mVisualQueryDetectionAttentionListener = new IVisualQueryDetectionAttentionListener.Stub() { @Override public void onAttentionGained() { mVisualQueryAttentionListeners.forEach(VisualQueryAttentionListener::onAttentionGained); } @Override public void onAttentionLost() { mVisualQueryAttentionListeners.forEach(VisualQueryAttentionListener::onAttentionLost); } }; private final CommandQueue mCommandQueue; protected final AssistUtils mAssistUtils; Loading Loading @@ -157,6 +191,7 @@ public class AssistManager { mSecureSettings = secureSettings; registerVoiceInteractionSessionListener(); registerVisualQueryRecognitionStatusListener(); mUiController = defaultUiController; Loading Loading @@ -266,6 +301,24 @@ public class AssistManager { mAssistUtils.hideCurrentSession(); } /** * Add the given {@link VisualQueryAttentionListener} to the list of listeners awaiting * notification of gaining/losing visual query attention. */ public void addVisualQueryAttentionListener(VisualQueryAttentionListener listener) { if (!mVisualQueryAttentionListeners.contains(listener)) { mVisualQueryAttentionListeners.add(listener); } } /** * Remove the given {@link VisualQueryAttentionListener} from the list of listeners awaiting * notification of gaining/losing visual query attention. */ public void removeVisualQueryAttentionListener(VisualQueryAttentionListener listener) { mVisualQueryAttentionListeners.remove(listener); } private void startAssistInternal(Bundle args, @NonNull ComponentName assistComponent, boolean isService) { if (isService) { Loading Loading @@ -326,6 +379,27 @@ public class AssistManager { null, null); } private void registerVisualQueryRecognitionStatusListener() { if (!mContext.getResources() .getBoolean(R.bool.config_enableVisualQueryAttentionDetection)) { return; } mAssistUtils.subscribeVisualQueryRecognitionStatus( new IVisualQueryRecognitionStatusListener.Stub() { @Override public void onStartPerceiving() { mAssistUtils.enableVisualQueryDetection( mVisualQueryDetectionAttentionListener); } @Override public void onStopPerceiving() { mAssistUtils.disableVisualQueryDetection(); } }); } public void launchVoiceAssistFromKeyguard() { mAssistUtils.launchVoiceAssistFromKeyguard(); } Loading packages/SystemUI/src/com/android/systemui/dreams/conditions/AssistantAttentionCondition.java +9 −45 Original line number Diff line number Diff line Loading @@ -16,10 +16,9 @@ package com.android.systemui.dreams.conditions; import com.android.internal.app.AssistUtils; import com.android.internal.app.IVisualQueryDetectionAttentionListener; import com.android.systemui.assist.AssistManager; import com.android.systemui.assist.AssistManager.VisualQueryAttentionListener; import com.android.systemui.dagger.qualifiers.Application; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.shared.condition.Condition; import javax.inject.Inject; Loading @@ -30,12 +29,10 @@ import kotlinx.coroutines.CoroutineScope; * {@link AssistantAttentionCondition} provides a signal when assistant has the user's attention. */ public class AssistantAttentionCondition extends Condition { private final DreamOverlayStateController mDreamOverlayStateController; private final AssistUtils mAssistUtils; private boolean mEnabled; private final AssistManager mAssistManager; private final IVisualQueryDetectionAttentionListener mVisualQueryDetectionAttentionListener = new IVisualQueryDetectionAttentionListener.Stub() { private final VisualQueryAttentionListener mVisualQueryAttentionListener = new VisualQueryAttentionListener() { @Override public void onAttentionGained() { updateCondition(true); Loading @@ -47,59 +44,26 @@ public class AssistantAttentionCondition extends Condition { } }; private final DreamOverlayStateController.Callback mCallback = new DreamOverlayStateController.Callback() { @Override public void onStateChanged() { if (mDreamOverlayStateController.isDreamOverlayStatusBarVisible()) { enableVisualQueryDetection(); } else { disableVisualQueryDetection(); } } }; @Inject public AssistantAttentionCondition( @Application CoroutineScope scope, DreamOverlayStateController dreamOverlayStateController, AssistUtils assistUtils) { AssistManager assistManager) { super(scope); mDreamOverlayStateController = dreamOverlayStateController; mAssistUtils = assistUtils; mAssistManager = assistManager; } @Override protected void start() { mDreamOverlayStateController.addCallback(mCallback); mAssistManager.addVisualQueryAttentionListener(mVisualQueryAttentionListener); } @Override protected void stop() { disableVisualQueryDetection(); mDreamOverlayStateController.removeCallback(mCallback); mAssistManager.removeVisualQueryAttentionListener(mVisualQueryAttentionListener); } @Override protected int getStartStrategy() { return START_EAGERLY; } private void enableVisualQueryDetection() { if (mEnabled) { return; } mEnabled = true; mAssistUtils.enableVisualQueryDetection(mVisualQueryDetectionAttentionListener); } private void disableVisualQueryDetection() { if (!mEnabled) { return; } mEnabled = false; mAssistUtils.disableVisualQueryDetection(); // Make sure the condition is set to false as well. updateCondition(false); } } packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java +15 −41 Original line number Diff line number Diff line Loading @@ -22,17 +22,14 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.os.RemoteException; import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; import com.android.internal.app.AssistUtils; import com.android.internal.app.IVisualQueryDetectionAttentionListener; import com.android.systemui.SysuiTestCase; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.assist.AssistManager; import com.android.systemui.assist.AssistManager.VisualQueryAttentionListener; import com.android.systemui.shared.condition.Condition; import org.junit.Before; Loading @@ -50,9 +47,7 @@ public class AssistantAttentionConditionTest extends SysuiTestCase { @Mock Condition.Callback mCallback; @Mock AssistUtils mAssistUtils; @Mock DreamOverlayStateController mDreamOverlayStateController; AssistManager mAssistManager; @Mock CoroutineScope mScope; Loading @@ -62,55 +57,34 @@ public class AssistantAttentionConditionTest extends SysuiTestCase { public void setup() { MockitoAnnotations.initMocks(this); mAssistantAttentionCondition = new AssistantAttentionCondition(mScope, mDreamOverlayStateController, mAssistUtils); mAssistantAttentionCondition = new AssistantAttentionCondition(mScope, mAssistManager); // Adding a callback also starts the condition. mAssistantAttentionCondition.addCallback(mCallback); } @Test public void testEnableVisualQueryDetection() { final ArgumentCaptor<DreamOverlayStateController.Callback> argumentCaptor = ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class); verify(mDreamOverlayStateController).addCallback(argumentCaptor.capture()); when(mDreamOverlayStateController.isDreamOverlayStatusBarVisible()).thenReturn(true); argumentCaptor.getValue().onStateChanged(); verify(mAssistUtils).enableVisualQueryDetection(any()); verify(mAssistManager).addVisualQueryAttentionListener( any(VisualQueryAttentionListener.class)); } @Test public void testDisableVisualQueryDetection() { final ArgumentCaptor<DreamOverlayStateController.Callback> argumentCaptor = ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class); verify(mDreamOverlayStateController).addCallback(argumentCaptor.capture()); when(mDreamOverlayStateController.isDreamOverlayStatusBarVisible()).thenReturn(true); argumentCaptor.getValue().onStateChanged(); when(mDreamOverlayStateController.isDreamOverlayStatusBarVisible()).thenReturn(false); argumentCaptor.getValue().onStateChanged(); verify(mAssistUtils).disableVisualQueryDetection(); mAssistantAttentionCondition.stop(); verify(mAssistManager).removeVisualQueryAttentionListener( any(VisualQueryAttentionListener.class)); } @Test public void testAttentionChangedTriggersCondition() throws RemoteException { final ArgumentCaptor<DreamOverlayStateController.Callback> callbackCaptor = ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class); verify(mDreamOverlayStateController).addCallback(callbackCaptor.capture()); when(mDreamOverlayStateController.isDreamOverlayStatusBarVisible()).thenReturn(true); callbackCaptor.getValue().onStateChanged(); final ArgumentCaptor<IVisualQueryDetectionAttentionListener> listenerCaptor = ArgumentCaptor.forClass(IVisualQueryDetectionAttentionListener.class); verify(mAssistUtils).enableVisualQueryDetection(listenerCaptor.capture()); public void testAttentionChangedTriggersCondition() { final ArgumentCaptor<VisualQueryAttentionListener> argumentCaptor = ArgumentCaptor.forClass(VisualQueryAttentionListener.class); verify(mAssistManager).addVisualQueryAttentionListener(argumentCaptor.capture()); listenerCaptor.getValue().onAttentionGained(); argumentCaptor.getValue().onAttentionGained(); assertThat(mAssistantAttentionCondition.isConditionMet()).isTrue(); listenerCaptor.getValue().onAttentionLost(); argumentCaptor.getValue().onAttentionLost(); assertThat(mAssistantAttentionCondition.isConditionMet()).isFalse(); verify(mCallback, times(2)).onConditionChanged(eq(mAssistantAttentionCondition)); Loading Loading
packages/SystemUI/res/values/config.xml +3 −0 Original line number Diff line number Diff line Loading @@ -917,4 +917,7 @@ "$packageName" part that will be replaced by the code with the package name of the target app. --> <string name="config_appStoreAppLinkTemplate" translatable="false"></string> <!-- Flag controlling whether visual query attention detection has been enabled. --> <bool name="config_enableVisualQueryAttentionDetection">false</bool> </resources>
packages/SystemUI/src/com/android/systemui/assist/AssistManager.java +76 −2 Original line number Diff line number Diff line Loading @@ -23,6 +23,8 @@ import android.service.voice.VoiceInteractionSession; import android.util.Log; import com.android.internal.app.AssistUtils; import com.android.internal.app.IVisualQueryDetectionAttentionListener; import com.android.internal.app.IVisualQueryRecognitionStatusListener; import com.android.internal.app.IVoiceInteractionSessionListener; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; Loading @@ -39,10 +41,13 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.util.settings.SecureSettings; import javax.inject.Inject; import dagger.Lazy; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; /** * Class to manage everything related to assist in SystemUI. */ Loading Loading @@ -78,6 +83,18 @@ public class AssistManager { void hide(); } /** * An interface for a listener that receives notification that visual query attention has * either been gained or lost. */ public interface VisualQueryAttentionListener { /** Called when visual query attention has been gained. */ void onAttentionGained(); /** Called when visual query attention has been lost. */ void onAttentionLost(); } private static final String TAG = "AssistManager"; // Note that VERBOSE logging may leak PII (e.g. transcription contents). Loading Loading @@ -127,6 +144,23 @@ public class AssistManager { private final SecureSettings mSecureSettings; private final DeviceProvisionedController mDeviceProvisionedController; private final List<VisualQueryAttentionListener> mVisualQueryAttentionListeners = new ArrayList<>(); private final IVisualQueryDetectionAttentionListener mVisualQueryDetectionAttentionListener = new IVisualQueryDetectionAttentionListener.Stub() { @Override public void onAttentionGained() { mVisualQueryAttentionListeners.forEach(VisualQueryAttentionListener::onAttentionGained); } @Override public void onAttentionLost() { mVisualQueryAttentionListeners.forEach(VisualQueryAttentionListener::onAttentionLost); } }; private final CommandQueue mCommandQueue; protected final AssistUtils mAssistUtils; Loading Loading @@ -157,6 +191,7 @@ public class AssistManager { mSecureSettings = secureSettings; registerVoiceInteractionSessionListener(); registerVisualQueryRecognitionStatusListener(); mUiController = defaultUiController; Loading Loading @@ -266,6 +301,24 @@ public class AssistManager { mAssistUtils.hideCurrentSession(); } /** * Add the given {@link VisualQueryAttentionListener} to the list of listeners awaiting * notification of gaining/losing visual query attention. */ public void addVisualQueryAttentionListener(VisualQueryAttentionListener listener) { if (!mVisualQueryAttentionListeners.contains(listener)) { mVisualQueryAttentionListeners.add(listener); } } /** * Remove the given {@link VisualQueryAttentionListener} from the list of listeners awaiting * notification of gaining/losing visual query attention. */ public void removeVisualQueryAttentionListener(VisualQueryAttentionListener listener) { mVisualQueryAttentionListeners.remove(listener); } private void startAssistInternal(Bundle args, @NonNull ComponentName assistComponent, boolean isService) { if (isService) { Loading Loading @@ -326,6 +379,27 @@ public class AssistManager { null, null); } private void registerVisualQueryRecognitionStatusListener() { if (!mContext.getResources() .getBoolean(R.bool.config_enableVisualQueryAttentionDetection)) { return; } mAssistUtils.subscribeVisualQueryRecognitionStatus( new IVisualQueryRecognitionStatusListener.Stub() { @Override public void onStartPerceiving() { mAssistUtils.enableVisualQueryDetection( mVisualQueryDetectionAttentionListener); } @Override public void onStopPerceiving() { mAssistUtils.disableVisualQueryDetection(); } }); } public void launchVoiceAssistFromKeyguard() { mAssistUtils.launchVoiceAssistFromKeyguard(); } Loading
packages/SystemUI/src/com/android/systemui/dreams/conditions/AssistantAttentionCondition.java +9 −45 Original line number Diff line number Diff line Loading @@ -16,10 +16,9 @@ package com.android.systemui.dreams.conditions; import com.android.internal.app.AssistUtils; import com.android.internal.app.IVisualQueryDetectionAttentionListener; import com.android.systemui.assist.AssistManager; import com.android.systemui.assist.AssistManager.VisualQueryAttentionListener; import com.android.systemui.dagger.qualifiers.Application; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.shared.condition.Condition; import javax.inject.Inject; Loading @@ -30,12 +29,10 @@ import kotlinx.coroutines.CoroutineScope; * {@link AssistantAttentionCondition} provides a signal when assistant has the user's attention. */ public class AssistantAttentionCondition extends Condition { private final DreamOverlayStateController mDreamOverlayStateController; private final AssistUtils mAssistUtils; private boolean mEnabled; private final AssistManager mAssistManager; private final IVisualQueryDetectionAttentionListener mVisualQueryDetectionAttentionListener = new IVisualQueryDetectionAttentionListener.Stub() { private final VisualQueryAttentionListener mVisualQueryAttentionListener = new VisualQueryAttentionListener() { @Override public void onAttentionGained() { updateCondition(true); Loading @@ -47,59 +44,26 @@ public class AssistantAttentionCondition extends Condition { } }; private final DreamOverlayStateController.Callback mCallback = new DreamOverlayStateController.Callback() { @Override public void onStateChanged() { if (mDreamOverlayStateController.isDreamOverlayStatusBarVisible()) { enableVisualQueryDetection(); } else { disableVisualQueryDetection(); } } }; @Inject public AssistantAttentionCondition( @Application CoroutineScope scope, DreamOverlayStateController dreamOverlayStateController, AssistUtils assistUtils) { AssistManager assistManager) { super(scope); mDreamOverlayStateController = dreamOverlayStateController; mAssistUtils = assistUtils; mAssistManager = assistManager; } @Override protected void start() { mDreamOverlayStateController.addCallback(mCallback); mAssistManager.addVisualQueryAttentionListener(mVisualQueryAttentionListener); } @Override protected void stop() { disableVisualQueryDetection(); mDreamOverlayStateController.removeCallback(mCallback); mAssistManager.removeVisualQueryAttentionListener(mVisualQueryAttentionListener); } @Override protected int getStartStrategy() { return START_EAGERLY; } private void enableVisualQueryDetection() { if (mEnabled) { return; } mEnabled = true; mAssistUtils.enableVisualQueryDetection(mVisualQueryDetectionAttentionListener); } private void disableVisualQueryDetection() { if (!mEnabled) { return; } mEnabled = false; mAssistUtils.disableVisualQueryDetection(); // Make sure the condition is set to false as well. updateCondition(false); } }
packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java +15 −41 Original line number Diff line number Diff line Loading @@ -22,17 +22,14 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.os.RemoteException; import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; import com.android.internal.app.AssistUtils; import com.android.internal.app.IVisualQueryDetectionAttentionListener; import com.android.systemui.SysuiTestCase; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.assist.AssistManager; import com.android.systemui.assist.AssistManager.VisualQueryAttentionListener; import com.android.systemui.shared.condition.Condition; import org.junit.Before; Loading @@ -50,9 +47,7 @@ public class AssistantAttentionConditionTest extends SysuiTestCase { @Mock Condition.Callback mCallback; @Mock AssistUtils mAssistUtils; @Mock DreamOverlayStateController mDreamOverlayStateController; AssistManager mAssistManager; @Mock CoroutineScope mScope; Loading @@ -62,55 +57,34 @@ public class AssistantAttentionConditionTest extends SysuiTestCase { public void setup() { MockitoAnnotations.initMocks(this); mAssistantAttentionCondition = new AssistantAttentionCondition(mScope, mDreamOverlayStateController, mAssistUtils); mAssistantAttentionCondition = new AssistantAttentionCondition(mScope, mAssistManager); // Adding a callback also starts the condition. mAssistantAttentionCondition.addCallback(mCallback); } @Test public void testEnableVisualQueryDetection() { final ArgumentCaptor<DreamOverlayStateController.Callback> argumentCaptor = ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class); verify(mDreamOverlayStateController).addCallback(argumentCaptor.capture()); when(mDreamOverlayStateController.isDreamOverlayStatusBarVisible()).thenReturn(true); argumentCaptor.getValue().onStateChanged(); verify(mAssistUtils).enableVisualQueryDetection(any()); verify(mAssistManager).addVisualQueryAttentionListener( any(VisualQueryAttentionListener.class)); } @Test public void testDisableVisualQueryDetection() { final ArgumentCaptor<DreamOverlayStateController.Callback> argumentCaptor = ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class); verify(mDreamOverlayStateController).addCallback(argumentCaptor.capture()); when(mDreamOverlayStateController.isDreamOverlayStatusBarVisible()).thenReturn(true); argumentCaptor.getValue().onStateChanged(); when(mDreamOverlayStateController.isDreamOverlayStatusBarVisible()).thenReturn(false); argumentCaptor.getValue().onStateChanged(); verify(mAssistUtils).disableVisualQueryDetection(); mAssistantAttentionCondition.stop(); verify(mAssistManager).removeVisualQueryAttentionListener( any(VisualQueryAttentionListener.class)); } @Test public void testAttentionChangedTriggersCondition() throws RemoteException { final ArgumentCaptor<DreamOverlayStateController.Callback> callbackCaptor = ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class); verify(mDreamOverlayStateController).addCallback(callbackCaptor.capture()); when(mDreamOverlayStateController.isDreamOverlayStatusBarVisible()).thenReturn(true); callbackCaptor.getValue().onStateChanged(); final ArgumentCaptor<IVisualQueryDetectionAttentionListener> listenerCaptor = ArgumentCaptor.forClass(IVisualQueryDetectionAttentionListener.class); verify(mAssistUtils).enableVisualQueryDetection(listenerCaptor.capture()); public void testAttentionChangedTriggersCondition() { final ArgumentCaptor<VisualQueryAttentionListener> argumentCaptor = ArgumentCaptor.forClass(VisualQueryAttentionListener.class); verify(mAssistManager).addVisualQueryAttentionListener(argumentCaptor.capture()); listenerCaptor.getValue().onAttentionGained(); argumentCaptor.getValue().onAttentionGained(); assertThat(mAssistantAttentionCondition.isConditionMet()).isTrue(); listenerCaptor.getValue().onAttentionLost(); argumentCaptor.getValue().onAttentionLost(); assertThat(mAssistantAttentionCondition.isConditionMet()).isFalse(); verify(mCallback, times(2)).onConditionChanged(eq(mAssistantAttentionCondition)); Loading