[Satellite Settings] Show device's app info on Satellite settings page.

- Shows device apps' info up to 3 on Satellite settings' page.
 - Add a new page to show all device's app info with Satellite supported.

Flag: com.android.internal.telephony.flags.satellite_25q4_apis
Fix: b/395813844
Test: atest pass
Change-Id: Ibd5e1c74b636639082ec9477f2b6796bcbc8340d
This commit is contained in:
tom hsu
2025-02-12 03:29:45 +00:00
committed by Tom Hsu
parent 63242bc3ff
commit bb80fe2df1
8 changed files with 491 additions and 0 deletions

View File

@@ -12607,6 +12607,12 @@
<string name="description_satellite_setting_messaging">satellite messaging</string>
<!-- Title for notifying user's account be able to use data transmission of Satellite" [CHAR_LIMIT=NONE] -->
<string name="title_have_satellite_data_plan">Use of data is included with your account</string>
<!-- Category title for Satellite supported apps info [CHAR LIMIT=60] -->
<string name="category_title_satellite_supported_apps">Supported apps on your phone</string>
<!-- Title for a entry to start apps list page with Satellite service supported. [CHAR LIMIT=60] -->
<string name="title_satellite_supported_app_list_entry">see all apps</string>
<!-- Title for a page of apps list page with Satellite service supported. [CHAR LIMIT=60] -->
<string name="title_satellite_supported_app_list_page">Supported apps on your phone</string>
<!-- Title for Apn settings in mobile network settings [CHAR LIMIT=60] -->
<string name="mobile_network_apn_title">Access Point Names</string>

View File

@@ -62,6 +62,23 @@
android:icon="@drawable/ic_android_satellite_24px"/>
</PreferenceCategory>
<PreferenceCategory
android:key="key_category_satellite_apps"
android:title="@string/category_title_satellite_supported_apps"
settings:isPreferenceVisible="false"
settings:controller="com.android.settings.network.telephony.SatelliteAppListCategoryController"
settings:searchable="false">
<!-- See all satellite apps -->
<Preference
android:key="key_see_all_satellite_apps"
android:title="@string/title_satellite_supported_app_list_entry"
android:icon="@drawable/ic_chevron_right_24dp"
android:fragment="com.android.settings.network.telephony.SatelliteAppListFragment"
android:order="5"
settings:searchable="false"/>
</PreferenceCategory>
<com.android.settingslib.widget.FooterPreference
android:key="satellite_setting_extra_info_footer_pref"
android:layout="@layout/satellite_setting_more_information_layout"

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/tools"
android:key="key_satellite_app_list"
android:title="@string/title_satellite_supported_app_list_page">
</androidx.preference.PreferenceScreen>

View File

@@ -0,0 +1,98 @@
/*
* 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;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import com.android.internal.telephony.flags.Flags;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.network.SatelliteRepository;
import com.android.settingslib.Utils;
import java.util.List;
/** A controller to show some of apps info which supported on Satellite service. */
public class SatelliteAppListCategoryController extends BasePreferenceController {
private static final String TAG = "SatelliteAppListCategoryController";
@VisibleForTesting
static final int MAXIMUM_OF_PREFERENCE_AMOUNT = 3;
private List<String> mPackageNameList;
public SatelliteAppListCategoryController(
@NonNull Context context,
@NonNull String preferenceKey) {
super(context, preferenceKey);
}
/** Initialize the necessary applications' data*/
public void init() {
SatelliteRepository satelliteRepository = new SatelliteRepository(mContext);
init(satelliteRepository);
}
@VisibleForTesting
void init(@NonNull SatelliteRepository satelliteRepository) {
mPackageNameList = satelliteRepository.getSatelliteDataOptimizedApps();
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
PreferenceCategory preferenceCategory = screen.findPreference(getPreferenceKey());
for (int i = 0; i < mPackageNameList.size() && i < MAXIMUM_OF_PREFERENCE_AMOUNT; i++) {
String packageName = mPackageNameList.get(i);
ApplicationInfo appInfo = getApplicationInfo(mContext, packageName);
if (appInfo != null) {
Drawable icon = Utils.getBadgedIcon(mContext, appInfo);
CharSequence name = appInfo.loadLabel(mContext.getPackageManager());
Preference pref = new Preference(mContext);
pref.setIcon(icon);
pref.setTitle(name);
preferenceCategory.addPreference(pref);
}
}
}
@Override
public int getAvailabilityStatus() {
if (!Flags.satellite25q4Apis()) {
return CONDITIONALLY_UNAVAILABLE;
}
return mPackageNameList.isEmpty()
? CONDITIONALLY_UNAVAILABLE
: AVAILABLE;
}
static ApplicationInfo getApplicationInfo(Context context, String packageName) {
try {
PackageManager pm = context.getPackageManager();
return pm.getApplicationInfoAsUser(packageName, /* flags= */ 0, context.getUserId());
} catch (PackageManager.NameNotFoundException e) {
return null;
}
}
}

