Add T&C URL to T&C string

Bug: 279611921
Test: Local build
Test: atest DeviceLockControllerRoboTests

Change-Id: Ica9bdc684646c60a376535da7d0a9fc81bcc6cf2
diff --git a/DeviceLockController/res/values/strings.xml b/DeviceLockController/res/values/strings.xml
index b3cf9d5..371af56 100644
--- a/DeviceLockController/res/values/strings.xml
+++ b/DeviceLockController/res/values/strings.xml
@@ -87,7 +87,7 @@
     <!-- Text explaining that the device can be restricted if payment is missing. [CHAR_LIMIT=NONE] -->
     <string name="restrict_device_if_missing_payment"><xliff:g id="provider name">%1$s</xliff:g> can restrict this device if you miss a payment</string>
     <!-- Text explaining that the device can be restricted if the user does not make payments. [CHAR_LIMIT=NONE] -->
-    <string name="restrict_device_if_dont_make_payment"><xliff:g id="provider name">%1$s</xliff:g> can restrict this device if you don\'t make the necessary payments. For details, view the Terms &amp; Conditions.</string>
+    <string name="restrict_device_if_dont_make_payment"><xliff:g id="provider name">%1$s</xliff:g> can restrict this device if you don\'t make the necessary payments. For details, view the <xliff:g id="terms_and_conditions_link_start">&lt;a href="%2$s"></xliff:g>Terms &amp; Conditions<xliff:g id="terms_and_conditions_link_end">&lt;/a&gt;</xliff:g>.</string>
     <!-- Text explaining that the device can be restricted if the owner of the device does not make payments. [CHAR_LIMIT=NONE] -->
     <string name="restrict_device_if_owner_doesnt_make_payment"><xliff:g id="provider name">%1$s</xliff:g> can restrict this device if the owner doesn\'t make payments</string>
     <!-- Button text. This button lets a user go back to previous screen. [CHAR_LIMIT=20] -->
diff --git a/DeviceLockController/src/com/android/devicelockcontroller/activities/ProvisionInfoListAdapter.java b/DeviceLockController/src/com/android/devicelockcontroller/activities/ProvisionInfoListAdapter.java
index 4a22d06..7f4debb 100644
--- a/DeviceLockController/src/com/android/devicelockcontroller/activities/ProvisionInfoListAdapter.java
+++ b/DeviceLockController/src/com/android/devicelockcontroller/activities/ProvisionInfoListAdapter.java
@@ -73,6 +73,10 @@
                     newValue -> notifyItemChanged(position));
             return;
         }
-        provisionInfoViewHolder.bind(getItem(position), providerName);
+
+        // TODO(b/282248521): Refactor ProvisionInfoList, ProviderName, and TermsAndConditionsUrl
+        //  Live Data.
+        provisionInfoViewHolder.bind(getItem(position), providerName,
+                mViewModel.mTermsAndConditionsUrlLiveData.getValue());
     }
 }
diff --git a/DeviceLockController/src/com/android/devicelockcontroller/activities/ProvisionInfoViewHolder.java b/DeviceLockController/src/com/android/devicelockcontroller/activities/ProvisionInfoViewHolder.java
index be3bc85..3695d21 100644
--- a/DeviceLockController/src/com/android/devicelockcontroller/activities/ProvisionInfoViewHolder.java
+++ b/DeviceLockController/src/com/android/devicelockcontroller/activities/ProvisionInfoViewHolder.java
@@ -17,11 +17,19 @@
 package com.android.devicelockcontroller.activities;
 
 import android.content.Context;
+import android.content.Intent;
+import android.text.Html;
+import android.text.Spannable;
+import android.text.SpannableString;
 import android.text.TextUtils;
+import android.text.method.LinkMovementMethod;
+import android.text.style.ClickableSpan;
+import android.text.style.URLSpan;
 import android.view.View;
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.devicelockcontroller.R;
@@ -42,16 +50,65 @@
         mTextView = itemView.findViewById(R.id.text_view_item_provision_info);
     }
 
