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