Snap for 13241370 from 158549abc1
to 25Q2-release
Change-Id: I37bafce136d2bf56eab1e8499ee522aae41a46c4
This commit is contained in:
@@ -8,6 +8,13 @@ flag {
|
||||
bug: "323791114"
|
||||
}
|
||||
|
||||
flag {
|
||||
name: "catalyst_display_settings_screen_25q3"
|
||||
namespace: "android_settings"
|
||||
description: "Flag for Display in 25Q3"
|
||||
bug: "352179685"
|
||||
}
|
||||
|
||||
flag {
|
||||
name: "catalyst_screen_timeout"
|
||||
namespace: "android_settings"
|
||||
@@ -35,3 +42,38 @@ flag {
|
||||
description: "Flag for Adaptive brightness"
|
||||
bug: "323791114"
|
||||
}
|
||||
|
||||
flag {
|
||||
name: "catalyst_night_display"
|
||||
namespace: "android_settings"
|
||||
description: "Flag for Night Light in 25Q3"
|
||||
bug: "352179685"
|
||||
}
|
||||
|
||||
flag {
|
||||
name: "catalyst_color_mode"
|
||||
namespace: "android_settings"
|
||||
description: "Flag for Colors in 25Q3"
|
||||
bug: "352179685"
|
||||
}
|
||||
|
||||
flag {
|
||||
name: "catalyst_gesture_system_navigation_input_summary"
|
||||
namespace: "android_settings"
|
||||
description: "Flag for Navigation mode in 25Q3"
|
||||
bug: "352179685"
|
||||
}
|
||||
|
||||
flag {
|
||||
name: "catalyst_screen_resolution"
|
||||
namespace: "android_settings"
|
||||
description: "Flag for Screen resolution in 25Q3"
|
||||
bug: "352179685"
|
||||
}
|
||||
|
||||
flag {
|
||||
name: "catalyst_auto_rotate"
|
||||
namespace: "android_settings"
|
||||
description: "Flag for Auto-rotate screen in 25Q3"
|
||||
bug: "352179685"
|
||||
}
|
||||
|
@@ -29,6 +29,13 @@ flag {
|
||||
bug: "323791114"
|
||||
}
|
||||
|
||||
flag {
|
||||
name: "catalyst_tether_settings_25q3"
|
||||
namespace: "android_settings"
|
||||
description: "Flag for widgets inside Hotspot & tethering for 25q3 release"
|
||||
bug: "352179685"
|
||||
}
|
||||
|
||||
flag {
|
||||
name: "catalyst_adaptive_connectivity"
|
||||
namespace: "android_settings"
|
||||
|
@@ -4,27 +4,41 @@ container: "system_ext"
|
||||
flag {
|
||||
name: "catalyst_sound_screen"
|
||||
namespace: "android_settings"
|
||||
description: "Flag for sound and vibration page"
|
||||
description: "Flag for Sound & vibration"
|
||||
bug: "323791114"
|
||||
}
|
||||
|
||||
flag {
|
||||
name: "catalyst_sound_screen_25q3"
|
||||
namespace: "android_settings"
|
||||
description: "Flag for Sound & vibration in 25Q3"
|
||||
bug: "352179685"
|
||||
}
|
||||
|
||||
flag {
|
||||
name: "catalyst_media_controls"
|
||||
namespace: "android_settings"
|
||||
description: "Flag for media page"
|
||||
description: "Flag for Media"
|
||||
bug: "337243570"
|
||||
}
|
||||
|
||||
flag {
|
||||
name: "catalyst_vibration_intensity_screen"
|
||||
namespace: "android_settings"
|
||||
description: "Flag for vibration and haptics page"
|
||||
description: "Flag for Vibration & haptics"
|
||||
bug: "323791114"
|
||||
}
|
||||
|
||||
flag {
|
||||
name: "catalyst_vibration_screen"
|
||||
namespace: "android_settings"
|
||||
description: "Flag for vibration and haptics full page migration"
|
||||
description: "Flag for Vibration & haptics full page migration"
|
||||
bug: "323791114"
|
||||
}
|
||||
|
||||
flag {
|
||||
name: "catalyst_spatial_audio"
|
||||
namespace: "android_settings"
|
||||
description: "Flag for Spatial audio in 25Q3"
|
||||
bug: "352179685"
|
||||
}
|
||||
|
@@ -80,3 +80,13 @@ flag {
|
||||
purpose: PURPOSE_BUGFIX
|
||||
}
|
||||
}
|
||||
|
||||
flag {
|
||||
name: "extended_screenshots_exclude_nested_scrollables"
|
||||
namespace: "systemui"
|
||||
description: "Sets a flag on the main scrollable container to exclude any nested scrollable views as potential targets for extended screenshots."
|
||||
bug: "399810823"
|
||||
metadata {
|
||||
purpose: PURPOSE_BUGFIX
|
||||
}
|
||||
}
|
||||
|
32
res/drawable/bluetooth_details_banner_background.xml
Normal file
32
res/drawable/bluetooth_details_banner_background.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?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.
|
||||
-->
|
||||
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="?android:colorControlHighlight">
|
||||
<item
|
||||
android:start="16dp"
|
||||
android:end="16dp"
|
||||
android:top="16dp"
|
||||
android:bottom="16dp">
|
||||
<shape android:shape="rectangle">
|
||||
<solid
|
||||
android:color="@color/settingslib_materialColorSurfaceVariant" />
|
||||
<corners
|
||||
android:radius="28dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
50
res/layout/bluetooth_details_banner.xml
Normal file
50
res/layout/bluetooth_details_banner.xml
Normal file
@@ -0,0 +1,50 @@
|
||||
<?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.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="36dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/bluetooth_details_banner_background">
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="start|top"
|
||||
android:orientation="horizontal"
|
||||
android:paddingBottom="8dp">
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/settingslib_ic_info_outline_24"
|
||||
android:tint="@color/settingslib_materialColorOnSurfaceVariant"
|
||||
android:importantForAccessibility="no" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bluetooth_details_banner_message"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="@color/settingslib_materialColorOnSurfaceVariant"
|
||||
android:hyphenationFrequency="normalFast"
|
||||
android:lineBreakWordStyle="phrase"
|
||||
android:ellipsize="marquee" />
|
||||
|
||||
</LinearLayout>
|
@@ -2099,6 +2099,8 @@
|
||||
<string name="bluetooth_key_missing_device_settings">Device settings</string>
|
||||
<!-- Button text to close the bluetooth key missing dialog-->
|
||||
<string name="bluetooth_key_missing_close">Close</string>
|
||||
<!-- Toast text to when bluetooth key is missing -->
|
||||
<string name="bluetooth_key_missing_toast"><xliff:g id="device_name">%1$s</xliff:g> failed to connect</string>
|
||||
|
||||
<!-- Title of device details screen [CHAR LIMIT=28]-->
|
||||
<string name="device_details_title">Device details</string>
|
||||
@@ -12225,9 +12227,6 @@ Data usage charges may apply.</string>
|
||||
<!-- Help URI, action disabled by restricted settings [DO NOT TRANSLATE] -->
|
||||
<string name="help_url_action_disabled_by_restricted_settings" translatable="false"></string>
|
||||
|
||||
<!-- Help URI, action disabled by advanced protection [DO NOT TRANSLATE] -->
|
||||
<string name="help_url_action_disabled_by_advanced_protection" translatable="false"></string>
|
||||
|
||||
<!-- Title label for dnd suggestion, which is displayed in Settings homepage [CHAR LIMIT=100] -->
|
||||
<string name="zen_suggestion_title">Update Do Not Disturb</string>
|
||||
|
||||
@@ -12725,12 +12724,24 @@ Data usage charges may apply.</string>
|
||||
<string name="summary_supported_service">You can text anyone, including emergency services. Your phone will reconnect to a mobile network when available.</string>
|
||||
<!-- Summary for satellite supported service for NTN manual connection type. [CHAR_LIMIT=NONE] -->
|
||||
<string name="summary_supported_service_for_manual_type">After your phone is connected, you can text anyone, including emergency services.</string>
|
||||
<!-- learn more text - more about satellite messaging [CHAR_LIMIT=NONE] -->
|
||||
<string name="satellite_setting_summary_more_information">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 <xliff:g id="carrier_name" example="T-Mobile">%1$s</xliff:g> for details.</string>
|
||||
<!-- learn more text - more about satellite messaging without emergency messaging support. [CHAR_LIMIT=NONE] -->
|
||||
<string name="satellite_setting_summary_more_information_no_emergency_messaging">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 <xliff:g id="carrier_name" example="T-Mobile">%1$s</xliff:g> for details.</string>
|
||||
<!-- more about satellite connectivity [CHAR_LIMIT=NONE] -->
|
||||
<string name="more_about_satellite_messaging">More about satellite connectivity</string>
|
||||
<!-- learn more text - title of disclaimer of satellite connectivity [CHAR_LIMIT=NONE] -->
|
||||
<string name="satellite_footer_content_section_0">Keep in mind</string>
|
||||
<!-- learn more text - part 1 of disclaimer of satellite connectivity [CHAR_LIMIT=NONE] -->
|
||||
<string name="satellite_footer_content_section_1">Satellite connectivity may take longer and is available only in some areas.</string>
|
||||
<!-- learn more text - part 2 of disclaimer of satellite connectivity [CHAR_LIMIT=NONE] -->
|
||||
<string name="satellite_footer_content_section_2">Weather and certain structures may affect your satellite connection.</string>
|
||||
<!-- learn more text - part 3 of disclaimer of satellite connectivity [CHAR_LIMIT=NONE] -->
|
||||
<string name="satellite_footer_content_section_3">Calling by satellite isn\u2019t available.</string>
|
||||
<!-- learn more text - part 4 of disclaimer of satellite connectivity [CHAR_LIMIT=NONE] -->
|
||||
<string name="satellite_footer_content_section_4">Emergency calls may still connect.</string>
|
||||
<!-- learn more text - part 5 of disclaimer of satellite connectivity [CHAR_LIMIT=NONE] -->
|
||||
<string name="satellite_footer_content_section_5">Mobile or Wi\u2011Fi network required to view external links.</string>
|
||||
<!-- learn more text - part 6 of disclaimer of satellite connectivity [CHAR_LIMIT=NONE] -->
|
||||
<string name="satellite_footer_content_section_6">Texting with emergency services may not be available in all areas.</string>
|
||||
<!-- learn more text - part 7 of disclaimer of satellite connectivity [CHAR_LIMIT=NONE] -->
|
||||
<string name="satellite_footer_content_section_7">It may take some time for account changes to show in Settings. Contact <xliff:g id="carrier_name" example="T-Mobile">%1$s</xliff:g> for details.</string>
|
||||
<!-- more about satellite connectivity with link resource [CHAR_LIMIT=NONE] -->
|
||||
<string name="more_about_satellite_connectivity">More about satellite connectivity</string>
|
||||
<!-- Title for satellite warning dialog to avoid user using wifi/bluetooth/airplane mode [CHAR_LIMIT=NONE] -->
|
||||
<string name="satellite_warning_dialog_title">Can’t turn on <xliff:g id="function" example="bluetooth">%1$s</xliff:g></string>
|
||||
<!-- Content for satellite warning dialog to avoid user using wifi/bluetooth/airplane mode [CHAR_LIMIT=NONE] -->
|
||||
|
@@ -19,6 +19,13 @@
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||
android:title="@string/device_details_title">
|
||||
|
||||
<com.android.settingslib.widget.LayoutPreference
|
||||
android:key="bluetooth_details_banner"
|
||||
android:layout="@layout/bluetooth_details_banner"
|
||||
android:selectable="false"
|
||||
settings:allowDividerBelow="true"
|
||||
settings:searchable="false"/>
|
||||
|
||||
<com.android.settingslib.widget.LayoutPreference
|
||||
android:key="bluetooth_device_header"
|
||||
android:layout="@layout/settings_entity_header"
|
||||
|
@@ -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"/>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
@@ -162,6 +162,12 @@ public class SettingsActivity extends SettingsBaseActivity
|
||||
public static final String EXTRA_SHOW_FRAGMENT_TAB =
|
||||
":settings:show_fragment_tab";
|
||||
|
||||
/**
|
||||
* Whether the settings homepage activity is initiated from a search result deeplink.
|
||||
*/
|
||||
public static final String EXTRA_IS_DEEPLINK_HOME_STARTED_FROM_SEARCH =
|
||||
":settings:is_deeplink_home_started_from_search";
|
||||
|
||||
public static final String META_DATA_KEY_FRAGMENT_CLASS =
|
||||
"com.android.settings.FRAGMENT_CLASS";
|
||||
|
||||
|
@@ -17,6 +17,7 @@
|
||||
package com.android.settings;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
@@ -103,6 +104,9 @@ public class SettingsLicenseActivity extends FragmentActivity implements
|
||||
| Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
|
||||
}
|
||||
intent.addCategory(Intent.CATEGORY_DEFAULT);
|
||||
ComponentName componentName = new ComponentName(
|
||||
"com.android.htmlviewer", "com.android.htmlviewer.HTMLViewerActivity");
|
||||
intent.setComponent(componentName);
|
||||
intent.setPackage("com.android.htmlviewer");
|
||||
|
||||
try {
|
||||
|
@@ -24,7 +24,9 @@ import android.content.pm.UserInfo
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import com.android.settings.SettingsActivity
|
||||
import com.android.settings.SettingsActivity.EXTRA_IS_DEEPLINK_HOME_STARTED_FROM_SEARCH
|
||||
import com.android.settings.Utils
|
||||
import com.android.settings.flags.Flags
|
||||
import com.android.settings.homepage.DeepLinkHomepageActivityInternal
|
||||
import com.android.settings.homepage.SettingsHomepageActivity
|
||||
import com.android.settings.password.PasswordUtils
|
||||
@@ -94,6 +96,28 @@ object EmbeddedDeepLinkUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the deep link trampoline intent for settings search results for large screen devices.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getTrampolineIntentForSearchResult(
|
||||
context: Context,
|
||||
intent: Intent,
|
||||
highlightMenuKey: String?
|
||||
): Intent {
|
||||
return getTrampolineIntent(intent, highlightMenuKey).apply {
|
||||
if (Flags.settingsSearchResultDeepLinkInSameTask()) {
|
||||
// Ensure the deep link intent does not include FLAG_ACTIVITY_NEW_TASK which
|
||||
// causes the search result deep link to open in a separate window.
|
||||
removeFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
putExtra(EXTRA_IS_DEEPLINK_HOME_STARTED_FROM_SEARCH, true)
|
||||
} else {
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
|
||||
}
|
||||
|
||||
setClass(context, DeepLinkHomepageActivityInternal::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the user is a sub profile.
|
||||
|
@@ -476,6 +476,7 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont
|
||||
Supplier<Integer> preloadedLowBatteryLevel,
|
||||
Supplier<Boolean> preloadedIsUntethered,
|
||||
Supplier<Integer> preloadedNativeBatteryLevel) {
|
||||
linearLayout.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
|
||||
final BluetoothDevice bluetoothDevice = mCachedDevice.getDevice();
|
||||
final String iconUri = preloadedIconUri.get();
|
||||
final ImageView imageView = linearLayout.findViewById(R.id.header_icon);
|
||||
@@ -685,6 +686,9 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont
|
||||
private void updateDisconnectLayout() {
|
||||
mLayoutPreference.findViewById(R.id.layout_left).setVisibility(View.GONE);
|
||||
mLayoutPreference.findViewById(R.id.layout_right).setVisibility(View.GONE);
|
||||
mLayoutPreference
|
||||
.findViewById(R.id.layout_middle)
|
||||
.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
|
||||
|
||||
// Hide title, battery icon and battery summary
|
||||
final LinearLayout linearLayout = mLayoutPreference.findViewById(R.id.layout_middle);
|
||||
|
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.bluetooth
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.TextView
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.PreferenceScreen
|
||||
import com.android.settings.R
|
||||
import com.android.settingslib.bluetooth.BluetoothUtils
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle
|
||||
import com.android.settingslib.widget.LayoutPreference
|
||||
|
||||
class BluetoothDetailsBannerController(
|
||||
private val context: Context,
|
||||
fragment: PreferenceFragmentCompat,
|
||||
private val cachedDevice: CachedBluetoothDevice,
|
||||
lifecycle: Lifecycle,
|
||||
) : BluetoothDetailsController(context, fragment, cachedDevice, lifecycle) {
|
||||
private lateinit var pref: LayoutPreference
|
||||
|
||||
override fun getPreferenceKey(): String = KEY_BLUETOOTH_DETAILS_BANNER
|
||||
|
||||
override fun init(screen: PreferenceScreen) {
|
||||
pref = screen.findPreference(KEY_BLUETOOTH_DETAILS_BANNER) ?: return
|
||||
}
|
||||
|
||||
override fun refresh() {
|
||||
pref.findViewById<TextView>(R.id.bluetooth_details_banner_message).text =
|
||||
context.getString(R.string.device_details_key_missing_title, cachedDevice.name)
|
||||
}
|
||||
|
||||
override fun isAvailable(): Boolean =
|
||||
BluetoothUtils.getKeyMissingCount(cachedDevice.device)?.let { it > 0 } ?: false
|
||||
|
||||
private companion object {
|
||||
const val KEY_BLUETOOTH_DETAILS_BANNER: String = "bluetooth_details_banner"
|
||||
}
|
||||
}
|
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.bluetooth
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.UserManager
|
||||
import android.view.View
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceCategory
|
||||
import androidx.preference.PreferenceGroup
|
||||
import com.android.settings.dashboard.RestrictedDashboardFragment
|
||||
|
||||
/** Base class for bluetooth settings which makes the preference visibility/order configurable. */
|
||||
abstract class BluetoothDetailsConfigurableFragment :
|
||||
RestrictedDashboardFragment(UserManager.DISALLOW_CONFIG_BLUETOOTH) {
|
||||
private var displayOrder: List<String>? = null
|
||||
|
||||
fun setPreferenceDisplayOrder(prefKeyOrder: List<String>?) {
|
||||
if (displayOrder == prefKeyOrder) {
|
||||
return
|
||||
}
|
||||
displayOrder = prefKeyOrder
|
||||
updatePreferenceOrder()
|
||||
}
|
||||
|
||||
private val invisiblePrefCategory: PreferenceGroup by lazy {
|
||||
preferenceScreen.findPreference<PreferenceGroup>(INVISIBLE_CATEGORY)
|
||||
?: run {
|
||||
PreferenceCategory(requireContext())
|
||||
.apply {
|
||||
key = INVISIBLE_CATEGORY
|
||||
isVisible = false
|
||||
isOrderingAsAdded = true
|
||||
}
|
||||
.also { preferenceScreen.addPreference(it) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
updatePreferenceOrder()
|
||||
}
|
||||
|
||||
private fun updatePreferenceOrder() {
|
||||
val order = displayOrder?: return
|
||||
if (preferenceScreen == null) {
|
||||
return
|
||||
}
|
||||
preferenceScreen.isOrderingAsAdded = true
|
||||
val allPrefs =
|
||||
(invisiblePrefCategory.getAndRemoveAll() + preferenceScreen.getAndRemoveAll()).filter {
|
||||
it != invisiblePrefCategory
|
||||
}
|
||||
allPrefs.forEach { it.order = Preference.DEFAULT_ORDER }
|
||||
val visiblePrefs =
|
||||
allPrefs.filter { order.contains(it.key) }.sortedBy { order.indexOf(it.key) }
|
||||
val invisiblePrefs = allPrefs.filter { !order.contains(it.key) }
|
||||
preferenceScreen.addPreferences(visiblePrefs)
|
||||
preferenceScreen.addPreference(invisiblePrefCategory)
|
||||
invisiblePrefCategory.addPreferences(invisiblePrefs)
|
||||
}
|
||||
|
||||
private fun PreferenceGroup.getAndRemoveAll(): List<Preference> {
|
||||
val prefs = mutableListOf<Preference>()
|
||||
for (i in 0..<preferenceCount) {
|
||||
prefs.add(getPreference(i))
|
||||
}
|
||||
removeAll()
|
||||
return prefs
|
||||
}
|
||||
|
||||
private fun PreferenceGroup.addPreferences(prefs: List<Preference>) {
|
||||
for (pref in prefs) {
|
||||
addPreference(pref)
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val INVISIBLE_CATEGORY = "invisible_profile_category"
|
||||
}
|
||||
}
|
@@ -62,7 +62,6 @@ public class BluetoothDetailsHeaderController extends BluetoothDetailsController
|
||||
final LayoutPreference headerPreference = screen.findPreference(KEY_DEVICE_HEADER);
|
||||
mHeaderController = EntityHeaderController.newInstance(mFragment.getActivity(), mFragment,
|
||||
headerPreference.findViewById(R.id.entity_header));
|
||||
screen.addPreference(headerPreference);
|
||||
}
|
||||
|
||||
protected void setHeaderProperties() {
|
||||
|
@@ -17,7 +17,6 @@
|
||||
package com.android.settings.bluetooth;
|
||||
|
||||
import static android.bluetooth.BluetoothDevice.BOND_NONE;
|
||||
import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.settings.SettingsEnums;
|
||||
@@ -49,7 +48,6 @@ import com.android.settings.R;
|
||||
import com.android.settings.bluetooth.ui.model.FragmentTypeModel;
|
||||
import com.android.settings.bluetooth.ui.view.DeviceDetailsFragmentFormatter;
|
||||
import com.android.settings.connecteddevice.stylus.StylusDevicesController;
|
||||
import com.android.settings.dashboard.RestrictedDashboardFragment;
|
||||
import com.android.settings.flags.Flags;
|
||||
import com.android.settings.inputmethod.KeyboardSettingsPreferenceController;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
@@ -66,7 +64,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment {
|
||||
public class BluetoothDeviceDetailsFragment extends BluetoothDetailsConfigurableFragment {
|
||||
public static final String KEY_DEVICE_ADDRESS = "device_address";
|
||||
private static final String TAG = "BTDeviceDetailsFrg";
|
||||
private static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25;
|
||||
@@ -102,6 +100,7 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
|
||||
BluetoothAdapter mBluetoothAdapter;
|
||||
@VisibleForTesting
|
||||
DeviceDetailsFragmentFormatter mFormatter;
|
||||
boolean mIsKeyMissingDevice = false;
|
||||
|
||||
@Nullable
|
||||
InputDevice mInputDevice;
|
||||
@@ -144,7 +143,7 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
|
||||
};
|
||||
|
||||
public BluetoothDeviceDetailsFragment() {
|
||||
super(DISALLOW_CONFIG_BLUETOOTH);
|
||||
super();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -212,6 +211,9 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
Integer keyMissingCount = BluetoothUtils.getKeyMissingCount(mCachedDevice.getDevice());
|
||||
mIsKeyMissingDevice = keyMissingCount != null && keyMissingCount > 0;
|
||||
setPreferenceDisplayOrder(generateDisplayedPreferenceKeys(mIsKeyMissingDevice));
|
||||
getController(
|
||||
AdvancedBluetoothDetailsHeaderController.class,
|
||||
controller -> controller.init(mCachedDevice, this));
|
||||
@@ -342,7 +344,7 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
if (Flags.enableBluetoothDeviceDetailsPolish()) {
|
||||
if (!mIsKeyMissingDevice && Flags.enableBluetoothDeviceDetailsPolish()) {
|
||||
if (mFormatter == null) {
|
||||
List<AbstractPreferenceController> controllers = getPreferenceControllers().stream()
|
||||
.flatMap(List::stream)
|
||||
@@ -412,12 +414,29 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
|
||||
return super.onOptionsItemSelected(menuItem);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private List<String> generateDisplayedPreferenceKeys(boolean bondingLoss) {
|
||||
if (bondingLoss) {
|
||||
return List.of(
|
||||
use(BluetoothDetailsBannerController.class).getPreferenceKey(),
|
||||
use(AdvancedBluetoothDetailsHeaderController.class).getPreferenceKey(),
|
||||
use(BluetoothDetailsHeaderController.class).getPreferenceKey(),
|
||||
use(LeAudioBluetoothDetailsHeaderController.class).getPreferenceKey(),
|
||||
use(BluetoothDetailsButtonsController.class).getPreferenceKey(),
|
||||
use(BluetoothDetailsMacAddressController.class).getPreferenceKey());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
|
||||
ArrayList<AbstractPreferenceController> controllers = new ArrayList<>();
|
||||
|
||||
if (mCachedDevice != null) {
|
||||
Lifecycle lifecycle = getSettingsLifecycle();
|
||||
controllers.add(
|
||||
new BluetoothDetailsBannerController(
|
||||
context, this, mCachedDevice, lifecycle));
|
||||
controllers.add(new BluetoothDetailsHeaderController(context, this, mCachedDevice,
|
||||
lifecycle));
|
||||
controllers.add(
|
||||
|
@@ -66,7 +66,7 @@ public class BluetoothKeyMissingDialogFragment extends InstrumentedDialogFragmen
|
||||
View view = getActivity().getLayoutInflater().inflate(R.layout.bluetooth_key_missing, null);
|
||||
TextView keyMissingTitle = view.findViewById(R.id.bluetooth_key_missing_title);
|
||||
keyMissingTitle.setText(
|
||||
getString(R.string.bluetooth_key_missing_title, mBluetoothDevice.getName()));
|
||||
getString(R.string.bluetooth_key_missing_title, mBluetoothDevice.getAlias()));
|
||||
builder.setView(view);
|
||||
builder.setPositiveButton(getString(R.string.bluetooth_key_missing_device_settings), this);
|
||||
builder.setNegativeButton(getString(R.string.bluetooth_key_missing_close), this);
|
||||
|
@@ -28,6 +28,7 @@ import android.os.PowerManager;
|
||||
import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
@@ -68,15 +69,23 @@ public final class BluetoothKeyMissingReceiver extends BroadcastReceiver {
|
||||
return;
|
||||
}
|
||||
Integer keyMissingCount = BluetoothUtils.getKeyMissingCount(device);
|
||||
if (keyMissingCount != null && keyMissingCount != 1) {
|
||||
Log.d(TAG, "Key missing count is " + keyMissingCount + ", skip.");
|
||||
return;
|
||||
}
|
||||
boolean keyMissingFirstTime = keyMissingCount == null || keyMissingCount == 1;
|
||||
if (shouldShowDialog(context, device, powerManager)) {
|
||||
Intent pairingIntent = getKeyMissingDialogIntent(context, device);
|
||||
Log.d(TAG, "Show key missing dialog:" + device);
|
||||
context.startActivityAsUser(pairingIntent, UserHandle.CURRENT);
|
||||
} else {
|
||||
if (keyMissingFirstTime) {
|
||||
Intent pairingIntent = getKeyMissingDialogIntent(context, device);
|
||||
Log.d(TAG, "Show key missing dialog:" + device);
|
||||
context.startActivityAsUser(pairingIntent, UserHandle.CURRENT);
|
||||
} else {
|
||||
Log.d(TAG, "Show key missing toast:" + device);
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(
|
||||
R.string.bluetooth_key_missing_toast,
|
||||
device.getAlias()),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
} else if (keyMissingFirstTime) {
|
||||
Log.d(TAG, "Show key missing notification: " + device);
|
||||
showNotification(context, device);
|
||||
}
|
||||
@@ -123,7 +132,7 @@ public final class BluetoothKeyMissingReceiver extends BroadcastReceiver {
|
||||
.setLocalOnly(true);
|
||||
builder.setContentTitle(
|
||||
context.getString(
|
||||
R.string.bluetooth_key_missing_title, bluetoothDevice.getName()))
|
||||
R.string.bluetooth_key_missing_title, bluetoothDevice.getAlias()))
|
||||
.setContentText(context.getString(R.string.bluetooth_key_missing_message))
|
||||
.setContentIntent(pairIntent)
|
||||
.setAutoCancel(true)
|
||||
|
@@ -80,7 +80,7 @@ public class HearingDeviceInputRoutingPreference extends CustomDialogPreferenceC
|
||||
setDialogTitle(R.string.bluetooth_hearing_device_input_routing_dialog_title);
|
||||
setDialogLayoutResource(R.layout.hearing_device_input_routing_dialog);
|
||||
setNegativeButtonText(R.string.cancel);
|
||||
setPositiveButtonText(R.string.done_button);
|
||||
setPositiveButtonText(R.string.done);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -26,9 +26,13 @@ import android.os.Bundle
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
@@ -227,6 +231,7 @@ class DeviceDetailsFragmentFormatterImpl(
|
||||
|
||||
dashboardFragment.lifecycleScope.launch {
|
||||
if (isLoading) {
|
||||
scrollToTop()
|
||||
dashboardFragment.setLoading(false, false)
|
||||
isLoading = false
|
||||
}
|
||||
@@ -265,12 +270,10 @@ class DeviceDetailsFragmentFormatterImpl(
|
||||
summary = model.summary
|
||||
icon = getDrawable(model.icon)
|
||||
onPreferenceClickListener =
|
||||
object : Preference.OnPreferenceClickListener {
|
||||
override fun onPreferenceClick(p: Preference): Boolean {
|
||||
logItemClick(prefKey, EVENT_CLICK_PRIMARY)
|
||||
model.action?.let { triggerAction(it) }
|
||||
return true
|
||||
}
|
||||
Preference.OnPreferenceClickListener {
|
||||
logItemClick(prefKey, EVENT_CLICK_PRIMARY)
|
||||
model.action?.let { triggerAction(it) }
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -296,7 +299,6 @@ class DeviceDetailsFragmentFormatterImpl(
|
||||
prefKey,
|
||||
if (newState) EVENT_SWITCH_ON else EVENT_SWITCH_OFF,
|
||||
)
|
||||
isEnabled = false
|
||||
model.onCheckedChange.invoke(newState)
|
||||
}
|
||||
return false
|
||||
@@ -314,12 +316,10 @@ class DeviceDetailsFragmentFormatterImpl(
|
||||
isEnabled = !model.disabled
|
||||
isSwitchEnabled = !model.disabled
|
||||
onPreferenceClickListener =
|
||||
object : Preference.OnPreferenceClickListener {
|
||||
override fun onPreferenceClick(p: Preference): Boolean {
|
||||
logItemClick(prefKey, EVENT_CLICK_PRIMARY)
|
||||
triggerAction(model.action)
|
||||
return true
|
||||
}
|
||||
Preference.OnPreferenceClickListener {
|
||||
logItemClick(prefKey, EVENT_CLICK_PRIMARY)
|
||||
triggerAction(model.action)
|
||||
true
|
||||
}
|
||||
onPreferenceChangeListener =
|
||||
object : Preference.OnPreferenceChangeListener {
|
||||
@@ -332,7 +332,6 @@ class DeviceDetailsFragmentFormatterImpl(
|
||||
prefKey,
|
||||
if (newState) EVENT_SWITCH_ON else EVENT_SWITCH_OFF,
|
||||
)
|
||||
isSwitchEnabled = false
|
||||
model.onCheckedChange.invoke(newState)
|
||||
return false
|
||||
}
|
||||
@@ -391,6 +390,12 @@ class DeviceDetailsFragmentFormatterImpl(
|
||||
deviceSettingIcon.bitmap.toDrawable(context.resources)
|
||||
is DeviceSettingIcon.ResourceIcon -> context.getDrawable(deviceSettingIcon.resId)
|
||||
null -> null
|
||||
}?.apply {
|
||||
setTint(
|
||||
context.getColor(
|
||||
com.android.settingslib.widget.theme.R.color.settingslib_materialColorOnSurfaceVariant
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -495,6 +500,19 @@ class DeviceDetailsFragmentFormatterImpl(
|
||||
}
|
||||
}
|
||||
|
||||
private fun scrollToTop() {
|
||||
// Temporary fix to make sure the screen is scroll to the top when rendering.
|
||||
ComposePreference(context).apply {
|
||||
order = -1
|
||||
isEnabled = false
|
||||
isSelectable = false
|
||||
setContent { Spacer(modifier = Modifier.height(1.dp)) }
|
||||
}.also {
|
||||
dashboardFragment.preferenceScreen.addPreference(it)
|
||||
dashboardFragment.scrollToPreference(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPreferenceKey(settingId: Int) = "DEVICE_SETTING_${settingId}"
|
||||
|
||||
private class SpotlightPreference(context: Context) : Preference(context) {
|
||||
|
@@ -40,7 +40,9 @@ import com.android.settings.R;
|
||||
import com.android.settings.bluetooth.BluetoothPairingDetail;
|
||||
import com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsQrCodeFragment;
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||
import com.android.settingslib.bluetooth.BluetoothLeBroadcastMetadataExt;
|
||||
import com.android.settingslib.bluetooth.BluetoothUtils;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
@@ -75,6 +77,9 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment {
|
||||
private static Pair<Integer, Object>[] sEventData = new Pair[0];
|
||||
@Nullable private static Fragment sHost;
|
||||
|
||||
AudioSharingFeatureProvider audioSharingFeatureProvider =
|
||||
FeatureFactory.getFeatureFactory().getAudioSharingFeatureProvider();
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.DIALOG_AUDIO_SHARING_ADD_DEVICE;
|
||||
@@ -158,6 +163,9 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment {
|
||||
Log.d(TAG, "Create dialog error: null deviceItems");
|
||||
return builder.build();
|
||||
}
|
||||
BluetoothLeBroadcastMetadata metadata = arguments.getParcelable(
|
||||
BUNDLE_KEY_BROADCAST_METADATA, BluetoothLeBroadcastMetadata.class);
|
||||
Drawable qrCodeDrawable = null;
|
||||
if (deviceItems.isEmpty()) {
|
||||
builder.setTitle(R.string.audio_sharing_share_dialog_title)
|
||||
.setCustomPositiveButton(
|
||||
@@ -181,9 +189,7 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment {
|
||||
}
|
||||
launcher.launch();
|
||||
});
|
||||
BluetoothLeBroadcastMetadata metadata = arguments.getParcelable(
|
||||
BUNDLE_KEY_BROADCAST_METADATA, BluetoothLeBroadcastMetadata.class);
|
||||
Drawable qrCodeDrawable = metadata == null ? null : getQrCodeDrawable(metadata,
|
||||
qrCodeDrawable = metadata == null ? null : getQrCodeDrawable(metadata,
|
||||
getContext()).orElse(null);
|
||||
if (qrCodeDrawable != null) {
|
||||
String broadcastName =
|
||||
@@ -195,8 +201,7 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment {
|
||||
new String(metadata.getBroadcastCode(), StandardCharsets.UTF_8)) :
|
||||
getString(R.string.audio_sharing_dialog_qr_code_content_no_password,
|
||||
broadcastName);
|
||||
builder.setCustomImage(qrCodeDrawable)
|
||||
.setCustomMessage(message)
|
||||
builder.setCustomMessage(message)
|
||||
.setCustomMessage2(R.string.audio_sharing_dialog_pair_new_device_content)
|
||||
.setCustomNegativeButton(R.string.audio_streams_dialog_close,
|
||||
v -> onCancelClick());
|
||||
@@ -251,7 +256,17 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment {
|
||||
.setCustomNegativeButton(
|
||||
com.android.settings.R.string.cancel, v -> onCancelClick());
|
||||
}
|
||||
return builder.build();
|
||||
Dialog dialog = builder.build();
|
||||
dialog.show();
|
||||
if (deviceItems.isEmpty() && qrCodeDrawable != null) {
|
||||
audioSharingFeatureProvider.setQrCode(
|
||||
this,
|
||||
dialog.getWindow().getDecorView(),
|
||||
R.id.description_image,
|
||||
qrCodeDrawable,
|
||||
BluetoothLeBroadcastMetadataExt.INSTANCE.toQrCodeString(metadata));
|
||||
}
|
||||
return dialog;
|
||||
}
|
||||
|
||||
private void onCancelClick() {
|
||||
|
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.connecteddevice.audiosharing;
|
||||
|
||||
import android.annotation.IdRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
/** Feature provider for the audio sharing features. */
|
||||
public interface AudioSharingFeatureProvider {
|
||||
/**
|
||||
* Sets the QR code for audio sharing dialogs
|
||||
*
|
||||
* @param fragment the fragment to be updated
|
||||
* @param qrcodeContainer the view to be updated
|
||||
* @param qrCodeImageViewId the view ID to search for
|
||||
* @param drawable the drawable asset of the QR code
|
||||
* @param qrCode the value of the qrCode
|
||||
*/
|
||||
public void setQrCode(
|
||||
@NonNull Fragment fragment,
|
||||
@NonNull View qrcodeContainer,
|
||||
@IdRes int qrCodeImageViewId,
|
||||
@NonNull Drawable drawable,
|
||||
@NonNull String qrCode);
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.connecteddevice.audiosharing;
|
||||
|
||||
import android.annotation.IdRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
/** Default implementation for {@link AudioSharingFeatureProvider} */
|
||||
public class AudioSharingFeatureProviderImpl implements AudioSharingFeatureProvider {
|
||||
public void setQrCode(
|
||||
@NonNull Fragment fragment,
|
||||
@NonNull View qrcodeContainer,
|
||||
@IdRes int qrCodeImageViewId,
|
||||
@NonNull Drawable drawable,
|
||||
@NonNull String qrCode) {
|
||||
ImageView imageView = ((ImageView) qrcodeContainer.requireViewById(qrCodeImageViewId));
|
||||
imageView.setImageDrawable(drawable);
|
||||
imageView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
@@ -37,11 +37,13 @@ import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.bluetooth.Utils;
|
||||
import com.android.settings.connecteddevice.audiosharing.AudioSharingFeatureProvider;
|
||||
import com.android.settings.core.InstrumentedFragment;
|
||||
import com.android.settingslib.bluetooth.BluetoothLeBroadcastMetadataExt;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
|
||||
import com.android.settingslib.qrcode.QrCodeGenerator;
|
||||
import com.android.settingslib.utils.ThreadUtils;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
|
||||
import com.google.zxing.WriterException;
|
||||
|
||||
@@ -52,6 +54,9 @@ import java.util.Optional;
|
||||
public class AudioStreamsQrCodeFragment extends InstrumentedFragment {
|
||||
private static final String TAG = "AudioStreamsQrCodeFragment";
|
||||
|
||||
AudioSharingFeatureProvider audioSharingFeatureProvider =
|
||||
FeatureFactory.getFeatureFactory().getAudioSharingFeatureProvider();
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.AUDIO_STREAM_QR_CODE;
|
||||
@@ -68,42 +73,52 @@ public class AudioStreamsQrCodeFragment extends InstrumentedFragment {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
// Collapse or expand the app bar based on orientation for better display the qr code image.
|
||||
AudioStreamsHelper.configureAppBarByOrientation(getActivity());
|
||||
var unused = ThreadUtils.postOnBackgroundThread(
|
||||
() -> {
|
||||
BluetoothLeBroadcastMetadata broadcastMetadata = getBroadcastMetadata();
|
||||
if (broadcastMetadata == null) {
|
||||
return;
|
||||
}
|
||||
Drawable drawable = getQrCodeDrawable(broadcastMetadata, getActivity()).orElse(
|
||||
null);
|
||||
if (drawable == null) {
|
||||
return;
|
||||
}
|
||||
var unused =
|
||||
ThreadUtils.postOnBackgroundThread(
|
||||
() -> {
|
||||
BluetoothLeBroadcastMetadata broadcastMetadata = getBroadcastMetadata();
|
||||
if (broadcastMetadata == null) {
|
||||
return;
|
||||
}
|
||||
Drawable drawable =
|
||||
getQrCodeDrawable(broadcastMetadata, getActivity())
|
||||
.orElse(null);
|
||||
if (drawable == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ThreadUtils.postOnMainThread(
|
||||
() -> {
|
||||
((ImageView) view.requireViewById(R.id.qrcode_view))
|
||||
.setImageDrawable(drawable);
|
||||
if (broadcastMetadata.getBroadcastCode() != null) {
|
||||
String password =
|
||||
new String(
|
||||
broadcastMetadata.getBroadcastCode(),
|
||||
StandardCharsets.UTF_8);
|
||||
String passwordText =
|
||||
getString(
|
||||
R.string.audio_streams_qr_code_page_password,
|
||||
password);
|
||||
((TextView) view.requireViewById(R.id.password))
|
||||
.setText(passwordText);
|
||||
}
|
||||
TextView summaryView = view.requireViewById(android.R.id.summary);
|
||||
String summary =
|
||||
getString(
|
||||
R.string.audio_streams_qr_code_page_description,
|
||||
broadcastMetadata.getBroadcastName());
|
||||
summaryView.setText(summary);
|
||||
});
|
||||
});
|
||||
ThreadUtils.postOnMainThread(
|
||||
() -> {
|
||||
audioSharingFeatureProvider.setQrCode(
|
||||
this,
|
||||
view,
|
||||
R.id.qrcode_view,
|
||||
drawable,
|
||||
BluetoothLeBroadcastMetadataExt.INSTANCE
|
||||
.toQrCodeString(broadcastMetadata));
|
||||
if (broadcastMetadata.getBroadcastCode() != null) {
|
||||
String password =
|
||||
new String(
|
||||
broadcastMetadata.getBroadcastCode(),
|
||||
StandardCharsets.UTF_8);
|
||||
String passwordText =
|
||||
getString(
|
||||
R.string
|
||||
.audio_streams_qr_code_page_password,
|
||||
password);
|
||||
((TextView) view.requireViewById(R.id.password))
|
||||
.setText(passwordText);
|
||||
}
|
||||
TextView summaryView =
|
||||
view.requireViewById(android.R.id.summary);
|
||||
String summary =
|
||||
getString(
|
||||
R.string
|
||||
.audio_streams_qr_code_page_description,
|
||||
broadcastMetadata.getBroadcastName());
|
||||
summaryView.setText(summary);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** Gets an optional drawable from metadata. */
|
||||
|
@@ -64,6 +64,9 @@ public interface PowerUsageFeatureProvider {
|
||||
/** Returns an allowlist of app names combined into the system-apps item */
|
||||
List<String> getSystemAppsAllowlist();
|
||||
|
||||
/** Returns the data retention days in the database */
|
||||
int getDataRetentionDays();
|
||||
|
||||
/** Check whether location setting is enabled */
|
||||
boolean isLocationSettingEnabled(String[] packages);
|
||||
|
||||
|
@@ -118,6 +118,11 @@ public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDataRetentionDays() {
|
||||
return 9;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLocationSettingEnabled(String[] packages) {
|
||||
return false;
|
||||
|
@@ -45,6 +45,7 @@ import com.android.settings.fuelgauge.BatteryUsageHistoricalLogEntry.Action;
|
||||
import com.android.settings.fuelgauge.BatteryUtils;
|
||||
import com.android.settings.fuelgauge.batteryusage.bugreport.BatteryUsageLogUtils;
|
||||
import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settingslib.fuelgauge.BatteryStatus;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
@@ -67,7 +68,6 @@ public final class DatabaseUtils {
|
||||
private static final String SHARED_PREFS_FILE = "battery_usage_shared_prefs";
|
||||
private static final long INVALID_TIMESTAMP = 0L;
|
||||
|
||||
static final int DATA_RETENTION_INTERVAL_DAY = 9;
|
||||
static final String KEY_LAST_LOAD_FULL_CHARGE_TIME = "last_load_full_charge_time";
|
||||
static final String KEY_LAST_UPLOAD_FULL_CHARGE_TIME = "last_upload_full_charge_time";
|
||||
static final String KEY_LAST_USAGE_SOURCE = "last_usage_source";
|
||||
@@ -468,11 +468,14 @@ public final class DatabaseUtils {
|
||||
AsyncTask.execute(
|
||||
() -> {
|
||||
try {
|
||||
final int dataRetentionDays =
|
||||
FeatureFactory.getFeatureFactory()
|
||||
.getPowerUsageFeatureProvider().getDataRetentionDays();
|
||||
final BatteryStateDatabase database =
|
||||
BatteryStateDatabase.getInstance(context.getApplicationContext());
|
||||
final long earliestTimestamp =
|
||||
Clock.systemUTC().millis()
|
||||
- Duration.ofDays(DATA_RETENTION_INTERVAL_DAY).toMillis();
|
||||
- Duration.ofDays(dataRetentionDays).toMillis();
|
||||
database.appUsageEventDao().clearAllBefore(earliestTimestamp);
|
||||
database.batteryEventDao().clearAllBefore(earliestTimestamp);
|
||||
database.batteryStateDao().clearAllBefore(earliestTimestamp);
|
||||
|
@@ -20,6 +20,7 @@ import static android.provider.Settings.ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY
|
||||
import static android.provider.Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY;
|
||||
import static android.provider.Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI;
|
||||
|
||||
import static com.android.settings.SettingsActivity.EXTRA_IS_DEEPLINK_HOME_STARTED_FROM_SEARCH;
|
||||
import static com.android.settings.SettingsActivity.EXTRA_USER_HANDLE;
|
||||
|
||||
import android.animation.LayoutTransition;
|
||||
@@ -232,7 +233,9 @@ public class SettingsHomepageActivity extends FragmentActivity implements
|
||||
}
|
||||
}
|
||||
|
||||
if (!isTaskRoot) {
|
||||
final boolean isDeepLinkStartedFromSearch = getIntent().getBooleanExtra(
|
||||
EXTRA_IS_DEEPLINK_HOME_STARTED_FROM_SEARCH, false /* defaultValue */);
|
||||
if (!isTaskRoot && !isDeepLinkStartedFromSearch) {
|
||||
if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
|
||||
Log.i(TAG, "Activity has been started, finishing");
|
||||
} else {
|
||||
@@ -775,6 +778,16 @@ public class SettingsHomepageActivity extends FragmentActivity implements
|
||||
// Prevent inner RecyclerView gets focus and invokes scrolling.
|
||||
view.setFocusableInTouchMode(true);
|
||||
view.requestFocus();
|
||||
|
||||
if (Flags.extendedScreenshotsExcludeNestedScrollables()) {
|
||||
// Force scroll capture to select the NestedScrollView, instead of the non-scrollable
|
||||
// RecyclerView which is contained inside it with no height constraint.
|
||||
final View scrollableContainer = findViewById(R.id.main_content_scrollable_container);
|
||||
if (scrollableContainer != null) {
|
||||
scrollableContainer.setScrollCaptureHint(
|
||||
View.SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateHomepageAppBar() {
|
||||
|
@@ -529,7 +529,11 @@ public class LocaleListEditor extends RestrictedSettingsFragment implements View
|
||||
@Nullable Locale defaultLocaleBeforeRemoval) {
|
||||
Locale currentSystemLocale = LocalePicker.getLocales().get(0);
|
||||
if (!localeInfo.getLocale().equals(currentSystemLocale)) {
|
||||
displayDialogFragment(localeInfo, true);
|
||||
if (Locale.getDefault().equals(localeInfo.getLocale())) {
|
||||
mAdapter.doTheUpdate();
|
||||
} else {
|
||||
displayDialogFragment(localeInfo, true);
|
||||
}
|
||||
} else {
|
||||
if (!localeInfo.isTranslated()) {
|
||||
if (defaultLocaleBeforeRemoval == null) {
|
||||
|
@@ -202,9 +202,6 @@ open class SatelliteRepository(
|
||||
* e.g. "com.android.settings"
|
||||
*/
|
||||
open fun getSatelliteDataOptimizedApps(): List<String> {
|
||||
if (!Flags.satellite25q4Apis()) {
|
||||
return emptyList()
|
||||
}
|
||||
val satelliteManager: SatelliteManager? =
|
||||
context.getSystemService(SatelliteManager::class.java)
|
||||
if (satelliteManager == null) {
|
||||
|
@@ -16,10 +16,17 @@
|
||||
|
||||
package com.android.settings.network.telephony.satellite;
|
||||
|
||||
import static android.telephony.CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_MANUAL;
|
||||
import static android.telephony.CarrierConfigManager.KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT;
|
||||
import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.PersistableBundle;
|
||||
import android.telephony.satellite.SatelliteManager;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
@@ -27,20 +34,23 @@ 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.settings.network.telephony.TelephonyBasePreferenceController;
|
||||
import com.android.settingslib.Utils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/** A controller to show some of apps info which supported on Satellite service. */
|
||||
public class SatelliteAppListCategoryController extends BasePreferenceController {
|
||||
public class SatelliteAppListCategoryController extends TelephonyBasePreferenceController {
|
||||
private static final String TAG = "SatelliteAppListCategoryController";
|
||||
@VisibleForTesting
|
||||
static final int MAXIMUM_OF_PREFERENCE_AMOUNT = 3;
|
||||
|
||||
private List<String> mPackageNameList;
|
||||
private boolean mIsSmsAvailable;
|
||||
private boolean mIsDataAvailable;
|
||||
private boolean mIsSatelliteEligible;
|
||||
private PersistableBundle mConfigBundle = new PersistableBundle();
|
||||
|
||||
public SatelliteAppListCategoryController(
|
||||
@NonNull Context context,
|
||||
@@ -49,14 +59,14 @@ public class SatelliteAppListCategoryController extends BasePreferenceController
|
||||
}
|
||||
|
||||
/** Initialize the necessary applications' data*/
|
||||
public void init() {
|
||||
SatelliteRepository satelliteRepository = new SatelliteRepository(mContext);
|
||||
init(satelliteRepository);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void init(@NonNull SatelliteRepository satelliteRepository) {
|
||||
mPackageNameList = satelliteRepository.getSatelliteDataOptimizedApps();
|
||||
public void init(int subId, @NonNull PersistableBundle configBundle, boolean isSmsAvailable,
|
||||
boolean isDataAvailable) {
|
||||
mSubId = subId;
|
||||
mConfigBundle = configBundle;
|
||||
mIsSmsAvailable = isSmsAvailable;
|
||||
mIsDataAvailable = isDataAvailable;
|
||||
mPackageNameList = getSatelliteDataOptimizedApps();
|
||||
mIsSatelliteEligible = isSatelliteEligible();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -78,13 +88,53 @@ public class SatelliteAppListCategoryController extends BasePreferenceController
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
if (!Flags.satellite25q4Apis()) {
|
||||
public int getAvailabilityStatus(int subId) {
|
||||
// Only when carrier support entitlement check, it shall check account eligible or not.
|
||||
if (mConfigBundle.getBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL)
|
||||
&& !mIsSatelliteEligible) {
|
||||
return CONDITIONALLY_UNAVAILABLE;
|
||||
}
|
||||
return mPackageNameList.isEmpty()
|
||||
? CONDITIONALLY_UNAVAILABLE
|
||||
: AVAILABLE;
|
||||
Log.d(TAG, "Supported apps have " + mPackageNameList.size());
|
||||
|
||||
return mIsDataAvailable && !mPackageNameList.isEmpty()
|
||||
? AVAILABLE_UNSEARCHABLE
|
||||
: CONDITIONALLY_UNAVAILABLE;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected List<String> getSatelliteDataOptimizedApps() {
|
||||
SatelliteManager satelliteManager = mContext.getSystemService(SatelliteManager.class);
|
||||
if (satelliteManager == null) {
|
||||
return List.of();
|
||||
}
|
||||
try {
|
||||
return satelliteManager.getSatelliteDataOptimizedApps();
|
||||
} catch (IllegalStateException e) {
|
||||
Log.d(TAG, "getSatelliteDataOptimizedApps failed due to " + e);
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected boolean isSatelliteEligible() {
|
||||
if (mConfigBundle.getInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT)
|
||||
== CARRIER_ROAMING_NTN_CONNECT_MANUAL) {
|
||||
return mIsSmsAvailable;
|
||||
}
|
||||
SatelliteManager satelliteManager = mContext.getSystemService(SatelliteManager.class);
|
||||
if (satelliteManager == null) {
|
||||
Log.d(TAG, "SatelliteManager is null.");
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
Set<Integer> restrictionReason =
|
||||
satelliteManager.getAttachRestrictionReasonsForCarrier(mSubId);
|
||||
return !restrictionReason.contains(
|
||||
SatelliteManager.SATELLITE_COMMUNICATION_RESTRICTION_REASON_ENTITLEMENT);
|
||||
} catch (SecurityException | IllegalStateException | IllegalArgumentException ex) {
|
||||
Log.d(TAG, "Error to getAttachRestrictionReasonsForCarrier : " + ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static ApplicationInfo getApplicationInfo(Context context, String packageName) {
|
||||
|
@@ -23,17 +23,16 @@ import static android.telephony.CarrierConfigManager.KEY_EMERGENCY_MESSAGING_SUP
|
||||
import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ATTACH_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 android.telephony.CarrierConfigManager.SATELLITE_DATA_SUPPORT_ONLY_RESTRICTED;
|
||||
|
||||
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 +44,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 +51,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 +63,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;
|
||||
|
||||
@@ -84,37 +79,30 @@ public class SatelliteSetting extends RestrictedDashboardFragment {
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
mActivity = getActivity();
|
||||
mSubId = mActivity.getIntent().getIntExtra(SUB_ID,
|
||||
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
|
||||
mConfigBundle = fetchCarrierConfigData(mSubId);
|
||||
mIsServiceDataType = getIntent().getBooleanExtra(EXTRA_IS_SERVICE_DATA_TYPE, false);
|
||||
mIsSmsAvailableForManualType = getIntent().getBooleanExtra(
|
||||
EXTRA_IS_SMS_AVAILABLE_FOR_MANUAL_TYPE, false);
|
||||
|
||||
use(SatelliteAppListCategoryController.class).init();
|
||||
use(SatelliteSettingAboutContentController.class).init(mSubId);
|
||||
use(SatelliteSettingAccountInfoController.class).init(mSubId, mConfigBundle,
|
||||
mIsSmsAvailableForManualType, mIsServiceDataType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@NonNull Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mSatelliteManager = mActivity.getSystemService(SatelliteManager.class);
|
||||
if (mSatelliteManager == null) {
|
||||
Log.d(TAG, "SatelliteManager is null, do nothing.");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isSatelliteAttachSupported(mSubId)) {
|
||||
mSubId = mActivity.getIntent().getIntExtra(SUB_ID,
|
||||
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
|
||||
mConfigBundle = fetchCarrierConfigData(mSubId);
|
||||
if (!isSatelliteAttachSupported()) {
|
||||
Log.d(TAG, "SatelliteSettings: KEY_SATELLITE_ATTACH_SUPPORTED_BOOL is false, "
|
||||
+ "do nothing.");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
mSimOperatorName = getSystemService(TelephonyManager.class).getSimOperatorName(mSubId);
|
||||
mIsServiceDataType = getIntent().getBooleanExtra(EXTRA_IS_SERVICE_DATA_TYPE, false);
|
||||
mIsSmsAvailableForManualType = getIntent().getBooleanExtra(
|
||||
EXTRA_IS_SMS_AVAILABLE_FOR_MANUAL_TYPE, false);
|
||||
boolean isDataAvailableAndNotRestricted = isDataAvailableAndNotRestricted();
|
||||
use(SatelliteAppListCategoryController.class).init(mSubId, mConfigBundle,
|
||||
mIsSmsAvailableForManualType, isDataAvailableAndNotRestricted);
|
||||
use(SatelliteSettingAboutContentController.class).init(mSubId);
|
||||
use(SatelliteSettingAccountInfoController.class).init(mSubId, mConfigBundle,
|
||||
mIsSmsAvailableForManualType, isDataAvailableAndNotRestricted);
|
||||
use(SatelliteSettingFooterController.class).init(mSubId, mConfigBundle);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -122,7 +110,6 @@ public class SatelliteSetting extends RestrictedDashboardFragment {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
boolean isSatelliteEligible = isSatelliteEligible();
|
||||
updateHowItWorksContent(isSatelliteEligible);
|
||||
updateFooterContent();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -154,34 +141,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;
|
||||
@@ -189,6 +148,7 @@ public class SatelliteSetting extends RestrictedDashboardFragment {
|
||||
try {
|
||||
Set<Integer> restrictionReason =
|
||||
mSatelliteManager.getAttachRestrictionReasonsForCarrier(mSubId);
|
||||
Log.d(TAG, "Restriction reason : " + restrictionReason);
|
||||
return !restrictionReason.contains(
|
||||
SatelliteManager.SATELLITE_COMMUNICATION_RESTRICTION_REASON_ENTITLEMENT);
|
||||
} catch (SecurityException | IllegalStateException | IllegalArgumentException ex) {
|
||||
@@ -218,19 +178,31 @@ 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);
|
||||
}
|
||||
|
||||
private boolean isSatelliteAttachSupported(int subId) {
|
||||
private boolean isSatelliteAttachSupported() {
|
||||
return mConfigBundle.getBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, false);
|
||||
}
|
||||
|
||||
private boolean isDataAvailableAndNotRestricted() {
|
||||
return getIntent().getBooleanExtra(EXTRA_IS_SERVICE_DATA_TYPE, false)
|
||||
&& !isDataRestricted();
|
||||
}
|
||||
|
||||
private boolean isDataRestricted() {
|
||||
int dataMode = SATELLITE_DATA_SUPPORT_ONLY_RESTRICTED;
|
||||
try {
|
||||
dataMode = mSatelliteManager.getSatelliteDataSupportMode(mSubId);
|
||||
Log.d(TAG, "Data mode : " + dataMode);
|
||||
} catch (IllegalStateException e) {
|
||||
Log.d(TAG, "Failed to get data mode : " + e);
|
||||
}
|
||||
return dataMode <= SATELLITE_DATA_SUPPORT_ONLY_RESTRICTED;
|
||||
}
|
||||
|
||||
private static void loge(String message) {
|
||||
Log.e(TAG, message);
|
||||
}
|
||||
|
@@ -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 = "<li> ";
|
||||
String subfix = "</li>";
|
||||
return prefix + mContext.getString(resId) + subfix;
|
||||
}
|
||||
|
||||
private String getHtmlStringCombination(int resId, Object... value) {
|
||||
String prefix = "<li> ";
|
||||
String subfix = "</li>";
|
||||
return prefix + mContext.getString(resId, value) + subfix;
|
||||
}
|
||||
|
||||
private String readSatelliteMoreInfoString() {
|
||||
return mConfigBundle.getString(KEY_SATELLITE_INFORMATION_REDIRECT_URL_STRING);
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
});
|
||||
|
@@ -25,6 +25,7 @@ import com.android.settings.biometrics.BiometricsFeatureProvider
|
||||
import com.android.settings.biometrics.face.FaceFeatureProvider
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintFeatureProvider
|
||||
import com.android.settings.bluetooth.BluetoothFeatureProvider
|
||||
import com.android.settings.connecteddevice.audiosharing.AudioSharingFeatureProvider
|
||||
import com.android.settings.connecteddevice.fastpair.FastPairFeatureProvider
|
||||
import com.android.settings.connecteddevice.stylus.StylusFeatureProvider
|
||||
import com.android.settings.dashboard.DashboardFeatureProvider
|
||||
@@ -179,6 +180,11 @@ abstract class FeatureFactory {
|
||||
*/
|
||||
abstract val fastPairFeatureProvider: FastPairFeatureProvider
|
||||
|
||||
/**
|
||||
* Gets implementation for audio sharing related feature.
|
||||
*/
|
||||
abstract val audioSharingFeatureProvider: AudioSharingFeatureProvider
|
||||
|
||||
/**
|
||||
* Gets implementation for Private Space account login feature.
|
||||
*/
|
||||
|
@@ -37,6 +37,8 @@ import com.android.settings.biometrics.fingerprint.FingerprintFeatureProvider
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintFeatureProviderImpl
|
||||
import com.android.settings.bluetooth.BluetoothFeatureProvider
|
||||
import com.android.settings.bluetooth.BluetoothFeatureProviderImpl
|
||||
import com.android.settings.connecteddevice.audiosharing.AudioSharingFeatureProvider
|
||||
import com.android.settings.connecteddevice.audiosharing.AudioSharingFeatureProviderImpl
|
||||
import com.android.settings.connecteddevice.dock.DockUpdaterFeatureProviderImpl
|
||||
import com.android.settings.connecteddevice.fastpair.FastPairFeatureProvider
|
||||
import com.android.settings.connecteddevice.fastpair.FastPairFeatureProviderImpl
|
||||
@@ -194,6 +196,10 @@ open class FeatureFactoryImpl : FeatureFactory() {
|
||||
FastPairFeatureProviderImpl()
|
||||
}
|
||||
|
||||
override val audioSharingFeatureProvider: AudioSharingFeatureProvider by lazy {
|
||||
AudioSharingFeatureProviderImpl()
|
||||
}
|
||||
|
||||
override val privateSpaceLoginFeatureProvider: PrivateSpaceLoginFeatureProvider by lazy {
|
||||
PrivateSpaceLoginFeatureProviderImpl()
|
||||
}
|
||||
|
@@ -18,7 +18,6 @@ package com.android.settings.regionalpreferences;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.LocaleList;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -92,7 +91,7 @@ public abstract class RegionPickerBaseListPreferenceController extends BasePrefe
|
||||
? getSuggestedLocaleList()
|
||||
: getSupportedLocaleList();
|
||||
if (getPreferenceCategoryKey().contains(KEY_SUGGESTED)) {
|
||||
Locale systemLocale = LocaleList.getDefault().get(0);
|
||||
Locale systemLocale = Locale.getDefault();
|
||||
LocaleStore.LocaleInfo localeInfo = LocaleStore.getLocaleInfo(systemLocale);
|
||||
result.add(localeInfo);
|
||||
}
|
||||
@@ -106,7 +105,7 @@ public abstract class RegionPickerBaseListPreferenceController extends BasePrefe
|
||||
mPreferenceCategory.addPreference(pref);
|
||||
pref.setTitle(locale.getFullCountryNameNative());
|
||||
pref.setKey(locale.toString());
|
||||
if (locale.getLocale().equals(LocaleList.getDefault().get(0))) {
|
||||
if (locale.getLocale().equals(Locale.getDefault())) {
|
||||
pref.setChecked(true);
|
||||
} else {
|
||||
pref.setChecked(false);
|
||||
@@ -154,7 +153,7 @@ public abstract class RegionPickerBaseListPreferenceController extends BasePrefe
|
||||
|
||||
private List<LocaleStore.LocaleInfo> getSortedLocaleList(
|
||||
List<LocaleStore.LocaleInfo> localeInfos) {
|
||||
final Locale sortingLocale = LocaleList.getDefault().get(0);
|
||||
final Locale sortingLocale = Locale.getDefault();
|
||||
final LocaleHelper.LocaleInfoComparator comp =
|
||||
new LocaleHelper.LocaleInfoComparator(sortingLocale, true);
|
||||
Collections.sort(localeInfos, comp);
|
||||
@@ -162,7 +161,7 @@ public abstract class RegionPickerBaseListPreferenceController extends BasePrefe
|
||||
}
|
||||
|
||||
private void switchRegion(LocaleStore.LocaleInfo localeInfo) {
|
||||
if (localeInfo.getLocale().equals(LocaleList.getDefault().get(0))) {
|
||||
if (localeInfo.getLocale().equals(Locale.getDefault())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -19,7 +19,6 @@ package com.android.settings.regionalpreferences;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.LocaleList;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -84,7 +83,7 @@ public class RegionPickerFragment extends DashboardFragment{
|
||||
|
||||
private List<AbstractPreferenceController> buildPreferenceControllers(
|
||||
@NonNull Context context) {
|
||||
Locale parentLocale = LocaleStore.getLocaleInfo(LocaleList.getDefault().get(0)).getParent();
|
||||
Locale parentLocale = LocaleStore.getLocaleInfo(Locale.getDefault()).getParent();
|
||||
LocaleStore.LocaleInfo parentLocaleInfo = LocaleStore.getLocaleInfo(parentLocale);
|
||||
SystemRegionSuggestedListPreferenceController mSuggestedListPreferenceController =
|
||||
new SystemRegionSuggestedListPreferenceController(
|
||||
|
@@ -17,7 +17,6 @@
|
||||
package com.android.settings.regionalpreferences;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.LocaleList;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.Preference;
|
||||
@@ -27,6 +26,8 @@ import com.android.internal.app.LocaleStore;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settings.flags.Flags;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/** A controller for the entry of region picker page */
|
||||
public class RegionPreferenceController extends BasePreferenceController {
|
||||
|
||||
@@ -39,7 +40,7 @@ public class RegionPreferenceController extends BasePreferenceController {
|
||||
super.displayPreference(screen);
|
||||
Preference preference = screen.findPreference(getPreferenceKey());
|
||||
LocaleStore.LocaleInfo localeInfo = LocaleStore.getLocaleInfo(
|
||||
LocaleList.getDefault().get(0));
|
||||
Locale.getDefault());
|
||||
preference.setSummary(localeInfo.getFullCountryNameNative());
|
||||
}
|
||||
|
||||
|
@@ -19,6 +19,7 @@ package com.android.settings.search;
|
||||
import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS;
|
||||
import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_TAB;
|
||||
import static com.android.settings.activityembedding.EmbeddedDeepLinkUtils.getTrampolineIntent;
|
||||
import static com.android.settings.activityembedding.EmbeddedDeepLinkUtils.getTrampolineIntentForSearchResult;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
@@ -35,7 +36,6 @@ import com.android.settings.SubSettings;
|
||||
import com.android.settings.activityembedding.ActivityEmbeddingRulesController;
|
||||
import com.android.settings.activityembedding.ActivityEmbeddingUtils;
|
||||
import com.android.settings.core.FeatureFlags;
|
||||
import com.android.settings.homepage.DeepLinkHomepageActivityInternal;
|
||||
import com.android.settings.homepage.SettingsHomepageActivity;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
|
||||
@@ -107,10 +107,7 @@ public class SearchResultTrampoline extends Activity {
|
||||
startActivity(intent);
|
||||
} else if (isSettingsIntelligence(callerPackage)) {
|
||||
if (FeatureFlagUtils.isEnabled(this, FeatureFlags.SETTINGS_SEARCH_ALWAYS_EXPAND)) {
|
||||
startActivity(getTrampolineIntent(intent, highlightMenuKey)
|
||||
.setClass(this, DeepLinkHomepageActivityInternal.class)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS));
|
||||
startActivity(getTrampolineIntentForSearchResult(this, intent, highlightMenuKey));
|
||||
} else {
|
||||
// Register SplitPairRule for SubSettings, set clearTop false to prevent unexpected
|
||||
// back navigation behavior.
|
||||
|
@@ -14,21 +14,23 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.security;
|
||||
package com.android.settings.security
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.security.advancedprotection.AdvancedProtectionManager
|
||||
import android.security.advancedprotection.AdvancedProtectionManager.EXTRA_SUPPORT_DIALOG_FEATURE
|
||||
import android.security.advancedprotection.AdvancedProtectionManager.EXTRA_SUPPORT_DIALOG_TYPE
|
||||
import android.security.advancedprotection.AdvancedProtectionManager.FEATURE_ID_DISALLOW_CELLULAR_2G
|
||||
import android.security.advancedprotection.AdvancedProtectionManager.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES
|
||||
import android.security.advancedprotection.AdvancedProtectionManager.FEATURE_ID_DISALLOW_WEP
|
||||
import android.content.pm.PackageManager
|
||||
import android.security.advancedprotection.AdvancedProtectionManager.FEATURE_ID_ENABLE_MTE
|
||||
import android.security.advancedprotection.AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION
|
||||
import android.security.advancedprotection.AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_DISABLED_SETTING
|
||||
import android.security.advancedprotection.AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_UNKNOWN
|
||||
import android.util.Log
|
||||
import android.view.WindowManager
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -38,7 +40,6 @@ import com.android.settingslib.spa.SpaDialogWindowTypeActivity
|
||||
import com.android.settingslib.spa.widget.dialog.AlertDialogButton
|
||||
import com.android.settingslib.spa.widget.dialog.SettingsAlertDialogContent
|
||||
import com.android.settingslib.wifi.WifiUtils.Companion.DIALOG_WINDOW_TYPE
|
||||
import android.security.advancedprotection.AdvancedProtectionManager
|
||||
|
||||
class ActionDisabledByAdvancedProtectionDialog : SpaDialogWindowTypeActivity() {
|
||||
|
||||
@@ -85,9 +86,12 @@ class ActionDisabledByAdvancedProtectionDialog : SpaDialogWindowTypeActivity() {
|
||||
return getString(messageId)
|
||||
}
|
||||
|
||||
private fun getSupportButtonIfExists(): AlertDialogButton? {
|
||||
@VisibleForTesting
|
||||
fun getSupportButtonIfExists(): AlertDialogButton? {
|
||||
try {
|
||||
val helpIntentUri = getString(R.string.help_url_action_disabled_by_advanced_protection)
|
||||
val helpIntentUri = getString(
|
||||
com.android.internal.R.string.config_help_url_action_disabled_by_advanced_protection
|
||||
)
|
||||
val helpIntent = Intent.parseUri(helpIntentUri, Intent.URI_INTENT_SCHEME)
|
||||
if (helpIntent == null) return null
|
||||
val helpActivityInfo = packageManager.resolveActivity(helpIntent, /* flags */ 0)
|
||||
@@ -118,7 +122,7 @@ class ActionDisabledByAdvancedProtectionDialog : SpaDialogWindowTypeActivity() {
|
||||
}
|
||||
|
||||
override fun getDialogWindowType(): Int? = if (intent.hasExtra(DIALOG_WINDOW_TYPE)) {
|
||||
intent.getIntExtra(DIALOG_WINDOW_TYPE, WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW)
|
||||
intent.getIntExtra(DIALOG_WINDOW_TYPE, WindowManager.LayoutParams.TYPE_APPLICATION)
|
||||
} else null
|
||||
|
||||
private fun getIntentFeatureId(): Int {
|
||||
|
@@ -162,12 +162,18 @@ public class WifiAPITest extends SettingsPreferenceFragment implements
|
||||
final EditText input = new EditText(getPrefContext());
|
||||
alert.setView(input);
|
||||
alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int whichButton) {
|
||||
public void onClick(DialogInterface dialog, int whichButton) {
|
||||
Editable value = input.getText();
|
||||
netid = Integer.parseInt(value.toString());
|
||||
mWifiManager.enableNetwork(netid, false);
|
||||
try {
|
||||
netid = Integer.parseInt(value.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
// Invalid netid
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
});
|
||||
mWifiManager.enableNetwork(netid, false);
|
||||
}
|
||||
});
|
||||
alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int whichButton) {
|
||||
// Canceled.
|
||||
|
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.bluetooth
|
||||
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import com.android.settings.R
|
||||
import com.android.settings.testutils.FakeFeatureFactory
|
||||
import com.android.settingslib.widget.LayoutPreference
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mockito.Mock
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
import org.mockito.junit.MockitoRule
|
||||
import org.mockito.kotlin.whenever
|
||||
|
||||
class BluetoothDetailsBannerControllerTest : BluetoothDetailsControllerTestBase() {
|
||||
@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
|
||||
|
||||
private lateinit var controller: BluetoothDetailsBannerController
|
||||
private lateinit var preference: LayoutPreference
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
FakeFeatureFactory.setupForTest()
|
||||
controller =
|
||||
BluetoothDetailsBannerController(mContext, mFragment, mCachedDevice, mLifecycle)
|
||||
preference = LayoutPreference(mContext, R.layout.bluetooth_details_banner)
|
||||
preference.key = controller.getPreferenceKey()
|
||||
mScreen.addPreference(preference)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun iaAvailable_notKeyMissing_false() {
|
||||
setupDevice(makeDefaultDeviceConfig())
|
||||
|
||||
assertThat(controller.isAvailable).isFalse()
|
||||
}
|
||||
|
||||
// TODO(b/379729762): add more tests after BluetoothDevice.getKeyMissingCount is available.
|
||||
}
|
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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.bluetooth
|
||||
|
||||
import android.content.Context
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.fragment.app.testing.EmptyFragmentActivity
|
||||
import androidx.preference.Preference
|
||||
import androidx.test.core.app.ActivityScenario
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mockito.spy
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
import org.mockito.junit.MockitoRule
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class BluetoothDetailsFragmentTest {
|
||||
@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
|
||||
|
||||
private lateinit var activity: FragmentActivity
|
||||
private lateinit var fragment: TestConfigurableFragment
|
||||
private lateinit var context: Context
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
context = spy(ApplicationProvider.getApplicationContext<Context>())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setPreferenceDisplayOrder_null_unchanged() = buildFragment {
|
||||
fragment.preferenceScreen.addPreference(Preference(context).apply { key = "key1" })
|
||||
fragment.preferenceScreen.addPreference(Preference(context).apply { key = "key2" })
|
||||
|
||||
fragment.setPreferenceDisplayOrder(null)
|
||||
|
||||
assertThat(this.displayedKeys).containsExactly("key1", "key2")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setPreferenceDisplayOrder_hideItem() = buildFragment {
|
||||
fragment.preferenceScreen.addPreference(Preference(context).apply { key = "key1" })
|
||||
fragment.preferenceScreen.addPreference(Preference(context).apply { key = "key2" })
|
||||
|
||||
fragment.setPreferenceDisplayOrder(mutableListOf("key2"))
|
||||
|
||||
assertThat(this.displayedKeys).containsExactly("key2")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setPreferenceDisplayOrder_hideAndReShownItem() = buildFragment {
|
||||
fragment.preferenceScreen.addPreference(Preference(context).apply { key = "key1" })
|
||||
fragment.preferenceScreen.addPreference(Preference(context).apply { key = "key2" })
|
||||
|
||||
fragment.setPreferenceDisplayOrder(mutableListOf("key2"))
|
||||
fragment.setPreferenceDisplayOrder(mutableListOf("key2", "key1"))
|
||||
|
||||
assertThat(this.displayedKeys).containsExactly("key2", "key1")
|
||||
}
|
||||
|
||||
private fun buildFragment(r: (() -> Unit)) {
|
||||
ActivityScenario.launch(EmptyFragmentActivity::class.java).use { activityScenario ->
|
||||
activityScenario.onActivity { activity: EmptyFragmentActivity ->
|
||||
this@BluetoothDetailsFragmentTest.activity = activity
|
||||
fragment = TestConfigurableFragment()
|
||||
activity.supportFragmentManager.beginTransaction().add(fragment, null).commitNow()
|
||||
fragment.setPreferenceScreen(
|
||||
fragment.preferenceManager.createPreferenceScreen(context)
|
||||
)
|
||||
r.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val displayedKeys: List<String>
|
||||
get() {
|
||||
val keys: MutableList<String> = mutableListOf()
|
||||
for (i in 0..<fragment.preferenceScreen.preferenceCount) {
|
||||
if (fragment.preferenceScreen.getPreference(i).isVisible) {
|
||||
keys.add(fragment.preferenceScreen.getPreference(i).key)
|
||||
}
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
class TestConfigurableFragment : BluetoothDetailsConfigurableFragment() {
|
||||
protected override fun getPreferenceScreenResId(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun getLogTag(): String {
|
||||
return "TAG"
|
||||
}
|
||||
|
||||
override fun getMetricsCategory(): Int {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
@@ -404,7 +404,7 @@ class DeviceDetailsFragmentFormatterTest {
|
||||
for (i in 0..<fragment.preferenceScreen.preferenceCount) {
|
||||
prefs.add(fragment.preferenceScreen.getPreference(i))
|
||||
}
|
||||
return prefs
|
||||
return prefs.filter { it.key != null }
|
||||
}
|
||||
|
||||
class TestFragment(context: Context) : DashboardFragment() {
|
||||
|
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.connecteddevice.audiosharing;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsQrCodeFragment;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class AudioSharingFeatureProviderImplTest {
|
||||
|
||||
private AudioSharingFeatureProvider mFeatureProvider;
|
||||
@Mock private Fragment mFragment;
|
||||
@Mock private View mockView;
|
||||
private Context mContext;
|
||||
@Mock private Drawable mDrawable;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
mFeatureProvider = new AudioSharingFeatureProviderImpl();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setQrCode_correctDialogLayout() {
|
||||
mFragment = new AudioSharingDialogFragment();
|
||||
View view =
|
||||
LayoutInflater.from(mContext)
|
||||
.inflate(R.layout.dialog_custom_body_audio_sharing, null);
|
||||
mFeatureProvider.setQrCode(mFragment, view, R.id.description_image, mDrawable, "");
|
||||
ImageView imageView = view.findViewById(R.id.description_image);
|
||||
|
||||
assertThat(imageView.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(imageView.getDrawable()).isEqualTo(mDrawable);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setQrCode_correctLayout() {
|
||||
mFragment = new AudioStreamsQrCodeFragment();
|
||||
View view =
|
||||
LayoutInflater.from(mContext)
|
||||
.inflate(R.layout.bluetooth_audio_streams_qr_code, null);
|
||||
mFeatureProvider.setQrCode(mFragment, view, R.id.qrcode_view, mDrawable, "");
|
||||
ImageView imageView = view.findViewById(R.id.qrcode_view);
|
||||
|
||||
assertThat(imageView.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(imageView.getDrawable()).isEqualTo(mDrawable);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void setQrCode_nonExistedViewId() {
|
||||
mFragment = new AudioStreamsQrCodeFragment();
|
||||
View view =
|
||||
LayoutInflater.from(mContext)
|
||||
.inflate(R.layout.bluetooth_audio_streams_qr_code, null);
|
||||
mFeatureProvider.setQrCode(mFragment, view, R.id.description_image, mDrawable, "");
|
||||
}
|
||||
}
|
@@ -32,6 +32,7 @@ import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDao;
|
||||
import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.testutils.BatteryTestUtils;
|
||||
|
||||
import org.junit.After;
|
||||
@@ -104,7 +105,9 @@ public final class PeriodicJobReceiverTest {
|
||||
@Test
|
||||
public void onReceive_containsExpiredData_clearsExpiredDataFromDatabase()
|
||||
throws InterruptedException {
|
||||
insertExpiredData(/* shiftDay= */ DatabaseUtils.DATA_RETENTION_INTERVAL_DAY);
|
||||
int dataRetentionDays = FeatureFactory.getFeatureFactory()
|
||||
.getPowerUsageFeatureProvider().getDataRetentionDays();
|
||||
insertExpiredData(/* shiftDay= */ dataRetentionDays);
|
||||
|
||||
mReceiver.onReceive(mContext, JOB_UPDATE_INTENT);
|
||||
|
||||
@@ -115,7 +118,9 @@ public final class PeriodicJobReceiverTest {
|
||||
@Test
|
||||
public void onReceive_withoutExpiredData_notClearsExpiredDataFromDatabase()
|
||||
throws InterruptedException {
|
||||
insertExpiredData(/* shiftDay= */ DatabaseUtils.DATA_RETENTION_INTERVAL_DAY - 1);
|
||||
int dataRetentionDays = FeatureFactory.getFeatureFactory()
|
||||
.getPowerUsageFeatureProvider().getDataRetentionDays();
|
||||
insertExpiredData(dataRetentionDays - 1);
|
||||
|
||||
mReceiver.onReceive(mContext, JOB_UPDATE_INTENT);
|
||||
|
||||
|
@@ -275,6 +275,7 @@ public class LocaleListEditorTest {
|
||||
public void showConfirmDialog_systemLocaleSelected_shouldShowLocaleChangeDialog()
|
||||
throws Exception {
|
||||
//pre-condition
|
||||
Locale.setDefault(Locale.forLanguageTag("zh-TW"));
|
||||
setUpLocaleConditions(true);
|
||||
final Configuration config = new Configuration();
|
||||
config.setLocales((LocaleList.forLanguageTags("zh-TW,en-US")));
|
||||
@@ -379,6 +380,7 @@ public class LocaleListEditorTest {
|
||||
@Test
|
||||
public void onTouch_dragDifferentLocaleToTop_showConfirmDialog() throws Exception {
|
||||
MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0.0f, 0.0f, 0);
|
||||
Locale.setDefault(Locale.forLanguageTag("zh-TW"));
|
||||
setUpLocaleConditions(true);
|
||||
final Configuration config = new Configuration();
|
||||
config.setLocales((LocaleList.forLanguageTags("zh-TW,en-US")));
|
||||
|
@@ -28,6 +28,7 @@ import com.android.settings.biometrics.BiometricsFeatureProvider;
|
||||
import com.android.settings.biometrics.face.FaceFeatureProvider;
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintFeatureProvider;
|
||||
import com.android.settings.bluetooth.BluetoothFeatureProvider;
|
||||
import com.android.settings.connecteddevice.audiosharing.AudioSharingFeatureProvider;
|
||||
import com.android.settings.connecteddevice.fastpair.FastPairFeatureProvider;
|
||||
import com.android.settings.connecteddevice.stylus.StylusFeatureProvider;
|
||||
import com.android.settings.dashboard.DashboardFeatureProvider;
|
||||
@@ -105,6 +106,7 @@ public class FakeFeatureFactory extends FeatureFactory {
|
||||
public DisplayFeatureProvider mDisplayFeatureProvider;
|
||||
public SyncAcrossDevicesFeatureProvider mSyncAcrossDevicesFeatureProvider;
|
||||
public AccessibilityFeedbackFeatureProvider mAccessibilityFeedbackFeatureProvider;
|
||||
public AudioSharingFeatureProvider mAudioSharingFeatureProvider;
|
||||
|
||||
/**
|
||||
* Call this in {@code @Before} method of the test class to use fake factory.
|
||||
@@ -155,6 +157,7 @@ public class FakeFeatureFactory extends FeatureFactory {
|
||||
mPrivateSpaceLoginFeatureProvider = mock(PrivateSpaceLoginFeatureProvider.class);
|
||||
mDisplayFeatureProvider = mock(DisplayFeatureProvider.class);
|
||||
mSyncAcrossDevicesFeatureProvider = mock(SyncAcrossDevicesFeatureProvider.class);
|
||||
mAudioSharingFeatureProvider = mock(AudioSharingFeatureProvider.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -347,4 +350,9 @@ public class FakeFeatureFactory extends FeatureFactory {
|
||||
public AccessibilityFeedbackFeatureProvider getAccessibilityFeedbackFeatureProvider() {
|
||||
return mAccessibilityFeedbackFeatureProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AudioSharingFeatureProvider getAudioSharingFeatureProvider() {
|
||||
return mAudioSharingFeatureProvider;
|
||||
}
|
||||
}
|
@@ -30,6 +30,14 @@
|
||||
<provider android:name="com.android.settings.slices.SettingsSliceProvider"
|
||||
android:authorities="${applicationId}.slices"
|
||||
tools:replace="android:authorities"/>
|
||||
|
||||
<activity android:name="com.android.settings.security.ActionDisabledByAdvancedProtectionDialogTest$HelpTestActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.android.settings.tests.spa_unit.HELP_ACTION" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<instrumentation
|
||||
|
@@ -19,16 +19,26 @@ package com.android.settings.activityembedding
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.platform.test.annotations.DisableFlags
|
||||
import android.platform.test.annotations.EnableFlags
|
||||
import android.platform.test.flag.junit.SetFlagsRule
|
||||
import android.provider.Settings
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settings.SettingsActivity.EXTRA_IS_DEEPLINK_HOME_STARTED_FROM_SEARCH
|
||||
import com.android.settings.activityembedding.EmbeddedDeepLinkUtils.getTrampolineIntent
|
||||
import com.android.settings.activityembedding.EmbeddedDeepLinkUtils.getTrampolineIntentForSearchResult
|
||||
import com.android.settings.flags.Flags
|
||||
import com.android.settings.homepage.DeepLinkHomepageActivityInternal
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class EmbeddedDeepLinkUtilsTest {
|
||||
@get:Rule
|
||||
val setFlagsRule = SetFlagsRule()
|
||||
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
|
||||
@@ -58,4 +68,72 @@ class EmbeddedDeepLinkUtilsTest {
|
||||
val parsedIntent = Intent.parseUri(intentUriString, Intent.URI_INTENT_SCHEME)
|
||||
assertThat(parsedIntent.action).isEqualTo(intent.action)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getTrampolineIntent_shouldNotHaveNewTaskFlag() {
|
||||
val intent = Intent("com.android.settings.SEARCH_RESULT_TRAMPOLINE")
|
||||
|
||||
val resultIntent = getTrampolineIntent(intent, "menu_key")
|
||||
|
||||
val hasNewTaskFlag = (resultIntent.flags and Intent.FLAG_ACTIVITY_NEW_TASK) != 0
|
||||
assertThat(hasNewTaskFlag).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getTrampolineIntentForSearchResult_shouldHaveDeepLinkHomepageClass() {
|
||||
val intent = Intent("com.android.settings.SEARCH_RESULT_TRAMPOLINE")
|
||||
|
||||
val resultIntent = getTrampolineIntentForSearchResult(context, intent, "menu_key")
|
||||
|
||||
val className = resultIntent.getComponent()!!.className
|
||||
assertThat(className).isEqualTo(DeepLinkHomepageActivityInternal::class.java.name)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisableFlags(Flags.FLAG_SETTINGS_SEARCH_RESULT_DEEP_LINK_IN_SAME_TASK)
|
||||
fun getTrampolineIntentForSearchResult_shouldHaveNewTaskFlag() {
|
||||
val intent = Intent("com.android.settings.SEARCH_RESULT_TRAMPOLINE")
|
||||
|
||||
val resultIntent = getTrampolineIntentForSearchResult(context, intent, "menu_key")
|
||||
|
||||
val hasNewTaskFlag = (resultIntent.flags and Intent.FLAG_ACTIVITY_NEW_TASK) != 0
|
||||
assertThat(hasNewTaskFlag).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(Flags.FLAG_SETTINGS_SEARCH_RESULT_DEEP_LINK_IN_SAME_TASK)
|
||||
fun getTrampolineIntentForSearchResult_shouldNotHaveNewTaskFlag() {
|
||||
val intent = Intent("com.android.settings.SEARCH_RESULT_TRAMPOLINE")
|
||||
|
||||
val resultIntent = getTrampolineIntentForSearchResult(context, intent, "menu_key")
|
||||
|
||||
val hasNewTaskFlag = (resultIntent.flags and Intent.FLAG_ACTIVITY_NEW_TASK) != 0
|
||||
assertThat(hasNewTaskFlag).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisableFlags(Flags.FLAG_SETTINGS_SEARCH_RESULT_DEEP_LINK_IN_SAME_TASK)
|
||||
fun getTrampolineIntentForSearchResult_shouldNotHaveExtraStartedFromSearch() {
|
||||
val intent = Intent("com.android.settings.SEARCH_RESULT_TRAMPOLINE")
|
||||
|
||||
val resultIntent = getTrampolineIntentForSearchResult(context, intent, "menu_key")
|
||||
|
||||
assertThat(resultIntent.hasExtra(EXTRA_IS_DEEPLINK_HOME_STARTED_FROM_SEARCH)).isFalse()
|
||||
assertThat(
|
||||
resultIntent.getBooleanExtra(EXTRA_IS_DEEPLINK_HOME_STARTED_FROM_SEARCH, false)
|
||||
).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(Flags.FLAG_SETTINGS_SEARCH_RESULT_DEEP_LINK_IN_SAME_TASK)
|
||||
fun getTrampolineIntentForSearchResult_shouldHaveExtraStartedFromSearch() {
|
||||
val intent = Intent("com.android.settings.SEARCH_RESULT_TRAMPOLINE")
|
||||
|
||||
val resultIntent = getTrampolineIntentForSearchResult(context, intent, "menu_key")
|
||||
|
||||
assertThat(resultIntent.hasExtra(EXTRA_IS_DEEPLINK_HOME_STARTED_FROM_SEARCH)).isTrue()
|
||||
assertThat(
|
||||
resultIntent.getBooleanExtra(EXTRA_IS_DEEPLINK_HOME_STARTED_FROM_SEARCH, false)
|
||||
).isTrue()
|
||||
}
|
||||
}
|
||||
|
@@ -16,9 +16,14 @@
|
||||
|
||||
package com.android.settings.security
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.ResolveInfo
|
||||
import android.os.Bundle
|
||||
import android.platform.test.annotations.RequiresFlagsEnabled
|
||||
import android.platform.test.flag.junit.CheckFlagsRule
|
||||
import android.platform.test.flag.junit.DeviceFlagsValueProvider
|
||||
@@ -36,9 +41,21 @@ import androidx.test.core.app.ActivityScenario.launch
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settings.R
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.ArgumentMatchers.anyInt
|
||||
import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.spy
|
||||
import org.mockito.kotlin.verify
|
||||
import org.mockito.kotlin.whenever
|
||||
|
||||
@RequiresFlagsEnabled(Flags.FLAG_AAPM_API)
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@@ -49,7 +66,9 @@ class ActionDisabledByAdvancedProtectionDialogTest {
|
||||
@get:Rule
|
||||
val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
|
||||
|
||||
val context: Context = ApplicationProvider.getApplicationContext()
|
||||
private val mockPackageManager = mock<PackageManager>()
|
||||
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
|
||||
@Test
|
||||
fun blockedInteractionDialog_showsCorrectTitleAndMessage() {
|
||||
@@ -159,6 +178,85 @@ class ActionDisabledByAdvancedProtectionDialogTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun helpIntentDoesNotExist_getSupportButtonIfExists_returnsNull() {
|
||||
launchDialogActivity(defaultIntent) { scenario ->
|
||||
scenario.onActivity { activity ->
|
||||
val spyActivity = spyOnActivityHelpIntentUri(activity, /* uriToReturn */ null)
|
||||
|
||||
val button = spyActivity.getSupportButtonIfExists()
|
||||
assertNull(button)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun helpIntentExistsAndDoesNotResolveToActivity_getSupportButtonIfExists_returnsNull() {
|
||||
launchDialogActivity(defaultIntent) { scenario ->
|
||||
scenario.onActivity { activity ->
|
||||
val spyActivity = spyOnActivityHelpIntentUri(activity, helpIntentUri)
|
||||
mockResolveActivity(spyActivity, /* resolveInfoToReturn */ null)
|
||||
|
||||
val button = spyActivity.getSupportButtonIfExists()
|
||||
assertNull(button)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun helpIntentExistsAndResolvesToActivity_getSupportButtonIfExists_returnsButton() {
|
||||
launchDialogActivity(defaultIntent) { scenario ->
|
||||
scenario.onActivity { activity ->
|
||||
val spyActivity = spyOnActivityHelpIntentUri(activity, helpIntentUri)
|
||||
val resolveInfoToReturn = ResolveInfo().apply {
|
||||
activityInfo = ActivityInfo().apply {
|
||||
packageName = HELP_INTENT_PKG_NAME
|
||||
}
|
||||
}
|
||||
mockResolveActivity(spyActivity, resolveInfoToReturn)
|
||||
|
||||
// 1. Check the button is returned.
|
||||
val button = spyActivity.getSupportButtonIfExists()
|
||||
assertNotNull(button)
|
||||
|
||||
// 2. Check the button has correct text.
|
||||
assertEquals(context.getString(
|
||||
R.string.disabled_by_advanced_protection_help_button_title), button!!.text
|
||||
)
|
||||
|
||||
// 3. Check the button's onClick launches the help activity and finishes the dialog.
|
||||
button.onClick()
|
||||
|
||||
val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
|
||||
verify(spyActivity).startActivity(intentCaptor.capture())
|
||||
val launchedIntent = intentCaptor.value
|
||||
assertEquals(HELP_INTENT_ACTION, launchedIntent.action)
|
||||
assertEquals(HELP_INTENT_PKG_NAME, launchedIntent.`package`)
|
||||
|
||||
assertTrue(spyActivity.isFinishing)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun spyOnActivityHelpIntentUri(
|
||||
activity: ActionDisabledByAdvancedProtectionDialog,
|
||||
uriToReturn: String?
|
||||
): ActionDisabledByAdvancedProtectionDialog {
|
||||
val spyActivity = spy(activity)
|
||||
val spyResources = spy(spyActivity.resources)
|
||||
doReturn(spyResources).whenever(spyActivity).resources
|
||||
doReturn(uriToReturn).whenever(spyResources).getString(helpUriResourceId)
|
||||
return spyActivity
|
||||
}
|
||||
|
||||
private fun mockResolveActivity(
|
||||
spyActivity: ActionDisabledByAdvancedProtectionDialog,
|
||||
resolveInfoToReturn: ResolveInfo?
|
||||
) {
|
||||
doReturn(mockPackageManager).whenever(spyActivity).packageManager
|
||||
doReturn(resolveInfoToReturn).whenever(mockPackageManager).resolveActivity(any(), anyInt())
|
||||
}
|
||||
|
||||
private fun launchDialogActivity(
|
||||
intent: Intent,
|
||||
onScenario: (ActivityScenario<ActionDisabledByAdvancedProtectionDialog>) -> Unit
|
||||
@@ -172,10 +270,23 @@ class ActionDisabledByAdvancedProtectionDialogTest {
|
||||
launch<ActionDisabledByAdvancedProtectionDialog>(intent).use(onScenario)
|
||||
}
|
||||
|
||||
class HelpTestActivity : Activity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val defaultIntent = AdvancedProtectionManager.createSupportIntent(
|
||||
FEATURE_ID_DISALLOW_CELLULAR_2G,
|
||||
SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION
|
||||
)
|
||||
const val HELP_INTENT_PKG_NAME = "com.android.settings.tests.spa_unit"
|
||||
const val HELP_INTENT_ACTION = "$HELP_INTENT_PKG_NAME.HELP_ACTION"
|
||||
val helpIntent = Intent(HELP_INTENT_ACTION).setPackage(HELP_INTENT_PKG_NAME)
|
||||
val helpIntentUri = helpIntent.toUri(Intent.URI_INTENT_SCHEME)
|
||||
val helpUriResourceId =
|
||||
com.android.internal.R.string.config_help_url_action_disabled_by_advanced_protection
|
||||
}
|
||||
}
|
||||
|
@@ -26,6 +26,7 @@ import com.android.settings.biometrics.BiometricsFeatureProvider
|
||||
import com.android.settings.biometrics.face.FaceFeatureProvider
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintFeatureProvider
|
||||
import com.android.settings.bluetooth.BluetoothFeatureProvider
|
||||
import com.android.settings.connecteddevice.audiosharing.AudioSharingFeatureProvider
|
||||
import com.android.settings.connecteddevice.fastpair.FastPairFeatureProvider
|
||||
import com.android.settings.connecteddevice.stylus.StylusFeatureProvider
|
||||
import com.android.settings.dashboard.DashboardFeatureProvider
|
||||
@@ -148,4 +149,6 @@ class FakeFeatureFactory : FeatureFactory() {
|
||||
get() = TODO("Not yet implemented")
|
||||
override val syncAcrossDevicesFeatureProvider: SyncAcrossDevicesFeatureProvider
|
||||
get() = TODO("Not yet implemented")
|
||||
override val audioSharingFeatureProvider: AudioSharingFeatureProvider
|
||||
get() = TODO("Not yet implemented")
|
||||
}
|
||||
|
@@ -16,7 +16,11 @@
|
||||
|
||||
package com.android.settings.network.telephony.satellite;
|
||||
|
||||
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
||||
import static android.telephony.CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_MANUAL;
|
||||
import static android.telephony.CarrierConfigManager.KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT;
|
||||
import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL;
|
||||
|
||||
import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE;
|
||||
import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
|
||||
import static com.android.settings.network.telephony.satellite.SatelliteAppListCategoryController.MAXIMUM_OF_PREFERENCE_AMOUNT;
|
||||
|
||||
@@ -31,6 +35,7 @@ import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Looper;
|
||||
import android.os.PersistableBundle;
|
||||
import android.platform.test.annotations.EnableFlags;
|
||||
|
||||
import androidx.preference.PreferenceCategory;
|
||||
@@ -39,7 +44,6 @@ 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;
|
||||
@@ -48,25 +52,23 @@ 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 int TEST_SUB_ID = 0;
|
||||
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;
|
||||
|
||||
private PersistableBundle mPersistableBundle = new PersistableBundle();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
@@ -75,16 +77,28 @@ public class SatelliteAppListCategoryControllerTest {
|
||||
}
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
when(mContext.getPackageManager()).thenReturn(mPackageManager);
|
||||
mPersistableBundle.putInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT,
|
||||
CARRIER_ROAMING_NTN_CONNECT_MANUAL);
|
||||
mPersistableBundle.putBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, false);
|
||||
}
|
||||
|
||||
@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);
|
||||
mController = new SatelliteAppListCategoryController(mContext, KEY) {
|
||||
@Override
|
||||
protected boolean isSatelliteEligible() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getSatelliteDataOptimizedApps() {
|
||||
return PACKAGE_NAMES;
|
||||
}
|
||||
};
|
||||
mController.init(TEST_SUB_ID, mPersistableBundle, true, true);
|
||||
PreferenceManager preferenceManager = new PreferenceManager(mContext);
|
||||
PreferenceScreen preferenceScreen = preferenceManager.createPreferenceScreen(mContext);
|
||||
PreferenceCategory category = new PreferenceCategory(mContext);
|
||||
@@ -100,25 +114,107 @@ public class SatelliteAppListCategoryControllerTest {
|
||||
@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);
|
||||
mController = new SatelliteAppListCategoryController(mContext, KEY) {
|
||||
@Override
|
||||
protected boolean isSatelliteEligible() {
|
||||
return true;
|
||||
}
|
||||
|
||||
int result = mController.getAvailabilityStatus();
|
||||
@Override
|
||||
protected List<String> getSatelliteDataOptimizedApps() {
|
||||
return PACKAGE_NAMES;
|
||||
}
|
||||
};
|
||||
mController.init(TEST_SUB_ID, mPersistableBundle, true, true);
|
||||
|
||||
assertThat(result).isEqualTo(AVAILABLE);
|
||||
int result = mController.getAvailabilityStatus(TEST_SUB_ID);
|
||||
|
||||
assertThat(result).isEqualTo(AVAILABLE_UNSEARCHABLE);
|
||||
}
|
||||
|
||||
@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);
|
||||
mController = new SatelliteAppListCategoryController(mContext, KEY) {
|
||||
@Override
|
||||
protected boolean isSatelliteEligible() {
|
||||
return true;
|
||||
}
|
||||
|
||||
int result = mController.getAvailabilityStatus();
|
||||
@Override
|
||||
protected List<String> getSatelliteDataOptimizedApps() {
|
||||
return List.of();
|
||||
}
|
||||
};
|
||||
mController.init(TEST_SUB_ID, mPersistableBundle, true, true);
|
||||
|
||||
int result = mController.getAvailabilityStatus(TEST_SUB_ID);
|
||||
|
||||
assertThat(result).isEqualTo(CONDITIONALLY_UNAVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(Flags.FLAG_SATELLITE_25Q4_APIS)
|
||||
public void getAvailabilityStatus_dataUnavailable_returnUnavailable() {
|
||||
mController = new SatelliteAppListCategoryController(mContext, KEY) {
|
||||
@Override
|
||||
protected boolean isSatelliteEligible() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getSatelliteDataOptimizedApps() {
|
||||
return PACKAGE_NAMES;
|
||||
}
|
||||
};
|
||||
mController.init(TEST_SUB_ID, mPersistableBundle, true, false);
|
||||
|
||||
int result = mController.getAvailabilityStatus(TEST_SUB_ID);
|
||||
|
||||
assertThat(result).isEqualTo(CONDITIONALLY_UNAVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(Flags.FLAG_SATELLITE_25Q4_APIS)
|
||||
public void getAvailabilityStatus_entitlementSupportedButAccountIneligible_returnUnavailable() {
|
||||
mPersistableBundle.putBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, true);
|
||||
mController = new SatelliteAppListCategoryController(mContext, KEY) {
|
||||
@Override
|
||||
protected boolean isSatelliteEligible() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getSatelliteDataOptimizedApps() {
|
||||
return PACKAGE_NAMES;
|
||||
}
|
||||
};
|
||||
mController.init(TEST_SUB_ID, mPersistableBundle, true, true);
|
||||
|
||||
int result = mController.getAvailabilityStatus(TEST_SUB_ID);
|
||||
|
||||
assertThat(result).isEqualTo(CONDITIONALLY_UNAVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(Flags.FLAG_SATELLITE_25Q4_APIS)
|
||||
public void getAvailabilityStatus_entitlementSupportedAndAccountEligible_returnAvailable() {
|
||||
mPersistableBundle.putBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, true);
|
||||
mController = new SatelliteAppListCategoryController(mContext, KEY) {
|
||||
@Override
|
||||
protected boolean isSatelliteEligible() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getSatelliteDataOptimizedApps() {
|
||||
return PACKAGE_NAMES;
|
||||
}
|
||||
};
|
||||
mController.init(TEST_SUB_ID, mPersistableBundle, true, true);
|
||||
|
||||
int result = mController.getAvailabilityStatus(TEST_SUB_ID);
|
||||
|
||||
assertThat(result).isEqualTo(AVAILABLE_UNSEARCHABLE);
|
||||
}
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -28,6 +28,7 @@ import com.android.settings.biometrics.BiometricsFeatureProvider;
|
||||
import com.android.settings.biometrics.face.FaceFeatureProvider;
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintFeatureProvider;
|
||||
import com.android.settings.bluetooth.BluetoothFeatureProvider;
|
||||
import com.android.settings.connecteddevice.audiosharing.AudioSharingFeatureProvider;
|
||||
import com.android.settings.connecteddevice.fastpair.FastPairFeatureProvider;
|
||||
import com.android.settings.connecteddevice.stylus.StylusFeatureProvider;
|
||||
import com.android.settings.dashboard.DashboardFeatureProvider;
|
||||
@@ -104,6 +105,7 @@ public class FakeFeatureFactory extends FeatureFactory {
|
||||
public DisplayFeatureProvider mDisplayFeatureProvider;
|
||||
public SyncAcrossDevicesFeatureProvider mSyncAcrossDevicesFeatureProvider;
|
||||
public AccessibilityFeedbackFeatureProvider mAccessibilityFeedbackFeatureProvider;
|
||||
public AudioSharingFeatureProvider mAudioSharingFeatureProvider;
|
||||
|
||||
/** Call this in {@code @Before} method of the test class to use fake factory. */
|
||||
public static FakeFeatureFactory setupForTest() {
|
||||
@@ -156,6 +158,7 @@ public class FakeFeatureFactory extends FeatureFactory {
|
||||
mPrivateSpaceLoginFeatureProvider = mock(PrivateSpaceLoginFeatureProvider.class);
|
||||
mDisplayFeatureProvider = mock(DisplayFeatureProvider.class);
|
||||
mSyncAcrossDevicesFeatureProvider = mock(SyncAcrossDevicesFeatureProvider.class);
|
||||
mAudioSharingFeatureProvider = mock(AudioSharingFeatureProvider.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -348,4 +351,9 @@ public class FakeFeatureFactory extends FeatureFactory {
|
||||
public AccessibilityFeedbackFeatureProvider getAccessibilityFeedbackFeatureProvider() {
|
||||
return mAccessibilityFeedbackFeatureProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AudioSharingFeatureProvider getAudioSharingFeatureProvider() {
|
||||
return mAudioSharingFeatureProvider;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user