diff --git a/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceController.java b/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceController.java index 4337b88bd53..8eb2dfcdf28 100644 --- a/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceController.java @@ -45,6 +45,12 @@ import com.android.settingslib.core.lifecycle.events.OnResume; import com.android.settingslib.net.ChartData; import com.android.settingslib.net.ChartDataLoaderCompat; +/** + * Deprecated in favor of {@link AppDataUsagePreferenceControllerV2} + * + * @deprecated + */ +@Deprecated public class AppDataUsagePreferenceController extends AppInfoPreferenceControllerBase implements LoaderManager.LoaderCallbacks, LifecycleObserver, OnResume, OnPause { diff --git a/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceControllerV2.java b/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceControllerV2.java new file mode 100644 index 00000000000..8e318bdbefb --- /dev/null +++ b/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceControllerV2.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2017 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.applications.appinfo; + +import android.content.Context; +import android.net.INetworkStatsService; +import android.net.INetworkStatsSession; +import android.net.NetworkTemplate; +import android.os.Bundle; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.text.format.DateUtils; +import android.text.format.Formatter; + +import androidx.annotation.VisibleForTesting; +import androidx.loader.app.LoaderManager; +import androidx.loader.content.Loader; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.SettingsPreferenceFragment; +import com.android.settings.Utils; +import com.android.settings.datausage.AppDataUsage; +import com.android.settings.datausage.DataUsageList; +import com.android.settings.datausage.DataUsageUtils; +import com.android.settingslib.AppItem; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnPause; +import com.android.settingslib.core.lifecycle.events.OnResume; +import com.android.settingslib.net.ChartData; +import com.android.settingslib.net.ChartDataLoaderCompat; + +public class AppDataUsagePreferenceControllerV2 extends AppInfoPreferenceControllerBase + implements LoaderManager.LoaderCallbacks, LifecycleObserver, OnResume, OnPause { + + private ChartData mChartData; + private INetworkStatsSession mStatsSession; + + public AppDataUsagePreferenceControllerV2(Context context,String key) { + super(context, key); + } + + @Override + public int getAvailabilityStatus() { + return isBandwidthControlEnabled() ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + if (isAvailable()) { + final INetworkStatsService statsService = INetworkStatsService.Stub.asInterface( + ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); + try { + mStatsSession = statsService.openSession(); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + } + + @Override + public void updateState(Preference preference) { + preference.setSummary(getDataSummary()); + } + + @Override + public void onResume() { + if (mStatsSession != null) { + final int uid = mParent.getAppEntry().info.uid; + final AppItem app = new AppItem(uid); + app.addUid(uid); + mParent.getLoaderManager().restartLoader(mParent.LOADER_CHART_DATA, + ChartDataLoaderCompat.buildArgs(getTemplate(mContext), app), + this); + } + } + + @Override + public void onPause() { + mParent.getLoaderManager().destroyLoader(mParent.LOADER_CHART_DATA); + } + + @Override + public Loader onCreateLoader(int id, Bundle args) { + return new ChartDataLoaderCompat(mContext, mStatsSession, args); + } + + @Override + public void onLoadFinished(Loader loader, ChartData data) { + mChartData = data; + updateState(mPreference); + } + + @Override + public void onLoaderReset(Loader loader) { + // Leave last result. + } + + @Override + protected Class getDetailFragmentClass() { + return AppDataUsage.class; + } + + private CharSequence getDataSummary() { + if (mChartData != null) { + final long totalBytes = mChartData.detail.getTotalBytes(); + if (totalBytes == 0) { + return mContext.getString(R.string.no_data_usage); + } + return mContext.getString(R.string.data_summary_format, + Formatter.formatFileSize(mContext, totalBytes), + DateUtils.formatDateTime(mContext, mChartData.detail.getStart(), + DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH)); + } + return mContext.getString(R.string.computing_size); + } + + private static NetworkTemplate getTemplate(Context context) { + if (DataUsageUtils.hasReadyMobileRadio(context)) { + return NetworkTemplate.buildTemplateMobileWildcard(); + } + if (DataUsageUtils.hasWifiRadio(context)) { + return NetworkTemplate.buildTemplateWifiWildcard(); + } + return NetworkTemplate.buildTemplateEthernet(); + } + + @VisibleForTesting + boolean isBandwidthControlEnabled() { + return Utils.isBandwidthControlEnabled(); + } + +} diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceControllerV2Test.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceControllerV2Test.java new file mode 100644 index 00000000000..0023250849b --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceControllerV2Test.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2018 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.applications.appinfo; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.net.ConnectivityManager; +import android.net.INetworkStatsSession; +import android.os.Bundle; + +import com.android.settings.core.BasePreferenceController; +import com.android.settings.datausage.AppDataUsage; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settingslib.applications.ApplicationsState.AppEntry; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.util.ReflectionHelpers; + +import androidx.loader.app.LoaderManager; +import androidx.preference.Preference; + +@RunWith(SettingsRobolectricTestRunner.class) +public class AppDataUsagePreferenceControllerV2Test { + + @Mock + private LoaderManager mLoaderManager; + @Mock + private AppInfoDashboardFragment mFragment; + + private Context mContext; + private AppDataUsagePreferenceControllerV2 mController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application.getApplicationContext()); + mController = spy(new AppDataUsagePreferenceControllerV2(mContext, "test_key")); + mController.setParentFragment(mFragment); + } + + @Test + public void getAvailabilityStatus_bandwidthControlEnabled_shouldReturnAvailable() { + doReturn(true).when(mController).isBandwidthControlEnabled(); + + assertThat(mController.getAvailabilityStatus()) + .isEqualTo(BasePreferenceController.AVAILABLE); + } + + @Test + public void getAvailabilityStatus_bandwidthControlDisabled_shouldReturnDisabled() { + doReturn(false).when(mController).isBandwidthControlEnabled(); + + assertThat(mController.getAvailabilityStatus()) + .isEqualTo(BasePreferenceController.CONDITIONALLY_UNAVAILABLE); + } + + @Test + public void onResume_noSession_shouldNotRestartDataLoader() { + doReturn(mLoaderManager).when(mFragment).getLoaderManager(); + + mController.onResume(); + + verify(mLoaderManager, never()).restartLoader( + AppInfoDashboardFragment.LOADER_CHART_DATA, Bundle.EMPTY, mController); + } + + @Test + public void onResume_hasSession_shouldRestartDataLoader() { + final ConnectivityManager connectivityManager = mock(ConnectivityManager.class); + when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)) + .thenReturn(connectivityManager); + when(connectivityManager.isNetworkSupported(anyInt())).thenReturn(true); + doReturn(mLoaderManager).when(mFragment).getLoaderManager(); + ReflectionHelpers.setField(mController, "mStatsSession", mock(INetworkStatsSession.class)); + final AppEntry appEntry = mock(AppEntry.class); + appEntry.info = new ApplicationInfo(); + when(mFragment.getAppEntry()).thenReturn(appEntry); + + mController.onResume(); + + verify(mLoaderManager).restartLoader( + eq(AppInfoDashboardFragment.LOADER_CHART_DATA), any(Bundle.class), eq(mController)); + } + + @Test + public void onPause_shouldDestroyDataLoader() { + doReturn(mLoaderManager).when(mFragment).getLoaderManager(); + + mController.onPause(); + + verify(mLoaderManager).destroyLoader(AppInfoDashboardFragment.LOADER_CHART_DATA); + } + + @Test + public void getDetailFragmentClass_shouldReturnAppDataUsage() { + assertThat(mController.getDetailFragmentClass()).isEqualTo(AppDataUsage.class); + } + + @Test + public void updateState_shouldUpdatePreferenceSummary() { + final Preference preference = mock(Preference.class); + + mController.updateState(preference); + + verify(preference).setSummary(any()); + } +}