1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.car.systembar; 18 19 import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE; 20 21 import android.app.role.RoleManager; 22 import android.content.ComponentName; 23 import android.content.Context; 24 import android.content.res.TypedArray; 25 import android.os.Bundle; 26 import android.os.RemoteException; 27 import android.util.AttributeSet; 28 import android.util.Log; 29 30 import com.android.internal.app.AssistUtils; 31 import com.android.internal.app.IVoiceInteractionSessionListener; 32 import com.android.internal.app.IVoiceInteractionSessionShowCallback; 33 34 import java.util.Set; 35 36 /** 37 * AssistantButton is an UI component that will trigger the Voice Interaction Service. 38 */ 39 public class AssistantButton extends CarSystemBarButton { 40 private static final String TAG = "AssistantButton"; 41 private final AssistUtils mAssistUtils; 42 private final IVoiceInteractionSessionShowCallback mShowCallback = 43 new IVoiceInteractionSessionShowCallback.Stub() { 44 @Override 45 public void onFailed() { 46 Log.w(TAG, "Failed to show VoiceInteractionSession"); 47 } 48 49 @Override 50 public void onShown() { 51 Log.d(TAG, "IVoiceInteractionSessionShowCallback onShown()"); 52 } 53 }; 54 AssistantButton(Context context, AttributeSet attrs)55 public AssistantButton(Context context, AttributeSet attrs) { 56 super(context, attrs); 57 mAssistUtils = new AssistUtils(context); 58 setOnClickListener(v -> showAssistant()); 59 mAssistUtils.registerVoiceInteractionSessionListener( 60 new IVoiceInteractionSessionListener.Stub() { 61 @Override 62 public void onVoiceSessionShown() throws RemoteException { 63 assistantSetSelected(/* selected= */ true); 64 } 65 66 @Override 67 public void onVoiceSessionHidden() throws RemoteException { 68 assistantSetSelected(/* selected= */ false); 69 } 70 71 @Override 72 public void onVoiceSessionWindowVisibilityChanged(boolean visible) 73 throws RemoteException { } 74 75 @Override 76 public void onSetUiHints(Bundle hints) { 77 } 78 } 79 ); 80 } 81 showAssistant()82 void showAssistant() { 83 if (canShowTosAcceptanceFlow()) { 84 SystemBarUtil.INSTANCE.showTosAcceptanceFlow(getContext(), getUserTracker()); 85 return; 86 } 87 final Bundle args = new Bundle(); 88 mAssistUtils.showSessionForActiveService(args, 89 SHOW_SOURCE_ASSIST_GESTURE, mShowCallback, /*activityToken=*/ null); 90 } 91 92 /** 93 * Helper method to check if tos acceptance flow can be launched. The tos flow can be launched 94 * when there is no active assistant selected by the system and the default assistant has been 95 * disabled because tos is unaccepted 96 * 97 * @return true if tos flow can be launched, false otherwise 98 */ canShowTosAcceptanceFlow()99 private boolean canShowTosAcceptanceFlow() { 100 ComponentName activeAssistantComponent = mAssistUtils.getActiveServiceComponentName(); 101 String defaultAssistantInConfig = 102 getContext().getString(com.android.internal.R.string.config_defaultAssistant); 103 Integer userId = getUserTracker() != null ? getUserTracker().getUserId() : null; 104 Set<String> tosDisabledApps = SystemBarUtil.INSTANCE 105 .getTosDisabledPackages(getContext(), userId); 106 boolean defaultAssistantDisabled = tosDisabledApps.contains(defaultAssistantInConfig); 107 108 return activeAssistantComponent == null && defaultAssistantDisabled; 109 } 110 111 @Override setUpIntents(TypedArray typedArray)112 protected void setUpIntents(TypedArray typedArray) { 113 // left blank because for the assistant button Intent will not be passed from the layout. 114 } 115 116 @Override getRoleName()117 protected String getRoleName() { 118 return RoleManager.ROLE_ASSISTANT; 119 } 120 121 @Override setSelected(boolean selected)122 public void setSelected(boolean selected) { 123 // override to no-op as AssistantButton will maintain its own selected state by listening to 124 // the actual voice interaction session. 125 } 126 assistantSetSelected(boolean selected)127 private void assistantSetSelected(boolean selected) { 128 if (hasSelectionState()) { 129 getContext().getMainExecutor().execute( 130 () -> AssistantButton.super.setSelected(selected)); 131 } 132 } 133 } 134