diff --git a/src/com/android/settings/privacy/PermissionBarChartPreferenceController.java b/src/com/android/settings/privacy/PermissionBarChartPreferenceController.java index 82464c956d7..2c6368e9468 100644 --- a/src/com/android/settings/privacy/PermissionBarChartPreferenceController.java +++ b/src/com/android/settings/privacy/PermissionBarChartPreferenceController.java @@ -31,28 +31,29 @@ import android.util.Log; import android.view.View; import androidx.annotation.NonNull; -import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.core.BasePreferenceController; +import com.android.settingslib.Utils; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.widget.BarChartInfo; import com.android.settingslib.widget.BarChartPreference; import com.android.settingslib.widget.BarViewInfo; -import com.android.settingslib.Utils; import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import java.util.concurrent.Executors; public class PermissionBarChartPreferenceController extends BasePreferenceController implements - PermissionControllerManager.OnPermissionUsageResultCallback { + PermissionControllerManager.OnPermissionUsageResultCallback, LifecycleObserver, OnStart { private static final String TAG = "BarChartPreferenceCtl"; private PackageManager mPackageManager; + private PrivacyDashboardFragment mParent; private BarChartPreference mBarChartPreference; private List mOldUsageInfos; @@ -62,6 +63,10 @@ public class PermissionBarChartPreferenceController extends BasePreferenceContro mPackageManager = context.getPackageManager(); } + public void setFragment(PrivacyDashboardFragment fragment) { + mParent = fragment; + } + @Override public int getAvailabilityStatus() { return Boolean.parseBoolean( @@ -90,7 +95,13 @@ public class PermissionBarChartPreferenceController extends BasePreferenceContro } @Override - public void updateState(Preference preference) { + public void onStart() { + if (!isAvailable()) { + return; + } + + mBarChartPreference.updateLoadingState(true /* isLoading */); + mParent.setLoadingEnabled(true /* enabled */); retrievePermissionUsageData(); } @@ -104,6 +115,9 @@ public class PermissionBarChartPreferenceController extends BasePreferenceContro mBarChartPreference.setBarViewInfos(createBarViews(usageInfos)); mOldUsageInfos = usageInfos; } + + mBarChartPreference.updateLoadingState(false /* isLoading */); + mParent.setLoadingEnabled(false /* enabled */); } private void retrievePermissionUsageData() { diff --git a/src/com/android/settings/privacy/PrivacyDashboardFragment.java b/src/com/android/settings/privacy/PrivacyDashboardFragment.java index 5823407d2c0..be6701e05b1 100644 --- a/src/com/android/settings/privacy/PrivacyDashboardFragment.java +++ b/src/com/android/settings/privacy/PrivacyDashboardFragment.java @@ -48,6 +48,11 @@ public class PrivacyDashboardFragment extends DashboardFragment { private static final String KEY_NOTIFICATION_WORK_PROFILE_NOTIFICATIONS = "privacy_lock_screen_work_profile_notifications"; + @VisibleForTesting + View mProgressHeader; + @VisibleForTesting + View mProgressAnimation; + @Override public int getMetricsCategory() { return SettingsEnums.TOP_LEVEL_PRIVACY; @@ -73,10 +78,17 @@ public class PrivacyDashboardFragment extends DashboardFragment { return buildPreferenceControllers(context, getSettingsLifecycle()); } + @Override + public void onAttach(Context context) { + super.onAttach(context); + use(PermissionBarChartPreferenceController.class).setFragment(this /* fragment */); + } + @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); styleActionBar(); + initLoadingBar(); } @VisibleForTesting @@ -97,6 +109,21 @@ public class PrivacyDashboardFragment extends DashboardFragment { } } + @VisibleForTesting + void initLoadingBar() { + mProgressHeader = setPinnedHeaderView(R.layout.progress_header); + mProgressAnimation = mProgressHeader.findViewById(R.id.progress_bar_animation); + setLoadingEnabled(false); + } + + @VisibleForTesting + void setLoadingEnabled(boolean enabled) { + if (mProgressHeader != null && mProgressAnimation != null) { + mProgressHeader.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE); + mProgressAnimation.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE); + } + } + private static List buildPreferenceControllers( Context context, Lifecycle lifecycle) { final List controllers = new ArrayList<>(); diff --git a/tests/robotests/src/com/android/settings/privacy/PermissionBarChartPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/privacy/PermissionBarChartPreferenceControllerTest.java index b9476304b72..f9913f671d8 100644 --- a/tests/robotests/src/com/android/settings/privacy/PermissionBarChartPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/privacy/PermissionBarChartPreferenceControllerTest.java @@ -22,18 +22,25 @@ import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; +import android.content.pm.UserInfo; +import android.os.UserManager; import android.permission.RuntimePermissionUsageInfo; import android.provider.DeviceConfig; import androidx.preference.PreferenceScreen; +import com.android.internal.widget.LockPatternUtils; +import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowDeviceConfig; +import com.android.settings.testutils.shadow.ShadowPermissionControllerManager; +import com.android.settings.testutils.shadow.ShadowUserManager; import com.android.settingslib.widget.BarChartInfo; import com.android.settingslib.widget.BarChartPreference; import com.android.settingslib.widget.BarViewInfo; @@ -47,26 +54,41 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; +import org.robolectric.shadow.api.Shadow; +import org.robolectric.shadows.androidx.fragment.FragmentController; import java.util.ArrayList; import java.util.List; @RunWith(RobolectricTestRunner.class) -@Config(shadows = {ShadowDeviceConfig.class}) +@Config(shadows = {ShadowDeviceConfig.class, ShadowUserManager.class, + ShadowPermissionControllerManager.class}) public class PermissionBarChartPreferenceControllerTest { @Mock private PreferenceScreen mScreen; + @Mock + private LockPatternUtils mLockPatternUtils; private PermissionBarChartPreferenceController mController; private BarChartPreference mPreference; + private PrivacyDashboardFragment mFragment; @Before public void setUp() { MockitoAnnotations.initMocks(this); - Context Context = RuntimeEnvironment.application; - mController = new PermissionBarChartPreferenceController(Context, "test_key"); - mPreference = spy(new BarChartPreference(Context)); + final Context context = RuntimeEnvironment.application; + final UserManager userManager = context.getSystemService(UserManager.class); + final ShadowUserManager shadowUserManager = Shadow.extract(userManager); + shadowUserManager.addProfile(new UserInfo(123, null, 0)); + when(FakeFeatureFactory.setupForTest().securityFeatureProvider.getLockPatternUtils( + any(Context.class))).thenReturn(mLockPatternUtils); + + mController = spy(new PermissionBarChartPreferenceController(context, "test_key")); + mFragment = spy(FragmentController.of(new PrivacyDashboardFragment()) + .create().start().get()); + mController.setFragment(mFragment); + mPreference = spy(new BarChartPreference(context)); when(mScreen.findPreference(mController.getPreferenceKey())) .thenReturn((BarChartPreference) mPreference); } @@ -130,4 +152,41 @@ public class PermissionBarChartPreferenceControllerTest { verify(mPreference, times(1)).setBarViewInfos(any(BarViewInfo[].class)); } + + @Test + public void onStart_permissionHubEnabled_shouldShowProgressBar() { + DeviceConfig.setProperty(DeviceConfig.Privacy.NAMESPACE, + DeviceConfig.Privacy.PROPERTY_PERMISSIONS_HUB_ENABLED, "true", true); + mController.displayPreference(mScreen); + + mController.onStart(); + + verify(mFragment).setLoadingEnabled(true /* enabled */); + verify(mPreference).updateLoadingState(true /* isLoading */); + } + + @Test + public void onStart_permissionHubDisabled_shouldNotShowProgressBar() { + DeviceConfig.setProperty(DeviceConfig.Privacy.NAMESPACE, + DeviceConfig.Privacy.PROPERTY_PERMISSIONS_HUB_ENABLED, "false", false); + + mController.onStart(); + + verify(mFragment, never()).setLoadingEnabled(true /* enabled */); + verify(mPreference, never()).updateLoadingState(true /* isLoading */); + } + + @Test + public void onPermissionUsageResult_shouldHideProgressBar() { + final List infos1 = new ArrayList<>(); + final RuntimePermissionUsageInfo info1 = + new RuntimePermissionUsageInfo("permission 1", 10); + infos1.add(info1); + mController.displayPreference(mScreen); + + mController.onPermissionUsageResult(infos1); + + verify(mFragment).setLoadingEnabled(false /* enabled */); + verify(mPreference).updateLoadingState(false /* isLoading */); + } } diff --git a/tests/robotests/src/com/android/settings/privacy/PrivacyDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/privacy/PrivacyDashboardFragmentTest.java index d073e9b80b5..161c39a772c 100644 --- a/tests/robotests/src/com/android/settings/privacy/PrivacyDashboardFragmentTest.java +++ b/tests/robotests/src/com/android/settings/privacy/PrivacyDashboardFragmentTest.java @@ -16,53 +16,89 @@ package com.android.settings.privacy; -import static org.mockito.Mockito.mock; +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.ActionBar; import android.content.Context; +import android.content.pm.UserInfo; import android.os.Bundle; +import android.os.UserManager; +import android.permission.PermissionControllerManager; import android.view.View; -import androidx.fragment.app.FragmentActivity; -import androidx.recyclerview.widget.RecyclerView; - -import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.internal.widget.LockPatternUtils; +import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.testutils.shadow.ShadowPermissionControllerManager; +import com.android.settings.testutils.shadow.ShadowUserManager; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.robolectric.Robolectric; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.shadow.api.Shadow; +import org.robolectric.shadows.androidx.fragment.FragmentController; + @RunWith(RobolectricTestRunner.class) +@Config(shadows = {ShadowUserManager.class, ShadowPermissionControllerManager.class}) public class PrivacyDashboardFragmentTest { + @Mock + private LockPatternUtils mLockPatternUtils; + @Mock + private PermissionControllerManager mPCM; + private Context mContext; private PrivacyDashboardFragment mFragment; @Before public void setUp() { + MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; - mFragment = spy(new PrivacyDashboardFragment()); + final UserManager userManager = mContext.getSystemService(UserManager.class); + final ShadowUserManager shadowUserManager = Shadow.extract(userManager); + shadowUserManager.addProfile(new UserInfo(123, null, 0)); + when(FakeFeatureFactory.setupForTest().securityFeatureProvider.getLockPatternUtils( + any(Context.class))).thenReturn(mLockPatternUtils); + mFragment = spy(FragmentController.of(new PrivacyDashboardFragment()) + .create().start().get()); } @Test public void onViewCreated_shouldCallStyleActionBar() { - final FragmentActivity activity = spy( - Robolectric.buildActivity(FragmentActivity.class).get()); - final ActionBar actionBar = mock(ActionBar.class); - - when(mFragment.getActivity()).thenReturn(activity); - when(mFragment.getSettingsLifecycle()).thenReturn(mock(Lifecycle.class)); - when(mFragment.getListView()).thenReturn(mock(RecyclerView.class)); - when(activity.getActionBar()).thenReturn(actionBar); - mFragment.onViewCreated(new View(mContext), new Bundle()); verify(mFragment).styleActionBar(); } + + @Test + public void onViewCreated_shouldInitLinearProgressBar() { + mFragment.onViewCreated(new View(mContext), new Bundle()); + + verify(mFragment).initLoadingBar(); + } + + @Test + public void updateLinearProgressbar_isVisible_shouldShowProgressBar() { + mFragment.setLoadingEnabled(true /* enabled */); + + assertThat(mFragment.mProgressHeader.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mFragment.mProgressAnimation.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void updateLinearProgressbar_isInVisible_shouldHideProgressBar() { + mFragment.setLoadingEnabled(false /* enabled */); + + assertThat(mFragment.mProgressHeader.getVisibility()).isEqualTo(View.INVISIBLE); + assertThat(mFragment.mProgressAnimation.getVisibility()).isEqualTo(View.INVISIBLE); + } } diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowPermissionControllerManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowPermissionControllerManager.java new file mode 100644 index 00000000000..091b244bdca --- /dev/null +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowPermissionControllerManager.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019 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.testutils.shadow; + +import android.annotation.CallbackExecutor; +import android.content.Context; +import android.permission.PermissionControllerManager; + +import androidx.annotation.NonNull; + +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; + +import java.util.concurrent.Executor; + +@Implements(PermissionControllerManager.class) +public class ShadowPermissionControllerManager { + + protected void __constructor__(Context context) { + // no nothing, everything is shadowed + } + + @Implementation + public void getPermissionUsages(boolean countSystem, long numMillis, + @NonNull @CallbackExecutor Executor executor, + @NonNull PermissionControllerManager.OnPermissionUsageResultCallback callback) { + + // Do nothing + } +}