1 /* 2 * Copyright (C) 2021 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 33 import androidx.annotation.VisibleForTesting; 34 35 import com.android.car.settings.R; 36 import com.android.car.settings.common.ConfirmationDialogFragment; 37 import com.android.car.settings.common.FragmentController; 38 import com.android.car.settings.common.PowerPolicyListener; 39 import com.android.car.settings.common.PreferenceController; 40 import com.android.car.settings.enterprise.EnterpriseUtils; 41 import com.android.car.ui.preference.CarUiTwoActionSwitchPreference; 42 43 /** 44 * Enables/disables ADAS (Advanced Driver-assistance systems) GNSS bypass via SwitchPreference. 45 */ 46 public class AdasLocationSwitchPreferenceController extends 47 PreferenceController<CarUiTwoActionSwitchPreference> { 48 private final Context mContext; 49 private final LocationManager mLocationManager; 50 51 private final BroadcastReceiver mAdasReceiver = new BroadcastReceiver() { 52 @Override 53 public void onReceive(Context context, Intent intent) { 54 refreshUi(); 55 } 56 }; 57 58 private final BroadcastReceiver mLocationReceiver = new BroadcastReceiver() { 59 @Override 60 public void onReceive(Context context, Intent intent) { 61 // Turns Driver assistance on when main location switch is on. Location service don't 62 // support the case where main location switch on and Driver assistance off 63 if (mLocationManager.isLocationEnabled()) { 64 mLocationManager.setAdasGnssLocationEnabled(true); 65 } 66 refreshUi(); 67 } 68 }; 69 70 private static final IntentFilter INTENT_FILTER_ADAS_GNSS_ENABLED_CHANGED = new IntentFilter( 71 LocationManager.ACTION_ADAS_GNSS_ENABLED_CHANGED); 72 73 private static final IntentFilter INTENT_FILTER_LOCATION_MODE_CHANGED = new IntentFilter( 74 LocationManager.MODE_CHANGED_ACTION); 75 @VisibleForTesting 76 final PowerPolicyListener mPowerPolicyListener; 77 AdasLocationSwitchPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)78 public AdasLocationSwitchPreferenceController(Context context, String preferenceKey, 79 FragmentController fragmentController, CarUxRestrictions uxRestrictions) { 80 super(context, preferenceKey, fragmentController, uxRestrictions); 81 mContext = context; 82 mLocationManager = context.getSystemService(LocationManager.class); 83 mPowerPolicyListener = new PowerPolicyListener(context, LOCATION, 84 isOn -> { 85 handlePowerPolicyChange(getPreference(), isOn); 86 }); 87 } 88 89 @Override getPreferenceType()90 protected Class<CarUiTwoActionSwitchPreference> getPreferenceType() { 91 return CarUiTwoActionSwitchPreference.class; 92 } 93 94 @Override getAvailabilityStatus()95 protected int getAvailabilityStatus() { 96 if (hasUserRestrictionByDpm(getContext(), DISALLOW_CONFIG_LOCATION) 97 || hasUserRestrictionByDpm(getContext(), DISALLOW_SHARE_LOCATION)) { 98 return AVAILABLE_FOR_VIEWING; 99 } 100 return AVAILABLE; 101 } 102 103 @Override updateState(CarUiTwoActionSwitchPreference preference)104 protected void updateState(CarUiTwoActionSwitchPreference preference) { 105 updateSwitchPreference(preference, mLocationManager.isAdasGnssLocationEnabled()); 106 } 107 108 @Override onCreateInternal()109 protected void onCreateInternal() { 110 getPreference().setOnSecondaryActionClickListener(isChecked -> { 111 if (!isChecked) { 112 getFragmentController().showDialog(getConfirmationDialog(), 113 ConfirmationDialogFragment.TAG); 114 refreshUi(); 115 } else { 116 mLocationManager.setAdasGnssLocationEnabled(true); 117 } 118 }); 119 setClickableWhileDisabled(getPreference(), /* clickable= */ true, p -> { 120 if (hasUserRestrictionByDpm(getContext(), DISALLOW_SHARE_LOCATION)) { 121 showActionDisabledByAdminDialog(DISALLOW_SHARE_LOCATION); 122 return; 123 } 124 if (hasUserRestrictionByDpm(getContext(), DISALLOW_CONFIG_LOCATION)) { 125 showActionDisabledByAdminDialog(DISALLOW_CONFIG_LOCATION); 126 return; 127 } 128 }); 129 } 130 131 @Override onStartInternal()132 protected void onStartInternal() { 133 mContext.registerReceiver(mAdasReceiver, INTENT_FILTER_ADAS_GNSS_ENABLED_CHANGED); 134 mContext.registerReceiver(mLocationReceiver, INTENT_FILTER_LOCATION_MODE_CHANGED); 135 } 136 137 @Override onResumeInternal()138 protected void onResumeInternal() { 139 mPowerPolicyListener.handleCurrentPolicy(); 140 } 141 142 @Override onStopInternal()143 protected void onStopInternal() { 144 mContext.unregisterReceiver(mAdasReceiver); 145 mContext.unregisterReceiver(mLocationReceiver); 146 } 147 148 @Override onDestroyInternal()149 protected void onDestroyInternal() { 150 mPowerPolicyListener.release(); 151 } 152 updateSwitchPreference(CarUiTwoActionSwitchPreference preference, boolean enabled)153 private void updateSwitchPreference(CarUiTwoActionSwitchPreference preference, 154 boolean enabled) { 155 if (enabled && hasUserRestrictionByDpm(getContext(), DISALLOW_SHARE_LOCATION)) { 156 preference.setSecondaryActionChecked(false); 157 preference.setSecondaryActionEnabled(false); 158 } else { 159 preference.setSecondaryActionChecked(enabled); 160 preference.setSecondaryActionEnabled(!mLocationManager.isLocationEnabled()); 161 } 162 } 163 handlePowerPolicyChange(CarUiTwoActionSwitchPreference preference, boolean enabled)164 private void handlePowerPolicyChange(CarUiTwoActionSwitchPreference preference, 165 boolean enabled) { 166 if (hasUserRestrictionByDpm(getContext(), DISALLOW_CONFIG_LOCATION) 167 || hasUserRestrictionByDpm(getContext(), DISALLOW_SHARE_LOCATION) 168 || mLocationManager.isLocationEnabled()) { 169 preference.setSecondaryActionEnabled(false); 170 return; 171 } 172 preference.setSecondaryActionEnabled(enabled); 173 } 174 175 /** 176 * Assigns confirm action as negative button listener and cancel action as positive button 177 * listener, because the UX design requires the cancel button has to be on right and the confirm 178 * button on left. 179 */ getConfirmationDialog()180 private ConfirmationDialogFragment getConfirmationDialog() { 181 return new ConfirmationDialogFragment.Builder(getContext()) 182 .setMessage(mContext 183 .getString(R.string.location_driver_assistance_toggle_off_warning)) 184 .setNegativeButton(mContext 185 .getString(R.string.driver_assistance_warning_confirm_label), arguments -> { 186 mLocationManager.setAdasGnssLocationEnabled(false); 187 }) 188 .setPositiveButton(android.R.string.cancel, 189 /* rejectListener= */ null) 190 .build(); 191 } 192 showActionDisabledByAdminDialog(String restrictionType)193 private void showActionDisabledByAdminDialog(String restrictionType) { 194 getFragmentController().showDialog( 195 EnterpriseUtils.getActionDisabledByAdminDialog(getContext(), 196 restrictionType), 197 DISABLED_BY_ADMIN_CONFIRM_DIALOG_TAG); 198 } 199 } 200