1 /*
2  * Copyright (C) 2020 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.car.settings.location;
18 
19 import static android.car.hardware.power.PowerComponent.LOCATION;
20 import static android.os.UserManager.DISALLOW_CONFIG_LOCATION;
21 import static android.os.UserManager.DISALLOW_SHARE_LOCATION;
22 
23 import static com.android.car.settings.enterprise.ActionDisabledByAdminDialogFragment.DISABLED_BY_ADMIN_CONFIRM_DIALOG_TAG;
24 import static com.android.car.settings.enterprise.EnterpriseUtils.hasUserRestrictionByDpm;
25 
26 import android.car.drivingstate.CarUxRestrictions;
27 import android.content.BroadcastReceiver;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.location.LocationManager;
32 import android.os.UserHandle;
33 import android.provider.Settings;
34 import android.widget.Toast;
35 
36 import androidx.annotation.VisibleForTesting;
37 
38 import com.android.car.settings.R;
39 import com.android.car.settings.common.ColoredSwitchPreference;
40 import com.android.car.settings.common.ConfirmationDialogFragment;
41 import com.android.car.settings.common.FragmentController;
42 import com.android.car.settings.common.Logger;
43 import com.android.car.settings.common.PowerPolicyListener;
44 import com.android.car.settings.common.PreferenceController;
45 import com.android.car.settings.enterprise.EnterpriseUtils;
46 import com.android.settingslib.Utils;
47 
48 /**
49  * Enables/disables location state via SwitchPreference.
50  */
51 public class LocationStateSwitchPreferenceController extends
52         PreferenceController<ColoredSwitchPreference> {
53     private static final Logger LOG = new Logger(
54             LocationStateSwitchPreferenceController.class);
55     private static final IntentFilter INTENT_FILTER_LOCATION_MODE_CHANGED =
56             new IntentFilter(LocationManager.MODE_CHANGED_ACTION);
57 
58     private boolean mIsPowerPolicyOn = true;
59 
60     private final Context mContext;
61     private final LocationManager mLocationManager;
62     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
63         @Override
64         public void onReceive(Context context, Intent intent) {
65             refreshUi();
66         }
67     };
68 
69     @VisibleForTesting
70     final PowerPolicyListener mPowerPolicyListener;
71 
LocationStateSwitchPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)72     public LocationStateSwitchPreferenceController(Context context,
73             String preferenceKey,
74             FragmentController fragmentController,
75             CarUxRestrictions uxRestrictions) {
76         super(context, preferenceKey, fragmentController, uxRestrictions);
77         mContext = context;
78         mLocationManager = context.getSystemService(LocationManager.class);
79         mPowerPolicyListener = new PowerPolicyListener(context, LOCATION,
80                 isOn -> {
81                     LOG.d("setting mIsPowerPolicyOn to " + isOn);
82                     mIsPowerPolicyOn = isOn;
83                     // mIsPowerPolicyOn is used in deciding the availability status.
84                     // Call refreshUi() so that the UI can be updated as per
85                     // getAvailabilityStatus().
86                     refreshUi();
87                 });
88     }
89 
90     @Override
getAvailabilityStatus()91     protected int getAvailabilityStatus() {
92         if (hasUserRestrictionByDpm(getContext(), DISALLOW_CONFIG_LOCATION)
93                 || hasUserRestrictionByDpm(getContext(), DISALLOW_SHARE_LOCATION)
94                 || !mIsPowerPolicyOn) {
95             return AVAILABLE_FOR_VIEWING;
96         }
97         return AVAILABLE;
98     }
99 
100     @Override
getPreferenceType()101     protected Class<ColoredSwitchPreference> getPreferenceType() {
102         return ColoredSwitchPreference.class;
103     }
104 
105     @Override
updateState(ColoredSwitchPreference preference)106     protected void updateState(ColoredSwitchPreference preference) {
107         updateSwitchPreference(preference, mLocationManager.isLocationEnabled());
108     }
109 
110     @Override
handlePreferenceChanged(ColoredSwitchPreference preference, Object newValue)111     protected boolean handlePreferenceChanged(ColoredSwitchPreference preference,
112             Object newValue) {
113         boolean locationEnabled = (Boolean) newValue;
114         if (!locationEnabled) {
115             getFragmentController().showDialog(getConfirmationDialog(),
116                     ConfirmationDialogFragment.TAG);
117             return false;
118         }
119         Utils.updateLocationEnabled(
120                 mContext,
121                 locationEnabled,
122                 UserHandle.myUserId(),
123                 Settings.Secure.LOCATION_CHANGER_SYSTEM_SETTINGS);
124         return true;
125     }
126 
127     @Override
onCreateInternal()128     protected void onCreateInternal() {
129         getPreference().setContentDescription(
130                 getContext().getString(R.string.location_state_switch_content_description));
131         setClickableWhileDisabled(getPreference(), /* clickable= */ true, p -> {
132             // All the cases here should coincide with the ones in getAvailabilityStatus()
133             if (!mIsPowerPolicyOn) {
134                 Toast.makeText(getContext(),
135                         getContext().getString(R.string.power_component_disabled),
136                         Toast.LENGTH_LONG).show();
137                 return;
138             }
139             if (hasUserRestrictionByDpm(getContext(), DISALLOW_SHARE_LOCATION)) {
140                 showActionDisabledByAdminDialog(DISALLOW_SHARE_LOCATION);
141                 return;
142             }
143             if (hasUserRestrictionByDpm(getContext(), DISALLOW_CONFIG_LOCATION)) {
144                 showActionDisabledByAdminDialog(DISALLOW_CONFIG_LOCATION);
145                 return;
146             }
147         });
148     }
149 
150     @Override
onStartInternal()151     protected void onStartInternal() {
152         mContext.registerReceiver(mReceiver, INTENT_FILTER_LOCATION_MODE_CHANGED);
153     }
154 
155     @Override
onResumeInternal()156     protected void onResumeInternal() {
157         mPowerPolicyListener.handleCurrentPolicy();
158     }
159 
160     @Override
onStopInternal()161     protected void onStopInternal() {
162         mContext.unregisterReceiver(mReceiver);
163     }
164 
165     @Override
onDestroyInternal()166     protected void onDestroyInternal() {
167         mPowerPolicyListener.release();
168     }
169 
updateSwitchPreference(ColoredSwitchPreference preference, boolean enabled)170     private void updateSwitchPreference(ColoredSwitchPreference preference,
171             boolean enabled) {
172         preference.setChecked(enabled
173                 && !hasUserRestrictionByDpm(getContext(), DISALLOW_SHARE_LOCATION));
174     }
175 
getConfirmationDialog()176     private ConfirmationDialogFragment getConfirmationDialog() {
177         return new ConfirmationDialogFragment.Builder(getContext())
178                 .setMessage(mContext.getString(R.string.location_toggle_off_warning))
179                 .setPositiveButton(android.R.string.ok, arguments -> {
180                     Utils.updateLocationEnabled(
181                             mContext,
182                             false,
183                             UserHandle.myUserId(),
184                             Settings.Secure.LOCATION_CHANGER_SYSTEM_SETTINGS);
185                 })
186                 .build();
187     }
188 
showActionDisabledByAdminDialog(String restrictionType)189     private void showActionDisabledByAdminDialog(String restrictionType) {
190         getFragmentController().showDialog(
191                 EnterpriseUtils.getActionDisabledByAdminDialog(getContext(),
192                         restrictionType),
193                 DISABLED_BY_ADMIN_CONFIRM_DIALOG_TAG);
194     }
195 }
196