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

Commit 9e9dc49c authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add progress bar for Privacy screen"

parents 29934110 64f5b796
Loading
Loading
Loading
Loading
+19 −5
Original line number Diff line number Diff line
@@ -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<RuntimePermissionUsageInfo> 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() {
+27 −0
Original line number Diff line number Diff line
@@ -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<AbstractPreferenceController> buildPreferenceControllers(
            Context context, Lifecycle lifecycle) {
        final List<AbstractPreferenceController> controllers = new ArrayList<>();
+63 −4
Original line number Diff line number Diff line
@@ -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<RuntimePermissionUsageInfo> 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 */);
    }
}
+52 −16
Original line number Diff line number Diff line
@@ -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);
        mFragment.onViewCreated(new View(mContext), new Bundle());

        when(mFragment.getActivity()).thenReturn(activity);
        when(mFragment.getSettingsLifecycle()).thenReturn(mock(Lifecycle.class));
        when(mFragment.getListView()).thenReturn(mock(RecyclerView.class));
        when(activity.getActionBar()).thenReturn(actionBar);
        verify(mFragment).styleActionBar();
    }

    @Test
    public void onViewCreated_shouldInitLinearProgressBar() {
        mFragment.onViewCreated(new View(mContext), new Bundle());

        verify(mFragment).styleActionBar();
        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);
    }
}
+44 −0
Original line number Diff line number Diff line
/*
 * 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
    }
}