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)
+ }
+}