• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.car.drivingstate.CarUxRestrictions;
20 import android.content.Context;
21 import android.content.res.Resources;
22 import android.widget.Toast;
23 
24 import androidx.annotation.VisibleForTesting;
25 
26 import com.android.car.settings.Flags;
27 import com.android.car.settings.R;
28 import com.android.car.settings.common.ConfirmationDialogFragment;
29 import com.android.car.settings.common.FragmentController;
30 import com.android.car.ui.preference.CarUiSwitchPreference;
31 
32 /**
33  * Enables/disables ADAS (Advanced Driver-assistance systems) GNSS bypass via SwitchPreference.
34  *
35  * <p>This switch is not affected by {@link android.os.UserManager#DISALLOW_CONFIG_LOCATION} or
36  * {@link android.os.UserManager#DISALLOW_SHARE_LOCATION} to prevent a device policy manager from
37  * changing settings that can negatively impact the safety of the driver.
38  */
39 public class AdasLocationSwitchPreferenceController extends
40         LocationStateListenerBasePreferenceController<CarUiSwitchPreference> {
41     private static final String AUTOMOTIVE_LOCATION_BYPASS_RESOURCE_NAME =
42             "config_defaultAdasGnssLocationEnabled";
43     private static final String AUTOMOTIVE_LOCATION_BYPASS_RESOURCE_TYPE = "bool";
44     private static final String AUTOMOTIVE_LOCATION_BYPASS_RESOURCE_PACKAGE = "android";
45 
46     @VisibleForTesting
47     boolean mIsClickable;
48     @VisibleForTesting
49     boolean mIsVisible;
50 
AdasLocationSwitchPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)51     public AdasLocationSwitchPreferenceController(Context context, String preferenceKey,
52             FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
53         super(context, preferenceKey, fragmentController, uxRestrictions);
54     }
55 
56     @Override
getPreferenceType()57     protected Class<CarUiSwitchPreference> getPreferenceType() {
58         return CarUiSwitchPreference.class;
59     }
60 
61     @Override
getDefaultAvailabilityStatus()62     protected int getDefaultAvailabilityStatus() {
63         if (Flags.requiredInfotainmentAppsSettingsPage()) {
64             if (!mIsVisible) {
65                 return CONDITIONALLY_UNAVAILABLE;
66             }
67             if (!getIsPowerPolicyOn() || getLocationManager().isLocationEnabled()) {
68                 return AVAILABLE_FOR_VIEWING;
69             }
70             return AVAILABLE;
71         } else {
72             return mIsClickable && getIsPowerPolicyOn() && !getLocationManager().isLocationEnabled()
73                     ? AVAILABLE
74                     : AVAILABLE_FOR_VIEWING;
75         }
76     }
77 
78     @Override
updateState(CarUiSwitchPreference preference)79     protected void updateState(CarUiSwitchPreference preference) {
80         preference.setChecked(getLocationManager().isAdasGnssLocationEnabled());
81     }
82 
83     @Override
handlePreferenceChanged(CarUiSwitchPreference preference, Object newValue)84     protected boolean handlePreferenceChanged(CarUiSwitchPreference preference, Object newValue) {
85         if (Flags.requiredInfotainmentAppsSettingsPage()) {
86             getLocationManager().setAdasGnssLocationEnabled((Boolean) newValue);
87             return true;
88         } else {
89             if (getLocationManager().isAdasGnssLocationEnabled()) {
90                 getFragmentController().showDialog(getConfirmationDialog(),
91                         ConfirmationDialogFragment.TAG);
92                 return false;
93             } else {
94                 getLocationManager().setAdasGnssLocationEnabled((Boolean) newValue);
95             }
96             return true;
97         }
98     }
99 
100     @Override
onCreateInternal()101     protected void onCreateInternal() {
102         addDefaultBypassLocationStateListener();
103         setMainLocationStateListener((isEnabled) -> {
104             // Turns ADAS location on when main location switch is on. Location service do
105             // not support the case where main location switch on and ADAS location off.
106             if (isEnabled) {
107                 getLocationManager().setAdasGnssLocationEnabled(true);
108             }
109         });
110         addDefaultPowerPolicyListener();
111 
112         if (Flags.requiredInfotainmentAppsSettingsPage()) {
113             Resources res = Resources.getSystem();
114             int resId = res.getIdentifier(
115                     AUTOMOTIVE_LOCATION_BYPASS_RESOURCE_NAME,
116                     AUTOMOTIVE_LOCATION_BYPASS_RESOURCE_TYPE,
117                     AUTOMOTIVE_LOCATION_BYPASS_RESOURCE_PACKAGE);
118             boolean defaultLocationBypassEnabled = res.getBoolean(resId);
119             // If by default automotive location bypass is on, then
120             // config_show_location_required_apps_toggle will dictate the visibility.
121             // Otherwise the toggle must be visible.
122             mIsVisible = defaultLocationBypassEnabled
123                     ? getContext().getResources().getBoolean(
124                             R.bool.config_show_location_required_apps_toggle)
125                     : true;
126         } else {
127             mIsClickable = getContext().getResources()
128                     .getBoolean(R.bool.config_allow_adas_location_switch_clickable);
129         }
130 
131         setClickableWhileDisabled(getPreference(), /* clickable= */true, preference -> {
132             if (Flags.requiredInfotainmentAppsSettingsPage()) {
133                 if (!getIsPowerPolicyOn()) {
134                     Toast.makeText(getContext(), R.string.power_component_disabled,
135                             Toast.LENGTH_LONG).show();
136                 }
137             } else {
138                 if (!mIsClickable) {
139                     getFragmentController().showDialog(getToggleDisabledDialog(),
140                             ConfirmationDialogFragment.TAG);
141                     return;
142                 }
143                 if (!getIsPowerPolicyOn()) {
144                     Toast.makeText(getContext(), R.string.power_component_disabled,
145                             Toast.LENGTH_LONG).show();
146                 }
147             }
148         });
149     }
150 
151     /**
152      * Assigns confirm action as negative button listener and cancel action as positive button
153      * listener, because the UX design requires the cancel button has to be on right and the confirm
154      * button on left.
155      */
getConfirmationDialog()156     private ConfirmationDialogFragment getConfirmationDialog() {
157         return new ConfirmationDialogFragment.Builder(getContext())
158                 .setMessage(R.string.adas_location_toggle_off_warning)
159                 .setNegativeButton(
160                         R.string.adas_location_toggle_confirm_label,
161                         arguments -> {
162                             // This if statement is included because the power policy handler runs
163                             // slightly after the UI is initialized. Therefore, there's a small
164                             // timeframe for the user to toggle the switch before the UI updates
165                             // and disables the switch because the power policy is off. This if
166                             // statement mitigates this issue by reverifying the power policy
167                             // status.
168                             if (getIsPowerPolicyOn()) {
169                                 getLocationManager().setAdasGnssLocationEnabled(false);
170                             }
171                         })
172                 .setPositiveButton(android.R.string.cancel, /* confirmListener= */ null)
173                 .build();
174     }
175 
176     private ConfirmationDialogFragment getToggleDisabledDialog() {
177         return new ConfirmationDialogFragment.Builder(getContext())
178                 .setMessage(R.string.adas_location_toggle_popup_summary)
179                 .setPositiveButton(android.R.string.ok, /* confirmListener= */ null)
180                 .build();
181     }
182 }
183