1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.fuelgauge;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.mockito.Mockito.doAnswer;
22 import static org.mockito.Mockito.doReturn;
23 import static org.mockito.Mockito.spy;
24 
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.pm.ApplicationInfo;
28 import android.content.pm.PackageManager;
29 import android.os.Bundle;
30 import android.os.UserHandle;
31 import android.util.IconDrawableFactory;
32 import android.util.SparseLongArray;
33 
34 import androidx.appcompat.app.AlertDialog;
35 import androidx.fragment.app.FragmentActivity;
36 import androidx.preference.CheckBoxPreference;
37 import androidx.preference.PreferenceCategory;
38 import androidx.preference.PreferenceManager;
39 
40 import com.android.settings.SettingsActivity;
41 import com.android.settings.core.InstrumentedPreferenceFragment;
42 import com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper;
43 import com.android.settings.fuelgauge.batterytip.AppInfo;
44 import com.android.settings.fuelgauge.batterytip.BatteryDatabaseManager;
45 import com.android.settings.fuelgauge.batterytip.BatteryTipDialogFragment;
46 import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
47 import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
48 import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip;
49 import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
50 
51 import org.junit.Before;
52 import org.junit.Test;
53 import org.junit.runner.RunWith;
54 import org.mockito.ArgumentCaptor;
55 import org.mockito.Mock;
56 import org.mockito.MockitoAnnotations;
57 import org.robolectric.RobolectricTestRunner;
58 import org.robolectric.RuntimeEnvironment;
59 import org.robolectric.annotation.Config;
60 import org.robolectric.shadows.androidx.fragment.FragmentController;
61 
62 import java.util.ArrayList;
63 import java.util.List;
64 import java.util.concurrent.TimeUnit;
65 
66 @RunWith(RobolectricTestRunner.class)
67 @Config(shadows = ShadowAlertDialogCompat.class)
68 public class RestrictedAppDetailsTest {
69 
70     private static final String PACKAGE_NAME = "com.android.app";
71     private static final int USER_ID = 10;
72     private static final int UID = UserHandle.getUid(USER_ID, 234);
73     private static final String APP_NAME = "app";
74 
75 
76     @Mock
77     private PackageManager mPackageManager;
78     @Mock
79     private ApplicationInfo mApplicationInfo;
80     @Mock
81     private IconDrawableFactory mIconDrawableFactory;
82     @Mock
83     private InstrumentedPreferenceFragment mFragment;
84     @Mock
85     private BatteryDatabaseManager mBatteryDatabaseManager;
86     private PreferenceManager mPreferenceManager;
87     private RestrictedAppDetails mRestrictedAppDetails;
88     private Context mContext;
89     private AppInfo mAppInfo;
90     private Intent mIntent;
91     private CheckBoxPreference mCheckBoxPreference;
92 
93     @Before
setUp()94     public void setUp() {
95         MockitoAnnotations.initMocks(this);
96 
97         mContext = spy(RuntimeEnvironment.application);
98         mRestrictedAppDetails = spy(new RestrictedAppDetails());
99         mAppInfo = new AppInfo.Builder()
100                 .setPackageName(PACKAGE_NAME)
101                 .setUid(UID)
102                 .build();
103 
104         mPreferenceManager = new PreferenceManager(mContext);
105 
106         doReturn(mPreferenceManager).when(mRestrictedAppDetails).getPreferenceManager();
107         doReturn(mContext).when(mFragment).getContext();
108         doReturn(mContext).when(mRestrictedAppDetails).getContext();
109         mRestrictedAppDetails.mPackageManager = mPackageManager;
110         mRestrictedAppDetails.mIconDrawableFactory = mIconDrawableFactory;
111         mRestrictedAppDetails.mAppInfos = new ArrayList<>();
112         mRestrictedAppDetails.mAppInfos.add(mAppInfo);
113         mRestrictedAppDetails.mRestrictedAppListGroup = spy(new PreferenceCategory(mContext));
114         mRestrictedAppDetails.mBatteryUtils = spy(new BatteryUtils(mContext));
115         mRestrictedAppDetails.mBatteryDatabaseManager = mBatteryDatabaseManager;
116         doReturn(mPreferenceManager).when(
117                 mRestrictedAppDetails.mRestrictedAppListGroup).getPreferenceManager();
118 
119         mCheckBoxPreference = new CheckBoxPreference(mContext);
120         mCheckBoxPreference.setKey(mRestrictedAppDetails.getKeyFromAppInfo(mAppInfo));
121     }
122 
123     @Test
refreshUi_displayPreference()124     public void refreshUi_displayPreference() throws Exception {
125         doReturn(mApplicationInfo).when(mPackageManager)
126                 .getApplicationInfoAsUser(PACKAGE_NAME, 0, USER_ID);
127         doReturn(APP_NAME).when(mPackageManager).getApplicationLabel(mApplicationInfo);
128         doReturn(true).when(mRestrictedAppDetails.mBatteryUtils).isForceAppStandbyEnabled(UID,
129                 PACKAGE_NAME);
130         final SparseLongArray timestampArray = new SparseLongArray();
131         timestampArray.put(UID, System.currentTimeMillis() - TimeUnit.HOURS.toMillis(5));
132         doReturn(timestampArray).when(mBatteryDatabaseManager)
133                 .queryActionTime(AnomalyDatabaseHelper.ActionType.RESTRICTION);
134 
135         mRestrictedAppDetails.refreshUi();
136 
137         assertThat(mRestrictedAppDetails.mRestrictedAppListGroup.getPreferenceCount()).isEqualTo(1);
138         final CheckBoxPreference preference =
139                 (CheckBoxPreference) mRestrictedAppDetails.mRestrictedAppListGroup.getPreference(0);
140         assertThat(preference.getTitle()).isEqualTo(APP_NAME);
141         assertThat(preference.isChecked()).isTrue();
142         assertThat(preference.getSummary()).isEqualTo("Restricted 5 hours ago");
143     }
144 
145     @Test
startRestrictedAppDetails_startWithCorrectData()146     public void startRestrictedAppDetails_startWithCorrectData() {
147         final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
148         doAnswer(invocation -> {
149             // Get the intent in which it has the app info bundle
150             mIntent = captor.getValue();
151             return true;
152         }).when(mContext).startActivity(captor.capture());
153 
154         RestrictedAppDetails.startRestrictedAppDetails(mFragment,
155                 mRestrictedAppDetails.mAppInfos);
156 
157         final Bundle bundle = mIntent.getBundleExtra(
158                 SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS);
159         // Verify the bundle has the correct info
160         final List<AppInfo> appInfos = bundle.getParcelableArrayList(
161                 RestrictedAppDetails.EXTRA_APP_INFO_LIST);
162         assertThat(appInfos).containsExactly(mAppInfo);
163     }
164 
165     @Test
createDialogFragment_toRestrict_createRestrictDialog()166     public void createDialogFragment_toRestrict_createRestrictDialog() {
167         final BatteryTipDialogFragment dialogFragment = mRestrictedAppDetails.createDialogFragment(
168                 mAppInfo, true);
169 
170         FragmentController.setupFragment(dialogFragment, FragmentActivity.class,
171                 0 /* containerViewId */, null /* bundle */);
172 
173         final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
174         ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
175         assertThat(shadowDialog.getTitle()).isEqualTo("Restrict app?");
176     }
177 
178     @Test
createDialogFragment_toUnrestrict_createUnrestrictDialog()179     public void createDialogFragment_toUnrestrict_createUnrestrictDialog() {
180         final BatteryTipDialogFragment dialogFragment = mRestrictedAppDetails.createDialogFragment(
181                 mAppInfo, false);
182 
183         FragmentController.setupFragment(dialogFragment, FragmentActivity.class,
184                 0 /* containerViewId */, null /* bundle */);
185 
186         final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
187         ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
188         assertThat(shadowDialog.getTitle()).isEqualTo("Remove restriction?");
189     }
190 
191     @Test
onBatteryTipHandled_restrict_setChecked()192     public void onBatteryTipHandled_restrict_setChecked() {
193         final RestrictAppTip restrictAppTip = new RestrictAppTip(BatteryTip.StateType.NEW,
194                 mAppInfo);
195         mRestrictedAppDetails.mRestrictedAppListGroup.addPreference(mCheckBoxPreference);
196 
197         mRestrictedAppDetails.onBatteryTipHandled(restrictAppTip);
198 
199         assertThat(mCheckBoxPreference.isChecked()).isTrue();
200     }
201 
202     @Test
onBatteryTipHandled_unrestrict_setUnchecked()203     public void onBatteryTipHandled_unrestrict_setUnchecked() {
204         final UnrestrictAppTip unrestrictAppTip = new UnrestrictAppTip(BatteryTip.StateType.NEW,
205                 mAppInfo);
206         mRestrictedAppDetails.mRestrictedAppListGroup.addPreference(mCheckBoxPreference);
207 
208         mRestrictedAppDetails.onBatteryTipHandled(unrestrictAppTip);
209 
210         assertThat(mCheckBoxPreference.isChecked()).isFalse();
211     }
212 }
213