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