Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 51dc7210 authored by Chaohui Wang's avatar Chaohui Wang Committed by Android (Google) Code Review
Browse files

Merge "Refresh DataUsageSummaryPreferenceController" into main

parents 01056301 513ca3a8
Loading
Loading
Loading
Loading
+19 −20
Original line number Diff line number Diff line
@@ -21,20 +21,18 @@ import android.net.NetworkPolicy
import android.net.NetworkTemplate
import android.text.TextUtils
import android.util.Log
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.preference.PreferenceScreen
import com.android.settings.R
import com.android.settings.datausage.lib.DataUsageLib.getMobileTemplate
import com.android.settings.datausage.lib.INetworkCycleDataRepository
import com.android.settings.datausage.lib.NetworkCycleDataRepository
import com.android.settings.network.ProxySubscriptionManager
import com.android.settings.network.policy.NetworkPolicyRepository
import com.android.settings.network.telephony.TelephonyBasePreferenceController
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
import kotlin.math.max
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

/**
@@ -47,6 +45,7 @@ open class DataUsageSummaryPreferenceController @JvmOverloads constructor(
    subId: Int,
    private val proxySubscriptionManager: ProxySubscriptionManager =
        ProxySubscriptionManager.getInstance(context),
    private val networkPolicyRepository: NetworkPolicyRepository = NetworkPolicyRepository(context),
    private val networkCycleDataRepositoryFactory: (
        template: NetworkTemplate,
    ) -> INetworkCycleDataRepository = { NetworkCycleDataRepository(context, it) },
@@ -64,37 +63,37 @@ open class DataUsageSummaryPreferenceController @JvmOverloads constructor(
            proxySubscriptionManager.getAccessibleSubscriptionInfo(mSubId)
        } else null
    }

    private val networkTemplate by lazy { getMobileTemplate(mContext, mSubId) }

    private val networkCycleDataRepository by lazy {
        networkCycleDataRepositoryFactory(getMobileTemplate(mContext, mSubId))
        networkCycleDataRepositoryFactory(networkTemplate)
    }
    private val policy by lazy { networkCycleDataRepository.getPolicy() }

    private lateinit var preference: DataUsageSummaryPreference

    override fun getAvailabilityStatus(subId: Int) =
        if (subInfo != null && policy != null) AVAILABLE else CONDITIONALLY_UNAVAILABLE
        if (subInfo != null) AVAILABLE else CONDITIONALLY_UNAVAILABLE

    override fun displayPreference(screen: PreferenceScreen) {
        super.displayPreference(screen)
        preference = screen.findPreference(preferenceKey)!!
        policy?.let {
            preference.setLimitInfo(it.getLimitInfo())
            val dataBarSize = max(it.limitBytes, it.warningBytes)
            if (dataBarSize > NetworkPolicy.WARNING_DISABLED) {
                setDataBarSize(dataBarSize)
            }
        }
    }

    override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
        viewLifecycleOwner.lifecycleScope.launch {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                update()
            }
        networkPolicyRepository.networkPolicyFlow(networkTemplate)
            .collectLatestWithLifecycle(viewLifecycleOwner) { policy ->
                preference.isVisible = subInfo != null && policy != null
                if (policy != null) update(policy)
            }
    }

    private suspend fun update() {
        val policy = policy ?: return
    private suspend fun update(policy: NetworkPolicy) {
        preference.setLimitInfo(policy.getLimitInfo())
        val dataBarSize = max(policy.limitBytes, policy.warningBytes)
        if (dataBarSize > NetworkPolicy.WARNING_DISABLED) {
            setDataBarSize(dataBarSize)
        }
        val dataPlanInfo = withContext(Dispatchers.Default) {
            dataPlanRepositoryFactory(networkCycleDataRepository).getDataPlanInfo(
                policy = policy,
+37 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settings.network.policy

import android.content.Context
import android.net.NetworkPolicy
import android.net.NetworkPolicyManager
import android.net.NetworkTemplate
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn

class NetworkPolicyRepository(context: Context) {
    private val networkPolicyManager = context.getSystemService(NetworkPolicyManager::class.java)!!

    fun getNetworkPolicy(networkTemplate: NetworkTemplate): NetworkPolicy? =
        networkPolicyManager.networkPolicies.find { policy -> policy.template == networkTemplate }

    fun networkPolicyFlow(networkTemplate: NetworkTemplate): Flow<NetworkPolicy?> = flow {
        emit(getNetworkPolicy(networkTemplate))
    }.flowOn(Dispatchers.Default)
}
+31 −14
Original line number Diff line number Diff line
@@ -32,8 +32,10 @@ import com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILA
import com.android.settings.datausage.lib.INetworkCycleDataRepository
import com.android.settings.datausage.lib.NetworkUsageData
import com.android.settings.network.ProxySubscriptionManager
import com.android.settings.network.policy.NetworkPolicyRepository
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Test
@@ -41,6 +43,7 @@ import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.clearInvocations
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
@@ -69,6 +72,10 @@ class DataUsageSummaryPreferenceControllerTest {
        on { get() } doReturn mockSubscriptionManager
    }

    private val mockNetworkPolicyRepository = mock<NetworkPolicyRepository> {
        on { networkPolicyFlow(any()) } doAnswer { flowOf(policy) }
    }

    private val fakeNetworkCycleDataRepository = object : INetworkCycleDataRepository {
        override fun getCycles(): List<Range<Long>> = emptyList()
        override fun getPolicy() = policy
@@ -86,6 +93,7 @@ class DataUsageSummaryPreferenceControllerTest {
        context = context,
        subId = SUB_ID,
        proxySubscriptionManager = mockProxySubscriptionManager,
        networkPolicyRepository = mockNetworkPolicyRepository,
        networkCycleDataRepositoryFactory = { fakeNetworkCycleDataRepository },
        dataPlanRepositoryFactory = { fakeDataPlanRepository },
    )
@@ -112,7 +120,7 @@ class DataUsageSummaryPreferenceControllerTest {
    }

    @Test
    fun getAvailabilityStatus_hasSubInfoAndPolicy_available() {
    fun getAvailabilityStatus_hasSubInfo_available() {
        mockProxySubscriptionManager.stub {
            on { getAccessibleSubscriptionInfo(SUB_ID) } doReturn SubscriptionInfo.Builder().build()
        }
@@ -134,36 +142,43 @@ class DataUsageSummaryPreferenceControllerTest {
    }

    @Test
    fun getAvailabilityStatus_noPolicy_conditionallyUnavailable() {
    fun onViewCreated_noPolicy_setInvisible() = runBlocking {
        policy = null
        controller.displayPreference(preferenceScreen)
        clearInvocations(preference)

        val availabilityStatus = controller.getAvailabilityStatus(SUB_ID)
        controller.onViewCreated(TestLifecycleOwner())
        delay(100)

        assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE)
        verify(preference).isVisible = false
    }

    @Test
    fun displayPreference_policyHasNoLimitInfo() {
    fun onViewCreated_policyHasNoLimitInfo() = runBlocking {
        policy = mock<NetworkPolicy>().apply {
            warningBytes = NetworkPolicy.WARNING_DISABLED
            limitBytes = NetworkPolicy.LIMIT_DISABLED
        }

        controller.displayPreference(preferenceScreen)

        controller.onViewCreated(TestLifecycleOwner())
        delay(100)

        verify(preference).setLimitInfo(null)
        verify(preference, never()).setLabels(any(), any())
    }

    @Test
    fun displayPreference_policyWarningOnly() {
    fun onViewCreated_policyWarningOnly() = runBlocking {
        policy = mock<NetworkPolicy>().apply {
            warningBytes = 1L
            limitBytes = NetworkPolicy.LIMIT_DISABLED
        }

        controller.displayPreference(preferenceScreen)

        controller.onViewCreated(TestLifecycleOwner())
        delay(100)

        val limitInfo = argumentCaptor {
            verify(preference).setLimitInfo(capture())
        }.firstValue.toString()
@@ -172,14 +187,16 @@ class DataUsageSummaryPreferenceControllerTest {
    }

    @Test
    fun displayPreference_policyLimitOnly() {
    fun onViewCreated_policyLimitOnly() = runBlocking {
        policy = mock<NetworkPolicy>().apply {
            warningBytes = NetworkPolicy.WARNING_DISABLED
            limitBytes = 1L
        }

        controller.displayPreference(preferenceScreen)

        controller.onViewCreated(TestLifecycleOwner())
        delay(100)

        val limitInfo = argumentCaptor {
            verify(preference).setLimitInfo(capture())
        }.firstValue.toString()
@@ -188,14 +205,16 @@ class DataUsageSummaryPreferenceControllerTest {
    }

    @Test
    fun displayPreference_policyHasWarningAndLimit() {
    fun onViewCreated_policyHasWarningAndLimit() = runBlocking {
        policy = mock<NetworkPolicy>().apply {
            warningBytes = BillingCycleSettings.GIB_IN_BYTES / 2
            limitBytes = BillingCycleSettings.GIB_IN_BYTES
        }

        controller.displayPreference(preferenceScreen)

        controller.onViewCreated(TestLifecycleOwner())
        delay(100)

        val limitInfo = argumentCaptor {
            verify(preference).setLimitInfo(capture())
        }.firstValue.toString()
@@ -207,7 +226,6 @@ class DataUsageSummaryPreferenceControllerTest {
    fun onViewCreated_emptyDataPlanInfo() = runBlocking {
        dataPlanInfo = EMPTY_DATA_PLAN_INFO
        controller.displayPreference(preferenceScreen)
        clearInvocations(preference)

        controller.onViewCreated(TestLifecycleOwner())
        delay(100)
@@ -229,7 +247,6 @@ class DataUsageSummaryPreferenceControllerTest {
    fun onViewCreated_positiveDataPlanInfo() = runBlocking {
        dataPlanInfo = POSITIVE_DATA_PLAN_INFO
        controller.displayPreference(preferenceScreen)
        clearInvocations(preference)

        controller.onViewCreated(TestLifecycleOwner())
        delay(100)
+72 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settings.network.policy

import android.content.Context
import android.net.NetworkPolicy
import android.net.NetworkPolicyManager
import android.net.NetworkTemplate
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.runBlocking
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy

@RunWith(AndroidJUnit4::class)
class NetworkPolicyRepositoryTest {

    private val mockNetworkPolicyManager = mock<NetworkPolicyManager> {
        on { networkPolicies } doReturn arrayOf(Policy1, Policy2)
    }

    private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
        on { getSystemService(NetworkPolicyManager::class.java) } doReturn mockNetworkPolicyManager
    }

    private val repository = NetworkPolicyRepository(context)

    @Test
    fun getNetworkPolicy() {
        val networkPolicy = repository.getNetworkPolicy(Template1)

        assertThat(networkPolicy).isSameInstanceAs(Policy1)
    }

    @Test
    fun networkPolicyFlow() = runBlocking {
        val networkPolicy = repository.networkPolicyFlow(Template2).firstWithTimeoutOrNull()

        assertThat(networkPolicy).isSameInstanceAs(Policy2)
    }

    private companion object {
        val Template1: NetworkTemplate =
            NetworkTemplate.Builder(NetworkTemplate.MATCH_MOBILE).build()
        val Template2: NetworkTemplate = NetworkTemplate.Builder(NetworkTemplate.MATCH_WIFI).build()
        val Policy1 = mock<NetworkPolicy>().apply {
            template = Template1
        }
        val Policy2 = mock<NetworkPolicy>().apply {
            template = Template2
        }
    }
}