diff options
| -rw-r--r-- | packages/SystemUI/Android.bp | 1 | ||||
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java | 1 | ||||
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManager.kt | 991 | ||||
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsViewModel.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailedViewModel.kt) | 0 | ||||
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacy.java | 4 | ||||
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt | 17 | ||||
| -rw-r--r-- | packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt | 819 |
7 files changed, 1824 insertions, 9 deletions
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index b88ae3751d22..b358bdcf3bd4 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -285,6 +285,7 @@ filegroup { "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 23210ef0e688..340cb68a83a4 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 @@ public class InternetDetailsContentController implements AccessPointController.A 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 000000000000..c64532a2c4ba --- /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 index f239a179d79a..f239a179d79a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailedViewModel.kt +++ b/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 82367ebb8c76..401f1a4d61bf 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.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.shade.ShadeDisplayAware; import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor; @@ -207,7 +208,8 @@ public class InternetDialogDelegateLegacy implements 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 8a54648f4541..5f82e60b63ec 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.animation.Expandable 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 @@ constructor( @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 @@ constructor( 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 @@ constructor( 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 000000000000..a192446e535b --- /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" + } +} |