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.settings.uwb; 18 19 import static android.uwb.UwbManager.AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_REGULATION; 20 import static android.uwb.UwbManager.AdapterStateCallback.STATE_DISABLED; 21 import static android.uwb.UwbManager.AdapterStateCallback.STATE_ENABLED_ACTIVE; 22 import static android.uwb.UwbManager.AdapterStateCallback.STATE_ENABLED_INACTIVE; 23 24 import static androidx.lifecycle.Lifecycle.Event.ON_START; 25 import static androidx.lifecycle.Lifecycle.Event.ON_STOP; 26 27 import android.content.BroadcastReceiver; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.IntentFilter; 31 import android.content.pm.PackageManager; 32 import android.os.Handler; 33 import android.os.HandlerExecutor; 34 import android.uwb.UwbManager; 35 import android.uwb.UwbManager.AdapterStateCallback; 36 37 import androidx.lifecycle.LifecycleObserver; 38 import androidx.lifecycle.OnLifecycleEvent; 39 import androidx.preference.Preference; 40 import androidx.preference.PreferenceScreen; 41 42 import com.android.internal.annotations.VisibleForTesting; 43 import com.android.settings.R; 44 import com.android.settings.core.TogglePreferenceController; 45 46 import java.util.concurrent.Executor; 47 48 /** Controller for "UWB" toggle. */ 49 public class UwbPreferenceController extends TogglePreferenceController implements 50 LifecycleObserver { 51 private final UwbManager mUwbManager; 52 private final UwbUtils mUwbUtils; 53 private boolean mAirplaneModeOn; 54 private /* @AdapterStateCallback.State */ int mState; 55 private /* @AdapterStateCallback.StateChangedReason */ int mStateReason; 56 private final BroadcastReceiver mAirplaneModeChangedReceiver; 57 private final AdapterStateCallback mAdapterStateCallback; 58 private final Executor mExecutor; 59 private final Handler mHandler; 60 private Preference mPreference; 61 62 @VisibleForTesting UwbPreferenceController(Context context, String key, UwbUtils uwbUtils)63 public UwbPreferenceController(Context context, String key, UwbUtils uwbUtils) { 64 super(context, key); 65 mHandler = new Handler(context.getMainLooper()); 66 mExecutor = new HandlerExecutor(mHandler); 67 mUwbUtils = uwbUtils; 68 if (isUwbSupportedOnDevice()) { 69 mUwbManager = context.getSystemService(UwbManager.class); 70 mAirplaneModeChangedReceiver = new BroadcastReceiver() { 71 @Override 72 public void onReceive(Context context, Intent intent) { 73 mAirplaneModeOn = mUwbUtils.isAirplaneModeOn(mContext); 74 updateState(mPreference); 75 } 76 }; 77 mAdapterStateCallback = (state, reason) -> { 78 mState = state; 79 mStateReason = reason; 80 updateState(mPreference); 81 }; 82 mState = mUwbManager.getAdapterState(); 83 } else { 84 mUwbManager = null; 85 mAirplaneModeChangedReceiver = null; 86 mAdapterStateCallback = null; 87 } 88 } 89 UwbPreferenceController(Context context, String key)90 public UwbPreferenceController(Context context, String key) { 91 this(context, key, new UwbUtils()); 92 } 93 isUwbSupportedOnDevice()94 public boolean isUwbSupportedOnDevice() { 95 return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_UWB); 96 } 97 isUwbDisabledDueToRegulatory()98 private boolean isUwbDisabledDueToRegulatory() { 99 return mState == STATE_DISABLED && mStateReason == STATE_CHANGED_REASON_SYSTEM_REGULATION; 100 } 101 102 @Override getAvailabilityStatus()103 public int getAvailabilityStatus() { 104 if (!isUwbSupportedOnDevice()) { 105 return UNSUPPORTED_ON_DEVICE; 106 } else if (mAirplaneModeOn) { 107 return DISABLED_DEPENDENT_SETTING; 108 } else if (isUwbDisabledDueToRegulatory()) { 109 return CONDITIONALLY_UNAVAILABLE; 110 } else { 111 return AVAILABLE; 112 } 113 } 114 115 @Override displayPreference(PreferenceScreen screen)116 public void displayPreference(PreferenceScreen screen) { 117 super.displayPreference(screen); 118 mPreference = screen.findPreference(getPreferenceKey()); 119 } 120 121 @Override isChecked()122 public boolean isChecked() { 123 if (!isUwbSupportedOnDevice()) { 124 return false; 125 } 126 return mState == STATE_ENABLED_ACTIVE || mState == STATE_ENABLED_INACTIVE; 127 } 128 129 @Override setChecked(boolean isChecked)130 public boolean setChecked(boolean isChecked) { 131 if (isUwbSupportedOnDevice()) { 132 if (mAirplaneModeOn) { 133 mUwbManager.setUwbEnabled(false); 134 } else { 135 mUwbManager.setUwbEnabled(isChecked); 136 } 137 } 138 return true; 139 } 140 141 /** Called when activity starts being displayed to user. */ 142 @OnLifecycleEvent(ON_START) onStart()143 public void onStart() { 144 if (isUwbSupportedOnDevice()) { 145 mState = mUwbManager.getAdapterState(); 146 mStateReason = AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN; 147 mAirplaneModeOn = mUwbUtils.isAirplaneModeOn(mContext); 148 mUwbManager.registerAdapterStateCallback(mExecutor, mAdapterStateCallback); 149 mContext.registerReceiver(mAirplaneModeChangedReceiver, 150 new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED), null, mHandler); 151 refreshSummary(mPreference); 152 } 153 } 154 155 /** Called when activity stops being displayed to user. */ 156 @OnLifecycleEvent(ON_STOP) onStop()157 public void onStop() { 158 if (isUwbSupportedOnDevice()) { 159 mUwbManager.unregisterAdapterStateCallback(mAdapterStateCallback); 160 mContext.unregisterReceiver(mAirplaneModeChangedReceiver); 161 } 162 } 163 164 @Override updateState(Preference preference)165 public void updateState(Preference preference) { 166 super.updateState(preference); 167 preference.setEnabled(!mAirplaneModeOn); 168 refreshSummary(mPreference); 169 } 170 171 @Override getSummary()172 public CharSequence getSummary() { 173 if (mAirplaneModeOn) { 174 return mContext.getResources().getString(R.string.uwb_settings_summary_airplane_mode); 175 } else if (isUwbDisabledDueToRegulatory()) { 176 return mContext.getResources().getString( 177 R.string.uwb_settings_summary_no_uwb_regulatory); 178 } else { 179 return mContext.getResources().getString(R.string.uwb_settings_summary); 180 } 181 } 182 183 @Override getSliceHighlightMenuRes()184 public int getSliceHighlightMenuRes() { 185 return R.string.menu_key_connected_devices; 186 } 187 188 @Override hasAsyncUpdate()189 public boolean hasAsyncUpdate() { 190 return true; 191 } 192 } 193 194