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 Context.RECEIVER_NOT_EXPORTED); 135 mContext.registerReceiver(mLocationReceiver, INTENT_FILTER_LOCATION_MODE_CHANGED, 136 Context.RECEIVER_NOT_EXPORTED); 137 } 138 139 @Override onResumeInternal()140 protected void onResumeInternal() { 141 mPowerPolicyListener.handleCurrentPolicy(); 142 } 143 144 @Override onStopInternal()145 protected void onStopInternal() { 146 mContext.unregisterReceiver(mAdasReceiver); 147 mContext.unregisterReceiver(mLocationReceiver); 148 } 149 150 @Override onDestroyInternal()151 protected void onDestroyInternal() { 152 mPowerPolicyListener.release(); 153 } 154 updateSwitchPreference(CarUiTwoActionSwitchPreference preference, boolean enabled)155 private void updateSwitchPreference(CarUiTwoActionSwitchPreference preference, 156 boolean enabled) { 157 if (enabled && hasUserRestrictionByDpm(getContext(), DISALLOW_SHARE_LOCATION)) { 158 preference.setSecondaryActionChecked(false); 159 preference.setSecondaryActionEnabled(false); 160 } else { 161 preference.setSecondaryActionChecked(enabled); 162 preference.setSecondaryActionEnabled(!mLocationManager.isLocationEnabled()); 163 } 164 } 165 handlePowerPolicyChange(CarUiTwoActionSwitchPreference preference, boolean enabled)166 private void handlePowerPolicyChange(CarUiTwoActionSwitchPreference preference, 167 boolean enabled) { 168 if (hasUserRestrictionByDpm(getContext(), DISALLOW_CONFIG_LOCATION) 169 || hasUserRestrictionByDpm(getContext(), DISALLOW_SHARE_LOCATION) 170 || mLocationManager.isLocationEnabled()) { 171 preference.setSecondaryActionEnabled(false); 172 return; 173 } 174 preference.setSecondaryActionEnabled(enabled); 175 } 176 177 /** 178 * Assigns confirm action as negative button listener and cancel action as positive button 179 * listener, because the UX design requires the cancel button has to be on right and the confirm 180 * button on left. 181 */ getConfirmationDialog()182 private ConfirmationDialogFragment getConfirmationDialog() { 183 return new ConfirmationDialogFragment.Builder(getContext()) 184 .setMessage(mContext 185 .getString(R.string.location_driver_assistance_toggle_off_warning)) 186 .setNegativeButton(mContext 187 .getString(R.string.driver_assistance_warning_confirm_label), arguments -> { 188 mLocationManager.setAdasGnssLocationEnabled(false); 189 }) 190 .setPositiveButton(android.R.string.cancel, 191 /* rejectListener= */ null) 192 .build(); 193 } 194 showActionDisabledByAdminDialog(String restrictionType)195 private void showActionDisabledByAdminDialog(String restrictionType) { 196 getFragmentController().showDialog( 197 EnterpriseUtils.getActionDisabledByAdminDialog(getContext(), 198 restrictionType), 199 DISABLED_BY_ADMIN_CONFIRM_DIALOG_TAG); 200 } 201 } 202