diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6d865c21096..5fcb29d2e1b 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -12725,12 +12725,24 @@ Data usage charges may apply.
You can text anyone, including emergency services. Your phone will reconnect to a mobile network when available.
After your phone is connected, you can text anyone, including emergency services.
-
- A satellite connection may be slower and is available only in some areas. Weather and certain structures may affect the connection. Calling by satellite isn\u2019t available. Emergency calls may still connect.\n\nIt may take some time for account changes to show in Settings. Contact %1$s for details.
-
- A satellite connection may be slower and is available only in some areas. Weather and certain structures may affect the connection. Calling by satellite isn\u2019t available. Emergency calls may still connect. Texting with emergency services may not be available in all areas.\n\nIt may take some time for account changes to show in Settings. Contact %1$s for details.
-
- More about satellite connectivity
+
+ Keep in mind
+
+ Satellite connectivity may take longer and is available only in some areas.
+
+ Weather and certain structures may affect your satellite connection.
+
+ Calling by satellite isn\u2019t available.
+
+ Emergency calls may still connect.
+
+ Mobile or Wi\u2011Fi network required to view external links.
+
+ Texting with emergency services may not be available in all areas.
+
+ It may take some time for account changes to show in Settings. Contact %1$s for details.
+
+ More about satellite connectivity
Can’t turn on %1$s
diff --git a/res/xml/satellite_setting.xml b/res/xml/satellite_setting.xml
index 06509700cf7..308d696e5a4 100644
--- a/res/xml/satellite_setting.xml
+++ b/res/xml/satellite_setting.xml
@@ -86,6 +86,7 @@
android:key="satellite_setting_extra_info_footer_pref"
android:layout="@layout/satellite_setting_more_information_layout"
android:selectable="false"
- settings:searchable="false"/>
+ settings:searchable="false"
+ settings:controller="com.android.settings.network.telephony.satellite.SatelliteSettingFooterController"/>
diff --git a/src/com/android/settings/network/telephony/satellite/SatelliteSetting.java b/src/com/android/settings/network/telephony/satellite/SatelliteSetting.java
index 5af26584613..e0f1d62a22e 100644
--- a/src/com/android/settings/network/telephony/satellite/SatelliteSetting.java
+++ b/src/com/android/settings/network/telephony/satellite/SatelliteSetting.java
@@ -27,13 +27,11 @@ import static android.telephony.CarrierConfigManager.KEY_SATELLITE_INFORMATION_R
import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.content.Context;
-import android.content.Intent;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.os.UserManager;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
import android.telephony.satellite.SatelliteManager;
import android.util.Log;
import android.view.View;
@@ -45,8 +43,6 @@ import androidx.preference.PreferenceCategory;
import com.android.settings.R;
import com.android.settings.dashboard.RestrictedDashboardFragment;
-import com.android.settingslib.HelpUtils;
-import com.android.settingslib.widget.FooterPreference;
import java.util.Set;
@@ -54,7 +50,6 @@ import java.util.Set;
public class SatelliteSetting extends RestrictedDashboardFragment {
private static final String TAG = "SatelliteSetting";
private static final String PREF_KEY_CATEGORY_HOW_IT_WORKS = "key_category_how_it_works";
- private static final String KEY_FOOTER_PREFERENCE = "satellite_setting_extra_info_footer_pref";
private static final String KEY_SATELLITE_CONNECTION_GUIDE = "key_satellite_connection_guide";
private static final String KEY_SUPPORTED_SERVICE = "key_supported_service";
@@ -67,7 +62,6 @@ public class SatelliteSetting extends RestrictedDashboardFragment {
private SatelliteManager mSatelliteManager;
private PersistableBundle mConfigBundle;
private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- private String mSimOperatorName = "";
private boolean mIsServiceDataType = false;
private boolean mIsSmsAvailableForManualType = false;
@@ -95,6 +89,7 @@ public class SatelliteSetting extends RestrictedDashboardFragment {
use(SatelliteSettingAboutContentController.class).init(mSubId);
use(SatelliteSettingAccountInfoController.class).init(mSubId, mConfigBundle,
mIsSmsAvailableForManualType, mIsServiceDataType);
+ use(SatelliteSettingFooterController.class).init(mSubId, mConfigBundle);
}
@Override
@@ -111,10 +106,7 @@ public class SatelliteSetting extends RestrictedDashboardFragment {
Log.d(TAG, "SatelliteSettings: KEY_SATELLITE_ATTACH_SUPPORTED_BOOL is false, "
+ "do nothing.");
finish();
- return;
}
-
- mSimOperatorName = getSystemService(TelephonyManager.class).getSimOperatorName(mSubId);
}
@Override
@@ -122,7 +114,6 @@ public class SatelliteSetting extends RestrictedDashboardFragment {
super.onViewCreated(view, savedInstanceState);
boolean isSatelliteEligible = isSatelliteEligible();
updateHowItWorksContent(isSatelliteEligible);
- updateFooterContent();
}
@Override
@@ -154,34 +145,6 @@ public class SatelliteSetting extends RestrictedDashboardFragment {
supportedService.setSummary(R.string.summary_supported_service_for_manual_type);
}
- private void updateFooterContent() {
- // More about satellite messaging
- FooterPreference footerPreference = findPreference(KEY_FOOTER_PREFERENCE);
- if (footerPreference != null) {
- int summary = mConfigBundle.getBoolean(KEY_EMERGENCY_MESSAGING_SUPPORTED_BOOL)
- ? R.string.satellite_setting_summary_more_information
- : R.string.satellite_setting_summary_more_information_no_emergency_messaging;
- footerPreference.setSummary(getResources().getString(summary, mSimOperatorName));
-
- final String[] link = new String[1];
- link[0] = readSatelliteMoreInfoString();
- if (link[0] != null && !link[0].isEmpty()) {
- footerPreference.setLearnMoreAction(view -> {
- if (!link[0].isEmpty()) {
- Intent helpIntent = HelpUtils.getHelpIntent(mActivity, link[0],
- this.getClass().getName());
- if (helpIntent != null) {
- mActivity.startActivityForResult(helpIntent, /*requestCode=*/ 0);
- }
- }
- });
-
- footerPreference.setLearnMoreText(
- getString(R.string.more_about_satellite_messaging));
- }
- }
- }
-
private boolean isSatelliteEligible() {
if (isCarrierRoamingNtnConnectedTypeManual()) {
return mIsSmsAvailableForManualType;
@@ -218,10 +181,6 @@ public class SatelliteSetting extends RestrictedDashboardFragment {
return bundle;
}
- private String readSatelliteMoreInfoString() {
- return mConfigBundle.getString(KEY_SATELLITE_INFORMATION_REDIRECT_URL_STRING, "");
- }
-
private boolean isCarrierRoamingNtnConnectedTypeManual() {
return CARRIER_ROAMING_NTN_CONNECT_MANUAL == mConfigBundle.getInt(
KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT, CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC);
diff --git a/src/com/android/settings/network/telephony/satellite/SatelliteSettingFooterController.java b/src/com/android/settings/network/telephony/satellite/SatelliteSettingFooterController.java
new file mode 100644
index 00000000000..a43ed87a63d
--- /dev/null
+++ b/src/com/android/settings/network/telephony/satellite/SatelliteSettingFooterController.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2025 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.settings.network.telephony.satellite;
+
+import static android.telephony.CarrierConfigManager.KEY_EMERGENCY_MESSAGING_SUPPORTED_BOOL;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_INFORMATION_REDIRECT_URL_STRING;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.PersistableBundle;
+import android.telephony.TelephonyManager;
+import android.text.Html;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.network.telephony.TelephonyBasePreferenceController;
+import com.android.settingslib.HelpUtils;
+import com.android.settingslib.widget.FooterPreference;
+
+/** A controller for showing the dynamic disclaimer of Satellite service. */
+public class SatelliteSettingFooterController extends TelephonyBasePreferenceController {
+ private static final String TAG = "SatelliteSettingFooterController";
+ @VisibleForTesting
+ static final String KEY_FOOTER_PREFERENCE = "satellite_setting_extra_info_footer_pref";
+
+ private PersistableBundle mConfigBundle = new PersistableBundle();
+ private String mSimOperatorName;
+
+ public SatelliteSettingFooterController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ void init(int subId, PersistableBundle configBundle) {
+ mSubId = subId;
+ mConfigBundle = configBundle;
+ mSimOperatorName = mContext.getSystemService(TelephonyManager.class).getSimOperatorName(
+ subId);
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ updateFooterContent(screen);
+ }
+
+ @Override
+ public int getAvailabilityStatus(int subId) {
+ return AVAILABLE_UNSEARCHABLE;
+ }
+
+ private void updateFooterContent(PreferenceScreen screen) {
+ // More about satellite messaging
+ FooterPreference footerPreference = screen.findPreference(KEY_FOOTER_PREFERENCE);
+ if (footerPreference == null) {
+ return;
+ }
+ footerPreference.setSummary(
+ Html.fromHtml(getFooterContent(), Html.FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM));
+ final String link = readSatelliteMoreInfoString();
+ if (link.isEmpty()) {
+ return;
+ }
+ footerPreference.setLearnMoreAction(view -> {
+ Intent helpIntent = HelpUtils.getHelpIntent(mContext, link, this.getClass().getName());
+ if (helpIntent != null) {
+ mContext.startActivityForResult(mContext.getPackageName(),
+ helpIntent, /*requestCode=*/ 0, null);
+ }
+ });
+ footerPreference.setLearnMoreText(
+ mContext.getString(R.string.more_about_satellite_connectivity));
+ }
+
+ private String getFooterContent() {
+ String result = "";
+ result = mContext.getString(R.string.satellite_footer_content_section_0) + "\n\n";
+ result += getHtmlStringCombination(R.string.satellite_footer_content_section_1);
+ result += getHtmlStringCombination(R.string.satellite_footer_content_section_2);
+ result += getHtmlStringCombination(R.string.satellite_footer_content_section_3);
+ result += getHtmlStringCombination(R.string.satellite_footer_content_section_4);
+ result += getHtmlStringCombination(R.string.satellite_footer_content_section_5);
+ if (!mConfigBundle.getBoolean(KEY_EMERGENCY_MESSAGING_SUPPORTED_BOOL)) {
+ result += getHtmlStringCombination(R.string.satellite_footer_content_section_6);
+ }
+ if (mConfigBundle.getBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL)) {
+ result += getHtmlStringCombination(R.string.satellite_footer_content_section_7,
+ mSimOperatorName);
+ }
+ return result;
+ }
+
+ private String getHtmlStringCombination(int resId) {
+ String prefix = "
";
+ String subfix = "";
+ return prefix + mContext.getString(resId) + subfix;
+ }
+
+ private String getHtmlStringCombination(int resId, Object... value) {
+ String prefix = " ";
+ String subfix = "";
+ return prefix + mContext.getString(resId, value) + subfix;
+ }
+
+ private String readSatelliteMoreInfoString() {
+ return mConfigBundle.getString(KEY_SATELLITE_INFORMATION_REDIRECT_URL_STRING);
+ }
+}
diff --git a/src/com/android/settings/network/telephony/satellite/SatelliteSettingsPreferenceCategoryController.java b/src/com/android/settings/network/telephony/satellite/SatelliteSettingsPreferenceCategoryController.java
index d0cb1bcc5ca..6bf635e18f8 100644
--- a/src/com/android/settings/network/telephony/satellite/SatelliteSettingsPreferenceCategoryController.java
+++ b/src/com/android/settings/network/telephony/satellite/SatelliteSettingsPreferenceCategoryController.java
@@ -159,7 +159,7 @@ public class SatelliteSettingsPreferenceCategoryController
@Override
public void onResult(Boolean result) {
mIsSatelliteSupported.set(result);
- Log.d(TAG, "Satellite requestIsSupported : " + result);
+ Log.d(TAG, "Satellite requestIsSupported onResult : " + result);
SatelliteSettingsPreferenceCategoryController.this.displayPreference();
}
});
diff --git a/tests/unit/src/com/android/settings/network/telephony/satellite/SatelliteSettingFooterControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/satellite/SatelliteSettingFooterControllerTest.java
new file mode 100644
index 00000000000..a5873474765
--- /dev/null
+++ b/tests/unit/src/com/android/settings/network/telephony/satellite/SatelliteSettingFooterControllerTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2025 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.settings.network.telephony.satellite;
+
+import static android.telephony.CarrierConfigManager.KEY_EMERGENCY_MESSAGING_SUPPORTED_BOOL;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_INFORMATION_REDIRECT_URL_STRING;
+
+import static com.android.settings.network.telephony.satellite.SatelliteSettingFooterController.KEY_FOOTER_PREFERENCE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.Looper;
+import android.os.PersistableBundle;
+import android.telephony.TelephonyManager;
+
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.testutils.ResourcesUtils;
+import com.android.settingslib.widget.FooterPreference;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+public class SatelliteSettingFooterControllerTest {
+ private static final int TEST_SUB_ID = 5;
+ private static final String TEST_OPERATOR_NAME = "test_operator_name";
+
+ @Rule
+ public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+ @Mock
+ private TelephonyManager mTelephonyManager;
+
+ private Context mContext;
+ private SatelliteSettingFooterController mController;
+ private final PersistableBundle mPersistableBundle = new PersistableBundle();
+
+ @Before
+ public void setUp() {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ mController = new SatelliteSettingFooterController(mContext,
+ KEY_FOOTER_PREFERENCE);
+ when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
+ when(mTelephonyManager.getSimOperatorName(TEST_SUB_ID)).thenReturn(TEST_OPERATOR_NAME);
+ mPersistableBundle.putString(KEY_SATELLITE_INFORMATION_REDIRECT_URL_STRING, "");
+ }
+
+ @Test
+ public void displayPreferenceScreen_updateContent_hasBasicContent() {
+ PreferenceScreen screen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
+ FooterPreference preference = new FooterPreference(mContext);
+ preference.setKey(KEY_FOOTER_PREFERENCE);
+ screen.addPreference(preference);
+ mController.init(TEST_SUB_ID, mPersistableBundle);
+
+ mController.displayPreference(screen);
+ String summary = preference.getSummary().toString();
+
+ assertThat(summary.contains(ResourcesUtils.getResourcesString(mContext,
+ "satellite_footer_content_section_0"))).isTrue();
+ assertThat(summary.contains(ResourcesUtils.getResourcesString(mContext,
+ "satellite_footer_content_section_1"))).isTrue();
+ assertThat(summary.contains(ResourcesUtils.getResourcesString(mContext,
+ "satellite_footer_content_section_2"))).isTrue();
+ assertThat(summary.contains(ResourcesUtils.getResourcesString(mContext,
+ "satellite_footer_content_section_3"))).isTrue();
+ assertThat(summary.contains(ResourcesUtils.getResourcesString(mContext,
+ "satellite_footer_content_section_4"))).isTrue();
+ assertThat(summary.contains(ResourcesUtils.getResourcesString(mContext,
+ "satellite_footer_content_section_5"))).isTrue();
+ }
+
+ @Test
+ public void displayPreferenceScreen_noEmergencyMsgSupport_hasEmergencyContent() {
+ mPersistableBundle.putBoolean(KEY_EMERGENCY_MESSAGING_SUPPORTED_BOOL, false);
+ PreferenceScreen screen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
+ FooterPreference preference = new FooterPreference(mContext);
+ preference.setKey(KEY_FOOTER_PREFERENCE);
+ screen.addPreference(preference);
+ mController.init(TEST_SUB_ID, mPersistableBundle);
+
+ mController.displayPreference(screen);
+ String summary = preference.getSummary().toString();
+
+ assertThat(summary.contains(ResourcesUtils.getResourcesString(mContext,
+ "satellite_footer_content_section_6"))).isTrue();
+ }
+
+ @Test
+ public void displayPreferenceScreen_emergencyMsgSupport_noEmergencyContent() {
+ mPersistableBundle.putBoolean(KEY_EMERGENCY_MESSAGING_SUPPORTED_BOOL, true);
+ PreferenceScreen screen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
+ FooterPreference preference = new FooterPreference(mContext);
+ preference.setKey(KEY_FOOTER_PREFERENCE);
+ screen.addPreference(preference);
+ mController.init(TEST_SUB_ID, mPersistableBundle);
+
+ mController.displayPreference(screen);
+ String summary = preference.getSummary().toString();
+
+ assertThat(summary.contains(ResourcesUtils.getResourcesString(mContext,
+ "satellite_footer_content_section_6"))).isFalse();
+ }
+
+ @Test
+ public void displayPreferenceScreen_entitlementSupport_hasEntitlementContent() {
+ mPersistableBundle.putBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, true);
+ PreferenceScreen screen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
+ FooterPreference preference = new FooterPreference(mContext);
+ preference.setKey(KEY_FOOTER_PREFERENCE);
+ screen.addPreference(preference);
+ mController.init(TEST_SUB_ID, mPersistableBundle);
+
+ mController.displayPreference(screen);
+ String summary = preference.getSummary().toString();
+
+ assertThat(summary.contains(TEST_OPERATOR_NAME)).isTrue();
+ }
+
+ @Test
+ public void displayPreferenceScreen_entitlementNotSupport_noEntitlementContent() {
+ mPersistableBundle.putBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, false);
+ PreferenceScreen screen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
+ FooterPreference preference = new FooterPreference(mContext);
+ preference.setKey(KEY_FOOTER_PREFERENCE);
+ screen.addPreference(preference);
+ mController.init(TEST_SUB_ID, mPersistableBundle);
+
+ mController.displayPreference(screen);
+ String summary = preference.getSummary().toString();
+
+ assertThat(summary.contains(TEST_OPERATOR_NAME)).isFalse();
+ }
+}