-    void bind(ProvisionInfo provisionInfo, String providerName) {
+    void bind(ProvisionInfo provisionInfo, String providerName,
+            @Nullable String termsAndConditionsUrl) {
         Context context = itemView.getContext();
         if (TextUtils.isEmpty(providerName)) {
             LogUtil.e(TAG, "Device provider name is empty, should not reach here.");
             return;
         }
-        mTextView.setText(context.getString(provisionInfo.getTextId(), providerName));
+
+        // The Terms and Conditions URL is known at runtime and required for the string used for the
+        // Device Subsidy program.
+        if (provisionInfo.getTextId() == R.string.restrict_device_if_dont_make_payment) {
+            if (TextUtils.isEmpty(termsAndConditionsUrl)) {
+                LogUtil.e(TAG, "Terms and Conditions URL is empty, should not reach here.");
+                return;
+            }
+
+            SpannableString spannedTextView = new SpannableString(Html.fromHtml(
+                    String.format(
+                            context.getString(R.string.restrict_device_if_dont_make_payment),
+                            providerName, termsAndConditionsUrl),
+                    Html.FROM_HTML_MODE_LEGACY));
+            URLSpan[] spans = spannedTextView.getSpans(0, spannedTextView.length(),
+                    URLSpan.class);
+
+            for (URLSpan span : spans) {
+                int start = spannedTextView.getSpanStart(span);
+                int end = spannedTextView.getSpanEnd(span);
+
+                ClickableSpan clickableSpan = new CustomClickableSpan(span.getURL());
+                spannedTextView.removeSpan(span);
+                spannedTextView.setSpan(clickableSpan, start, end,
+                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            }
+
+            mTextView.setText(spannedTextView);
+            mTextView.setMovementMethod(LinkMovementMethod.getInstance());
+        } else {
+            mTextView.setText(context.getString(provisionInfo.getTextId(), providerName));
+        }
+
         mTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(provisionInfo.getDrawableId(),
                 /* top=*/ 0,
                 /* end=*/ 0,
                 /* bottom=*/ 0);
     }
+
+    private static final class CustomClickableSpan extends ClickableSpan {
+
+        final String mUrl;
+
+        CustomClickableSpan(String url) {
+            mUrl = url;
+        }
+
+        @Override
+        public void onClick(@NonNull View view) {
+            Intent webIntent = new Intent(view.getContext(), HelpActivity.class);
+            webIntent.putExtra(HelpActivity.EXTRA_URL_PARAM, mUrl);
+            view.getContext().startActivity(webIntent);
+        }
+    }
 }
diff --git a/DeviceLockController/src/com/android/devicelockcontroller/activities/ProvisionInfoViewModel.java b/DeviceLockController/src/com/android/devicelockcontroller/activities/ProvisionInfoViewModel.java
index 3d8593e..12c3652 100644
--- a/DeviceLockController/src/com/android/devicelockcontroller/activities/ProvisionInfoViewModel.java
+++ b/DeviceLockController/src/com/android/devicelockcontroller/activities/ProvisionInfoViewModel.java
@@ -46,16 +46,17 @@
     final MutableLiveData<Integer> mSubheaderTextIdLiveData;
     final MutableLiveData<List<ProvisionInfo>> mProvisionInfoListLiveData;
     final MutableLiveData<String> mProviderNameLiveData;
+    final MutableLiveData<String> mTermsAndConditionsUrlLiveData;
     final MediatorLiveData<Pair<Integer, String>> mHeaderTextLiveData;
     final MediatorLiveData<Pair<Integer, String>> mSubHeaderTextLiveData;
 
-
     public ProvisionInfoViewModel() {
         mProvisionInfoListLiveData = new MutableLiveData<>();
         mHeaderDrawableIdLiveData = new MutableLiveData<>();
         mHeaderTextIdLiveData = new MutableLiveData<>();
         mSubheaderTextIdLiveData = new MutableLiveData<>();
         mProviderNameLiveData = new MutableLiveData<>();
+        mTermsAndConditionsUrlLiveData = new MutableLiveData<>();
         mHeaderTextLiveData = new MediatorLiveData<>();
         mHeaderTextLiveData.addSource(mHeaderTextIdLiveData,
                 id -> {
@@ -104,5 +105,24 @@
                         LogUtil.e(TAG, "Failed to get Kiosk app provider name", t);
                     }
                 }, MoreExecutors.directExecutor());
+
+        Futures.addCallback(
+                SetupParametersClient.getInstance().getTermsAndConditionsUrl(),
+                new FutureCallback<>() {
+                    @Override
+                    public void onSuccess(String termsAndConditionsUrl) {
+                        if (TextUtils.isEmpty(termsAndConditionsUrl)) {
+                            LogUtil.e(TAG,
+                                    "Terms and Conditions URL is empty, should not reach here.");
+                            return;
+                        }
+                        mTermsAndConditionsUrlLiveData.postValue(termsAndConditionsUrl);
+                    }
+
+                    @Override
+                    public void onFailure(Throwable t) {
+                        LogUtil.e(TAG, "Failed to get Terms and Conditions URL", t);
+                    }
+                }, MoreExecutors.directExecutor());
     }
 }