1 /* 2 * Copyright (C) 2024 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.location.LocationManager.EXTRA_ADAS_GNSS_ENABLED; 21 import static android.location.LocationManager.EXTRA_LOCATION_ENABLED; 22 23 import android.car.drivingstate.CarUxRestrictions; 24 import android.content.BroadcastReceiver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.location.LocationManager; 29 30 import androidx.annotation.VisibleForTesting; 31 import androidx.preference.Preference; 32 33 import com.android.car.settings.common.FragmentController; 34 import com.android.car.settings.common.Logger; 35 import com.android.car.settings.common.PowerPolicyListener; 36 import com.android.car.settings.common.PreferenceController; 37 38 /** 39 * Abstract PreferenceController that listens to location and power policy state 40 * changes and will refresh the UI when events happen. 41 * 42 * @param <V> the upper bound on the type of {@link Preference} on which the controller expects 43 * to operate. 44 */ 45 public abstract class LocationStateListenerBasePreferenceController<V extends Preference> extends 46 PreferenceController<V> { 47 protected static final Logger LOG = 48 new Logger(LocationStateListenerBasePreferenceController.class); 49 50 /** 51 * A Listener which responds to enabling or disabling of {@link 52 * LocationManager#MODE_CHANGED_ACTION} and {@link 53 * LocationManager#ACTION_ADAS_GNSS_ENABLED_CHANGED} on the device. 54 */ 55 public interface LocationStateListener { 56 57 /** 58 * A callback run any time we receive a broadcast stating the location enable state has 59 * changed. 60 * @param isEnabled Whether or not location is enabled 61 */ onLocationStateChange(boolean isEnabled)62 void onLocationStateChange(boolean isEnabled); 63 } 64 65 private final LocationManager mLocationManager; 66 private final BroadcastReceiver mLocationReceiver = new BroadcastReceiver() { 67 @Override 68 public void onReceive(Context context, Intent intent) { 69 if (intent.getAction().equals(LocationManager.ACTION_ADAS_GNSS_ENABLED_CHANGED)) { 70 boolean isBypassLocationEnabled = 71 intent.getBooleanExtra(EXTRA_ADAS_GNSS_ENABLED, true); 72 mBypassLocationStateListener.onLocationStateChange(isBypassLocationEnabled); 73 } 74 if (intent.getAction().equals(LocationManager.MODE_CHANGED_ACTION)) { 75 boolean isMainLocationEnabled = 76 intent.getBooleanExtra(EXTRA_LOCATION_ENABLED, true); 77 mMainLocationStateListener.onLocationStateChange(isMainLocationEnabled); 78 } 79 refreshUi(); 80 } 81 }; 82 83 private LocationStateListener mBypassLocationStateListener; 84 private LocationStateListener mMainLocationStateListener; 85 @VisibleForTesting 86 PowerPolicyListener mPowerPolicyListener; 87 private boolean mIsPowerPolicyOn = true; 88 LocationStateListenerBasePreferenceController( Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)89 public LocationStateListenerBasePreferenceController( 90 Context context, 91 String preferenceKey, 92 FragmentController fragmentController, 93 CarUxRestrictions uxRestrictions) { 94 super(context, preferenceKey, fragmentController, uxRestrictions); 95 mLocationManager = context.getSystemService(LocationManager.class); 96 } 97 98 @Override onStartInternal()99 protected void onStartInternal() { 100 if (mBypassLocationStateListener == null && mMainLocationStateListener == null) { 101 return; 102 } 103 IntentFilter locationChangeFilter = new IntentFilter(); 104 if (mBypassLocationStateListener != null) { 105 locationChangeFilter.addAction(LocationManager.ACTION_ADAS_GNSS_ENABLED_CHANGED); 106 } 107 if (mMainLocationStateListener != null) { 108 locationChangeFilter.addAction(LocationManager.MODE_CHANGED_ACTION); 109 } 110 getContext().registerReceiver( 111 mLocationReceiver, locationChangeFilter, Context.RECEIVER_NOT_EXPORTED); 112 } 113 114 @Override onResumeInternal()115 protected void onResumeInternal() { 116 if (mPowerPolicyListener != null) { 117 mPowerPolicyListener.handleCurrentPolicy(); 118 } 119 } 120 121 @Override onStopInternal()122 protected void onStopInternal() { 123 if (mBypassLocationStateListener != null || mMainLocationStateListener != null) { 124 getContext().unregisterReceiver(mLocationReceiver); 125 } 126 } 127 128 @Override onDestroyInternal()129 protected void onDestroyInternal() { 130 if (mPowerPolicyListener != null) { 131 mPowerPolicyListener.release(); 132 } 133 } 134 setBypassLocationStateListener(LocationStateListener listener)135 private void setBypassLocationStateListener(LocationStateListener listener) { 136 mBypassLocationStateListener = listener; 137 } 138 139 /** 140 * Add the default bypass location state listener. 141 * The default listener triggers a UI refresh when the state changes. 142 */ addDefaultBypassLocationStateListener()143 protected void addDefaultBypassLocationStateListener() { 144 setBypassLocationStateListener(isEnabled -> {}); 145 } 146 setMainLocationStateListener(LocationStateListener listener)147 protected void setMainLocationStateListener(LocationStateListener listener) { 148 mMainLocationStateListener = listener; 149 } 150 151 /** 152 * Add the default main location state listener. 153 * The default listener triggers a UI refresh when the state changes. 154 */ addDefaultMainLocationStateListener()155 protected void addDefaultMainLocationStateListener() { 156 setMainLocationStateListener(isEnabled -> {}); 157 } 158 setPowerPolicyListener(PowerPolicyListener listener)159 private void setPowerPolicyListener(PowerPolicyListener listener) { 160 mPowerPolicyListener = listener; 161 } 162 163 /** 164 * Add the default location power policy listener. 165 * The default listener triggers a UI refresh when the state changes. 166 */ addDefaultPowerPolicyListener()167 protected void addDefaultPowerPolicyListener() { 168 setPowerPolicyListener(new PowerPolicyListener(getContext(), LOCATION, 169 isOn -> { 170 mIsPowerPolicyOn = isOn; 171 refreshUi(); 172 })); 173 } 174 getLocationManager()175 protected LocationManager getLocationManager() { 176 return mLocationManager; 177 } 178 getIsPowerPolicyOn()179 protected boolean getIsPowerPolicyOn() { 180 return mIsPowerPolicyOn; 181 } 182 } 183 184