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     @Mock private PackageManager mPackageManager;
76     @Mock private ApplicationInfo mApplicationInfo;
77     @Mock private IconDrawableFactory mIconDrawableFactory;
78     @Mock private InstrumentedPreferenceFragment mFragment;
79     @Mock private BatteryDatabaseManager mBatteryDatabaseManager;
80     private PreferenceManager mPreferenceManager;
81     private RestrictedAppDetails mRestrictedAppDetails;
82     private Context mContext;
83     private AppInfo mAppInfo;
84     private Intent mIntent;
85     private CheckBoxPreference mCheckBoxPreference;
86 
87     @Before
setUp()88     public void setUp() {
89         MockitoAnnotations.initMocks(this);
90 
91         mContext = spy(RuntimeEnvironment.application);
92         mRestrictedAppDetails = spy(new RestrictedAppDetails());
93         mAppInfo = new AppInfo.Builder().setPackageName(PACKAGE_NAME).setUid(UID).build();
94 
95         mPreferenceManager = new PreferenceManager(mContext);
96 
97         doReturn(mPreferenceManager).when(mRestrictedAppDetails).getPreferenceManager();
98         doReturn(mContext).when(mFragment).getContext();
99         doReturn(mContext).when(mRestrictedAppDetails).getContext();
100         mRestrictedAppDetails.mPackageManager = mPackageManager;
101         mRestrictedAppDetails.mIconDrawableFactory = mIconDrawableFactory;
102         mRestrictedAppDetails.mAppInfos = new ArrayList<>();
103         mRestrictedAppDetails.mAppInfos.add(mAppInfo);
104         mRestrictedAppDetails.mRestrictedAppListGroup = spy(new PreferenceCategory(mContext));
105         mRestrictedAppDetails.mBatteryUtils = spy(new BatteryUtils(mContext));
106         mRestrictedAppDetails.mBatteryDatabaseManager = mBatteryDatabaseManager;
107         doReturn(mPreferenceManager)
108                 .when(mRestrictedAppDetails.mRestrictedAppListGroup)
109                 .getPreferenceManager();
110 
111         mCheckBoxPreference = new CheckBoxPreference(mContext);
112         mCheckBoxPreference.setKey(mRestrictedAppDetails.getKeyFromAppInfo(mAppInfo));
113     }
114 
115     @Test
refreshUi_displayPreference()116     public void refreshUi_displayPreference() throws Exception {
117         doReturn(mApplicationInfo)
118                 .when(mPackageManager)
119                 .getApplicationInfoAsUser(PACKAGE_NAME, 0, USER_ID);
120         doReturn(APP_NAME).when(mPackageManager).getApplicationLabel(mApplicationInfo);
121         doReturn(true)
122                 .when(mRestrictedAppDetails.mBatteryUtils)
123                 .isForceAppStandbyEnabled(UID, PACKAGE_NAME);
124         final SparseLongArray timestampArray = new SparseLongArray();
125         timestampArray.put(UID, System.currentTimeMillis() - TimeUnit.HOURS.toMillis(5));
126         doReturn(timestampArray)
127                 .when(mBatteryDatabaseManager)
128                 .queryActionTime(AnomalyDatabaseHelper.ActionType.RESTRICTION);
129 
130         mRestrictedAppDetails.refreshUi();
131 
132         assertThat(mRestrictedAppDetails.mRestrictedAppListGroup.getPreferenceCount()).isEqualTo(1);
133         final CheckBoxPreference preference =
134                 (CheckBoxPreference) mRestrictedAppDetails.mRestrictedAppListGroup.getPreference(0);
135         assertThat(preference.getTitle()).isEqualTo(APP_NAME);
136         assertThat(preference.isChecked()).isTrue();
137         assertThat(preference.getSummary()).isEqualTo("Restricted 5 hours ago");
138     }
139 
140     @Test
startRestrictedAppDetails_startWithCorrectData()141     public void startRestrictedAppDetails_startWithCorrectData() {
142         final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
143         doAnswer(
144                         invocation -> {
145                             // Get the intent in which it has the app info bundle
146                             mIntent = captor.getValue();
147                             return true;
148                         })
149                 .when(mContext)
150                 .startActivity(captor.capture());
151 
152         RestrictedAppDetails.startRestrictedAppDetails(mFragment, mRestrictedAppDetails.mAppInfos);
153 
154         final Bundle bundle =
155                 mIntent.getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS);
156         // Verify the bundle has the correct info
157         final List<AppInfo> appInfos =
158                 bundle.getParcelableArrayList(RestrictedAppDetails.EXTRA_APP_INFO_LIST);
159         assertThat(appInfos).containsExactly(mAppInfo);
160     }
161 
162     @Test
createDialogFragment_toRestrict_createRestrictDialog()163     public void createDialogFragment_toRestrict_createRestrictDialog() {
164         final BatteryTipDialogFragment dialogFragment =
165                 mRestrictedAppDetails.createDialogFragment(mAppInfo, true);
166 
167         FragmentController.setupFragment(
168                 dialogFragment, FragmentActivity.class, 0 /* containerViewId */, null /* bundle */);
169 
170         final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
171         ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
172         assertThat(shadowDialog.getTitle()).isEqualTo("Restrict app?");
173     }
174 
175     @Test
createDialogFragment_toUnrestrict_createUnrestrictDialog()176     public void createDialogFragment_toUnrestrict_createUnrestrictDialog() {
177         final BatteryTipDialogFragment dialogFragment =
178                 mRestrictedAppDetails.createDialogFragment(mAppInfo, false);
179 
180         FragmentController.setupFragment(
181                 dialogFragment, FragmentActivity.class, 0 /* containerViewId */, null /* bundle */);
182 
183         final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
184         ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
185         assertThat(shadowDialog.getTitle()).isEqualTo("Remove restriction?");
186     }
187 
188     @Test
onBatteryTipHandled_restrict_setChecked()189     public void onBatteryTipHandled_restrict_setChecked() {
190         final RestrictAppTip restrictAppTip =
191                 new RestrictAppTip(BatteryTip.StateType.NEW, mAppInfo);
192         mRestrictedAppDetails.mRestrictedAppListGroup.addPreference(mCheckBoxPreference);
193 
194         mRestrictedAppDetails.onBatteryTipHandled(restrictAppTip);
195 
196         assertThat(mCheckBoxPreference.isChecked()).isTrue();
197     }
198 
199     @Test
onBatteryTipHandled_unrestrict_setUnchecked()200     public void onBatteryTipHandled_unrestrict_setUnchecked() {
201         final UnrestrictAppTip unrestrictAppTip =
202                 new UnrestrictAppTip(BatteryTip.StateType.NEW, mAppInfo);
203         mRestrictedAppDetails.mRestrictedAppListGroup.addPreference(mCheckBoxPreference);
204 
205         mRestrictedAppDetails.onBatteryTipHandled(unrestrictAppTip);
206 
207         assertThat(mCheckBoxPreference.isChecked()).isFalse();
208     }
209 }
210