Snap for 11517367 from 12108acc07 to 24Q2-release
Change-Id: I09b5a93f800b059e4821d1634e44aa7e5a0bccbf
This commit is contained in:
75
res/layout/keyboard_layout_picker_one_pane_land.xml
Normal file
75
res/layout/keyboard_layout_picker_one_pane_land.xml
Normal file
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (C) 2024 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:id="@+id/keyboard_layout_picker_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="@dimen/keyboard_picker_margin_one_pane"
|
||||
android:layout_marginVertical="@dimen/keyboard_picker_margin_one_pane_large"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/keyboard_layout_preview_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginEnd="@dimen/keyboard_picker_margin_one_pane"
|
||||
android:background="@drawable/keyboard_review_layout_background">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/keyboard_layout_preview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="@dimen/keyboard_picker_margin_small"
|
||||
android:layout_marginTop="@dimen/keyboard_picker_margin_small"
|
||||
android:layout_marginBottom="@dimen/keyboard_picker_margin_large"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="fitCenter" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/keyboard_layout_preview_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/keyboard_picker_margin_large"
|
||||
android:layout_gravity="bottom"
|
||||
android:gravity="center"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="@dimen/keyboard_picker_text_size" />
|
||||
</FrameLayout>
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="@dimen/keyboard_picker_margin_one_pane"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/keyboard_layout_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/colorBackground"
|
||||
android:elevation="1dp"
|
||||
android:outlineAmbientShadowColor="@android:color/transparent"
|
||||
android:outlineSpotShadowColor="@android:color/transparent" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/keyboard_layouts"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/keyboard_picker_margin_small"
|
||||
android:background="?android:attr/colorBackground" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -808,4 +808,8 @@
|
||||
<!-- Array of carrier id to allow the pSIM conversion-->
|
||||
<integer-array name="config_psim_conversion_menu_enabled_carrier" translatable="false">
|
||||
</integer-array>
|
||||
|
||||
<!-- Array of carrier id that uses reusable activation code-->
|
||||
<integer-array name="config_carrier_use_rac" translatable="false">
|
||||
</integer-array>
|
||||
</resources>
|
||||
|
||||
@@ -168,6 +168,8 @@
|
||||
<!-- Keyboard -->
|
||||
<dimen name="keyboard_picker_margin_large">68dp</dimen>
|
||||
<dimen name="keyboard_picker_margin">24dp</dimen>
|
||||
<dimen name="keyboard_picker_margin_one_pane_large">48dp</dimen>
|
||||
<dimen name="keyboard_picker_margin_one_pane">24dp</dimen>
|
||||
<dimen name="keyboard_picker_margin_small">16dp</dimen>
|
||||
<dimen name="keyboard_picker_radius">28dp</dimen>
|
||||
<dimen name="keyboard_picker_text_size">16sp</dimen>
|
||||
|
||||
@@ -2060,6 +2060,10 @@
|
||||
<string name="wifi_settings_wep_networks_button_allow">Allow WEP</string>
|
||||
<!-- Wi-Fi settings dialog. Button text of dialog displayed when WEP network toggle is blocked. [CHAR LIMIT=NONE] -->
|
||||
<string name="wifi_settings_ssid_block_button_close">Close</string>
|
||||
<!-- Wi-Fi settings dialog. Title of dialog displayed when the user turns off “Allow WEP networks” while connected to a WEP network. [CHAR LIMIT=NONE] -->
|
||||
<string name="wifi_settings_wep_networks_disconnect_title">Disconnect from <xliff:g id="name">%1$s</xliff:g>?</string>
|
||||
<!-- Wi-Fi settings dialog. Summary text of dialog displayed when the user turns off “Allow WEP networks” while connected to a WEP network. [CHAR LIMIT=NONE] -->
|
||||
<string name="wifi_settings_wep_networks_disconnect_summary">You\u0027re connected to a WEP network. If you block these networks, you\u0027ll be disconnected.</string>
|
||||
|
||||
<!-- Dialog for Access Points --> <skip />
|
||||
<!-- Label to show/hide advanced options [CHAR LIMIT=40] -->
|
||||
@@ -5154,14 +5158,14 @@
|
||||
<!-- Generic title for editing the shortcuts of multiple accessibility features. [CHAR LIMIT=NONE] -->
|
||||
<string name="accessibility_shortcut_edit_screen_title">Edit accessibility shortcuts</string>
|
||||
<!-- Prompt for editing the shortcuts of multiple accessibility features. [CHAR LIMIT=NONE] -->
|
||||
<string name="accessibility_shortcut_edit_screen_prompt">Chose your shortcut for %1$s</string>
|
||||
<string name="accessibility_shortcut_edit_screen_prompt">Choose your shortcut for %1$s</string>
|
||||
|
||||
<!-- Button text for the accessibility dialog continue to the next screen for hearing aid. [CHAR LIMIT=32] -->
|
||||
<string name="accessibility_hearingaid_instruction_continue_button">Continue</string>
|
||||
<!-- Title for the accessibility preference for hearing devices. [CHAR LIMIT=35] -->
|
||||
<string name="accessibility_hearingaid_title">Hearing devices</string>
|
||||
<!-- Introduction for the Hearing devices page to introduce feature. [CHAR LIMIT=NONE] -->
|
||||
<string name="accessibility_hearingaid_intro">You can use hearing aids, cochlear implants, and other amplification devices with your phone</string>
|
||||
<!-- Introduction for the Hearing devices page to introduce feature. [CHAR LIMIT=NONE BACKUP_MESSAGE_ID=5856992709195963850] -->
|
||||
<string name="accessibility_hearingaid_intro">Set up and manage ASHA and LE Audio hearing aids, cochlear implants, and other amplification devices</string>
|
||||
<!-- Summary for the accessibility preference for hearing aid when not connected. [CHAR LIMIT=50] -->
|
||||
<string name="accessibility_hearingaid_not_connected_summary">No hearing devices connected</string>
|
||||
<!-- Summary for the accessibility preference for hearing aid when adding new devices. [CHAR LIMIT=50] -->
|
||||
@@ -5196,6 +5200,8 @@
|
||||
<string name="accessibility_hac_mode_summary">Improves compatibility with telecoils and reduces unwanted noise</string>
|
||||
<!-- Title for accessibility hearing device page footer. [CHAR LIMIT=40] -->
|
||||
<string name="accessibility_hearing_device_about_title">About hearing devices</string>
|
||||
<!-- Description for text in accessibility hearing aids footer. [CHAR LIMIT=NONE BACKUP_MESSAGE_ID=7451899224828040581] -->
|
||||
<string name="accessibility_hearing_device_footer_summary">To find other hearing devices that aren’t supported by ASHA or LE Audio, tap <b>Pair new device</b> > <b>See more devices</b></string>
|
||||
<!-- Title for the pair hearing device page. [CHAR LIMIT=25] -->
|
||||
<string name="accessibility_hearing_device_pairing_page_title">Pair hearing device</string>
|
||||
<!-- Subtitle for the pair hearing device page. [CHAR LIMIT=NONE] -->
|
||||
@@ -12338,6 +12344,15 @@
|
||||
<!-- Summary for UWB preference when UWB is unavailable due to regulatory requirements. [CHAR_LIMIT=NONE]-->
|
||||
<string name="uwb_settings_summary_no_uwb_regulatory">UWB is unavailable in the current location</string>
|
||||
|
||||
<!-- Title for Thread network preference [CHAR_LIMIT=60] -->
|
||||
<string name="thread_network_settings_title">Thread</string>
|
||||
|
||||
<!-- Summary for Thread network preference. [CHAR_LIMIT=NONE]-->
|
||||
<string name="thread_network_settings_summary">Connect to compatible devices using Thread for a seamless smart home experience</string>
|
||||
|
||||
<!-- Summary for Thread network preference when airplane mode is enabled. [CHAR_LIMIT=NONE]-->
|
||||
<string name="thread_network_settings_summary_airplane_mode">Turn off airplane mode to use Thread</string>
|
||||
|
||||
<!-- Label for the camera use toggle [CHAR LIMIT=40] -->
|
||||
<string name="camera_toggle_title">Camera access</string>
|
||||
<!-- Label for the camera use toggle [CHAR LIMIT=40] -->
|
||||
|
||||
@@ -60,4 +60,11 @@
|
||||
settings:searchable="true"
|
||||
settings:controller="com.android.settings.accessibility.HearingAidCompatibilityPreferenceController"/>
|
||||
</PreferenceCategory>
|
||||
|
||||
<com.android.settings.accessibility.AccessibilityFooterPreference
|
||||
android:key="hearing_device_footer"
|
||||
android:title="@string/accessibility_hearing_device_footer_summary"
|
||||
android:selectable="false"
|
||||
settings:searchable="false"
|
||||
settings:controller="com.android.settings.accessibility.HearingDeviceFooterPreferenceController"/>
|
||||
</PreferenceScreen>
|
||||
@@ -63,6 +63,15 @@
|
||||
settings:useAdminDisabledSummary="true"
|
||||
settings:userRestriction="no_ultra_wideband_radio" />
|
||||
|
||||
<com.android.settingslib.RestrictedSwitchPreference
|
||||
android:key="thread_network_settings"
|
||||
android:title="@string/thread_network_settings_title"
|
||||
android:order="110"
|
||||
android:summary="@string/summary_placeholder"
|
||||
settings:controller="com.android.settings.connecteddevice.threadnetwork.ThreadNetworkPreferenceController"
|
||||
settings:userRestriction="no_thread_network"
|
||||
settings:useAdminDisabledSummary="true"/>
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="dashboard_tile_placeholder"
|
||||
android:order="-8" />
|
||||
|
||||
@@ -38,5 +38,4 @@
|
||||
settings:useAdminDisabledSummary="true"
|
||||
settings:controller="com.android.settings.accessibility.ViewAllBluetoothDevicesPreferenceController"/>
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
@@ -40,8 +42,7 @@ public class ColorContrastFragment extends DashboardFragment {
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
// TODO(b/326539398): Add metrics tracking for color contrast.
|
||||
return 0;
|
||||
return SettingsEnums.ACCESSIBILITY_COLOR_CONTRAST;
|
||||
}
|
||||
|
||||
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
|
||||
@@ -563,7 +563,7 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
|
||||
@Override
|
||||
@StringRes
|
||||
protected int getAgreeButtonTextRes() {
|
||||
return R.string.security_settings_fingerprint_enroll_introduction_agree;
|
||||
return R.string.security_settings_face_enroll_introduction_agree;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
include platform/packages/modules/Connectivity:/thread/OWNERS
|
||||
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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.threadnetwork
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.thread.ThreadNetworkController
|
||||
import android.net.thread.ThreadNetworkController.StateCallback
|
||||
import android.net.thread.ThreadNetworkException
|
||||
import android.net.thread.ThreadNetworkManager
|
||||
import android.os.OutcomeReceiver
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleEventObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import com.android.net.thread.platform.flags.Flags
|
||||
import com.android.settings.R
|
||||
import com.android.settings.core.TogglePreferenceController
|
||||
import java.util.concurrent.Executor
|
||||
|
||||
/** Controller for the "Thread" toggle in "Connected devices > Connection preferences". */
|
||||
class ThreadNetworkPreferenceController @VisibleForTesting constructor(
|
||||
context: Context,
|
||||
key: String,
|
||||
private val executor: Executor,
|
||||
private val threadController: BaseThreadNetworkController?
|
||||
) : TogglePreferenceController(context, key), LifecycleEventObserver {
|
||||
private val stateCallback: StateCallback
|
||||
private val airplaneModeReceiver: BroadcastReceiver
|
||||
private var threadEnabled = false
|
||||
private var airplaneModeOn = false
|
||||
private var preference: Preference? = null
|
||||
|
||||
/**
|
||||
* A testable interface for [ThreadNetworkController] which is `final`.
|
||||
*
|
||||
* We are in a awkward situation that Android API guideline suggest `final` for API classes
|
||||
* while Robolectric test is being deprecated for platform testing (See
|
||||
* tests/robotests/new_tests_hook.sh). This force us to use "mockito-target-extended" but it's
|
||||
* conflicting with the default "mockito-target" which is somehow indirectly depended by the
|
||||
* `SettingsUnitTests` target.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
interface BaseThreadNetworkController {
|
||||
fun setEnabled(
|
||||
enabled: Boolean,
|
||||
executor: Executor,
|
||||
receiver: OutcomeReceiver<Void?, ThreadNetworkException>
|
||||
)
|
||||
|
||||
fun registerStateCallback(executor: Executor, callback: StateCallback)
|
||||
|
||||
fun unregisterStateCallback(callback: StateCallback)
|
||||
}
|
||||
|
||||
constructor(context: Context, key: String) : this(
|
||||
context,
|
||||
key,
|
||||
ContextCompat.getMainExecutor(context),
|
||||
getThreadNetworkController(context)
|
||||
)
|
||||
|
||||
init {
|
||||
stateCallback = newStateCallback()
|
||||
airplaneModeReceiver = newAirPlaneModeReceiver()
|
||||
}
|
||||
|
||||
val isThreadSupportedOnDevice: Boolean
|
||||
get() = threadController != null
|
||||
|
||||
private fun newStateCallback(): StateCallback {
|
||||
return object : StateCallback {
|
||||
override fun onThreadEnableStateChanged(enabledState: Int) {
|
||||
threadEnabled = enabledState == ThreadNetworkController.STATE_ENABLED
|
||||
}
|
||||
|
||||
override fun onDeviceRoleChanged(role: Int) {}
|
||||
}
|
||||
}
|
||||
|
||||
private fun newAirPlaneModeReceiver(): BroadcastReceiver {
|
||||
return object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
airplaneModeOn = isAirplaneModeOn(context)
|
||||
Log.i(TAG, "Airplane mode is " + if (airplaneModeOn) "ON" else "OFF")
|
||||
preference?.let { preference -> updateState(preference) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAvailabilityStatus(): Int {
|
||||
return if (!Flags.threadEnabledPlatform()) {
|
||||
CONDITIONALLY_UNAVAILABLE
|
||||
} else if (!isThreadSupportedOnDevice) {
|
||||
UNSUPPORTED_ON_DEVICE
|
||||
} else if (airplaneModeOn) {
|
||||
DISABLED_DEPENDENT_SETTING
|
||||
} else {
|
||||
AVAILABLE
|
||||
}
|
||||
}
|
||||
|
||||
override fun displayPreference(screen: PreferenceScreen) {
|
||||
super.displayPreference(screen)
|
||||
preference = screen.findPreference(preferenceKey)
|
||||
}
|
||||
|
||||
override fun isChecked(): Boolean {
|
||||
// TODO (b/322742298):
|
||||
// Check airplane mode here because it's planned to disable Thread state in airplane mode
|
||||
// (code in the mainline module). But it's currently not implemented yet (b/322742298).
|
||||
// By design, the toggle should be unchecked in airplane mode, so explicitly check the
|
||||
// airplane mode here to acchieve the same UX.
|
||||
return !airplaneModeOn && threadEnabled
|
||||
}
|
||||
|
||||
override fun setChecked(isChecked: Boolean): Boolean {
|
||||
if (threadController == null) {
|
||||
return false
|
||||
}
|
||||
val action = if (isChecked) "enable" else "disable"
|
||||
threadController.setEnabled(
|
||||
isChecked,
|
||||
executor,
|
||||
object : OutcomeReceiver<Void?, ThreadNetworkException> {
|
||||
override fun onError(e: ThreadNetworkException) {
|
||||
// TODO(b/327549838): gracefully handle the failure by resetting the UI state
|
||||
Log.e(TAG, "Failed to $action Thread", e)
|
||||
}
|
||||
|
||||
override fun onResult(unused: Void?) {
|
||||
Log.d(TAG, "Successfully $action Thread")
|
||||
}
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onStateChanged(lifecycleOwner: LifecycleOwner, event: Lifecycle.Event) {
|
||||
if (threadController == null) {
|
||||
return
|
||||
}
|
||||
|
||||
when (event) {
|
||||
Lifecycle.Event.ON_START -> {
|
||||
threadController.registerStateCallback(executor, stateCallback)
|
||||
airplaneModeOn = isAirplaneModeOn(mContext)
|
||||
mContext.registerReceiver(
|
||||
airplaneModeReceiver,
|
||||
IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)
|
||||
)
|
||||
preference?.let { preference -> updateState(preference) }
|
||||
}
|
||||
Lifecycle.Event.ON_STOP -> {
|
||||
threadController.unregisterStateCallback(stateCallback)
|
||||
mContext.unregisterReceiver(airplaneModeReceiver)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateState(preference: Preference) {
|
||||
super.updateState(preference)
|
||||
preference.isEnabled = !airplaneModeOn
|
||||
refreshSummary(preference)
|
||||
}
|
||||
|
||||
override fun getSummary(): CharSequence {
|
||||
val resId: Int = if (airplaneModeOn) {
|
||||
R.string.thread_network_settings_summary_airplane_mode
|
||||
} else {
|
||||
R.string.thread_network_settings_summary
|
||||
}
|
||||
return mContext.getResources().getString(resId)
|
||||
}
|
||||
|
||||
override fun getSliceHighlightMenuRes(): Int {
|
||||
return R.string.menu_key_connected_devices
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "ThreadNetworkSettings"
|
||||
private fun getThreadNetworkController(context: Context): BaseThreadNetworkController? {
|
||||
if (!context.packageManager.hasSystemFeature(PackageManager.FEATURE_THREAD_NETWORK)) {
|
||||
return null
|
||||
}
|
||||
val manager = context.getSystemService(ThreadNetworkManager::class.java) ?: return null
|
||||
val controller = manager.allThreadNetworkControllers[0]
|
||||
return object : BaseThreadNetworkController {
|
||||
override fun setEnabled(
|
||||
enabled: Boolean,
|
||||
executor: Executor,
|
||||
receiver: OutcomeReceiver<Void?, ThreadNetworkException>
|
||||
) {
|
||||
controller.setEnabled(enabled, executor, receiver)
|
||||
}
|
||||
|
||||
override fun registerStateCallback(executor: Executor, callback: StateCallback) {
|
||||
controller.registerStateCallback(executor, callback)
|
||||
}
|
||||
|
||||
override fun unregisterStateCallback(callback: StateCallback) {
|
||||
controller.unregisterStateCallback(callback)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isAirplaneModeOn(context: Context): Boolean {
|
||||
return Settings.Global.getInt(
|
||||
context.contentResolver,
|
||||
Settings.Global.AIRPLANE_MODE_ON,
|
||||
0
|
||||
) == 1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -187,12 +187,6 @@ public class SettingsHomepageActivity extends FragmentActivity implements
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (!isTaskRoot() && !isSingleTask()) {
|
||||
Log.i(TAG, "Not task root nor single task, finish");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
mIsEmbeddingActivityEnabled = ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this);
|
||||
if (mIsEmbeddingActivityEnabled) {
|
||||
final UserManager um = getSystemService(UserManager.class);
|
||||
@@ -319,12 +313,6 @@ public class SettingsHomepageActivity extends FragmentActivity implements
|
||||
updateHomepageUI();
|
||||
}
|
||||
|
||||
private boolean isSingleTask() {
|
||||
ActivityInfo info = getIntent().resolveActivityInfo(getPackageManager(),
|
||||
PackageManager.MATCH_DEFAULT_ONLY);
|
||||
return info.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK;
|
||||
}
|
||||
|
||||
private void updateSplitLayout() {
|
||||
if (!mIsEmbeddingActivityEnabled) {
|
||||
return;
|
||||
|
||||
@@ -16,9 +16,11 @@
|
||||
|
||||
package com.android.settings.inputmethod;
|
||||
|
||||
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.hardware.input.KeyboardLayout;
|
||||
@@ -35,6 +37,7 @@ import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.android.hardware.input.Flags;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.activityembedding.ActivityEmbeddingUtils;
|
||||
|
||||
//TODO: b/316243168 - [Physical Keyboard Setting] Refactor NewKeyboardLayoutPickerFragment
|
||||
public class NewKeyboardLayoutPickerFragment extends Fragment {
|
||||
@@ -81,7 +84,7 @@ public class NewKeyboardLayoutPickerFragment extends Fragment {
|
||||
Bundle savedInstanceState) {
|
||||
mInputManager = requireContext().getSystemService(InputManager.class);
|
||||
ViewGroup fragmentView = (ViewGroup) inflater.inflate(
|
||||
R.layout.keyboard_layout_picker, container, false);
|
||||
getPickerLayout(getResources().getConfiguration()), container, false);
|
||||
mKeyboardLayoutPreview = fragmentView.findViewById(R.id.keyboard_layout_preview);
|
||||
mKeyboardLayoutPreviewText = fragmentView.findViewById(R.id.keyboard_layout_preview_name);
|
||||
if (!Flags.keyboardLayoutPreviewFlag()) {
|
||||
@@ -102,6 +105,12 @@ public class NewKeyboardLayoutPickerFragment extends Fragment {
|
||||
return fragmentView;
|
||||
}
|
||||
|
||||
private int getPickerLayout(Configuration configuration) {
|
||||
return !ActivityEmbeddingUtils.isAlreadyEmbedded(this.getActivity())
|
||||
&& configuration.orientation == ORIENTATION_LANDSCAPE
|
||||
? R.layout.keyboard_layout_picker_one_pane_land : R.layout.keyboard_layout_picker;
|
||||
}
|
||||
|
||||
private void updateViewMarginForPreviewFlagOff(ViewGroup fragmentView) {
|
||||
LinearLayout previewContainer = fragmentView.findViewById(
|
||||
R.id.keyboard_layout_picker_container);
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.android.settings.network
|
||||
|
||||
import android.app.ProgressDialog
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
@@ -46,12 +45,12 @@ import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
@@ -59,12 +58,16 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.android.settings.R
|
||||
import com.android.settings.SidecarFragment
|
||||
import com.android.settings.network.telephony.SubscriptionActionDialogActivity
|
||||
import com.android.settings.network.telephony.ToggleSubscriptionDialogActivity
|
||||
import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
|
||||
import com.android.settings.spa.network.SimOnboardingPageProvider.getRoute
|
||||
import com.android.settingslib.spa.SpaBaseDialogActivity
|
||||
import com.android.settingslib.spa.framework.theme.SettingsDimension
|
||||
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
|
||||
import com.android.settingslib.spa.widget.dialog.AlertDialogButton
|
||||
import com.android.settingslib.spa.widget.dialog.getDialogWidth
|
||||
import com.android.settingslib.spa.widget.dialog.rememberAlertDialogPresenter
|
||||
import com.android.settingslib.spa.widget.editor.SettingsOutlinedTextField
|
||||
import com.android.settingslib.spa.widget.ui.SettingsTitle
|
||||
import com.android.settingslib.spaprivileged.framework.common.userManager
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -78,8 +81,8 @@ import kotlinx.coroutines.launch
|
||||
class SimOnboardingActivity : SpaBaseDialogActivity() {
|
||||
lateinit var scope: CoroutineScope
|
||||
lateinit var showBottomSheet: MutableState<Boolean>
|
||||
lateinit var showError: MutableState<Boolean>
|
||||
lateinit var showDialog: MutableState<Boolean>
|
||||
lateinit var showError: MutableState<ErrorType>
|
||||
lateinit var showProgressDialog: MutableState<Boolean>
|
||||
|
||||
private var switchToEuiccSubscriptionSidecar: SwitchToEuiccSubscriptionSidecar? = null
|
||||
private var switchToRemovableSlotSidecar: SwitchToRemovableSlotSidecar? = null
|
||||
@@ -101,13 +104,19 @@ class SimOnboardingActivity : SpaBaseDialogActivity() {
|
||||
return
|
||||
}
|
||||
|
||||
if (onboardingService.activeSubInfoList.isEmpty()) {
|
||||
// TODO: refactor and replace the ToggleSubscriptionDialogActivity
|
||||
Log.d(TAG, "onboardingService.activeSubInfoList is empty" +
|
||||
", start ToggleSubscriptionDialogActivity")
|
||||
this.startActivity(ToggleSubscriptionDialogActivity
|
||||
.getIntent(this.applicationContext, targetSubId, true))
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
switchToEuiccSubscriptionSidecar = SwitchToEuiccSubscriptionSidecar.get(fragmentManager)
|
||||
switchToRemovableSlotSidecar = SwitchToRemovableSlotSidecar.get(fragmentManager)
|
||||
enableMultiSimSidecar = EnableMultiSimSidecar.get(fragmentManager)
|
||||
|
||||
setContent {
|
||||
Content()
|
||||
}
|
||||
}
|
||||
|
||||
override fun finish() {
|
||||
@@ -116,15 +125,14 @@ class SimOnboardingActivity : SpaBaseDialogActivity() {
|
||||
super.finish()
|
||||
}
|
||||
|
||||
var callbackListener: (Int) -> Unit = {
|
||||
var callbackListener: (CallbackType) -> Unit = {
|
||||
Log.d(TAG, "Receive the CALLBACK: $it")
|
||||
when (it) {
|
||||
CALLBACK_ERROR -> {
|
||||
CallbackType.CALLBACK_ERROR -> {
|
||||
setProgressDialog(false)
|
||||
showError.value = true
|
||||
}
|
||||
|
||||
CALLBACK_ONBOARDING_COMPLETE -> {
|
||||
CallbackType.CALLBACK_ONBOARDING_COMPLETE -> {
|
||||
showBottomSheet.value = false
|
||||
setProgressDialog(true)
|
||||
scope.launch {
|
||||
@@ -134,26 +142,29 @@ class SimOnboardingActivity : SpaBaseDialogActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
CALLBACK_SETUP_NAME -> {
|
||||
CallbackType.CALLBACK_SETUP_NAME -> {
|
||||
scope.launch {
|
||||
onboardingService.startSetupName()
|
||||
}
|
||||
}
|
||||
|
||||
CALLBACK_SETUP_PRIMARY_SIM -> {
|
||||
CallbackType.CALLBACK_SETUP_PRIMARY_SIM -> {
|
||||
scope.launch {
|
||||
onboardingService.startSetupPrimarySim(this@SimOnboardingActivity)
|
||||
}
|
||||
}
|
||||
|
||||
CALLBACK_FINISH -> {
|
||||
CallbackType.CALLBACK_FINISH -> {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setProgressDialog(enable: Boolean) {
|
||||
showDialog.value = enable
|
||||
if (!this::showProgressDialog.isInitialized) {
|
||||
return
|
||||
}
|
||||
showProgressDialog.value = enable
|
||||
val progressState = if (enable) {
|
||||
SubscriptionActionDialogActivity.PROGRESS_IS_SHOWING
|
||||
} else {
|
||||
@@ -165,16 +176,19 @@ class SimOnboardingActivity : SpaBaseDialogActivity() {
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
override fun Content() {
|
||||
showBottomSheet = remember { mutableStateOf(true) }
|
||||
showError = remember { mutableStateOf(false) }
|
||||
showDialog = remember { mutableStateOf(false) }
|
||||
showBottomSheet = remember { mutableStateOf(false) }
|
||||
showError = remember { mutableStateOf(ErrorType.ERROR_NONE) }
|
||||
showProgressDialog = remember { mutableStateOf(false) }
|
||||
scope = rememberCoroutineScope()
|
||||
|
||||
registerSidecarReceiverFlow()
|
||||
|
||||
if(showError.value){
|
||||
// show error
|
||||
return
|
||||
ErrorDialogImpl()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
if (onboardingService.activeSubInfoList.isNotEmpty()) {
|
||||
showBottomSheet.value = true
|
||||
}
|
||||
}
|
||||
|
||||
if (showBottomSheet.value) {
|
||||
@@ -195,7 +209,9 @@ class SimOnboardingActivity : SpaBaseDialogActivity() {
|
||||
},
|
||||
cancelAction = { finish() },
|
||||
)
|
||||
} else {
|
||||
}
|
||||
|
||||
if(showProgressDialog.value) {
|
||||
ProgressDialogImpl()
|
||||
}
|
||||
}
|
||||
@@ -203,7 +219,6 @@ class SimOnboardingActivity : SpaBaseDialogActivity() {
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ProgressDialogImpl() {
|
||||
if(showDialog.value) {
|
||||
// TODO: Create the SPA's ProgressDialog and using SPA's widget
|
||||
BasicAlertDialog(
|
||||
onDismissRequest = {},
|
||||
@@ -235,6 +250,55 @@ class SimOnboardingActivity : SpaBaseDialogActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ErrorDialogImpl(){
|
||||
// EuiccSlotSidecar showErrorDialog
|
||||
val errorDialogPresenterForEuiccSlotSidecar = rememberAlertDialogPresenter(
|
||||
confirmButton = AlertDialogButton(
|
||||
stringResource(android.R.string.ok)
|
||||
) {
|
||||
finish()
|
||||
},
|
||||
title = stringResource(R.string.privileged_action_disable_fail_title),
|
||||
text = {
|
||||
Text(stringResource(R.string.privileged_action_disable_fail_text))
|
||||
},
|
||||
)
|
||||
|
||||
// RemovableSlotSidecar showErrorDialog
|
||||
val errorDialogPresenterForRemovableSlotSidecar = rememberAlertDialogPresenter(
|
||||
confirmButton = AlertDialogButton(
|
||||
stringResource(android.R.string.ok)
|
||||
) {
|
||||
finish()
|
||||
},
|
||||
title = stringResource(R.string.sim_action_enable_sim_fail_title),
|
||||
text = {
|
||||
Text(stringResource(R.string.sim_action_enable_sim_fail_text))
|
||||
},
|
||||
)
|
||||
|
||||
// enableDSDS showErrorDialog
|
||||
val errorDialogPresenterForMultiSimSidecar = rememberAlertDialogPresenter(
|
||||
confirmButton = AlertDialogButton(
|
||||
stringResource(android.R.string.ok)
|
||||
) {
|
||||
finish()
|
||||
},
|
||||
title = stringResource(R.string.dsds_activation_failure_title),
|
||||
text = {
|
||||
Text(stringResource(R.string.dsds_activation_failure_body_msg2))
|
||||
},
|
||||
)
|
||||
|
||||
// show error
|
||||
when (showError.value) {
|
||||
ErrorType.ERROR_EUICC_SLOT -> errorDialogPresenterForEuiccSlotSidecar.open()
|
||||
ErrorType.ERROR_REMOVABLE_SLOT -> errorDialogPresenterForRemovableSlotSidecar.open()
|
||||
ErrorType.ERROR_ENABLE_DSDS -> errorDialogPresenterForMultiSimSidecar.open()
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -304,13 +368,14 @@ class SimOnboardingActivity : SpaBaseDialogActivity() {
|
||||
SidecarFragment.State.SUCCESS -> {
|
||||
Log.i(TAG, "Successfully enable the eSIM profile.")
|
||||
switchToEuiccSubscriptionSidecar!!.reset()
|
||||
callbackListener(CALLBACK_SETUP_NAME)
|
||||
callbackListener(CallbackType.CALLBACK_SETUP_NAME)
|
||||
}
|
||||
|
||||
SidecarFragment.State.ERROR -> {
|
||||
Log.i(TAG, "Failed to enable the eSIM profile.")
|
||||
switchToEuiccSubscriptionSidecar!!.reset()
|
||||
callbackListener(CALLBACK_ERROR)
|
||||
showError.value = ErrorType.ERROR_EUICC_SLOT
|
||||
callbackListener(CallbackType.CALLBACK_ERROR)
|
||||
// TODO: showErrorDialog and using privileged_action_disable_fail_title and
|
||||
// privileged_action_disable_fail_text
|
||||
}
|
||||
@@ -323,13 +388,14 @@ class SimOnboardingActivity : SpaBaseDialogActivity() {
|
||||
Log.i(TAG, "Successfully switched to removable slot.")
|
||||
switchToRemovableSlotSidecar!!.reset()
|
||||
onboardingService.handleTogglePsimAction()
|
||||
callbackListener(CALLBACK_SETUP_NAME)
|
||||
callbackListener(CallbackType.CALLBACK_SETUP_NAME)
|
||||
}
|
||||
|
||||
SidecarFragment.State.ERROR -> {
|
||||
Log.e(TAG, "Failed switching to removable slot.")
|
||||
switchToRemovableSlotSidecar!!.reset()
|
||||
callbackListener(CALLBACK_ERROR)
|
||||
showError.value = ErrorType.ERROR_REMOVABLE_SLOT
|
||||
callbackListener(CallbackType.CALLBACK_ERROR)
|
||||
// TODO: showErrorDialog and using sim_action_enable_sim_fail_title and
|
||||
// sim_action_enable_sim_fail_text
|
||||
}
|
||||
@@ -347,7 +413,8 @@ class SimOnboardingActivity : SpaBaseDialogActivity() {
|
||||
SidecarFragment.State.ERROR -> {
|
||||
enableMultiSimSidecar!!.reset()
|
||||
Log.i(TAG, "Failed to switch to DSDS without rebooting.")
|
||||
callbackListener(CALLBACK_ERROR)
|
||||
showError.value = ErrorType.ERROR_ENABLE_DSDS
|
||||
callbackListener(CallbackType.CALLBACK_ERROR)
|
||||
// TODO: showErrorDialog and using dsds_activation_failure_title and
|
||||
// dsds_activation_failure_body_msg2
|
||||
}
|
||||
@@ -370,7 +437,7 @@ class SimOnboardingActivity : SpaBaseDialogActivity() {
|
||||
}
|
||||
Log.i(TAG, "DSDS enabled, start to enable pSIM profile.")
|
||||
onboardingService.handleTogglePsimAction()
|
||||
callbackListener(CALLBACK_FINISH)
|
||||
callbackListener(CallbackType.CALLBACK_FINISH)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -426,7 +493,7 @@ class SimOnboardingActivity : SpaBaseDialogActivity() {
|
||||
Log.i(TAG, "setProgressState:$state")
|
||||
}
|
||||
|
||||
fun initServiceData(context: Context,targetSubId: Int, callback:(Int)->Unit) {
|
||||
fun initServiceData(context: Context,targetSubId: Int, callback:(CallbackType)->Unit) {
|
||||
onboardingService.initData(targetSubId, context,callback)
|
||||
}
|
||||
|
||||
@@ -445,10 +512,20 @@ class SimOnboardingActivity : SpaBaseDialogActivity() {
|
||||
var onboardingService:SimOnboardingService = SimOnboardingService()
|
||||
const val TAG = "SimOnboardingActivity"
|
||||
const val SUB_ID = "sub_id"
|
||||
const val CALLBACK_ERROR = -1
|
||||
const val CALLBACK_ONBOARDING_COMPLETE = 1
|
||||
const val CALLBACK_SETUP_NAME = 2
|
||||
const val CALLBACK_SETUP_PRIMARY_SIM = 3
|
||||
const val CALLBACK_FINISH = 4
|
||||
|
||||
enum class ErrorType(val value:Int){
|
||||
ERROR_NONE(-1),
|
||||
ERROR_EUICC_SLOT(1),
|
||||
ERROR_REMOVABLE_SLOT(2),
|
||||
ERROR_ENABLE_DSDS(3)
|
||||
}
|
||||
|
||||
enum class CallbackType(val value:Int){
|
||||
CALLBACK_ERROR(-1),
|
||||
CALLBACK_ONBOARDING_COMPLETE(1),
|
||||
CALLBACK_SETUP_NAME(2),
|
||||
CALLBACK_SETUP_PRIMARY_SIM(3),
|
||||
CALLBACK_FINISH(4)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import android.telephony.TelephonyManager
|
||||
import android.telephony.UiccCardInfo
|
||||
import android.telephony.UiccSlotInfo
|
||||
import android.util.Log
|
||||
import com.android.settings.network.SimOnboardingActivity.Companion.CallbackType
|
||||
import com.android.settings.spa.network.setAutomaticData
|
||||
import com.android.settings.spa.network.setDefaultData
|
||||
import com.android.settings.spa.network.setDefaultSms
|
||||
@@ -31,7 +32,6 @@ import com.android.settingslib.utils.ThreadUtils
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
|
||||
private const val TAG = "SimOnboardingService"
|
||||
private const val INVALID = SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||
|
||||
@@ -60,7 +60,7 @@ class SimOnboardingService {
|
||||
.map { it.subscriptionId }
|
||||
.firstOrNull() ?: SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||
}
|
||||
var callback: (Int) -> Unit = {}
|
||||
var callback: (CallbackType) -> Unit = {}
|
||||
|
||||
var isMultipleEnabledProfilesSupported: Boolean = false
|
||||
get() {
|
||||
@@ -135,24 +135,24 @@ class SimOnboardingService {
|
||||
userSelectedSubInfoList.clear()
|
||||
}
|
||||
|
||||
fun initData(inputTargetSubId:Int,context: Context, callback: (Int) -> Unit) {
|
||||
fun initData(inputTargetSubId: Int,
|
||||
context: Context,
|
||||
callback: (CallbackType) -> Unit) {
|
||||
this.callback = callback
|
||||
targetSubId = inputTargetSubId
|
||||
subscriptionManager = context.getSystemService(SubscriptionManager::class.java)
|
||||
telephonyManager = context.getSystemService(TelephonyManager::class.java)
|
||||
Log.d(
|
||||
TAG, "startInit: targetSubId:$targetSubId"
|
||||
TAG, "startInit: targetSubId:$targetSubId, activeSubInfoList: $activeSubInfoList"
|
||||
)
|
||||
ThreadUtils.postOnBackgroundThread {
|
||||
activeSubInfoList = SubscriptionUtil.getActiveSubscriptions(subscriptionManager)
|
||||
|
||||
ThreadUtils.postOnBackgroundThread {
|
||||
availableSubInfoList = SubscriptionUtil.getAvailableSubscriptions(context)
|
||||
targetSubInfo =
|
||||
availableSubInfoList.find { subInfo -> subInfo.subscriptionId == targetSubId }
|
||||
targetSubInfo?.let { userSelectedSubInfoList.add(it) }
|
||||
Log.d(
|
||||
TAG, "targetSubId: $targetSubId" + ", targetSubInfo: $targetSubInfo" +
|
||||
". activeSubInfoList: $activeSubInfoList"
|
||||
)
|
||||
Log.d(TAG, "targetSubId: $targetSubId , targetSubInfo: $targetSubInfo")
|
||||
slotInfoList = telephonyManager?.uiccSlotsInfo?.toList() ?: listOf()
|
||||
Log.d(TAG, "slotInfoList: $slotInfoList.")
|
||||
uiccCardInfoList = telephonyManager?.uiccCardsInfo!!
|
||||
@@ -196,6 +196,16 @@ class SimOnboardingService {
|
||||
return userSelectedSubInfoList.toList()
|
||||
}
|
||||
|
||||
fun getSelectedSubscriptionInfoListWithRenaming(): List<SubscriptionInfo> {
|
||||
if (userSelectedSubInfoList.isEmpty()){
|
||||
Log.d(TAG, "userSelectedSubInfoList is empty")
|
||||
return activeSubInfoList
|
||||
}
|
||||
return userSelectedSubInfoList.map {
|
||||
SubscriptionInfo.Builder(it).setDisplayName(getSubscriptionInfoDisplayName(it)).build()
|
||||
}.toList()
|
||||
}
|
||||
|
||||
fun addItemForRenaming(subInfo: SubscriptionInfo, newName: String) {
|
||||
if (subInfo.displayName == newName) {
|
||||
return
|
||||
@@ -212,7 +222,11 @@ class SimOnboardingService {
|
||||
}
|
||||
|
||||
fun addCurrentItemForSelectedSim() {
|
||||
if (userSelectedSubInfoList.size < getActiveModemCount) {
|
||||
userSelectedSubInfoList.addAll(activeSubInfoList)
|
||||
Log.d(TAG, "addCurrentItemForSelectedSim: userSelectedSubInfoList:" +
|
||||
", $userSelectedSubInfoList")
|
||||
}
|
||||
}
|
||||
|
||||
fun addItemForSelectedSim(selectedSubInfo: SubscriptionInfo) {
|
||||
@@ -249,7 +263,7 @@ class SimOnboardingService {
|
||||
|
||||
fun startActivatingSim(){
|
||||
// TODO: start to activate sim
|
||||
callback(SimOnboardingActivity.CALLBACK_FINISH)
|
||||
callback(CallbackType.CALLBACK_FINISH)
|
||||
}
|
||||
|
||||
suspend fun startSetupName() {
|
||||
@@ -261,7 +275,7 @@ class SimOnboardingService {
|
||||
)
|
||||
}
|
||||
// next action is SETUP_PRIMARY_SIM
|
||||
callback(SimOnboardingActivity.CALLBACK_SETUP_PRIMARY_SIM)
|
||||
callback(CallbackType.CALLBACK_SETUP_PRIMARY_SIM)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,7 +304,7 @@ class SimOnboardingService {
|
||||
}
|
||||
|
||||
// no next action, send finish
|
||||
callback(SimOnboardingActivity.CALLBACK_FINISH)
|
||||
callback(CallbackType.CALLBACK_FINISH)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,9 +49,11 @@ import com.android.settings.flags.Flags;
|
||||
import com.android.settings.network.helper.SelectableSubscriptions;
|
||||
import com.android.settings.network.helper.SubscriptionAnnotation;
|
||||
import com.android.settings.network.telephony.DeleteEuiccSubscriptionDialogActivity;
|
||||
import com.android.settings.network.telephony.EuiccRacConnectivityDialogActivity;
|
||||
import com.android.settings.network.telephony.ToggleSubscriptionDialogActivity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@@ -557,14 +559,22 @@ public class SubscriptionUtil {
|
||||
* @param context {@code Context}
|
||||
* @param subId The id of subscription need to be deleted.
|
||||
*/
|
||||
public static void startDeleteEuiccSubscriptionDialogActivity(Context context, int subId) {
|
||||
public static void startDeleteEuiccSubscriptionDialogActivity(Context context, int subId,
|
||||
int carrierId) {
|
||||
if (!SubscriptionManager.isUsableSubscriptionId(subId)) {
|
||||
Log.i(TAG, "Unable to delete subscription due to invalid subscription ID.");
|
||||
return;
|
||||
}
|
||||
// TODO(b/325693582): Add verification if carrier is RAC and logic for new dialog
|
||||
final int[] carriersThatUseRAC = context.getResources().getIntArray(
|
||||
R.array.config_carrier_use_rac);
|
||||
boolean isCarrierRac = Arrays.stream(carriersThatUseRAC).anyMatch(cid -> cid == carrierId);
|
||||
|
||||
if (isCarrierRac && !isConnectedToWifiOrDifferentSubId(context, subId)) {
|
||||
context.startActivity(EuiccRacConnectivityDialogActivity.getIntent(context, subId));
|
||||
} else {
|
||||
context.startActivity(DeleteEuiccSubscriptionDialogActivity.getIntent(context, subId));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and returns a subscription with a specific subscription ID.
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
|
||||
class DeleteSimProfilePreferenceController(context: Context, preferenceKey: String) :
|
||||
BasePreferenceController(context, preferenceKey) {
|
||||
private var subscriptionId: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||
private var carrierId: Int = TelephonyManager.UNKNOWN_CARRIER_ID
|
||||
private var subscriptionInfo: SubscriptionInfo? = null
|
||||
private lateinit var preference: Preference
|
||||
|
||||
@@ -40,6 +41,9 @@ class DeleteSimProfilePreferenceController(context: Context, preferenceKey: Stri
|
||||
this.subscriptionId = subscriptionId
|
||||
subscriptionInfo = SubscriptionUtil.getAvailableSubscriptions(mContext)
|
||||
.find { it.subscriptionId == subscriptionId && it.isEmbedded }
|
||||
subscriptionInfo?.let {
|
||||
carrierId = it.carrierId
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAvailabilityStatus() = when (subscriptionInfo) {
|
||||
@@ -67,7 +71,8 @@ class DeleteSimProfilePreferenceController(context: Context, preferenceKey: Stri
|
||||
}
|
||||
|
||||
private fun deleteSim() {
|
||||
SubscriptionUtil.startDeleteEuiccSubscriptionDialogActivity(mContext, subscriptionId)
|
||||
SubscriptionUtil.startDeleteEuiccSubscriptionDialogActivity(mContext, subscriptionId,
|
||||
carrierId)
|
||||
// result handled in MobileNetworkSettings
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import androidx.annotation.Nullable;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.internal.telephony.flags.Flags;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.network.CarrierConfigCache;
|
||||
@@ -58,6 +59,11 @@ public class SatelliteSettingPreferenceController extends
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus(int subId) {
|
||||
if (!Flags.carrierEnabledSatelliteFlag()) {
|
||||
logd("getAvailabilityStatus() : carrierEnabledSatelliteFlag is disabled");
|
||||
return UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
|
||||
final PersistableBundle carrierConfig = mCarrierConfigCache.getConfigForSubId(subId);
|
||||
final boolean isSatelliteAttachSupported = carrierConfig.getBoolean(
|
||||
CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL);
|
||||
|
||||
@@ -37,6 +37,7 @@ import com.android.settings.media.MediaOutputIndicatorWorker;
|
||||
import com.android.settings.slices.CustomSliceRegistry;
|
||||
import com.android.settings.slices.SliceBackgroundWorker;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.flags.Flags;
|
||||
import com.android.settingslib.media.BluetoothMediaDevice;
|
||||
import com.android.settingslib.media.MediaDevice;
|
||||
import com.android.settingslib.media.MediaOutputConstants;
|
||||
@@ -94,7 +95,9 @@ public class MediaVolumePreferenceController extends VolumeSeekBarPreferenceCont
|
||||
|
||||
@VisibleForTesting
|
||||
boolean isSupportEndItem() {
|
||||
return getWorker() != null && getWorker().isBroadcastSupported()
|
||||
return Flags.legacyLeAudioSharing()
|
||||
&& getWorker() != null
|
||||
&& getWorker().isBroadcastSupported()
|
||||
&& (getWorker().isDeviceBroadcasting() || isConnectedBLEDevice());
|
||||
}
|
||||
|
||||
@@ -114,7 +117,8 @@ public class MediaVolumePreferenceController extends VolumeSeekBarPreferenceCont
|
||||
if (mPreference != null) {
|
||||
if (mPreference.isMuted()) {
|
||||
mPreference.updateContentDescription(
|
||||
mContext.getString(R.string.volume_content_description_silent_mode,
|
||||
mContext.getString(
|
||||
R.string.volume_content_description_silent_mode,
|
||||
mPreference.getTitle()));
|
||||
} else {
|
||||
mPreference.updateContentDescription(mPreference.getTitle());
|
||||
@@ -134,10 +138,15 @@ public class MediaVolumePreferenceController extends VolumeSeekBarPreferenceCont
|
||||
if (getWorker().isDeviceBroadcasting()) {
|
||||
intent.setPackage(MediaOutputConstants.SYSTEMUI_PACKAGE_NAME);
|
||||
intent.setAction(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG);
|
||||
intent.putExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME,
|
||||
intent.putExtra(
|
||||
MediaOutputConstants.EXTRA_PACKAGE_NAME,
|
||||
getWorker().getActiveLocalMediaController().getPackageName());
|
||||
|
||||
pi = PendingIntent.getBroadcast(context, 0 /* requestCode */, intent,
|
||||
pi =
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
0 /* requestCode */,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
} else {
|
||||
final CachedBluetoothDevice bluetoothDevice =
|
||||
@@ -147,14 +156,20 @@ public class MediaVolumePreferenceController extends VolumeSeekBarPreferenceCont
|
||||
return null;
|
||||
}
|
||||
intent.setAction(ACTION_LAUNCH_BROADCAST_DIALOG);
|
||||
intent.putExtra(BluetoothBroadcastDialog.KEY_APP_LABEL,
|
||||
intent.putExtra(
|
||||
BluetoothBroadcastDialog.KEY_APP_LABEL,
|
||||
Utils.getApplicationLabel(mContext, getWorker().getPackageName()));
|
||||
intent.putExtra(BluetoothBroadcastDialog.KEY_DEVICE_ADDRESS,
|
||||
bluetoothDevice.getAddress());
|
||||
intent.putExtra(BluetoothBroadcastDialog.KEY_MEDIA_STREAMING, getWorker() != null
|
||||
&& getWorker().getActiveLocalMediaController() != null);
|
||||
intent.putExtra(
|
||||
BluetoothBroadcastDialog.KEY_DEVICE_ADDRESS, bluetoothDevice.getAddress());
|
||||
intent.putExtra(
|
||||
BluetoothBroadcastDialog.KEY_MEDIA_STREAMING,
|
||||
getWorker() != null && getWorker().getActiveLocalMediaController() != null);
|
||||
|
||||
pi = PendingIntent.getActivity(context, 0 /* requestCode */, intent,
|
||||
pi =
|
||||
PendingIntent.getActivity(
|
||||
context,
|
||||
0 /* requestCode */,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
}
|
||||
|
||||
@@ -164,8 +179,8 @@ public class MediaVolumePreferenceController extends VolumeSeekBarPreferenceCont
|
||||
}
|
||||
|
||||
private IconCompat getBroadcastIcon(Context context) {
|
||||
final Drawable drawable = context.getDrawable(
|
||||
com.android.settingslib.R.drawable.settings_input_antenna);
|
||||
final Drawable drawable =
|
||||
context.getDrawable(com.android.settingslib.R.drawable.settings_input_antenna);
|
||||
if (drawable != null) {
|
||||
drawable.setTint(Utils.getColorAccentDefaultColor(context));
|
||||
return Utils.createIconWithDrawable(drawable);
|
||||
|
||||
@@ -136,9 +136,7 @@ public class SimDialogActivity extends FragmentActivity {
|
||||
}
|
||||
|
||||
if (Flags.isDualSimOnboardingEnabled()
|
||||
&& getProgressState() == SubscriptionActionDialogActivity.PROGRESS_IS_SHOWING
|
||||
&& (dialogType == PREFERRED_PICK
|
||||
|| dialogType == DATA_PICK
|
||||
&& (dialogType == DATA_PICK
|
||||
|| dialogType == CALLS_PICK
|
||||
|| dialogType == SMS_PICK)) {
|
||||
Log.d(TAG, "Finish the sim dialog since the sim onboarding is shown");
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.NavType
|
||||
@@ -33,6 +34,7 @@ import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navArgument
|
||||
import com.android.settings.R
|
||||
import com.android.settings.network.SimOnboardingActivity
|
||||
import com.android.settings.network.SimOnboardingActivity.Companion.CallbackType
|
||||
import com.android.settings.network.SimOnboardingService
|
||||
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
|
||||
import com.android.settingslib.spa.framework.common.SettingsPageProvider
|
||||
@@ -92,7 +94,7 @@ fun PageImpl(onboardingService:SimOnboardingService,navHostController: NavHostCo
|
||||
val context = LocalContext.current
|
||||
var finishOnboarding: () -> Unit = {
|
||||
context.getActivity()?.finish()
|
||||
onboardingService.callback(SimOnboardingActivity.CALLBACK_FINISH)
|
||||
onboardingService.callback(CallbackType.CALLBACK_FINISH)
|
||||
}
|
||||
|
||||
NavHost(
|
||||
@@ -101,10 +103,13 @@ fun PageImpl(onboardingService:SimOnboardingService,navHostController: NavHostCo
|
||||
) {
|
||||
composable(route = SimOnboardingScreen.LabelSim.name) {
|
||||
val nextPage =
|
||||
if (onboardingService.isMultipleEnabledProfilesSupported && onboardingService.isAllOfSlotAssigned) {
|
||||
if (onboardingService.isMultipleEnabledProfilesSupported
|
||||
&& onboardingService.isAllOfSlotAssigned) {
|
||||
SimOnboardingScreen.SelectSim.name
|
||||
} else {
|
||||
LaunchedEffect(Unit) {
|
||||
onboardingService.addCurrentItemForSelectedSim()
|
||||
}
|
||||
SimOnboardingScreen.PrimarySim.name
|
||||
}
|
||||
SimOnboardingLabelSimImpl(
|
||||
@@ -116,7 +121,7 @@ fun PageImpl(onboardingService:SimOnboardingService,navHostController: NavHostCo
|
||||
composable(route = SimOnboardingScreen.PrimarySim.name) {
|
||||
SimOnboardingPrimarySimImpl(
|
||||
nextAction = {
|
||||
onboardingService.callback(SimOnboardingActivity.CALLBACK_ONBOARDING_COMPLETE)
|
||||
onboardingService.callback(CallbackType.CALLBACK_ONBOARDING_COMPLETE)
|
||||
context.getActivity()?.finish()
|
||||
},
|
||||
cancelAction = finishOnboarding,
|
||||
|
||||
@@ -77,7 +77,8 @@ fun SimOnboardingPrimarySimImpl(
|
||||
SettingsBody(stringResource(id = R.string.sim_onboarding_primary_sim_msg))
|
||||
}
|
||||
|
||||
var selectedSubscriptionInfoList = onboardingService.getSelectedSubscriptionInfoList()
|
||||
var selectedSubscriptionInfoList =
|
||||
onboardingService.getSelectedSubscriptionInfoListWithRenaming()
|
||||
callsSelectedId.intValue = onboardingService.targetPrimarySimCalls
|
||||
textsSelectedId.intValue = onboardingService.targetPrimarySimTexts
|
||||
mobileDataSelectedId.intValue = onboardingService.targetPrimarySimMobileData
|
||||
|
||||
@@ -18,24 +18,33 @@ package com.android.settings.wifi
|
||||
|
||||
import android.content.Context
|
||||
import android.net.wifi.WifiManager
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import com.android.settings.R
|
||||
import com.android.settings.spa.preference.ComposePreferenceController
|
||||
import com.android.settingslib.spa.framework.compose.OverridableFlow
|
||||
import com.android.settingslib.spa.widget.dialog.AlertDialogButton
|
||||
import com.android.settingslib.spa.widget.dialog.SettingsAlertDialogWithIcon
|
||||
import com.android.settingslib.spa.widget.preference.SwitchPreference
|
||||
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
|
||||
import com.android.wifi.flags.Flags
|
||||
import com.android.wifitrackerlib.WifiEntry
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.asExecutor
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
|
||||
/** Controller that controls whether the Wi-Fi Wakeup feature should be enabled. */
|
||||
/** Controller that controls whether the WEP network can be connected. */
|
||||
class WepNetworksPreferenceController(context: Context, preferenceKey: String) :
|
||||
ComposePreferenceController(context, preferenceKey) {
|
||||
|
||||
@@ -47,6 +56,8 @@ class WepNetworksPreferenceController(context: Context, preferenceKey: String) :
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val checked by wepAllowedFlow.flow.collectAsStateWithLifecycle(initialValue = null)
|
||||
var openDialog by rememberSaveable { mutableStateOf(false) }
|
||||
val wifiInfo = wifiManager.connectionInfo
|
||||
SwitchPreference(object : SwitchPreferenceModel {
|
||||
override val title = stringResource(R.string.wifi_allow_wep_networks)
|
||||
override val summary = { getSummary() }
|
||||
@@ -54,10 +65,40 @@ class WepNetworksPreferenceController(context: Context, preferenceKey: String) :
|
||||
override val changeable: () -> Boolean
|
||||
get() = { carrierAllowed }
|
||||
override val onCheckedChange: (Boolean) -> Unit = { newChecked ->
|
||||
if (!newChecked && wifiInfo.currentSecurityType == WifiEntry.SECURITY_WEP) {
|
||||
openDialog = true
|
||||
} else {
|
||||
wifiManager.setWepAllowed(newChecked)
|
||||
wepAllowedFlow.override(newChecked)
|
||||
}
|
||||
}
|
||||
})
|
||||
if (openDialog) {
|
||||
SettingsAlertDialogWithIcon(
|
||||
onDismissRequest = { openDialog = false },
|
||||
confirmButton = AlertDialogButton(
|
||||
stringResource(R.string.wifi_disconnect_button_text)
|
||||
) {
|
||||
wifiManager.setWepAllowed(false)
|
||||
wepAllowedFlow.override(false)
|
||||
openDialog = false
|
||||
},
|
||||
dismissButton =
|
||||
AlertDialogButton(
|
||||
stringResource(R.string.wifi_cancel)
|
||||
) { openDialog = false },
|
||||
title = String.format(
|
||||
stringResource(R.string.wifi_settings_wep_networks_disconnect_title),
|
||||
wifiInfo.ssid.removeSurrounding("\"")
|
||||
),
|
||||
text = {
|
||||
Text(
|
||||
stringResource(R.string.wifi_settings_wep_networks_disconnect_summary),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSummary(): String = mContext.getString(
|
||||
|
||||
@@ -22,6 +22,7 @@ import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.UiModeManager;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
@@ -59,7 +60,8 @@ public class ColorContrastFragmentTest {
|
||||
|
||||
@Test
|
||||
public void getMetricsCategory_returnsCorrectCategory() {
|
||||
assertThat(mFragment.getMetricsCategory()).isEqualTo(0);
|
||||
assertThat(mFragment.getMetricsCategory()).isEqualTo(
|
||||
SettingsEnums.ACCESSIBILITY_COLOR_CONTRAST);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.settings.applications.appinfo;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -25,10 +26,12 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.applications.ProcStatsData;
|
||||
@@ -37,14 +40,14 @@ import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settings.testutils.shadow.ShadowUserManager;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
@@ -54,6 +57,8 @@ import org.robolectric.util.ReflectionHelpers;
|
||||
com.android.settings.testutils.shadow.ShadowFragment.class,
|
||||
})
|
||||
public class AppMemoryPreferenceControllerTest {
|
||||
@Rule
|
||||
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||
|
||||
@Mock
|
||||
private SettingsActivity mActivity;
|
||||
@@ -69,9 +74,11 @@ public class AppMemoryPreferenceControllerTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
ShadowUserManager.getShadow().setIsAdminUser(true);
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
UserManager userManager = mock(UserManager.class);
|
||||
when(userManager.isAdminUser()).thenReturn(true);
|
||||
doReturn(userManager).when(mContext).getSystemService(Context.USER_SERVICE);
|
||||
|
||||
mController =
|
||||
spy(new AppMemoryPreferenceController(mContext, mFragment, null /* lifecycle */));
|
||||
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
|
||||
@@ -80,7 +87,6 @@ public class AppMemoryPreferenceControllerTest {
|
||||
when(mFragment.getActivity()).thenReturn(mActivity);
|
||||
}
|
||||
|
||||
@Ignore("b/313582035")
|
||||
@Test
|
||||
@Config(qualifiers = "mcc999")
|
||||
public void getAvailabilityStatus_developmentSettingsEnabled_shouldReturnAvailable() {
|
||||
|
||||
@@ -29,7 +29,6 @@ import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -223,28 +222,6 @@ public class SettingsHomepageActivityTest {
|
||||
& SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onCreate_notTaskRoot_shouldFinishActivity() {
|
||||
SettingsHomepageActivity activity =
|
||||
spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get());
|
||||
doReturn(false).when(activity).isTaskRoot();
|
||||
|
||||
activity.onCreate(/* savedInstanceState */ null);
|
||||
|
||||
verify(activity).finish();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onCreate_singleTaskActivity_shouldNotFinishActivity() {
|
||||
SettingsHomepageActivity activity =
|
||||
spy(Robolectric.buildActivity(DeepLinkHomepageActivity.class).get());
|
||||
doReturn(false).when(activity).isTaskRoot();
|
||||
|
||||
activity.onCreate(/* savedInstanceState */ null);
|
||||
|
||||
verify(activity, never()).finish();
|
||||
}
|
||||
|
||||
/** This test is for large screen devices Activity embedding. */
|
||||
@Test
|
||||
@Config(shadows = ShadowActivityEmbeddingUtils.class)
|
||||
|
||||
@@ -31,17 +31,23 @@ import android.content.Intent;
|
||||
import android.media.AudioManager;
|
||||
import android.media.session.MediaController;
|
||||
import android.net.Uri;
|
||||
import android.platform.test.annotations.RequiresFlagsDisabled;
|
||||
import android.platform.test.annotations.RequiresFlagsEnabled;
|
||||
import android.platform.test.flag.junit.CheckFlagsRule;
|
||||
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
|
||||
|
||||
import androidx.slice.builders.SliceAction;
|
||||
|
||||
import com.android.settings.media.MediaOutputIndicatorWorker;
|
||||
import com.android.settings.slices.SliceBackgroundWorker;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.flags.Flags;
|
||||
import com.android.settingslib.media.BluetoothMediaDevice;
|
||||
import com.android.settingslib.media.MediaDevice;
|
||||
import com.android.settingslib.media.MediaOutputConstants;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
@@ -60,16 +66,16 @@ public class MediaVolumePreferenceControllerTest {
|
||||
"android.settings.MEDIA_BROADCAST_DIALOG";
|
||||
private static MediaOutputIndicatorWorker sMediaOutputIndicatorWorker;
|
||||
|
||||
@Rule
|
||||
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
|
||||
|
||||
private MediaVolumePreferenceController mController;
|
||||
|
||||
private Context mContext;
|
||||
|
||||
@Mock
|
||||
private MediaController mMediaController;
|
||||
@Mock
|
||||
private MediaDevice mDevice1;
|
||||
@Mock
|
||||
private MediaDevice mDevice2;
|
||||
@Mock private MediaController mMediaController;
|
||||
@Mock private MediaDevice mDevice1;
|
||||
@Mock private MediaDevice mDevice2;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
@@ -77,8 +83,8 @@ public class MediaVolumePreferenceControllerTest {
|
||||
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mController = new MediaVolumePreferenceController(mContext);
|
||||
sMediaOutputIndicatorWorker = spy(
|
||||
new MediaOutputIndicatorWorker(mContext, VOLUME_MEDIA_URI));
|
||||
sMediaOutputIndicatorWorker =
|
||||
spy(new MediaOutputIndicatorWorker(mContext, VOLUME_MEDIA_URI));
|
||||
when(mDevice1.isBLEDevice()).thenReturn(true);
|
||||
when(mDevice2.isBLEDevice()).thenReturn(false);
|
||||
}
|
||||
@@ -101,8 +107,8 @@ public class MediaVolumePreferenceControllerTest {
|
||||
|
||||
@Test
|
||||
public void isSliceableCorrectKey_returnsTrue() {
|
||||
final MediaVolumePreferenceController controller = new MediaVolumePreferenceController(
|
||||
mContext);
|
||||
final MediaVolumePreferenceController controller =
|
||||
new MediaVolumePreferenceController(mContext);
|
||||
assertThat(controller.isSliceable()).isTrue();
|
||||
}
|
||||
|
||||
@@ -112,6 +118,17 @@ public class MediaVolumePreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsDisabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
|
||||
public void isSupportEndItem_flagOff_returnsFalse() {
|
||||
doReturn(true).when(sMediaOutputIndicatorWorker).isBroadcastSupported();
|
||||
doReturn(false).when(sMediaOutputIndicatorWorker).isDeviceBroadcasting();
|
||||
doReturn(mDevice1).when(sMediaOutputIndicatorWorker).getCurrentConnectedMediaDevice();
|
||||
|
||||
assertThat(mController.isSupportEndItem()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
|
||||
public void isSupportEndItem_withBleDevice_returnsTrue() {
|
||||
doReturn(true).when(sMediaOutputIndicatorWorker).isBroadcastSupported();
|
||||
doReturn(false).when(sMediaOutputIndicatorWorker).isDeviceBroadcasting();
|
||||
@@ -121,6 +138,7 @@ public class MediaVolumePreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
|
||||
public void isSupportEndItem_notSupportedBroadcast_returnsFalse() {
|
||||
doReturn(false).when(sMediaOutputIndicatorWorker).isBroadcastSupported();
|
||||
doReturn(mDevice1).when(sMediaOutputIndicatorWorker).getCurrentConnectedMediaDevice();
|
||||
@@ -129,6 +147,7 @@ public class MediaVolumePreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
|
||||
public void isSupportEndItem_withNonBleDevice_returnsFalse() {
|
||||
doReturn(true).when(sMediaOutputIndicatorWorker).isBroadcastSupported();
|
||||
doReturn(false).when(sMediaOutputIndicatorWorker).isDeviceBroadcasting();
|
||||
@@ -138,6 +157,7 @@ public class MediaVolumePreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
|
||||
public void isSupportEndItem_deviceIsBroadcastingAndConnectedToNonBleDevice_returnsTrue() {
|
||||
doReturn(true).when(sMediaOutputIndicatorWorker).isBroadcastSupported();
|
||||
doReturn(true).when(sMediaOutputIndicatorWorker).isDeviceBroadcasting();
|
||||
@@ -147,6 +167,7 @@ public class MediaVolumePreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
|
||||
public void isSupportEndItem_deviceIsNotBroadcastingAndConnectedToNonBleDevice_returnsFalse() {
|
||||
doReturn(true).when(sMediaOutputIndicatorWorker).isBroadcastSupported();
|
||||
doReturn(false).when(sMediaOutputIndicatorWorker).isDeviceBroadcasting();
|
||||
@@ -155,8 +176,20 @@ public class MediaVolumePreferenceControllerTest {
|
||||
assertThat(mController.isSupportEndItem()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsDisabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
|
||||
public void getSliceEndItem_flagOff_getsNullSliceAction() {
|
||||
doReturn(true).when(sMediaOutputIndicatorWorker).isBroadcastSupported();
|
||||
doReturn(true).when(sMediaOutputIndicatorWorker).isDeviceBroadcasting();
|
||||
doReturn(mDevice2).when(sMediaOutputIndicatorWorker).getCurrentConnectedMediaDevice();
|
||||
|
||||
final SliceAction sliceAction = mController.getSliceEndItem(mContext);
|
||||
|
||||
assertThat(sliceAction).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
|
||||
public void getSliceEndItem_NotSupportEndItem_getsNullSliceAction() {
|
||||
doReturn(true).when(sMediaOutputIndicatorWorker).isBroadcastSupported();
|
||||
doReturn(false).when(sMediaOutputIndicatorWorker).isDeviceBroadcasting();
|
||||
@@ -168,22 +201,26 @@ public class MediaVolumePreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
|
||||
public void getSliceEndItem_deviceIsBroadcasting_getsBroadcastIntent() {
|
||||
doReturn(true).when(sMediaOutputIndicatorWorker).isBroadcastSupported();
|
||||
doReturn(mDevice1).when(sMediaOutputIndicatorWorker).getCurrentConnectedMediaDevice();
|
||||
doReturn(true).when(sMediaOutputIndicatorWorker).isDeviceBroadcasting();
|
||||
doReturn(mMediaController).when(sMediaOutputIndicatorWorker)
|
||||
doReturn(mMediaController)
|
||||
.when(sMediaOutputIndicatorWorker)
|
||||
.getActiveLocalMediaController();
|
||||
|
||||
final SliceAction sliceAction = mController.getSliceEndItem(mContext);
|
||||
|
||||
final PendingIntent endItemPendingIntent = sliceAction.getAction();
|
||||
final PendingIntent expectedToggleIntent = getBroadcastIntent(
|
||||
final PendingIntent expectedToggleIntent =
|
||||
getBroadcastIntent(
|
||||
MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG);
|
||||
assertThat(endItemPendingIntent).isEqualTo(expectedToggleIntent);
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
|
||||
public void getSliceEndItem_deviceIsNotBroadcasting_getsActivityIntent() {
|
||||
final MediaDevice device = mock(BluetoothMediaDevice.class);
|
||||
final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
|
||||
@@ -192,7 +229,8 @@ public class MediaVolumePreferenceControllerTest {
|
||||
doReturn(true).when(sMediaOutputIndicatorWorker).isBroadcastSupported();
|
||||
doReturn(device).when(sMediaOutputIndicatorWorker).getCurrentConnectedMediaDevice();
|
||||
doReturn(false).when(sMediaOutputIndicatorWorker).isDeviceBroadcasting();
|
||||
doReturn(mMediaController).when(sMediaOutputIndicatorWorker)
|
||||
doReturn(mMediaController)
|
||||
.when(sMediaOutputIndicatorWorker)
|
||||
.getActiveLocalMediaController();
|
||||
|
||||
final SliceAction sliceAction = mController.getSliceEndItem(mContext);
|
||||
@@ -215,13 +253,19 @@ public class MediaVolumePreferenceControllerTest {
|
||||
private PendingIntent getBroadcastIntent(String action) {
|
||||
final Intent intent = new Intent(action);
|
||||
intent.setPackage(MediaOutputConstants.SYSTEMUI_PACKAGE_NAME);
|
||||
return PendingIntent.getBroadcast(mContext, 0 /* requestCode */, intent,
|
||||
return PendingIntent.getBroadcast(
|
||||
mContext,
|
||||
0 /* requestCode */,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
}
|
||||
|
||||
private PendingIntent getActivityIntent(String action) {
|
||||
final Intent intent = new Intent(action);
|
||||
return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent,
|
||||
return PendingIntent.getActivity(
|
||||
mContext,
|
||||
0 /* requestCode */,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
|
||||
import com.android.settings.testutils.shadow.ShadowUserManager;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.Robolectric;
|
||||
@@ -44,7 +43,6 @@ import org.robolectric.annotation.Config;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Ignore("b/315133235")
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {ShadowUserManager.class, ShadowDevicePolicyManager.class})
|
||||
public class RestrictedButtonTest {
|
||||
|
||||
@@ -35,18 +35,19 @@ import android.widget.ImageView;
|
||||
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.testutils.shadow.ShadowSettingsMediaPlayer;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadows.androidx.fragment.FragmentController;
|
||||
|
||||
@@ -55,7 +56,8 @@ import org.robolectric.shadows.androidx.fragment.FragmentController;
|
||||
public class VideoPreferenceTest {
|
||||
private static final int VIDEO_WIDTH = 100;
|
||||
private static final int VIDEO_HEIGHT = 150;
|
||||
|
||||
@Rule
|
||||
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||
private VideoPreference.AnimationController mAnimationController;
|
||||
@Mock
|
||||
private ImageView fakePreview;
|
||||
@@ -68,9 +70,7 @@ public class VideoPreferenceTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
mAnimationController = spy(
|
||||
new MediaAnimationController(mContext, R.raw.sample_video));
|
||||
mVideoPreference = new VideoPreference(mContext, null /* attrs */);
|
||||
@@ -141,7 +141,6 @@ public class VideoPreferenceTest {
|
||||
assertThat(mAnimationController.isPlaying()).isTrue();
|
||||
}
|
||||
|
||||
@Ignore("b/315133235")
|
||||
@Test
|
||||
@Config(qualifiers = "mcc999")
|
||||
public void onViewVisible_createAnimationController() {
|
||||
|
||||
@@ -20,6 +20,8 @@ import android.content.Context
|
||||
import android.net.wifi.WifiManager
|
||||
import androidx.compose.ui.test.assertIsOff
|
||||
import androidx.compose.ui.test.assertIsOn
|
||||
import androidx.compose.ui.test.isDisplayed
|
||||
import androidx.compose.ui.test.isNotDisplayed
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.onRoot
|
||||
@@ -28,12 +30,16 @@ import androidx.preference.PreferenceManager
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settings.R
|
||||
import com.android.settings.dashboard.DashboardFragment
|
||||
import com.android.settings.spa.preference.ComposePreference
|
||||
import com.android.settingslib.spa.testutils.onDialogText
|
||||
import com.android.wifitrackerlib.WifiEntry
|
||||
import java.util.function.Consumer
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.doAnswer
|
||||
import org.mockito.kotlin.doReturn
|
||||
@@ -48,25 +54,30 @@ class WepNetworksPreferenceControllerTest {
|
||||
|
||||
private var wepAllowed = true
|
||||
|
||||
private val mockWifiManager = mock<WifiManager> {
|
||||
private var mockWifiInfo = mock<android.net.wifi.WifiInfo> {
|
||||
on { it.currentSecurityType } doReturn WifiEntry.SECURITY_EAP
|
||||
on { it.ssid } doReturn SSID
|
||||
}
|
||||
|
||||
private var mockWifiManager = mock<WifiManager> {
|
||||
on { queryWepAllowed(any(), any()) } doAnswer {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val consumer = it.arguments[1] as Consumer<Boolean>
|
||||
consumer.accept(wepAllowed)
|
||||
}
|
||||
on { it.isWepSupported } doReturn true
|
||||
on { it.connectionInfo } doReturn mockWifiInfo
|
||||
}
|
||||
|
||||
private var context: Context =
|
||||
spy(ApplicationProvider.getApplicationContext()) {
|
||||
on { getSystemService(WifiManager::class.java) } doReturn mockWifiManager
|
||||
}
|
||||
private var controller = WepNetworksPreferenceController(context, TEST_KEY)
|
||||
|
||||
private var controller = WepNetworksPreferenceController(context, TEST_KEY)
|
||||
private val preference = ComposePreference(context).apply { key = TEST_KEY }
|
||||
private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context)
|
||||
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
preferenceScreen.addPreference(preference)
|
||||
@@ -79,6 +90,7 @@ class WepNetworksPreferenceControllerTest {
|
||||
composeTestRule.setContent {
|
||||
controller.Content()
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.wifi_allow_wep_networks))
|
||||
.assertIsOn()
|
||||
}
|
||||
@@ -89,6 +101,7 @@ class WepNetworksPreferenceControllerTest {
|
||||
composeTestRule.setContent {
|
||||
controller.Content()
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.wifi_allow_wep_networks))
|
||||
.assertIsOff()
|
||||
}
|
||||
@@ -101,7 +114,6 @@ class WepNetworksPreferenceControllerTest {
|
||||
}
|
||||
|
||||
composeTestRule.onRoot().performClick()
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.wifi_allow_wep_networks))
|
||||
.assertIsOn()
|
||||
}
|
||||
@@ -114,12 +126,38 @@ class WepNetworksPreferenceControllerTest {
|
||||
}
|
||||
|
||||
composeTestRule.onRoot().performClick()
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.wifi_allow_wep_networks))
|
||||
.assertIsOff()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenClick_wepAllowed_openDialog() {
|
||||
wepAllowed = true
|
||||
Mockito.`when`(mockWifiInfo.currentSecurityType).thenReturn(WifiEntry.SECURITY_WEP)
|
||||
composeTestRule.setContent {
|
||||
controller.Content()
|
||||
}
|
||||
|
||||
composeTestRule.onRoot().performClick()
|
||||
composeTestRule.onDialogText(context.getString(R.string.wifi_disconnect_button_text))
|
||||
.isDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenClick_wepDisallowed_openDialog() {
|
||||
wepAllowed = false
|
||||
Mockito.`when`(mockWifiInfo.currentSecurityType).thenReturn(WifiEntry.SECURITY_WEP)
|
||||
composeTestRule.setContent {
|
||||
controller.Content()
|
||||
}
|
||||
|
||||
composeTestRule.onRoot().performClick()
|
||||
composeTestRule.onDialogText(context.getString(R.string.wifi_disconnect_button_text))
|
||||
.isNotDisplayed()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val TEST_KEY = "test_key"
|
||||
const val SSID = "ssid"
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ android_test {
|
||||
"androidx.test.rules",
|
||||
"androidx.test.ext.junit",
|
||||
"androidx.preference_preference",
|
||||
"flag-junit",
|
||||
"mockito-target-minus-junit4",
|
||||
"platform-test-annotations",
|
||||
"platform-test-rules",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
include platform/packages/modules/Connectivity:/thread/OWNERS
|
||||
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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.threadnetwork
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.thread.ThreadNetworkController.STATE_DISABLED
|
||||
import android.net.thread.ThreadNetworkController.STATE_DISABLING
|
||||
import android.net.thread.ThreadNetworkController.STATE_ENABLED
|
||||
import android.net.thread.ThreadNetworkController.StateCallback
|
||||
import android.net.thread.ThreadNetworkException
|
||||
import android.os.OutcomeReceiver
|
||||
import android.platform.test.flag.junit.SetFlagsRule
|
||||
import android.provider.Settings
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.preference.SwitchPreference
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.net.thread.platform.flags.Flags
|
||||
import com.android.settings.R
|
||||
import com.android.settings.core.BasePreferenceController.AVAILABLE
|
||||
import com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE
|
||||
import com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING
|
||||
import com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE
|
||||
import com.android.settings.connecteddevice.threadnetwork.ThreadNetworkPreferenceController.BaseThreadNetworkController
|
||||
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.ArgumentCaptor
|
||||
import org.mockito.ArgumentMatchers.any
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.spy
|
||||
import org.mockito.Mockito.verify
|
||||
import java.util.concurrent.Executor
|
||||
|
||||
/** Unit tests for [ThreadNetworkPreferenceController]. */
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ThreadNetworkPreferenceControllerTest {
|
||||
@get:Rule
|
||||
val mSetFlagsRule = SetFlagsRule()
|
||||
private lateinit var context: Context
|
||||
private lateinit var executor: Executor
|
||||
private lateinit var controller: ThreadNetworkPreferenceController
|
||||
private lateinit var fakeThreadNetworkController: FakeThreadNetworkController
|
||||
private lateinit var preference: SwitchPreference
|
||||
private val broadcastReceiverArgumentCaptor = ArgumentCaptor.forClass(
|
||||
BroadcastReceiver::class.java
|
||||
)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_THREAD_ENABLED_PLATFORM)
|
||||
context = spy(ApplicationProvider.getApplicationContext<Context>())
|
||||
executor = ContextCompat.getMainExecutor(context)
|
||||
fakeThreadNetworkController = FakeThreadNetworkController(executor)
|
||||
controller = newControllerWithThreadFeatureSupported(true)
|
||||
val preferenceManager = PreferenceManager(context)
|
||||
val preferenceScreen = preferenceManager.createPreferenceScreen(context)
|
||||
preference = SwitchPreference(context)
|
||||
preference.key = "thread_network_settings"
|
||||
preferenceScreen.addPreference(preference)
|
||||
controller.displayPreference(preferenceScreen)
|
||||
|
||||
Settings.Global.putInt(context.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0)
|
||||
}
|
||||
|
||||
private fun newControllerWithThreadFeatureSupported(
|
||||
present: Boolean
|
||||
): ThreadNetworkPreferenceController {
|
||||
return ThreadNetworkPreferenceController(
|
||||
context,
|
||||
"thread_network_settings" /* key */,
|
||||
executor,
|
||||
if (present) fakeThreadNetworkController else null
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun availabilityStatus_flagDisabled_returnsConditionallyUnavailable() {
|
||||
mSetFlagsRule.disableFlags(Flags.FLAG_THREAD_ENABLED_PLATFORM)
|
||||
assertThat(controller.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun availabilityStatus_airPlaneModeOn_returnsDisabledDependentSetting() {
|
||||
Settings.Global.putInt(context.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 1)
|
||||
controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
|
||||
|
||||
assertThat(controller.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun availabilityStatus_airPlaneModeOff_returnsAvailable() {
|
||||
Settings.Global.putInt(context.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0)
|
||||
controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
|
||||
|
||||
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun availabilityStatus_threadFeatureNotSupported_returnsUnsupported() {
|
||||
controller = newControllerWithThreadFeatureSupported(false)
|
||||
controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
|
||||
|
||||
assertThat(fakeThreadNetworkController.registeredStateCallback).isNull()
|
||||
assertThat(controller.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isChecked_threadSetEnabled_returnsTrue() {
|
||||
fakeThreadNetworkController.setEnabled(true, executor) { }
|
||||
controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
|
||||
|
||||
assertThat(controller.isChecked).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isChecked_threadSetDisabled_returnsFalse() {
|
||||
fakeThreadNetworkController.setEnabled(false, executor) { }
|
||||
controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
|
||||
|
||||
assertThat(controller.isChecked).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setChecked_setChecked_threadIsEnabled() {
|
||||
controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
|
||||
|
||||
controller.setChecked(true)
|
||||
|
||||
assertThat(fakeThreadNetworkController.isEnabled).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setChecked_setUnchecked_threadIsDisabled() {
|
||||
controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
|
||||
|
||||
controller.setChecked(false)
|
||||
|
||||
assertThat(fakeThreadNetworkController.isEnabled).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updatePreference_airPlaneModeOff_preferenceEnabled() {
|
||||
Settings.Global.putInt(context.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0)
|
||||
controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
|
||||
|
||||
assertThat(preference.isEnabled).isTrue()
|
||||
assertThat(preference.summary).isEqualTo(
|
||||
context.resources.getString(R.string.thread_network_settings_summary)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updatePreference_airPlaneModeOn_preferenceDisabled() {
|
||||
Settings.Global.putInt(context.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 1)
|
||||
controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
|
||||
|
||||
assertThat(preference.isEnabled).isFalse()
|
||||
assertThat(preference.summary).isEqualTo(
|
||||
context.resources.getString(R.string.thread_network_settings_summary_airplane_mode)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updatePreference_airPlaneModeTurnedOn_preferenceDisabled() {
|
||||
Settings.Global.putInt(context.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0)
|
||||
startControllerAndCaptureCallbacks()
|
||||
|
||||
Settings.Global.putInt(context.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 1)
|
||||
broadcastReceiverArgumentCaptor.value.onReceive(context, Intent())
|
||||
|
||||
assertThat(preference.isEnabled).isFalse()
|
||||
assertThat(preference.summary).isEqualTo(
|
||||
context.resources.getString(R.string.thread_network_settings_summary_airplane_mode)
|
||||
)
|
||||
}
|
||||
|
||||
private fun startControllerAndCaptureCallbacks() {
|
||||
controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
|
||||
verify(context)!!.registerReceiver(broadcastReceiverArgumentCaptor.capture(), any())
|
||||
}
|
||||
|
||||
private class FakeThreadNetworkController(private val executor: Executor) :
|
||||
BaseThreadNetworkController {
|
||||
var isEnabled = true
|
||||
private set
|
||||
var registeredStateCallback: StateCallback? = null
|
||||
private set
|
||||
|
||||
override fun setEnabled(
|
||||
enabled: Boolean,
|
||||
executor: Executor,
|
||||
receiver: OutcomeReceiver<Void?, ThreadNetworkException>
|
||||
) {
|
||||
isEnabled = enabled
|
||||
if (registeredStateCallback != null) {
|
||||
if (!isEnabled) {
|
||||
executor.execute {
|
||||
registeredStateCallback!!.onThreadEnableStateChanged(
|
||||
STATE_DISABLING
|
||||
)
|
||||
}
|
||||
executor.execute {
|
||||
registeredStateCallback!!.onThreadEnableStateChanged(
|
||||
STATE_DISABLED
|
||||
)
|
||||
}
|
||||
} else {
|
||||
executor.execute {
|
||||
registeredStateCallback!!.onThreadEnableStateChanged(
|
||||
STATE_ENABLED
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
executor.execute { receiver.onResult(null) }
|
||||
}
|
||||
|
||||
override fun registerStateCallback(
|
||||
executor: Executor,
|
||||
callback: StateCallback
|
||||
) {
|
||||
require(callback !== registeredStateCallback) { "callback is already registered" }
|
||||
registeredStateCallback = callback
|
||||
val enabledState =
|
||||
if (isEnabled) STATE_ENABLED else STATE_DISABLED
|
||||
executor.execute { registeredStateCallback!!.onThreadEnableStateChanged(enabledState) }
|
||||
}
|
||||
|
||||
override fun unregisterStateCallback(callback: StateCallback) {
|
||||
requireNotNull(registeredStateCallback) { "callback is already unregistered" }
|
||||
registeredStateCallback = null
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user