Launch tos acceptance flow from assistant button in system navbar

Bug: 292570223
Test: Manual testing
Change-Id: I33f628aef330ae084bb19c09587dba9c05f130cd
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 658321b..ed8e11c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -243,4 +243,11 @@
     <!-- Instructions telling the user to enter their text password to unlock the keyguard [CHAR LIMIT=30] -->
     <string name="car_keyguard_enter_your_password">Enter your password</string>
 
+    <!-- Intent action meant to invoke user TOS flow.
+        The intent URI has to be formatted according to Intent.URI_INTENT_SCHEME
+        URI, for example should contain the intent action and any extras if necessary:
+        "intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=true;end"
+   -->
+    <string name="user_tos_activity_intent">intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end</string>
+
 </resources>
diff --git a/src/com/android/systemui/car/systembar/AssistantButton.java b/src/com/android/systemui/car/systembar/AssistantButton.java
index f16d06a..fcbea28 100644
--- a/src/com/android/systemui/car/systembar/AssistantButton.java
+++ b/src/com/android/systemui/car/systembar/AssistantButton.java
@@ -19,6 +19,7 @@
 import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE;
 
 import android.app.role.RoleManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.os.Bundle;
@@ -30,6 +31,8 @@
 import com.android.internal.app.IVoiceInteractionSessionListener;
 import com.android.internal.app.IVoiceInteractionSessionShowCallback;
 
+import java.util.Set;
+
 /**
  * AssistantButton is an UI component that will trigger the Voice Interaction Service.
  */
@@ -77,11 +80,34 @@
     }
 
     void showAssistant() {
+        if (canShowTosAcceptanceFlow()) {
+            SystemBarUtil.INSTANCE.showTosAcceptanceFlow(getContext(), getUserTracker());
+            return;
+        }
         final Bundle args = new Bundle();
         mAssistUtils.showSessionForActiveService(args,
                 SHOW_SOURCE_ASSIST_GESTURE, mShowCallback, /*activityToken=*/ null);
     }
 
+    /**
+     * Helper method to check if tos acceptance flow can be launched. The tos flow can be launched
+     * when there is no active assistant selected by the system and the default assistant has been
+     * disabled because tos is unaccepted
+     *
+     * @return true if tos flow can be launched, false otherwise
+     */
+    private boolean canShowTosAcceptanceFlow() {
+        ComponentName activeAssistantComponent = mAssistUtils.getActiveServiceComponentName();
+        String defaultAssistantInConfig =
+                getContext().getString(com.android.internal.R.string.config_defaultAssistant);
+        Integer userId = getUserTracker() != null ? getUserTracker().getUserId() : null;
+        Set<String> tosDisabledApps = SystemBarUtil.INSTANCE
+                .getTosDisabledPackages(getContext(), userId);
+        boolean defaultAssistantDisabled = tosDisabledApps.contains(defaultAssistantInConfig);
+
+        return activeAssistantComponent == null && defaultAssistantDisabled;
+    }
+
     @Override
     protected void setUpIntents(TypedArray typedArray) {
         // left blank because for the assistant button Intent will not be passed from the layout.
diff --git a/src/com/android/systemui/car/systembar/CarSystemBarButton.java b/src/com/android/systemui/car/systembar/CarSystemBarButton.java
index ff163bc..580cd23 100644
--- a/src/com/android/systemui/car/systembar/CarSystemBarButton.java
+++ b/src/com/android/systemui/car/systembar/CarSystemBarButton.java
@@ -440,4 +440,9 @@
             icon.setAlpha(mHighlightWhenSelected && mSelected ? mSelectedAlpha : mUnselectedAlpha);
         }
     }
+
+    @Nullable
+    protected UserTracker getUserTracker() {
+        return mUserTracker;
+    }
 }
diff --git a/src/com/android/systemui/car/systembar/SystemBarUtil.kt b/src/com/android/systemui/car/systembar/SystemBarUtil.kt
new file mode 100644
index 0000000..633b05c
--- /dev/null
+++ b/src/com/android/systemui/car/systembar/SystemBarUtil.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2023 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.car.systembar
+
+import android.app.ActivityOptions
+import android.car.settings.CarSettings.Secure.KEY_UNACCEPTED_TOS_DISABLED_APPS
+import android.content.Context
+import android.content.Intent
+import android.os.UserHandle
+import android.provider.Settings
+import android.text.TextUtils
+import android.util.ArraySet
+import android.util.Log
+import com.android.systemui.R
+import com.android.systemui.settings.UserTracker
+import java.net.URISyntaxException
+
+object SystemBarUtil {
+    private const val TAG = "SystemBarUtil"
+    private const val TOS_DISABLED_APPS_SEPARATOR = ","
+
+    /**
+     * Returns a set of packages that are disabled by tos
+     *
+     * @param context The application context
+     * @param uid A user id for a particular user
+     *
+     * @return Set of packages disabled by tos
+     */
+    fun getTosDisabledPackages(context: Context, uid: Int?): Set<String> {
+        if (uid == null) {
+            return ArraySet()
+        }
+        val settingsValue = Settings.Secure
+                .getStringForUser(context.contentResolver, KEY_UNACCEPTED_TOS_DISABLED_APPS, uid)
+        return if (TextUtils.isEmpty(settingsValue)) {
+            ArraySet()
+        } else {
+            settingsValue.split(TOS_DISABLED_APPS_SEPARATOR).toSet()
+        }
+    }
+
+    /**
+     * Gets the intent for launching the TOS acceptance flow
+     *
+     * @param context The app context
+     * @param id The desired resource identifier
+     *
+     * @return TOS intent, or null
+     */
+    fun getIntentForTosAcceptanceFlow(context: Context, id: Int): Intent? {
+        val tosIntentName = context.resources.getString(id)
+        return try {
+            Intent.parseUri(tosIntentName, Intent.URI_ANDROID_APP_SCHEME)
+        } catch (e: URISyntaxException) {
+            Log.e(TAG, "Invalid intent URI in user_tos_activity_intent", e)
+            null
+        }
+    }
+
+    /**
+     * Helper method that launches an activity with an intent for a particular user.
+     *
+     * @param context The app context
+     * @param intent The description of the activity to start.
+     * @param userId The UserHandle of the user to start this activity for.
+     */
+    fun launchApp(context: Context, intent: Intent, userId: UserHandle) {
+        val options = ActivityOptions.makeBasic()
+        options.launchDisplayId = context.displayId
+        context.startActivityAsUser(intent, options.toBundle(), userId)
+    }
+
+    /**
+     * Launch the TOS acceptance flow
+     *
+     * @param context The app context
+     * @param userTracker user tracker object
+     */
+    fun showTosAcceptanceFlow(context: Context, userTracker: UserTracker?) {
+        val tosIntent = getIntentForTosAcceptanceFlow(context, R.string.user_tos_activity_intent)
+        val userHandle = userTracker?.userHandle
+        if (tosIntent == null) {
+            Log.w(TAG, "Unable to launch TOS flow from Assistant because intent is null")
+            return
+        }
+        if (userHandle == null) {
+            Log.w(TAG, "Unable to launch TOS flow from Assistant because userid is null")
+            return
+        }
+        launchApp(context, tosIntent, userHandle)
+    }
+}