1 /* 2 * Copyright (C) 2021 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 android.car.builtin.util; 18 19 import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_PUSH_TO_TALK; 20 21 import android.annotation.NonNull; 22 import android.annotation.SystemApi; 23 import android.app.ActivityManager; 24 import android.car.builtin.annotation.AddedIn; 25 import android.car.builtin.annotation.PlatformVersion; 26 import android.content.Context; 27 import android.os.Bundle; 28 import android.os.RemoteException; 29 import android.os.SystemClock; 30 31 import com.android.internal.annotations.VisibleForTesting; 32 import com.android.internal.app.AssistUtils; 33 import com.android.internal.app.IVoiceInteractionSessionListener; 34 import com.android.internal.app.IVoiceInteractionSessionShowCallback; 35 36 import java.util.Objects; 37 38 /** 39 * Class to wrap {@link AssistUtils}. 40 * @hide 41 */ 42 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) 43 public final class AssistUtilsHelper { 44 45 private static final String TAG = AssistUtilsHelper.class.getSimpleName(); 46 47 /** 48 * Used as a boolean extra field on show the session for the currently active voice interaction 49 * service, {@code true} indicates that the service was launch from a key event, 50 * {@code false} otherwise. 51 */ 52 @VisibleForTesting 53 static final String EXTRA_CAR_PUSH_TO_TALK = 54 "com.android.car.input.EXTRA_CAR_PUSH_TO_TALK"; 55 56 /** 57 * Used as a long extra field on show the session for the currently active voice interaction 58 * service, the value indicates the button press time measured in milliseconds since the last 59 * boot up. 60 */ 61 @VisibleForTesting 62 static final String EXTRA_TRIGGER_TIMESTAMP_PUSH_TO_TALK_MS = 63 "com.android.car.input.EXTRA_TRIGGER_TIMESTAMP_PUSH_TO_TALK_MS"; 64 65 /** 66 * Determines if there is a voice interaction session running. 67 * 68 * @param context used to build the assist utils. 69 * @return {@code true} if a session is running, {@code false} otherwise. 70 */ 71 @AddedIn(PlatformVersion.TIRAMISU_0) isSessionRunning(@onNull Context context)72 public static boolean isSessionRunning(@NonNull Context context) { 73 AssistUtils assistUtils = getAssistUtils(context); 74 75 return assistUtils.isSessionRunning(); 76 } 77 78 /** 79 * Hides the current voice interaction session running 80 * 81 * @param context used to build the assist utils. 82 */ 83 @AddedIn(PlatformVersion.TIRAMISU_0) hideCurrentSession(@onNull Context context)84 public static void hideCurrentSession(@NonNull Context context) { 85 AssistUtils assistUtils = getAssistUtils(context); 86 87 assistUtils.hideCurrentSession(); 88 } 89 90 /** 91 * Registers a listener to monitor when the voice sessions are shown or hidden. 92 * 93 * @param context used to build the assist utils. 94 * @param sessionListener listener that will receive shown or hidden voice sessions callback. 95 */ 96 // TODO(b/221604866) : Add unregister method 97 @AddedIn(PlatformVersion.TIRAMISU_0) registerVoiceInteractionSessionListenerHelper(@onNull Context context, @NonNull VoiceInteractionSessionListenerHelper sessionListener)98 public static void registerVoiceInteractionSessionListenerHelper(@NonNull Context context, 99 @NonNull VoiceInteractionSessionListenerHelper sessionListener) { 100 Objects.requireNonNull(sessionListener, "Session listener must not be null."); 101 102 AssistUtils assistUtils = getAssistUtils(context); 103 104 assistUtils.registerVoiceInteractionSessionListener( 105 new InternalVoiceInteractionSessionListener(sessionListener)); 106 } 107 108 /** 109 * Shows the {@link android.service.voice.VoiceInteractionSession.SHOW_SOURCE_PUSH_TO_TALK} 110 * session for active service, if the assistant component is active for the current user. 111 * 112 * @return whether the assistant component is active for the current user. 113 */ 114 @AddedIn(PlatformVersion.TIRAMISU_0) showPushToTalkSessionForActiveService(@onNull Context context, @NonNull VoiceInteractionSessionShowCallbackHelper callback)115 public static boolean showPushToTalkSessionForActiveService(@NonNull Context context, 116 @NonNull VoiceInteractionSessionShowCallbackHelper callback) { 117 Objects.requireNonNull(callback, "On shown callback must not be null."); 118 119 AssistUtils assistUtils = getAssistUtils(context); 120 int currentUserId = ActivityManager.getCurrentUser(); 121 122 123 if (assistUtils.getAssistComponentForUser(currentUserId) == null) { 124 Slogf.d(TAG, "showPushToTalkSessionForActiveService(): no component for user %d", 125 currentUserId); 126 return false; 127 } 128 129 Bundle args = new Bundle(); 130 args.putBoolean(EXTRA_CAR_PUSH_TO_TALK, true); 131 args.putLong(EXTRA_TRIGGER_TIMESTAMP_PUSH_TO_TALK_MS, SystemClock.elapsedRealtime()); 132 133 IVoiceInteractionSessionShowCallback callbackWrapper = 134 new InternalVoiceInteractionSessionShowCallback(callback); 135 136 return assistUtils.showSessionForActiveService(args, SHOW_SOURCE_PUSH_TO_TALK, 137 callbackWrapper, /* activityToken= */ null); 138 } 139 getAssistUtils(@onNull Context context)140 private static AssistUtils getAssistUtils(@NonNull Context context) { 141 Objects.requireNonNull(context, "Context must not be null."); 142 return new AssistUtils(context); 143 } 144 145 /** 146 * See {@link IVoiceInteractionSessionShowCallback} 147 */ 148 public interface VoiceInteractionSessionShowCallbackHelper { 149 /** 150 * See {@link IVoiceInteractionSessionShowCallback#onFailed()} 151 */ 152 @AddedIn(PlatformVersion.TIRAMISU_1) onFailed()153 void onFailed(); 154 155 /** 156 * See {@link IVoiceInteractionSessionShowCallback#onShow()} 157 */ 158 @AddedIn(PlatformVersion.TIRAMISU_1) onShown()159 void onShown(); 160 } 161 162 /** 163 * See {@link IVoiceInteractionSessionListener} 164 */ 165 public interface VoiceInteractionSessionListenerHelper { 166 167 /** 168 * See {@link IVoiceInteractionSessionListener#onVoiceSessionShown()} 169 */ 170 @AddedIn(PlatformVersion.TIRAMISU_1) onVoiceSessionShown()171 void onVoiceSessionShown(); 172 173 /** 174 * See {@link IVoiceInteractionSessionListener#onVoiceSessionHidden()} 175 */ 176 @AddedIn(PlatformVersion.TIRAMISU_1) onVoiceSessionHidden()177 void onVoiceSessionHidden(); 178 } 179 180 private static final class InternalVoiceInteractionSessionShowCallback extends 181 IVoiceInteractionSessionShowCallback.Stub { 182 private final VoiceInteractionSessionShowCallbackHelper mCallbackHelper; 183 InternalVoiceInteractionSessionShowCallback( VoiceInteractionSessionShowCallbackHelper callbackHelper)184 InternalVoiceInteractionSessionShowCallback( 185 VoiceInteractionSessionShowCallbackHelper callbackHelper) { 186 mCallbackHelper = callbackHelper; 187 } 188 189 @Override onFailed()190 public void onFailed() { 191 mCallbackHelper.onFailed(); 192 } 193 194 @Override onShown()195 public void onShown() { 196 mCallbackHelper.onShown(); 197 } 198 } 199 200 private static final class InternalVoiceInteractionSessionListener extends 201 IVoiceInteractionSessionListener.Stub { 202 203 private final VoiceInteractionSessionListenerHelper mListenerHelper; 204 InternalVoiceInteractionSessionListener( VoiceInteractionSessionListenerHelper listenerHelper)205 InternalVoiceInteractionSessionListener( 206 VoiceInteractionSessionListenerHelper listenerHelper) { 207 mListenerHelper = listenerHelper; 208 } 209 210 @Override onVoiceSessionShown()211 public void onVoiceSessionShown() throws RemoteException { 212 mListenerHelper.onVoiceSessionShown(); 213 } 214 215 @Override onVoiceSessionHidden()216 public void onVoiceSessionHidden() throws RemoteException { 217 mListenerHelper.onVoiceSessionHidden(); 218 } 219 220 @Override onSetUiHints(Bundle args)221 public void onSetUiHints(Bundle args) throws RemoteException { 222 Slogf.d(TAG, "onSetUiHints() not used"); 223 } 224 225 @Override onVoiceSessionWindowVisibilityChanged(boolean visible)226 public void onVoiceSessionWindowVisibilityChanged(boolean visible) 227 throws RemoteException { 228 Slogf.d(TAG, "onVoiceSessionWindowVisibilityChanged() not used"); 229 } 230 } 231 AssistUtilsHelper()232 private AssistUtilsHelper() { 233 throw new UnsupportedOperationException("contains only static members"); 234 } 235 } 236