View File

@@ -0,0 +1,126 @@
/*
* 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;
import static com.android.settings.network.telephony.SatelliteAppListCategoryController.getApplicationInfo;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.graphics.drawable.Drawable;
import android.os.UserManager;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.dashboard.RestrictedDashboardFragment;
import com.android.settings.network.SatelliteRepository;
import com.android.settingslib.Utils;
import com.android.settingslib.core.AbstractPreferenceController;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.List;
import java.util.stream.Collectors;
/** Shows all applications which support satellite service. */
public class SatelliteAppListFragment extends RestrictedDashboardFragment {
public SatelliteAppListFragment() {
super(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
use(SatelliteAppListPreferenceController.class).init();
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
SatelliteAppListPreferenceController satelliteAppListPreferenceController =
new SatelliteAppListPreferenceController(getContext());
return List.of(satelliteAppListPreferenceController);
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.satellite_settings_apps_list;
}
@Override
protected String getLogTag() {
return "SatelliteAppListFragment";
}
@Override
public int getMetricsCategory() {
return SettingsEnums.SATELLITE_APPS_LIST;
}
@VisibleForTesting
static class SatelliteAppListPreferenceController extends BasePreferenceController {
private static final String TAG = "SatelliteAppListPreferenceController";
private static final String KEY = "key_satellite_app_list";
private List<ApplicationInfo> mApplicationInfoList = List.of();
SatelliteAppListPreferenceController(@NonNull Context context) {
super(context, KEY);
}
public void init() {
SatelliteRepository satelliteRepository = new SatelliteRepository(mContext);
init(satelliteRepository);
}
void init(@NonNull SatelliteRepository satelliteRepository) {
mApplicationInfoList =
satelliteRepository.getSatelliteDataOptimizedApps()
.stream()
.map(name -> getApplicationInfo(mContext, name))
.collect(Collectors.toList());
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
if (mApplicationInfoList.isEmpty()) {
return;
}
mApplicationInfoList.forEach(appInfo -> {
if (appInfo != null) {
Log.i(TAG, "Add preference to UI : " + appInfo.packageName);
Drawable icon = Utils.getBadgedIcon(mContext, appInfo);
CharSequence name = appInfo.loadLabel(mContext.getPackageManager());
Preference pref = new Preference(mContext);
pref.setIcon(icon);
pref.setTitle(name);
screen.addPreference(pref);
}
});
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
}
}

View File

@@ -26,6 +26,7 @@ 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.graphics.Typeface;
import android.graphics.drawable.Drawable;
@@ -92,6 +93,12 @@ public class SatelliteSetting extends RestrictedDashboardFragment {
return SettingsEnums.SATELLITE_SETTING;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
use(SatelliteAppListCategoryController.class).init();
}
@Override
public void onCreate(@NonNull Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

View File

@@ -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;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
import static com.android.settings.network.telephony.SatelliteAppListCategoryController.MAXIMUM_OF_PREFERENCE_AMOUNT;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Looper;
import android.platform.test.annotations.EnableFlags;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import com.android.internal.telephony.flags.Flags;
import com.android.settings.network.SatelliteRepository;
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;
import java.util.Collections;
import java.util.List;
public class SatelliteAppListCategoryControllerTest {
@Rule
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
private static final List<String> PACKAGE_NAMES = List.of("com.android.settings",
"com.android.apps.messaging", "com.android.dialer", "com.android.systemui");
private static final String KEY = "SatelliteAppListCategoryControllerTest";
@Mock
private PackageManager mPackageManager;
@Mock
private SatelliteRepository mRepository;
private Context mContext;
private SatelliteAppListCategoryController mController;
@Before
public void setUp() {
if (Looper.myLooper() == null) {
Looper.prepare();
}
mContext = spy(ApplicationProvider.getApplicationContext());
when(mContext.getPackageManager()).thenReturn(mPackageManager);
}
@Test
@EnableFlags(Flags.FLAG_SATELLITE_25Q4_APIS)
public void displayPreference_has4SatSupportedApps_showMaxPreference() throws Exception {
when(mRepository.getSatelliteDataOptimizedApps()).thenReturn(PACKAGE_NAMES);
when(mPackageManager.getApplicationInfoAsUser(any(), anyInt(), anyInt())).thenReturn(
new ApplicationInfo());
mController = new SatelliteAppListCategoryController(mContext, KEY);
mController.init(mRepository);
PreferenceManager preferenceManager = new PreferenceManager(mContext);
PreferenceScreen preferenceScreen = preferenceManager.createPreferenceScreen(mContext);
PreferenceCategory category = new PreferenceCategory(mContext);
category.setKey(mController.getPreferenceKey());
preferenceScreen.addPreference(category);
mController.displayPreference(preferenceScreen);
assertThat(category.getPreferenceCount() == MAXIMUM_OF_PREFERENCE_AMOUNT).isTrue();
}
@Test
@EnableFlags(Flags.FLAG_SATELLITE_25Q4_APIS)
public void getAvailabilityStatus_hasSatSupportedApps_returnAvailable() {
when(mRepository.getSatelliteDataOptimizedApps()).thenReturn(PACKAGE_NAMES);
mController = new SatelliteAppListCategoryController(mContext, KEY);
mController.init(mRepository);
int result = mController.getAvailabilityStatus();
assertThat(result).isEqualTo(AVAILABLE);
}
@Test
@EnableFlags(Flags.FLAG_SATELLITE_25Q4_APIS)
public void getAvailabilityStatus_noSatSupportedApps_returnUnavailable() {
List<String> packageNames = Collections.emptyList();
when(mRepository.getSatelliteDataOptimizedApps()).thenReturn(packageNames);
mController = new SatelliteAppListCategoryController(mContext, KEY);
mController.init(mRepository);
int result = mController.getAvailabilityStatus();
assertThat(result).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
}

View File

@@ -0,0 +1,92 @@
/*
* 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;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Looper;
import android.platform.test.annotations.EnableFlags;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import com.android.internal.telephony.flags.Flags;
import com.android.settings.network.SatelliteRepository;
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;
import java.util.List;
public class SatelliteAppListFragmentTest {
@Rule
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
private static final List<String> PACKAGE_NAMES = List.of(
"com.android.settings",
"com.android.apps.messaging",
"com.android.dialer",
"com.android.systemui"
);
private static final String KEY = "SatelliteAppListPreferenceController";
@Mock
private PackageManager mPackageManager;
@Mock
private SatelliteRepository mRepository;
private Context mContext;
private SatelliteAppListFragment.SatelliteAppListPreferenceController mController;
@Before
public void setUp() {
if (Looper.myLooper() == null) {
Looper.prepare();
}
mContext = spy(ApplicationProvider.getApplicationContext());
when(mContext.getPackageManager()).thenReturn(mPackageManager);
}
@Test
@EnableFlags(Flags.FLAG_SATELLITE_25Q4_APIS)
public void displayPreference_has4SatSupportedApps_showMaxPreference() throws Exception {
when(mRepository.getSatelliteDataOptimizedApps()).thenReturn(PACKAGE_NAMES);
when(mPackageManager.getApplicationInfoAsUser(any(), anyInt(), anyInt())).thenReturn(
new ApplicationInfo());
mController = new SatelliteAppListFragment.SatelliteAppListPreferenceController(mContext);
mController.init(mRepository);
PreferenceManager preferenceManager = new PreferenceManager(mContext);
PreferenceScreen preferenceScreen = preferenceManager.createPreferenceScreen(mContext);
mController.displayPreference(preferenceScreen);
assertThat(preferenceScreen.getPreferenceCount() == PACKAGE_NAMES.size()).isTrue();
}
}