[QSDetailedView] Add the `InternetDetailsContentManager`
Extract the view logic out from the `InternetDialogDelegate` into the
`InternetDetailsContentManager`. This class will later be used by
the non dialog details view.
Also renamed `InternetDetailedViewModel` -> `InternetDetailsViewModel`
to make the file name consistent with the class name.
Bug:b/377388104
Flag: com.android.systemui.qs_tile_detailed_view
Test: InternetDetailsContentManagerTest
Change-Id: I1e4183f7e30f11ecb35c2d48882c58294010f939
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index b88ae37..b358bdc 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -285,6 +285,7 @@
"tests/src/**/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java",
"tests/src/**/systemui/shared/system/RemoteTransitionTest.java",
"tests/src/**/systemui/qs/tiles/dialog/InternetDetailsContentControllerTest.java",
+ "tests/src/**/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt",
"tests/src/**/systemui/qs/external/TileLifecycleManagerTest.java",
"tests/src/**/systemui/ScreenDecorationsTest.java",
"tests/src/**/systemui/statusbar/policy/BatteryControllerStartableTest.java",
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java
index 23210ef..340cb68 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java
@@ -373,7 +373,6 @@
mConnectivityManager.setAirplaneMode(false);
}
- @VisibleForTesting
protected int getDefaultDataSubscriptionId() {
return mSubscriptionManager.getDefaultDataSubscriptionId();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManager.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManager.kt
new file mode 100644
index 0000000..c64532a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManager.kt
@@ -0,0 +1,991 @@
+/*
+ * 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.systemui.qs.tiles.dialog
+
+import android.app.AlertDialog
+import android.content.Context
+import android.content.DialogInterface
+import android.graphics.drawable.Drawable
+import android.net.Network
+import android.net.NetworkCapabilities
+import android.os.Handler
+import android.telephony.ServiceState
+import android.telephony.SignalStrength
+import android.telephony.SubscriptionManager
+import android.telephony.TelephonyDisplayInfo
+import android.text.Html
+import android.text.Layout
+import android.text.TextUtils
+import android.text.method.LinkMovementMethod
+import android.util.Log
+import android.view.View
+import android.view.ViewStub
+import android.view.WindowManager
+import android.widget.Button
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.ProgressBar
+import android.widget.Switch
+import android.widget.TextView
+import androidx.annotation.MainThread
+import androidx.annotation.WorkerThread
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.LifecycleRegistry
+import androidx.lifecycle.MutableLiveData
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.telephony.flags.Flags
+import com.android.settingslib.satellite.SatelliteDialogUtils.TYPE_IS_WIFI
+import com.android.settingslib.satellite.SatelliteDialogUtils.mayStartSatelliteWarningDialog
+import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils
+import com.android.systemui.Prefs
+import com.android.systemui.accessibility.floatingmenu.AnnotationLinkSpan
+import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.flags.QsDetailedView
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.wifitrackerlib.WifiEntry
+import com.google.common.annotations.VisibleForTesting
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.util.concurrent.Executor
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+
+/**
+ * View content for the Internet tile details that handles all UI interactions and state management.
+ *
+ * @param internetDialog non-null if the details should be shown as part of a dialog and null
+ * otherwise.
+ */
+// TODO: b/377388104 Make this content for details view only.
+class InternetDetailsContentManager
+@AssistedInject
+constructor(
+ private val internetDetailsContentController: InternetDetailsContentController,
+ @Assisted(CAN_CONFIG_MOBILE_DATA) private val canConfigMobileData: Boolean,
+ @Assisted(CAN_CONFIG_WIFI) private val canConfigWifi: Boolean,
+ @Assisted private val coroutineScope: CoroutineScope,
+ @Assisted private var context: Context,
+ @Assisted private var internetDialog: SystemUIDialog?,
+ private val uiEventLogger: UiEventLogger,
+ private val dialogTransitionAnimator: DialogTransitionAnimator,
+ @Main private val handler: Handler,
+ @Background private val backgroundExecutor: Executor,
+ private val keyguard: KeyguardStateController,
+) {
+ // Lifecycle
+ private lateinit var lifecycleRegistry: LifecycleRegistry
+ @VisibleForTesting internal var lifecycleOwner: LifecycleOwner? = null
+ @VisibleForTesting internal val internetContentData = MutableLiveData<InternetContent>()
+ @VisibleForTesting internal var connectedWifiEntry: WifiEntry? = null
+ @VisibleForTesting internal var isProgressBarVisible = false
+
+ // UI Components
+ private lateinit var contentView: View
+ private lateinit var internetDialogTitleView: TextView
+ private lateinit var internetDialogSubTitleView: TextView
+ private lateinit var divider: View
+ private lateinit var progressBar: ProgressBar
+ private lateinit var ethernetLayout: LinearLayout
+ private lateinit var mobileNetworkLayout: LinearLayout
+ private var secondaryMobileNetworkLayout: LinearLayout? = null
+ private lateinit var turnWifiOnLayout: LinearLayout
+ private lateinit var wifiToggleTitleTextView: TextView
+ private lateinit var wifiScanNotifyLayout: LinearLayout
+ private lateinit var wifiScanNotifyTextView: TextView
+ private lateinit var connectedWifiListLayout: LinearLayout
+ private lateinit var connectedWifiIcon: ImageView
+ private lateinit var connectedWifiTitleTextView: TextView
+ private lateinit var connectedWifiSummaryTextView: TextView
+ private lateinit var wifiSettingsIcon: ImageView
+ private lateinit var wifiRecyclerView: RecyclerView
+ private lateinit var seeAllLayout: LinearLayout
+ private lateinit var signalIcon: ImageView
+ private lateinit var mobileTitleTextView: TextView
+ private lateinit var mobileSummaryTextView: TextView
+ private lateinit var airplaneModeSummaryTextView: TextView
+ private lateinit var mobileDataToggle: Switch
+ private lateinit var mobileToggleDivider: View
+ private lateinit var wifiToggle: Switch
+ private lateinit var shareWifiButton: Button
+ private lateinit var airplaneModeButton: Button
+ private var alertDialog: AlertDialog? = null
+ private lateinit var doneButton: Button
+
+ private val canChangeWifiState =
+ WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(context)
+ private var wifiNetworkHeight = 0
+ private var backgroundOn: Drawable? = null
+ private var backgroundOff: Drawable? = null
+ private var clickJob: Job? = null
+ private var defaultDataSubId = internetDetailsContentController.defaultDataSubscriptionId
+ @VisibleForTesting
+ internal var adapter = InternetAdapter(internetDetailsContentController, coroutineScope)
+ @VisibleForTesting internal var wifiEntriesCount: Int = 0
+ @VisibleForTesting internal var hasMoreWifiEntries: Boolean = false
+
+ @AssistedFactory
+ interface Factory {
+ fun create(
+ @Assisted(CAN_CONFIG_MOBILE_DATA) canConfigMobileData: Boolean,
+ @Assisted(CAN_CONFIG_WIFI) canConfigWifi: Boolean,
+ coroutineScope: CoroutineScope,
+ context: Context,
+ internetDialog: SystemUIDialog?,
+ ): InternetDetailsContentManager
+ }
+
+ /**
+ * Binds the content manager to the provided content view.
+ *
+ * This method initializes the lifecycle, views, click listeners, and UI of the details content.
+ * It also updates the UI with the current Wi-Fi network information.
+ *
+ * @param contentView The view to which the content manager should be bound.
+ */
+ fun bind(contentView: View) {
+ if (DEBUG) {
+ Log.d(TAG, "Bind InternetDetailsContentManager")
+ }
+
+ this.contentView = contentView
+
+ initializeLifecycle()
+ initializeViews()
+ updateDetailsUI(getStartingInternetContent())
+ initializeAndConfigure()
+ }
+
+ /**
+ * Initializes the LifecycleRegistry if it hasn't been initialized yet. It sets the initial
+ * state of the LifecycleRegistry to Lifecycle.State.CREATED.
+ */
+ fun initializeLifecycle() {
+ if (!::lifecycleRegistry.isInitialized) {
+ lifecycleOwner =
+ object : LifecycleOwner {
+ override val lifecycle: Lifecycle
+ get() = lifecycleRegistry
+ }
+ lifecycleRegistry = LifecycleRegistry(lifecycleOwner!!)
+ }
+ lifecycleRegistry.currentState = Lifecycle.State.CREATED
+ }
+
+ private fun initializeViews() {
+ // Set accessibility properties
+ contentView.accessibilityPaneTitle =
+ context.getText(R.string.accessibility_desc_quick_settings)
+
+ // Get dimension resources
+ wifiNetworkHeight =
+ context.resources.getDimensionPixelSize(R.dimen.internet_dialog_wifi_network_height)
+
+ // Initialize LiveData observer
+ internetContentData.observe(lifecycleOwner!!) { internetContent ->
+ updateDetailsUI(internetContent)
+ }
+
+ // Network layouts
+ internetDialogTitleView = contentView.requireViewById(R.id.internet_dialog_title)
+ internetDialogSubTitleView = contentView.requireViewById(R.id.internet_dialog_subtitle)
+ divider = contentView.requireViewById(R.id.divider)
+ progressBar = contentView.requireViewById(R.id.wifi_searching_progress)
+
+ // Set wifi, mobile and ethernet layouts
+ setWifiLayout()
+ setMobileLayout()
+ ethernetLayout = contentView.requireViewById(R.id.ethernet_layout)
+
+ // Done button is only visible for the dialog view
+ doneButton = contentView.requireViewById(R.id.done_button)
+ if (internetDialog == null) {
+ doneButton.visibility = View.GONE
+ } else {
+ // Set done button if qs details view is not enabled.
+ doneButton.setOnClickListener { internetDialog!!.dismiss() }
+ }
+
+ // Share WiFi
+ shareWifiButton = contentView.requireViewById(R.id.share_wifi_button)
+ shareWifiButton.setOnClickListener { view ->
+ if (
+ internetDetailsContentController.mayLaunchShareWifiSettings(
+ connectedWifiEntry,
+ view,
+ )
+ ) {
+ uiEventLogger.log(InternetDetailsEvent.SHARE_WIFI_QS_BUTTON_CLICKED)
+ }
+ }
+
+ // Airplane mode
+ airplaneModeButton = contentView.requireViewById(R.id.apm_button)
+ airplaneModeButton.setOnClickListener {
+ internetDetailsContentController.setAirplaneModeDisabled()
+ }
+ airplaneModeSummaryTextView = contentView.requireViewById(R.id.airplane_mode_summary)
+
+ // Background drawables
+ backgroundOn = context.getDrawable(R.drawable.settingslib_switch_bar_bg_on)
+ backgroundOff = context.getDrawable(R.drawable.internet_dialog_selected_effect)
+ }
+
+ private fun setWifiLayout() {
+ // Initialize Wi-Fi related views
+ turnWifiOnLayout = contentView.requireViewById(R.id.turn_on_wifi_layout)
+ wifiToggleTitleTextView = contentView.requireViewById(R.id.wifi_toggle_title)
+ wifiScanNotifyLayout = contentView.requireViewById(R.id.wifi_scan_notify_layout)
+ wifiScanNotifyTextView = contentView.requireViewById(R.id.wifi_scan_notify_text)
+ connectedWifiListLayout = contentView.requireViewById(R.id.wifi_connected_layout)
+ connectedWifiIcon = contentView.requireViewById(R.id.wifi_connected_icon)
+ connectedWifiTitleTextView = contentView.requireViewById(R.id.wifi_connected_title)
+ connectedWifiSummaryTextView = contentView.requireViewById(R.id.wifi_connected_summary)
+ wifiSettingsIcon = contentView.requireViewById(R.id.wifi_settings_icon)
+ wifiToggle = contentView.requireViewById(R.id.wifi_toggle)
+ wifiRecyclerView =
+ contentView.requireViewById<RecyclerView>(R.id.wifi_list_layout).apply {
+ layoutManager = LinearLayoutManager(context)
+ adapter = this@InternetDetailsContentManager.adapter
+ }
+ seeAllLayout = contentView.requireViewById(R.id.see_all_layout)
+
+ // Set click listeners for Wi-Fi related views
+ wifiToggle.setOnClickListener {
+ val isChecked = wifiToggle.isChecked
+ handleWifiToggleClicked(isChecked)
+ }
+ connectedWifiListLayout.setOnClickListener(this::onClickConnectedWifi)
+ seeAllLayout.setOnClickListener(this::onClickSeeMoreButton)
+ }
+
+ private fun setMobileLayout() {
+ // Initialize mobile data related views
+ mobileNetworkLayout = contentView.requireViewById(R.id.mobile_network_layout)
+ signalIcon = contentView.requireViewById(R.id.signal_icon)
+ mobileTitleTextView = contentView.requireViewById(R.id.mobile_title)
+ mobileSummaryTextView = contentView.requireViewById(R.id.mobile_summary)
+ mobileDataToggle = contentView.requireViewById(R.id.mobile_toggle)
+ mobileToggleDivider = contentView.requireViewById(R.id.mobile_toggle_divider)
+
+ // Set click listeners for mobile data related views
+ mobileNetworkLayout.setOnClickListener {
+ val autoSwitchNonDdsSubId: Int =
+ internetDetailsContentController.getActiveAutoSwitchNonDdsSubId()
+ if (autoSwitchNonDdsSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ showTurnOffAutoDataSwitchDialog(autoSwitchNonDdsSubId)
+ }
+ internetDetailsContentController.connectCarrierNetwork()
+ }
+
+ // Mobile data toggle
+ mobileDataToggle.setOnClickListener {
+ val isChecked = mobileDataToggle.isChecked
+ if (!isChecked && shouldShowMobileDialog()) {
+ mobileDataToggle.isChecked = true
+ showTurnOffMobileDialog()
+ } else if (internetDetailsContentController.isMobileDataEnabled != isChecked) {
+ internetDetailsContentController.setMobileDataEnabled(
+ context,
+ defaultDataSubId,
+ isChecked,
+ false,
+ )
+ }
+ }
+ }
+
+ /**
+ * This function ensures the component is in the RESUMED state and sets up the internet details
+ * content controller.
+ *
+ * If the component is already in the RESUMED state, this function does nothing.
+ */
+ fun initializeAndConfigure() {
+ // If the current state is RESUMED, it's already initialized.
+ if (lifecycleRegistry.currentState == Lifecycle.State.RESUMED) {
+ return
+ }
+
+ lifecycleRegistry.currentState = Lifecycle.State.RESUMED
+ internetDetailsContentController.onStart(internetDetailsCallback, canConfigWifi)
+ if (!canConfigWifi) {
+ hideWifiViews()
+ }
+ }
+
+ private fun getDialogTitleText(): CharSequence {
+ return internetDetailsContentController.getDialogTitleText()
+ }
+
+ private fun updateDetailsUI(internetContent: InternetContent) {
+ if (DEBUG) {
+ Log.d(TAG, "updateDetailsUI ")
+ }
+ if (QsDetailedView.isEnabled) {
+ internetDialogTitleView.visibility = View.GONE
+ internetDialogSubTitleView.visibility = View.GONE
+ } else {
+ internetDialogTitleView.text = internetContent.internetDialogTitleString
+ internetDialogSubTitleView.text = internetContent.internetDialogSubTitle
+ }
+ airplaneModeButton.visibility =
+ if (internetContent.isAirplaneModeEnabled) View.VISIBLE else View.GONE
+
+ updateEthernetUI(internetContent)
+ updateMobileUI(internetContent)
+ updateWifiUI(internetContent)
+ }
+
+ private fun getStartingInternetContent(): InternetContent {
+ return InternetContent(
+ internetDialogTitleString = getDialogTitleText(),
+ internetDialogSubTitle = getSubtitleText(),
+ isWifiEnabled = internetDetailsContentController.isWifiEnabled,
+ isDeviceLocked = internetDetailsContentController.isDeviceLocked,
+ )
+ }
+
+ private fun getSubtitleText(): String {
+ return internetDetailsContentController.getSubtitleText(isProgressBarVisible).toString()
+ }
+
+ @VisibleForTesting
+ internal fun hideWifiViews() {
+ setProgressBarVisible(false)
+ turnWifiOnLayout.visibility = View.GONE
+ connectedWifiListLayout.visibility = View.GONE
+ wifiRecyclerView.visibility = View.GONE
+ seeAllLayout.visibility = View.GONE
+ shareWifiButton.visibility = View.GONE
+ }
+
+ private fun setProgressBarVisible(visible: Boolean) {
+ if (isProgressBarVisible == visible) {
+ return
+ }
+
+ // Set the indeterminate value from false to true each time to ensure that the progress bar
+ // resets its animation and starts at the leftmost starting point each time it is displayed.
+ isProgressBarVisible = visible
+ progressBar.visibility = if (visible) View.VISIBLE else View.GONE
+ progressBar.isIndeterminate = visible
+ divider.visibility = if (visible) View.GONE else View.VISIBLE
+ internetDialogSubTitleView.text = getSubtitleText()
+ }
+
+ private fun showTurnOffAutoDataSwitchDialog(subId: Int) {
+ var carrierName: CharSequence? = getMobileNetworkTitle(defaultDataSubId)
+ if (TextUtils.isEmpty(carrierName)) {
+ carrierName = getDefaultCarrierName()
+ }
+ alertDialog =
+ AlertDialog.Builder(context)
+ .setTitle(context.getString(R.string.auto_data_switch_disable_title, carrierName))
+ .setMessage(R.string.auto_data_switch_disable_message)
+ .setNegativeButton(R.string.auto_data_switch_dialog_negative_button) { _, _ -> }
+ .setPositiveButton(R.string.auto_data_switch_dialog_positive_button) { _, _ ->
+ internetDetailsContentController.setAutoDataSwitchMobileDataPolicy(
+ subId,
+ /* enable= */ false,
+ )
+ secondaryMobileNetworkLayout?.visibility = View.GONE
+ }
+ .create()
+ alertDialog!!.window?.setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG)
+ SystemUIDialog.setShowForAllUsers(alertDialog, true)
+ SystemUIDialog.registerDismissListener(alertDialog)
+ SystemUIDialog.setWindowOnTop(alertDialog, keyguard.isShowing())
+ if (QsDetailedView.isEnabled) {
+ alertDialog!!.show()
+ } else {
+ dialogTransitionAnimator.showFromDialog(alertDialog!!, internetDialog!!, null, false)
+ Log.e(TAG, "Internet dialog is shown with the refactor code")
+ }
+ }
+
+ private fun shouldShowMobileDialog(): Boolean {
+ val mobileDataTurnedOff =
+ Prefs.getBoolean(context, Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA, false)
+ return internetDetailsContentController.isMobileDataEnabled && !mobileDataTurnedOff
+ }
+
+ private fun getMobileNetworkTitle(subId: Int): CharSequence {
+ return internetDetailsContentController.getMobileNetworkTitle(subId)
+ }
+
+ private fun showTurnOffMobileDialog() {
+ val context = contentView.context
+ var carrierName: CharSequence? = getMobileNetworkTitle(defaultDataSubId)
+ val isInService: Boolean =
+ internetDetailsContentController.isVoiceStateInService(defaultDataSubId)
+ if (TextUtils.isEmpty(carrierName) || !isInService) {
+ carrierName = getDefaultCarrierName()
+ }
+ alertDialog =
+ AlertDialog.Builder(context)
+ .setTitle(R.string.mobile_data_disable_title)
+ .setMessage(context.getString(R.string.mobile_data_disable_message, carrierName))
+ .setNegativeButton(android.R.string.cancel) { _: DialogInterface?, _: Int -> }
+ .setPositiveButton(
+ com.android.internal.R.string.alert_windows_notification_turn_off_action
+ ) { _: DialogInterface?, _: Int ->
+ internetDetailsContentController.setMobileDataEnabled(
+ context,
+ defaultDataSubId,
+ false,
+ false,
+ )
+ mobileDataToggle.isChecked = false
+ Prefs.putBoolean(context, Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA, true)
+ }
+ .create()
+ alertDialog!!.window?.setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG)
+ SystemUIDialog.setShowForAllUsers(alertDialog, true)
+ SystemUIDialog.registerDismissListener(alertDialog)
+ SystemUIDialog.setWindowOnTop(alertDialog, keyguard.isShowing())
+ if (QsDetailedView.isEnabled) {
+ alertDialog!!.show()
+ } else {
+ dialogTransitionAnimator.showFromDialog(alertDialog!!, internetDialog!!, null, false)
+ }
+ }
+
+ private fun onClickConnectedWifi(view: View?) {
+ if (connectedWifiEntry == null) {
+ return
+ }
+ internetDetailsContentController.launchWifiDetailsSetting(connectedWifiEntry!!.key, view)
+ }
+
+ private fun onClickSeeMoreButton(view: View?) {
+ internetDetailsContentController.launchNetworkSetting(view)
+ }
+
+ private fun handleWifiToggleClicked(isChecked: Boolean) {
+ if (Flags.oemEnabledSatelliteFlag()) {
+ if (clickJob != null && !clickJob!!.isCompleted) {
+ return
+ }
+ clickJob =
+ mayStartSatelliteWarningDialog(contentView.context, coroutineScope, TYPE_IS_WIFI) {
+ isAllowClick: Boolean ->
+ if (isAllowClick) {
+ setWifiEnabled(isChecked)
+ } else {
+ wifiToggle.isChecked = !isChecked
+ }
+ }
+ return
+ }
+ setWifiEnabled(isChecked)
+ }
+
+ private fun setWifiEnabled(isEnabled: Boolean) {
+ if (internetDetailsContentController.isWifiEnabled == isEnabled) {
+ return
+ }
+ internetDetailsContentController.isWifiEnabled = isEnabled
+ }
+
+ @MainThread
+ private fun updateEthernetUI(internetContent: InternetContent) {
+ ethernetLayout.visibility = if (internetContent.hasEthernet) View.VISIBLE else View.GONE
+ }
+
+ private fun updateWifiUI(internetContent: InternetContent) {
+ if (!canConfigWifi) {
+ return
+ }
+
+ updateWifiToggle(internetContent)
+ updateConnectedWifi(internetContent)
+ updateWifiListAndSeeAll(internetContent)
+ updateWifiScanNotify(internetContent)
+ }
+
+ private fun updateMobileUI(internetContent: InternetContent) {
+ if (!internetContent.shouldUpdateMobileNetwork) {
+ return
+ }
+
+ val isNetworkConnected =
+ internetContent.activeNetworkIsCellular || internetContent.isCarrierNetworkActive
+ // 1. Mobile network should be gone if airplane mode ON or the list of active
+ // subscriptionId is null.
+ // 2. Carrier network should be gone if airplane mode ON and Wi-Fi is OFF.
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ /*msg = */ "updateMobileUI, isCarrierNetworkActive = " +
+ internetContent.isCarrierNetworkActive,
+ )
+ }
+
+ if (
+ !internetContent.hasActiveSubIdOnDds &&
+ (!internetContent.isWifiEnabled || !internetContent.isCarrierNetworkActive)
+ ) {
+ mobileNetworkLayout.visibility = View.GONE
+ secondaryMobileNetworkLayout?.visibility = View.GONE
+ return
+ }
+
+ mobileNetworkLayout.visibility = View.VISIBLE
+ mobileDataToggle.setChecked(internetDetailsContentController.isMobileDataEnabled)
+ mobileTitleTextView.text = getMobileNetworkTitle(defaultDataSubId)
+ val summary = getMobileNetworkSummary(defaultDataSubId)
+ if (!TextUtils.isEmpty(summary)) {
+ mobileSummaryTextView.text = Html.fromHtml(summary, Html.FROM_HTML_MODE_LEGACY)
+ mobileSummaryTextView.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+ mobileSummaryTextView.visibility = View.VISIBLE
+ } else {
+ mobileSummaryTextView.visibility = View.GONE
+ }
+ backgroundExecutor.execute {
+ val drawable = getSignalStrengthDrawable(defaultDataSubId)
+ handler.post { signalIcon.setImageDrawable(drawable) }
+ }
+
+ mobileDataToggle.visibility = if (canConfigMobileData) View.VISIBLE else View.INVISIBLE
+ mobileToggleDivider.visibility = if (canConfigMobileData) View.VISIBLE else View.INVISIBLE
+ val primaryColor =
+ if (isNetworkConnected) R.color.connected_network_primary_color
+ else R.color.disconnected_network_primary_color
+ mobileToggleDivider.setBackgroundColor(context.getColor(primaryColor))
+
+ // Display the info for the non-DDS if it's actively being used
+ val autoSwitchNonDdsSubId: Int = internetContent.activeAutoSwitchNonDdsSubId
+
+ val nonDdsVisibility =
+ if (autoSwitchNonDdsSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) View.VISIBLE
+ else View.GONE
+
+ val secondaryRes =
+ if (isNetworkConnected) R.style.TextAppearance_InternetDialog_Secondary_Active
+ else R.style.TextAppearance_InternetDialog_Secondary
+ if (nonDdsVisibility == View.VISIBLE) {
+ // non DDS is the currently active sub, set primary visual for it
+ setNonDDSActive(autoSwitchNonDdsSubId)
+ } else {
+ mobileNetworkLayout.background = if (isNetworkConnected) backgroundOn else backgroundOff
+ mobileTitleTextView.setTextAppearance(
+ if (isNetworkConnected) R.style.TextAppearance_InternetDialog_Active
+ else R.style.TextAppearance_InternetDialog
+ )
+ mobileSummaryTextView.setTextAppearance(secondaryRes)
+ }
+
+ secondaryMobileNetworkLayout?.visibility = nonDdsVisibility
+
+ // Set airplane mode to the summary for carrier network
+ if (internetContent.isAirplaneModeEnabled) {
+ airplaneModeSummaryTextView.apply {
+ visibility = View.VISIBLE
+ text = context.getText(R.string.airplane_mode)
+ setTextAppearance(secondaryRes)
+ }
+ } else {
+ airplaneModeSummaryTextView.visibility = View.GONE
+ }
+ }
+
+ private fun setNonDDSActive(autoSwitchNonDdsSubId: Int) {
+ val stub: ViewStub = contentView.findViewById(R.id.secondary_mobile_network_stub)
+ stub.inflate()
+ secondaryMobileNetworkLayout =
+ contentView.findViewById(R.id.secondary_mobile_network_layout)
+ secondaryMobileNetworkLayout?.setOnClickListener { view: View? ->
+ this.onClickConnectedSecondarySub(view)
+ }
+ secondaryMobileNetworkLayout?.background = backgroundOn
+
+ contentView.requireViewById<TextView>(R.id.secondary_mobile_title).apply {
+ text = getMobileNetworkTitle(autoSwitchNonDdsSubId)
+ setTextAppearance(R.style.TextAppearance_InternetDialog_Active)
+ }
+
+ val summary = getMobileNetworkSummary(autoSwitchNonDdsSubId)
+ contentView.requireViewById<TextView>(R.id.secondary_mobile_summary).apply {
+ if (!TextUtils.isEmpty(summary)) {
+ text = Html.fromHtml(summary, Html.FROM_HTML_MODE_LEGACY)
+ breakStrategy = Layout.BREAK_STRATEGY_SIMPLE
+ setTextAppearance(R.style.TextAppearance_InternetDialog_Active)
+ }
+ }
+
+ val secondarySignalIcon: ImageView = contentView.requireViewById(R.id.secondary_signal_icon)
+ backgroundExecutor.execute {
+ val drawable = getSignalStrengthDrawable(autoSwitchNonDdsSubId)
+ handler.post { secondarySignalIcon.setImageDrawable(drawable) }
+ }
+
+ contentView.requireViewById<ImageView>(R.id.secondary_settings_icon).apply {
+ setColorFilter(context.getColor(R.color.connected_network_primary_color))
+ }
+
+ // set secondary visual for default data sub
+ mobileNetworkLayout.background = backgroundOff
+ mobileTitleTextView.setTextAppearance(R.style.TextAppearance_InternetDialog)
+ mobileSummaryTextView.setTextAppearance(R.style.TextAppearance_InternetDialog_Secondary)
+ signalIcon.setColorFilter(context.getColor(R.color.connected_network_secondary_color))
+ }
+
+ @MainThread
+ private fun updateWifiToggle(internetContent: InternetContent) {
+ if (wifiToggle.isChecked != internetContent.isWifiEnabled) {
+ wifiToggle.isChecked = internetContent.isWifiEnabled
+ }
+ if (internetContent.isDeviceLocked) {
+ wifiToggleTitleTextView.setTextAppearance(
+ if ((connectedWifiEntry != null)) R.style.TextAppearance_InternetDialog_Active
+ else R.style.TextAppearance_InternetDialog
+ )
+ }
+ turnWifiOnLayout.background =
+ if ((internetContent.isDeviceLocked && connectedWifiEntry != null)) backgroundOn
+ else null
+
+ if (!canChangeWifiState && wifiToggle.isEnabled) {
+ wifiToggle.isEnabled = false
+ wifiToggleTitleTextView.isEnabled = false
+ contentView.requireViewById<TextView>(R.id.wifi_toggle_summary).apply {
+ isEnabled = false
+ visibility = View.VISIBLE
+ }
+ }
+ }
+
+ @MainThread
+ private fun updateConnectedWifi(internetContent: InternetContent) {
+ if (
+ !internetContent.isWifiEnabled ||
+ connectedWifiEntry == null ||
+ internetContent.isDeviceLocked
+ ) {
+ connectedWifiListLayout.visibility = View.GONE
+ shareWifiButton.visibility = View.GONE
+ return
+ }
+ connectedWifiListLayout.visibility = View.VISIBLE
+ connectedWifiTitleTextView.text = connectedWifiEntry!!.title
+ connectedWifiSummaryTextView.text = connectedWifiEntry!!.getSummary(false)
+ connectedWifiIcon.setImageDrawable(
+ internetDetailsContentController.getInternetWifiDrawable(connectedWifiEntry!!)
+ )
+ wifiSettingsIcon.setColorFilter(context.getColor(R.color.connected_network_primary_color))
+
+ val canShareWifi =
+ internetDetailsContentController.getConfiguratorQrCodeGeneratorIntentOrNull(
+ connectedWifiEntry
+ ) != null
+ shareWifiButton.visibility = if (canShareWifi) View.VISIBLE else View.GONE
+
+ secondaryMobileNetworkLayout?.visibility = View.GONE
+ }
+
+ @MainThread
+ private fun updateWifiListAndSeeAll(internetContent: InternetContent) {
+ if (!internetContent.isWifiEnabled || internetContent.isDeviceLocked) {
+ wifiRecyclerView.visibility = View.GONE
+ seeAllLayout.visibility = View.GONE
+ return
+ }
+ val wifiListMaxCount = getWifiListMaxCount()
+ if (adapter.itemCount > wifiListMaxCount) {
+ hasMoreWifiEntries = true
+ }
+ adapter.setMaxEntriesCount(wifiListMaxCount)
+ val wifiListMinHeight = wifiNetworkHeight * wifiListMaxCount
+ if (wifiRecyclerView.minimumHeight != wifiListMinHeight) {
+ wifiRecyclerView.minimumHeight = wifiListMinHeight
+ }
+ wifiRecyclerView.visibility = View.VISIBLE
+ seeAllLayout.visibility = if (hasMoreWifiEntries) View.VISIBLE else View.INVISIBLE
+ }
+
+ @MainThread
+ private fun updateWifiScanNotify(internetContent: InternetContent) {
+ if (
+ internetContent.isWifiEnabled ||
+ !internetContent.isWifiScanEnabled ||
+ internetContent.isDeviceLocked
+ ) {
+ wifiScanNotifyLayout.visibility = View.GONE
+ return
+ }
+
+ if (TextUtils.isEmpty(wifiScanNotifyTextView.text)) {
+ val linkInfo =
+ AnnotationLinkSpan.LinkInfo(AnnotationLinkSpan.LinkInfo.DEFAULT_ANNOTATION) {
+ view: View? ->
+ internetDetailsContentController.launchWifiScanningSetting(view)
+ }
+ wifiScanNotifyTextView.text =
+ AnnotationLinkSpan.linkify(
+ context.getText(R.string.wifi_scan_notify_message),
+ linkInfo,
+ )
+ wifiScanNotifyTextView.movementMethod = LinkMovementMethod.getInstance()
+ }
+ wifiScanNotifyLayout.visibility = View.VISIBLE
+ }
+
+ @VisibleForTesting
+ @MainThread
+ internal fun getWifiListMaxCount(): Int {
+ // Use the maximum count of networks to calculate the remaining count for Wi-Fi networks.
+ var count = MAX_NETWORK_COUNT
+ if (ethernetLayout.visibility == View.VISIBLE) {
+ count -= 1
+ }
+ if (mobileNetworkLayout.visibility == View.VISIBLE) {
+ count -= 1
+ }
+
+ // If the remaining count is greater than the maximum count of the Wi-Fi network, the
+ // maximum count of the Wi-Fi network is used.
+ if (count > InternetDetailsContentController.MAX_WIFI_ENTRY_COUNT) {
+ count = InternetDetailsContentController.MAX_WIFI_ENTRY_COUNT
+ }
+ if (connectedWifiListLayout.visibility == View.VISIBLE) {
+ count -= 1
+ }
+ return count
+ }
+
+ private fun getMobileNetworkSummary(subId: Int): String {
+ return internetDetailsContentController.getMobileNetworkSummary(subId)
+ }
+
+ /** For DSDS auto data switch */
+ private fun onClickConnectedSecondarySub(view: View?) {
+ internetDetailsContentController.launchMobileNetworkSettings(view)
+ }
+
+ private fun getSignalStrengthDrawable(subId: Int): Drawable {
+ return internetDetailsContentController.getSignalStrengthDrawable(subId)
+ }
+
+ /**
+ * Unbinds all listeners and resources associated with the view. This method should be called
+ * when the view is no longer needed.
+ */
+ fun unBind() {
+ if (DEBUG) {
+ Log.d(TAG, "unBind")
+ }
+ lifecycleRegistry.currentState = Lifecycle.State.DESTROYED
+ mobileNetworkLayout.setOnClickListener(null)
+ connectedWifiListLayout.setOnClickListener(null)
+ secondaryMobileNetworkLayout?.setOnClickListener(null)
+ seeAllLayout.setOnClickListener(null)
+ wifiToggle.setOnCheckedChangeListener(null)
+ doneButton.setOnClickListener(null)
+ shareWifiButton.setOnClickListener(null)
+ airplaneModeButton.setOnClickListener(null)
+ internetDetailsContentController.onStop()
+ }
+
+ /**
+ * Update the internet details content when receiving the callback.
+ *
+ * @param shouldUpdateMobileNetwork `true` for update the mobile network layout, otherwise
+ * `false`.
+ */
+ @VisibleForTesting
+ internal fun updateContent(shouldUpdateMobileNetwork: Boolean) {
+ backgroundExecutor.execute {
+ internetContentData.postValue(getInternetContent(shouldUpdateMobileNetwork))
+ }
+ }
+
+ private fun getInternetContent(shouldUpdateMobileNetwork: Boolean): InternetContent {
+ return InternetContent(
+ shouldUpdateMobileNetwork = shouldUpdateMobileNetwork,
+ internetDialogTitleString = getDialogTitleText(),
+ internetDialogSubTitle = getSubtitleText(),
+ activeNetworkIsCellular =
+ if (shouldUpdateMobileNetwork)
+ internetDetailsContentController.activeNetworkIsCellular()
+ else false,
+ isCarrierNetworkActive =
+ if (shouldUpdateMobileNetwork)
+ internetDetailsContentController.isCarrierNetworkActive()
+ else false,
+ isAirplaneModeEnabled = internetDetailsContentController.isAirplaneModeEnabled,
+ hasEthernet = internetDetailsContentController.hasEthernet(),
+ isWifiEnabled = internetDetailsContentController.isWifiEnabled,
+ hasActiveSubIdOnDds = internetDetailsContentController.hasActiveSubIdOnDds(),
+ isDeviceLocked = internetDetailsContentController.isDeviceLocked,
+ isWifiScanEnabled = internetDetailsContentController.isWifiScanEnabled(),
+ activeAutoSwitchNonDdsSubId =
+ internetDetailsContentController.getActiveAutoSwitchNonDdsSubId(),
+ )
+ }
+
+ /**
+ * Handles window focus changes. If the activity loses focus and the system UI dialog is
+ * showing, it dismisses the current alert dialog to prevent it from persisting in the
+ * background.
+ *
+ * @param dialog The internet system UI dialog whose focus state has changed.
+ * @param hasFocus True if the window has gained focus, false otherwise.
+ */
+ fun onWindowFocusChanged(dialog: SystemUIDialog, hasFocus: Boolean) {
+ if (alertDialog != null && !alertDialog!!.isShowing) {
+ if (!hasFocus && dialog.isShowing) {
+ dialog.dismiss()
+ }
+ }
+ }
+
+ private fun getDefaultCarrierName(): String? {
+ return context.getString(R.string.mobile_data_disable_message_default_carrier)
+ }
+
+ @VisibleForTesting
+ internal val internetDetailsCallback =
+ object : InternetDetailsContentController.InternetDialogCallback {
+ override fun onRefreshCarrierInfo() {
+ updateContent(shouldUpdateMobileNetwork = true)
+ }
+
+ override fun onSimStateChanged() {
+ updateContent(shouldUpdateMobileNetwork = true)
+ }
+
+ @WorkerThread
+ override fun onCapabilitiesChanged(
+ network: Network?,
+ networkCapabilities: NetworkCapabilities?,
+ ) {
+ updateContent(shouldUpdateMobileNetwork = true)
+ }
+
+ @WorkerThread
+ override fun onLost(network: Network) {
+ updateContent(shouldUpdateMobileNetwork = true)
+ }
+
+ override fun onSubscriptionsChanged(dataSubId: Int) {
+ defaultDataSubId = dataSubId
+ updateContent(shouldUpdateMobileNetwork = true)
+ }
+
+ override fun onServiceStateChanged(serviceState: ServiceState?) {
+ updateContent(shouldUpdateMobileNetwork = true)
+ }
+
+ @WorkerThread
+ override fun onDataConnectionStateChanged(state: Int, networkType: Int) {
+ updateContent(shouldUpdateMobileNetwork = true)
+ }
+
+ override fun onSignalStrengthsChanged(signalStrength: SignalStrength?) {
+ updateContent(shouldUpdateMobileNetwork = true)
+ }
+
+ override fun onUserMobileDataStateChanged(enabled: Boolean) {
+ updateContent(shouldUpdateMobileNetwork = true)
+ }
+
+ override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo?) {
+ updateContent(shouldUpdateMobileNetwork = true)
+ }
+
+ override fun onCarrierNetworkChange(active: Boolean) {
+ updateContent(shouldUpdateMobileNetwork = true)
+ }
+
+ override fun dismissDialog() {
+ if (DEBUG) {
+ Log.d(TAG, "dismissDialog")
+ }
+ if (internetDialog != null) {
+ internetDialog!!.dismiss()
+ internetDialog = null
+ }
+ }
+
+ override fun onAccessPointsChanged(
+ wifiEntries: MutableList<WifiEntry>?,
+ connectedEntry: WifiEntry?,
+ ifHasMoreWifiEntries: Boolean,
+ ) {
+ // Should update the carrier network layout when it is connected under airplane
+ // mode ON.
+ val shouldUpdateCarrierNetwork =
+ (mobileNetworkLayout.visibility == View.VISIBLE) &&
+ internetDetailsContentController.isAirplaneModeEnabled
+ handler.post {
+ connectedWifiEntry = connectedEntry
+ wifiEntriesCount = wifiEntries?.size ?: 0
+ hasMoreWifiEntries = ifHasMoreWifiEntries
+ updateContent(shouldUpdateCarrierNetwork)
+ adapter.setWifiEntries(wifiEntries, wifiEntriesCount)
+ adapter.notifyDataSetChanged()
+ }
+ }
+
+ override fun onWifiScan(isScan: Boolean) {
+ setProgressBarVisible(isScan)
+ }
+ }
+
+ enum class InternetDetailsEvent(private val id: Int) : UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "The Internet details became visible on the screen.")
+ INTERNET_DETAILS_VISIBLE(2071),
+ @UiEvent(doc = "The share wifi button is clicked.") SHARE_WIFI_QS_BUTTON_CLICKED(1462);
+
+ override fun getId(): Int {
+ return id
+ }
+ }
+
+ @VisibleForTesting
+ data class InternetContent(
+ val internetDialogTitleString: CharSequence,
+ val internetDialogSubTitle: CharSequence,
+ val isAirplaneModeEnabled: Boolean = false,
+ val hasEthernet: Boolean = false,
+ val shouldUpdateMobileNetwork: Boolean = false,
+ val activeNetworkIsCellular: Boolean = false,
+ val isCarrierNetworkActive: Boolean = false,
+ val isWifiEnabled: Boolean = false,
+ val hasActiveSubIdOnDds: Boolean = false,
+ val isDeviceLocked: Boolean = false,
+ val isWifiScanEnabled: Boolean = false,
+ val activeAutoSwitchNonDdsSubId: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+ )
+
+ companion object {
+ private const val TAG = "InternetDetailsContent"
+ private val DEBUG: Boolean = Log.isLoggable(TAG, Log.DEBUG)
+ private const val MAX_NETWORK_COUNT = 4
+ const val CAN_CONFIG_MOBILE_DATA = "can_config_mobile_data"
+ const val CAN_CONFIG_WIFI = "can_config_wifi"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailedViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsViewModel.kt
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailedViewModel.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsViewModel.kt
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacy.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacy.java
index 82367eb..401f1a4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacy.java
@@ -70,6 +70,7 @@
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.qs.flags.QsDetailedView;
import com.android.systemui.res.R;
import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor;
@@ -207,7 +208,8 @@
KeyguardStateController keyguardStateController,
SystemUIDialog.Factory systemUIDialogFactory,
ShadeDialogContextInteractor shadeDialogContextInteractor) {
- // TODO: b/377388104 QsDetailedView.assertInLegacyMode();
+ // If `QsDetailedView` is enabled, it should show the details view.
+ QsDetailedView.assertInLegacyMode();
mAboveStatusBar = aboveStatusBar;
mSystemUIDialogFactory = systemUIDialogFactory;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt
index 8a54648..5f82e60 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt
@@ -23,6 +23,7 @@
import com.android.systemui.coroutines.newTracingContext
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.flags.QsDetailedView
import com.android.systemui.statusbar.phone.SystemUIDialog
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -42,21 +43,24 @@
@Background private val bgDispatcher: CoroutineDispatcher,
) {
private lateinit var coroutineScope: CoroutineScope
+
companion object {
private const val INTERACTION_JANK_TAG = "internet"
var dialog: SystemUIDialog? = null
}
/**
- * Creates a [InternetDialogDelegateLegacy]. The dialog will be animated from [expandable] if
- * it is not null.
+ * Creates a [InternetDialogDelegateLegacy]. The dialog will be animated from [expandable] if it
+ * is not null.
*/
fun create(
aboveStatusBar: Boolean,
canConfigMobileData: Boolean,
canConfigWifi: Boolean,
- expandable: Expandable?
+ expandable: Expandable?,
) {
+ // If `QsDetailedView` is enabled, it should show the details view.
+ QsDetailedView.assertInLegacyMode()
if (dialog != null) {
if (DEBUG) {
Log.d(TAG, "InternetDialog is showing, do not create it twice.")
@@ -64,11 +68,11 @@
return
} else {
coroutineScope = CoroutineScope(bgDispatcher + newTracingContext("InternetDialogScope"))
- // TODO: b/377388104 check the QsDetailedView flag to use the correct dialogFactory
dialog =
dialogFactory
.create(aboveStatusBar, canConfigMobileData, canConfigWifi, coroutineScope)
.createDialog()
+
val controller =
expandable?.dialogTransitionController(
DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, INTERACTION_JANK_TAG)
@@ -77,10 +81,9 @@
dialogTransitionAnimator.show(
dialog!!,
controller,
- animateBackgroundBoundsChange = true
+ animateBackgroundBoundsChange = true,
)
- }
- ?: dialog?.show()
+ } ?: dialog?.show()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt
new file mode 100644
index 0000000..a192446
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt
@@ -0,0 +1,819 @@
+/*
+ * 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.systemui.qs.tiles.dialog
+
+import android.content.Intent
+import android.os.Handler
+import android.os.fakeExecutorHandler
+import android.telephony.SubscriptionManager
+import android.telephony.TelephonyManager
+import android.telephony.telephonyManager
+import android.testing.TestableLooper.RunWithLooper
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.Button
+import android.widget.LinearLayout
+import android.widget.Switch
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import androidx.test.annotation.UiThreadTest
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.internal.logging.UiEventLogger
+import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.flags.setFlagValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import com.android.wifitrackerlib.WifiEntry
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers
+import org.mockito.MockitoSession
+import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@RunWithLooper(setAsMainLooper = true)
+@UiThreadTest
+class InternetDetailsContentManagerTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val handler: Handler = kosmos.fakeExecutorHandler
+ private val scope: CoroutineScope = mock<CoroutineScope>()
+ private val telephonyManager: TelephonyManager = kosmos.telephonyManager
+ private val internetWifiEntry: WifiEntry = mock<WifiEntry>()
+ private val wifiEntries: List<WifiEntry> = mock<List<WifiEntry>>()
+ private val internetAdapter = mock<InternetAdapter>()
+ private val internetDetailsContentController: InternetDetailsContentController =
+ mock<InternetDetailsContentController>()
+ private val keyguard: KeyguardStateController = mock<KeyguardStateController>()
+ private val dialogTransitionAnimator: DialogTransitionAnimator =
+ mock<DialogTransitionAnimator>()
+ private val bgExecutor = FakeExecutor(FakeSystemClock())
+ private lateinit var internetDetailsContentManager: InternetDetailsContentManager
+ private var subTitle: View? = null
+ private var ethernet: LinearLayout? = null
+ private var mobileDataLayout: LinearLayout? = null
+ private var mobileToggleSwitch: Switch? = null
+ private var wifiToggle: LinearLayout? = null
+ private var wifiToggleSwitch: Switch? = null
+ private var wifiToggleSummary: TextView? = null
+ private var connectedWifi: LinearLayout? = null
+ private var wifiList: RecyclerView? = null
+ private var seeAll: LinearLayout? = null
+ private var wifiScanNotify: LinearLayout? = null
+ private var airplaneModeSummaryText: TextView? = null
+ private var mockitoSession: MockitoSession? = null
+ private var sharedWifiButton: Button? = null
+ private lateinit var contentView: View
+
+ @Before
+ fun setUp() {
+ // TODO: b/377388104 enable this flag after integrating with details view.
+ mSetFlagsRule.setFlagValue(Flags.FLAG_QS_TILE_DETAILED_VIEW, false)
+ whenever(telephonyManager.createForSubscriptionId(ArgumentMatchers.anyInt()))
+ .thenReturn(telephonyManager)
+ whenever(internetWifiEntry.title).thenReturn(WIFI_TITLE)
+ whenever(internetWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY)
+ whenever(internetWifiEntry.isDefaultNetwork).thenReturn(true)
+ whenever(internetWifiEntry.hasInternetAccess()).thenReturn(true)
+ whenever(wifiEntries.size).thenReturn(1)
+ whenever(internetDetailsContentController.getDialogTitleText()).thenReturn(TITLE)
+ whenever(internetDetailsContentController.getMobileNetworkTitle(ArgumentMatchers.anyInt()))
+ .thenReturn(MOBILE_NETWORK_TITLE)
+ whenever(
+ internetDetailsContentController.getMobileNetworkSummary(ArgumentMatchers.anyInt())
+ )
+ .thenReturn(MOBILE_NETWORK_SUMMARY)
+ whenever(internetDetailsContentController.isWifiEnabled).thenReturn(true)
+ whenever(internetDetailsContentController.activeAutoSwitchNonDdsSubId)
+ .thenReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+ mockitoSession =
+ ExtendedMockito.mockitoSession()
+ .spyStatic(WifiEnterpriseRestrictionUtils::class.java)
+ .startMocking()
+ whenever(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(true)
+ createView()
+ }
+
+ private fun createView() {
+ contentView =
+ LayoutInflater.from(mContext).inflate(R.layout.internet_connectivity_dialog, null)
+ internetDetailsContentManager =
+ InternetDetailsContentManager(
+ internetDetailsContentController,
+ canConfigMobileData = true,
+ canConfigWifi = true,
+ coroutineScope = scope,
+ context = mContext,
+ internetDialog = null,
+ uiEventLogger = mock<UiEventLogger>(),
+ dialogTransitionAnimator = dialogTransitionAnimator,
+ handler = handler,
+ backgroundExecutor = bgExecutor,
+ keyguard = keyguard,
+ )
+
+ internetDetailsContentManager.bind(contentView)
+ internetDetailsContentManager.adapter = internetAdapter
+ internetDetailsContentManager.connectedWifiEntry = internetWifiEntry
+ internetDetailsContentManager.wifiEntriesCount = wifiEntries.size
+
+ subTitle = contentView.requireViewById(R.id.internet_dialog_subtitle)
+ ethernet = contentView.requireViewById(R.id.ethernet_layout)
+ mobileDataLayout = contentView.requireViewById(R.id.mobile_network_layout)
+ mobileToggleSwitch = contentView.requireViewById(R.id.mobile_toggle)
+ wifiToggle = contentView.requireViewById(R.id.turn_on_wifi_layout)
+ wifiToggleSwitch = contentView.requireViewById(R.id.wifi_toggle)
+ wifiToggleSummary = contentView.requireViewById(R.id.wifi_toggle_summary)
+ connectedWifi = contentView.requireViewById(R.id.wifi_connected_layout)
+ wifiList = contentView.requireViewById(R.id.wifi_list_layout)
+ seeAll = contentView.requireViewById(R.id.see_all_layout)
+ wifiScanNotify = contentView.requireViewById(R.id.wifi_scan_notify_layout)
+ airplaneModeSummaryText = contentView.requireViewById(R.id.airplane_mode_summary)
+ sharedWifiButton = contentView.requireViewById(R.id.share_wifi_button)
+ }
+
+ @After
+ fun tearDown() {
+ internetDetailsContentManager.unBind()
+ mockitoSession!!.finishMocking()
+ }
+
+ @Test
+ fun createView_setAccessibilityPaneTitleToQuickSettings() {
+ assertThat(contentView.accessibilityPaneTitle)
+ .isEqualTo(mContext.getText(R.string.accessibility_desc_quick_settings))
+ }
+
+ @Test
+ fun hideWifiViews_WifiViewsGone() {
+ internetDetailsContentManager.hideWifiViews()
+
+ assertThat(internetDetailsContentManager.isProgressBarVisible).isFalse()
+ assertThat(wifiToggle!!.visibility).isEqualTo(View.GONE)
+ assertThat(connectedWifi!!.visibility).isEqualTo(View.GONE)
+ assertThat(wifiList!!.visibility).isEqualTo(View.GONE)
+ assertThat(seeAll!!.visibility).isEqualTo(View.GONE)
+ }
+
+ @Test
+ fun updateContent_withApmOn_internetDialogSubTitleGone() {
+ whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(true)
+ internetDetailsContentManager.updateContent(true)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(subTitle!!.visibility).isEqualTo(View.VISIBLE)
+ }
+ }
+
+ @Test
+ fun updateContent_withApmOff_internetDialogSubTitleVisible() {
+ whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(false)
+ internetDetailsContentManager.updateContent(true)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(subTitle!!.visibility).isEqualTo(View.VISIBLE)
+ }
+ }
+
+ @Test
+ fun updateContent_apmOffAndHasEthernet_showEthernet() {
+ whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(false)
+ whenever(internetDetailsContentController.hasEthernet()).thenReturn(true)
+ internetDetailsContentManager.updateContent(true)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(ethernet!!.visibility).isEqualTo(View.VISIBLE)
+ }
+ }
+
+ @Test
+ fun updateContent_apmOffAndNoEthernet_hideEthernet() {
+ whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(false)
+ whenever(internetDetailsContentController.hasEthernet()).thenReturn(false)
+ internetDetailsContentManager.updateContent(true)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(ethernet!!.visibility).isEqualTo(View.GONE)
+ }
+ }
+
+ @Test
+ fun updateContent_apmOnAndHasEthernet_showEthernet() {
+ whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(true)
+ whenever(internetDetailsContentController.hasEthernet()).thenReturn(true)
+ internetDetailsContentManager.updateContent(true)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(ethernet!!.visibility).isEqualTo(View.VISIBLE)
+ }
+ }
+
+ @Test
+ fun updateContent_apmOnAndNoEthernet_hideEthernet() {
+ whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(true)
+ whenever(internetDetailsContentController.hasEthernet()).thenReturn(false)
+ internetDetailsContentManager.updateContent(true)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(ethernet!!.visibility).isEqualTo(View.GONE)
+ }
+ }
+
+ @Test
+ fun updateContent_apmOffAndNotCarrierNetwork_mobileDataLayoutGone() {
+ // Mobile network should be gone if the list of active subscriptionId is null.
+ whenever(internetDetailsContentController.isCarrierNetworkActive).thenReturn(false)
+ whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(false)
+ whenever(internetDetailsContentController.hasActiveSubIdOnDds()).thenReturn(false)
+ internetDetailsContentManager.updateContent(true)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(mobileDataLayout!!.visibility).isEqualTo(View.GONE)
+ }
+ }
+
+ @Test
+ fun updateContent_apmOnWithCarrierNetworkAndWifiStatus_mobileDataLayoutVisible() {
+ // Carrier network should be visible if airplane mode ON and Wi-Fi is ON.
+ whenever(internetDetailsContentController.isCarrierNetworkActive).thenReturn(true)
+ whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(true)
+ whenever(internetDetailsContentController.isWifiEnabled).thenReturn(true)
+ internetDetailsContentManager.updateContent(true)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(mobileDataLayout!!.visibility).isEqualTo(View.VISIBLE)
+ }
+ }
+
+ @Test
+ fun updateContent_apmOnWithCarrierNetworkAndWifiStatus_mobileDataLayoutGone() {
+ // Carrier network should be gone if airplane mode ON and Wi-Fi is off.
+ whenever(internetDetailsContentController.isCarrierNetworkActive).thenReturn(true)
+ whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(true)
+ whenever(internetDetailsContentController.isWifiEnabled).thenReturn(false)
+ internetDetailsContentManager.updateContent(true)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(mobileDataLayout!!.visibility).isEqualTo(View.GONE)
+ }
+ }
+
+ @Test
+ fun updateContent_apmOnAndNoCarrierNetwork_mobileDataLayoutGone() {
+ whenever(internetDetailsContentController.isCarrierNetworkActive).thenReturn(false)
+ whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(true)
+ internetDetailsContentManager.updateContent(true)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(mobileDataLayout!!.visibility).isEqualTo(View.GONE)
+ }
+ }
+
+ @Test
+ fun updateContent_apmOnAndWifiOnHasCarrierNetwork_showAirplaneSummary() {
+ whenever(internetDetailsContentController.isCarrierNetworkActive).thenReturn(true)
+ whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(true)
+ internetDetailsContentManager.connectedWifiEntry = null
+ whenever(internetDetailsContentController.activeNetworkIsCellular()).thenReturn(false)
+ internetDetailsContentManager.updateContent(true)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(mobileDataLayout!!.visibility).isEqualTo(View.VISIBLE)
+ assertThat(airplaneModeSummaryText!!.visibility).isEqualTo(View.VISIBLE)
+ }
+ }
+
+ @Test
+ fun updateContent_apmOffAndWifiOnHasCarrierNetwork_notShowApmSummary() {
+ whenever(internetDetailsContentController.isCarrierNetworkActive).thenReturn(true)
+ whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(false)
+ internetDetailsContentManager.connectedWifiEntry = null
+ whenever(internetDetailsContentController.activeNetworkIsCellular()).thenReturn(false)
+ internetDetailsContentManager.updateContent(true)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(airplaneModeSummaryText!!.visibility).isEqualTo(View.GONE)
+ }
+ }
+
+ @Test
+ fun updateContent_apmOffAndHasCarrierNetwork_notShowApmSummary() {
+ whenever(internetDetailsContentController.isCarrierNetworkActive).thenReturn(true)
+ whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(false)
+ internetDetailsContentManager.updateContent(true)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(airplaneModeSummaryText!!.visibility).isEqualTo(View.GONE)
+ }
+ }
+
+ @Test
+ fun updateContent_apmOnAndNoCarrierNetwork_notShowApmSummary() {
+ whenever(internetDetailsContentController.isCarrierNetworkActive).thenReturn(false)
+ whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(true)
+ internetDetailsContentManager.updateContent(true)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(airplaneModeSummaryText!!.visibility).isEqualTo(View.GONE)
+ }
+ }
+
+ @Test
+ fun updateContent_mobileDataIsEnabled_checkMobileDataSwitch() {
+ whenever(internetDetailsContentController.hasActiveSubIdOnDds()).thenReturn(true)
+ whenever(internetDetailsContentController.isCarrierNetworkActive).thenReturn(true)
+ whenever(internetDetailsContentController.isMobileDataEnabled).thenReturn(true)
+ mobileToggleSwitch!!.isChecked = false
+ internetDetailsContentManager.updateContent(true)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(mobileToggleSwitch!!.isChecked).isTrue()
+ }
+ }
+
+ @Test
+ fun updateContent_mobileDataIsNotChanged_checkMobileDataSwitch() {
+ whenever(internetDetailsContentController.hasActiveSubIdOnDds()).thenReturn(true)
+ whenever(internetDetailsContentController.isCarrierNetworkActive).thenReturn(true)
+ whenever(internetDetailsContentController.isMobileDataEnabled).thenReturn(false)
+ mobileToggleSwitch!!.isChecked = false
+ internetDetailsContentManager.updateContent(true)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(mobileToggleSwitch!!.isChecked).isFalse()
+ }
+ }
+
+ @Test
+ fun updateContent_wifiOnAndHasInternetWifi_showConnectedWifi() {
+ whenever(internetDetailsContentController.activeAutoSwitchNonDdsSubId).thenReturn(1)
+ whenever(internetDetailsContentController.hasActiveSubIdOnDds()).thenReturn(true)
+
+ // The preconditions WiFi ON and Internet WiFi are already in setUp()
+ whenever(internetDetailsContentController.activeNetworkIsCellular()).thenReturn(false)
+
+ internetDetailsContentManager.updateContent(true)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(connectedWifi!!.visibility).isEqualTo(View.VISIBLE)
+ val secondaryLayout =
+ contentView.requireViewById<LinearLayout>(R.id.secondary_mobile_network_layout)
+ assertThat(secondaryLayout.visibility).isEqualTo(View.GONE)
+ }
+ }
+
+ @Test
+ fun updateContent_wifiOnAndNoConnectedWifi_hideConnectedWifi() {
+ // The precondition WiFi ON is already in setUp()
+ internetDetailsContentManager.connectedWifiEntry = null
+ whenever(internetDetailsContentController.activeNetworkIsCellular()).thenReturn(false)
+
+ internetDetailsContentManager.updateContent(false)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(connectedWifi!!.visibility).isEqualTo(View.GONE)
+ }
+ }
+
+ @Test
+ fun updateContent_wifiOnAndNoWifiEntry_showWifiListAndSeeAllArea() {
+ // The precondition WiFi ON is already in setUp()
+ internetDetailsContentManager.connectedWifiEntry = null
+ internetDetailsContentManager.wifiEntriesCount = 0
+ internetDetailsContentManager.updateContent(false)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(connectedWifi!!.visibility).isEqualTo(View.GONE)
+ // Show a blank block to fix the details content height even if there is no WiFi list
+ assertThat(wifiList!!.visibility).isEqualTo(View.VISIBLE)
+ verify(internetAdapter).setMaxEntriesCount(3)
+ assertThat(seeAll!!.visibility).isEqualTo(View.INVISIBLE)
+ }
+ }
+
+ @Test
+ fun updateContent_wifiOnAndOneWifiEntry_showWifiListAndSeeAllArea() {
+ // The precondition WiFi ON is already in setUp()
+ internetDetailsContentManager.connectedWifiEntry = null
+ internetDetailsContentManager.wifiEntriesCount = 1
+ internetDetailsContentManager.updateContent(false)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(connectedWifi!!.visibility).isEqualTo(View.GONE)
+ // Show a blank block to fix the details content height even if there is no WiFi list
+ assertThat(wifiList!!.visibility).isEqualTo(View.VISIBLE)
+ verify(internetAdapter).setMaxEntriesCount(3)
+ assertThat(seeAll!!.visibility).isEqualTo(View.INVISIBLE)
+ }
+ }
+
+ @Test
+ fun updateContent_wifiOnAndHasConnectedWifi_showAllWifiAndSeeAllArea() {
+ // The preconditions WiFi ON and WiFi entries are already in setUp()
+ internetDetailsContentManager.wifiEntriesCount = 0
+ internetDetailsContentManager.updateContent(false)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(connectedWifi!!.visibility).isEqualTo(View.VISIBLE)
+ // Show a blank block to fix the details content height even if there is no WiFi list
+ assertThat(wifiList!!.visibility).isEqualTo(View.VISIBLE)
+ verify(internetAdapter).setMaxEntriesCount(2)
+ assertThat(seeAll!!.visibility).isEqualTo(View.INVISIBLE)
+ }
+ }
+
+ @Test
+ fun updateContent_wifiOnAndHasMaxWifiList_showWifiListAndSeeAll() {
+ // The preconditions WiFi ON and WiFi entries are already in setUp()
+ internetDetailsContentManager.connectedWifiEntry = null
+ internetDetailsContentManager.wifiEntriesCount =
+ InternetDetailsContentController.MAX_WIFI_ENTRY_COUNT
+ internetDetailsContentManager.hasMoreWifiEntries = true
+ internetDetailsContentManager.updateContent(false)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(connectedWifi!!.visibility).isEqualTo(View.GONE)
+ assertThat(wifiList!!.visibility).isEqualTo(View.VISIBLE)
+ verify(internetAdapter).setMaxEntriesCount(3)
+ assertThat(seeAll!!.visibility).isEqualTo(View.VISIBLE)
+ }
+ }
+
+ @Test
+ fun updateContent_wifiOnAndHasBothWifiEntry_showBothWifiEntryAndSeeAll() {
+ // The preconditions WiFi ON and WiFi entries are already in setUp()
+ internetDetailsContentManager.wifiEntriesCount =
+ InternetDetailsContentController.MAX_WIFI_ENTRY_COUNT - 1
+ internetDetailsContentManager.hasMoreWifiEntries = true
+ internetDetailsContentManager.updateContent(false)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(connectedWifi!!.visibility).isEqualTo(View.VISIBLE)
+ assertThat(wifiList!!.visibility).isEqualTo(View.VISIBLE)
+ verify(internetAdapter).setMaxEntriesCount(2)
+ assertThat(seeAll!!.visibility).isEqualTo(View.VISIBLE)
+ }
+ }
+
+ @Test
+ fun updateContent_deviceLockedAndNoConnectedWifi_showWifiToggle() {
+ // The preconditions WiFi entries are already in setUp()
+ whenever(internetDetailsContentController.isDeviceLocked).thenReturn(true)
+ internetDetailsContentManager.connectedWifiEntry = null
+ internetDetailsContentManager.updateContent(false)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ // Show WiFi Toggle without background
+ assertThat(wifiToggle!!.visibility).isEqualTo(View.VISIBLE)
+ assertThat(wifiToggle!!.background).isNull()
+ // Hide Wi-Fi networks and See all
+ assertThat(connectedWifi!!.visibility).isEqualTo(View.GONE)
+ assertThat(wifiList!!.visibility).isEqualTo(View.GONE)
+ assertThat(seeAll!!.visibility).isEqualTo(View.GONE)
+ }
+ }
+
+ @Test
+ fun updateContent_deviceLockedAndHasConnectedWifi_showWifiToggleWithBackground() {
+ // The preconditions WiFi ON and WiFi entries are already in setUp()
+ whenever(internetDetailsContentController.isDeviceLocked).thenReturn(true)
+ internetDetailsContentManager.updateContent(false)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ // Show WiFi Toggle with highlight background
+ assertThat(wifiToggle!!.visibility).isEqualTo(View.VISIBLE)
+ assertThat(wifiToggle!!.background).isNotNull()
+ // Hide Wi-Fi networks and See all
+ assertThat(connectedWifi!!.visibility).isEqualTo(View.GONE)
+ assertThat(wifiList!!.visibility).isEqualTo(View.GONE)
+ assertThat(seeAll!!.visibility).isEqualTo(View.GONE)
+ }
+ }
+
+ @Test
+ fun updateContent_disallowChangeWifiState_disableWifiSwitch() {
+ whenever(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext))
+ .thenReturn(false)
+ createView()
+ internetDetailsContentManager.updateContent(false)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ // Disable Wi-Fi switch and show restriction message in summary.
+ assertThat(wifiToggleSwitch!!.isEnabled).isFalse()
+ assertThat(wifiToggleSummary!!.visibility).isEqualTo(View.VISIBLE)
+ assertThat(wifiToggleSummary!!.text.length).isNotEqualTo(0)
+ }
+ }
+
+ @Test
+ fun updateContent_allowChangeWifiState_enableWifiSwitch() {
+ whenever(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(true)
+ createView()
+ internetDetailsContentManager.updateContent(false)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ // Enable Wi-Fi switch and hide restriction message in summary.
+ assertThat(wifiToggleSwitch!!.isEnabled).isTrue()
+ assertThat(wifiToggleSummary!!.visibility).isEqualTo(View.GONE)
+ }
+ }
+
+ @Test
+ fun updateContent_showSecondaryDataSub() {
+ whenever(internetDetailsContentController.activeAutoSwitchNonDdsSubId).thenReturn(1)
+ whenever(internetDetailsContentController.hasActiveSubIdOnDds()).thenReturn(true)
+ whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(false)
+
+ clearInvocations(internetDetailsContentController)
+ internetDetailsContentManager.updateContent(true)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ val primaryLayout =
+ contentView.requireViewById<LinearLayout>(R.id.mobile_network_layout)
+ val secondaryLayout =
+ contentView.requireViewById<LinearLayout>(R.id.secondary_mobile_network_layout)
+
+ verify(internetDetailsContentController).getMobileNetworkSummary(1)
+ assertThat(primaryLayout.background).isNotEqualTo(secondaryLayout.background)
+ }
+ }
+
+ @Test
+ fun updateContent_wifiOn_hideWifiScanNotify() {
+ // The preconditions WiFi ON and WiFi entries are already in setUp()
+ internetDetailsContentManager.updateContent(false)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(wifiScanNotify!!.visibility).isEqualTo(View.GONE)
+ }
+
+ assertThat(wifiScanNotify!!.visibility).isEqualTo(View.GONE)
+ }
+
+ @Test
+ fun updateContent_wifiOffAndWifiScanOff_hideWifiScanNotify() {
+ whenever(internetDetailsContentController.isWifiEnabled).thenReturn(false)
+ whenever(internetDetailsContentController.isWifiScanEnabled).thenReturn(false)
+ internetDetailsContentManager.updateContent(false)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(wifiScanNotify!!.visibility).isEqualTo(View.GONE)
+ }
+
+ assertThat(wifiScanNotify!!.visibility).isEqualTo(View.GONE)
+ }
+
+ @Test
+ fun updateContent_wifiOffAndWifiScanOnAndDeviceLocked_hideWifiScanNotify() {
+ whenever(internetDetailsContentController.isWifiEnabled).thenReturn(false)
+ whenever(internetDetailsContentController.isWifiScanEnabled).thenReturn(true)
+ whenever(internetDetailsContentController.isDeviceLocked).thenReturn(true)
+ internetDetailsContentManager.updateContent(false)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(wifiScanNotify!!.visibility).isEqualTo(View.GONE)
+ }
+
+ assertThat(wifiScanNotify!!.visibility).isEqualTo(View.GONE)
+ }
+
+ @Test
+ fun updateContent_wifiOffAndWifiScanOnAndDeviceUnlocked_showWifiScanNotify() {
+ whenever(internetDetailsContentController.isWifiEnabled).thenReturn(false)
+ whenever(internetDetailsContentController.isWifiScanEnabled).thenReturn(true)
+ whenever(internetDetailsContentController.isDeviceLocked).thenReturn(false)
+ internetDetailsContentManager.updateContent(false)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(wifiScanNotify!!.visibility).isEqualTo(View.VISIBLE)
+ val wifiScanNotifyText =
+ contentView.requireViewById<TextView>(R.id.wifi_scan_notify_text)
+ assertThat(wifiScanNotifyText.text.length).isNotEqualTo(0)
+ assertThat(wifiScanNotifyText.movementMethod).isNotNull()
+ }
+ }
+
+ @Test
+ fun updateContent_wifiIsDisabled_uncheckWifiSwitch() {
+ whenever(internetDetailsContentController.isWifiEnabled).thenReturn(false)
+ wifiToggleSwitch!!.isChecked = true
+ internetDetailsContentManager.updateContent(false)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(wifiToggleSwitch!!.isChecked).isFalse()
+ }
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun updateContent_wifiIsEnabled_checkWifiSwitch() {
+ whenever(internetDetailsContentController.isWifiEnabled).thenReturn(true)
+ wifiToggleSwitch!!.isChecked = false
+ internetDetailsContentManager.updateContent(false)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(wifiToggleSwitch!!.isChecked).isTrue()
+ }
+ }
+
+ @Test
+ fun onClickSeeMoreButton_clickSeeAll_verifyLaunchNetworkSetting() {
+ seeAll!!.performClick()
+
+ verify(internetDetailsContentController)
+ .launchNetworkSetting(contentView.requireViewById(R.id.see_all_layout))
+ }
+
+ @Test
+ fun onWifiScan_isScanTrue_setProgressBarVisibleTrue() {
+ internetDetailsContentManager.isProgressBarVisible = false
+
+ internetDetailsContentManager.internetDetailsCallback.onWifiScan(true)
+
+ assertThat(internetDetailsContentManager.isProgressBarVisible).isTrue()
+ }
+
+ @Test
+ fun onWifiScan_isScanFalse_setProgressBarVisibleFalse() {
+ internetDetailsContentManager.isProgressBarVisible = true
+
+ internetDetailsContentManager.internetDetailsCallback.onWifiScan(false)
+
+ assertThat(internetDetailsContentManager.isProgressBarVisible).isFalse()
+ }
+
+ @Test
+ fun updateContent_shareWifiIntentNull_hideButton() {
+ whenever(
+ internetDetailsContentController.getConfiguratorQrCodeGeneratorIntentOrNull(
+ ArgumentMatchers.any()
+ )
+ )
+ .thenReturn(null)
+ internetDetailsContentManager.updateContent(false)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(sharedWifiButton?.visibility).isEqualTo(View.GONE)
+ }
+ }
+
+ @Test
+ fun updateContent_shareWifiShareable_showButton() {
+ whenever(
+ internetDetailsContentController.getConfiguratorQrCodeGeneratorIntentOrNull(
+ ArgumentMatchers.any()
+ )
+ )
+ .thenReturn(Intent())
+ internetDetailsContentManager.updateContent(false)
+ bgExecutor.runAllReady()
+
+ internetDetailsContentManager.internetContentData.observe(
+ internetDetailsContentManager.lifecycleOwner!!
+ ) {
+ assertThat(sharedWifiButton?.visibility).isEqualTo(View.VISIBLE)
+ }
+ }
+
+ companion object {
+ private const val TITLE = "Internet"
+ private const val MOBILE_NETWORK_TITLE = "Mobile Title"
+ private const val MOBILE_NETWORK_SUMMARY = "Mobile Summary"
+ private const val WIFI_TITLE = "Connected Wi-Fi Title"
+ private const val WIFI_SUMMARY = "Connected Wi-Fi Summary"
+ }
+}