1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11  * KIND, either express or implied. See the License for the specific language governing
12  * permissions and limitations under the License.
13  */
14 package com.android.settings.location;
15 
16 import static java.util.concurrent.TimeUnit.DAYS;
17 
18 import android.Manifest;
19 import android.content.Context;
20 import android.content.Intent;
21 import android.icu.text.RelativeDateTimeFormatter;
22 import android.provider.DeviceConfig;
23 import android.view.View;
24 
25 import androidx.annotation.VisibleForTesting;
26 import androidx.preference.Preference;
27 import androidx.preference.PreferenceScreen;
28 
29 import com.android.settings.R;
30 import com.android.settings.Utils;
31 import com.android.settings.core.PreferenceControllerMixin;
32 import com.android.settingslib.core.AbstractPreferenceController;
33 import com.android.settingslib.location.RecentLocationAccesses;
34 import com.android.settingslib.utils.StringUtil;
35 import com.android.settingslib.widget.AppEntitiesHeaderController;
36 import com.android.settingslib.widget.AppEntityInfo;
37 import com.android.settingslib.widget.LayoutPreference;
38 
39 import java.util.List;
40 
41 public class RecentLocationAccessPreferenceController extends AbstractPreferenceController
42         implements PreferenceControllerMixin {
43     /** Key for the recent location apps dashboard */
44     private static final String KEY_APPS_DASHBOARD = "apps_dashboard";
45     private final RecentLocationAccesses mRecentLocationAccesses;
46     private AppEntitiesHeaderController mController;
47     private static final int MAXIMUM_APP_COUNT = 3;
48 
RecentLocationAccessPreferenceController(Context context)49     public RecentLocationAccessPreferenceController(Context context) {
50         this(context, new RecentLocationAccesses(context));
51     }
52 
53     @VisibleForTesting
RecentLocationAccessPreferenceController(Context context, RecentLocationAccesses recentAccesses)54     RecentLocationAccessPreferenceController(Context context,
55             RecentLocationAccesses recentAccesses) {
56         super(context);
57         mRecentLocationAccesses = recentAccesses;
58     }
59 
60     @Override
getPreferenceKey()61     public String getPreferenceKey() {
62         return KEY_APPS_DASHBOARD;
63     }
64 
65     @Override
isAvailable()66     public boolean isAvailable() {
67         return Boolean.parseBoolean(
68                 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_PRIVACY,
69                         Utils.PROPERTY_PERMISSIONS_HUB_ENABLED));
70     }
71 
72     @Override
displayPreference(PreferenceScreen screen)73     public void displayPreference(PreferenceScreen screen) {
74         super.displayPreference(screen);
75         final LayoutPreference preference = screen.findPreference(KEY_APPS_DASHBOARD);
76         final View view = preference.findViewById(R.id.app_entities_header);
77         mController = AppEntitiesHeaderController.newInstance(mContext, view)
78                 .setHeaderTitleRes(R.string.location_category_recent_location_access)
79                 .setHeaderDetailsRes(R.string.location_recent_location_access_view_details)
80                 .setHeaderEmptyRes(R.string.location_no_recent_accesses)
81                 .setHeaderDetailsClickListener((View v) -> {
82                     final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE);
83                     intent.putExtra(Intent.EXTRA_PERMISSION_NAME,
84                             Manifest.permission.ACCESS_FINE_LOCATION);
85                     intent.putExtra(Intent.EXTRA_DURATION_MILLIS, DAYS.toMillis(1));
86                     mContext.startActivity(intent);
87                 });
88     }
89 
90     @Override
updateState(Preference preference)91     public void updateState(Preference preference) {
92         updateRecentApps();
93     }
94 
updateRecentApps()95     private void updateRecentApps() {
96         final List<RecentLocationAccesses.Access> recentLocationAccesses =
97                 mRecentLocationAccesses.getAppListSorted();
98         if (recentLocationAccesses.size() > 0) {
99             // Display the top 3 preferences to container in original order.
100             int i = 0;
101             for (; i < Math.min(recentLocationAccesses.size(), MAXIMUM_APP_COUNT); i++) {
102                 final RecentLocationAccesses.Access access = recentLocationAccesses.get(i);
103                 final AppEntityInfo appEntityInfo = new AppEntityInfo.Builder()
104                         .setIcon(access.icon)
105                         .setTitle(access.label)
106                         .setSummary(StringUtil.formatRelativeTime(mContext,
107                                 System.currentTimeMillis() - access.accessFinishTime, false,
108                                 RelativeDateTimeFormatter.Style.SHORT))
109                         .setOnClickListener((v) -> {
110                             final Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSION);
111                             intent.putExtra(Intent.EXTRA_PERMISSION_NAME,
112                                     Manifest.permission.ACCESS_FINE_LOCATION);
113                             intent.putExtra(Intent.EXTRA_PACKAGE_NAME, access.packageName);
114                             intent.putExtra(Intent.EXTRA_USER, access.userHandle);
115                             mContext.startActivity(intent);
116                         })
117                         .build();
118                 mController.setAppEntity(i, appEntityInfo);
119             }
120             for (; i < MAXIMUM_APP_COUNT; i++) {
121                 mController.removeAppEntity(i);
122             }
123         }
124         mController.apply();
125     }
126 }
127