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 } else { 83 mUwbManager = null; 84 mAirplaneModeChangedReceiver = null; 85 mAdapterStateCallback = null; 86 } 87 } 88 UwbPreferenceController(Context context, String key)89 public UwbPreferenceController(Context context, String key) { 90 this(context, key, new UwbUtils()); 91 } 92 isUwbSupportedOnDevice()93 public boolean isUwbSupportedOnDevice() { 94 return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_UWB); 95 } 96 isUwbDisabledDueToRegulatory()97 private boolean isUwbDisabledDueToRegulatory() { 98 return mState == STATE_DISABLED && mStateReason == STATE_CHANGED_REASON_SYSTEM_REGULATION; 99 } 100 101 @Override getAvailabilityStatus()102 public int getAvailabilityStatus() { 103 if (!isUwbSupportedOnDevice()) { 104 return UNSUPPORTED_ON_DEVICE; 105 } else if (mAirplaneModeOn) { 106 return DISABLED_DEPENDENT_SETTING; 107 } else if (isUwbDisabledDueToRegulatory()) { 108 return CONDITIONALLY_UNAVAILABLE; 109 } else { 110 return AVAILABLE; 111 } 112 } 113 114 @Override displayPreference(PreferenceScreen screen)115 public void displayPreference(PreferenceScreen screen) { 116 super.displayPreference(screen); 117 mPreference = screen.findPreference(getPreferenceKey()); 118 } 119 120 @Override isChecked()121 public boolean isChecked() { 122 if (!isUwbSupportedOnDevice()) { 123 return false; 124 } 125 return mState == STATE_ENABLED_ACTIVE || mState == STATE_ENABLED_INACTIVE; 126 } 127 128 @Override setChecked(boolean isChecked)129 public boolean setChecked(boolean isChecked) { 130 if (isUwbSupportedOnDevice()) { 131 if (mAirplaneModeOn) { 132 mUwbManager.setUwbEnabled(false); 133 } else { 134 mUwbManager.setUwbEnabled(isChecked); 135 } 136 } 137 return true; 138 } 139 140 /** Called when activity starts being displayed to user. */ 141 @OnLifecycleEvent(ON_START) onStart()142 public void onStart() { 143 if (isUwbSupportedOnDevice()) { 144 mState = mUwbManager.getAdapterState(); 145 mStateReason = AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN; 146 mAirplaneModeOn = mUwbUtils.isAirplaneModeOn(mContext); 147 mUwbManager.registerAdapterStateCallback(mExecutor, mAdapterStateCallback); 148 mContext.registerReceiver(mAirplaneModeChangedReceiver, 149 new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED), null, mHandler); 150 refreshSummary(mPreference); 151 } 152 } 153 154 /** Called when activity stops being displayed to user. */ 155 @OnLifecycleEvent(ON_STOP) onStop()156 public void onStop() { 157 if (isUwbSupportedOnDevice()) { 158 mUwbManager.unregisterAdapterStateCallback(mAdapterStateCallback); 159 mContext.unregisterReceiver(mAirplaneModeChangedReceiver); 160 } 161 } 162 163 @Override updateState(Preference preference)164 public void updateState(Preference preference) { 165 super.updateState(preference); 166 preference.setEnabled(!mAirplaneModeOn); 167 refreshSummary(mPreference); 168 } 169 170 @Override getSummary()171 public CharSequence getSummary() { 172 if (mAirplaneModeOn) { 173 return mContext.getResources().getString(R.string.uwb_settings_summary_airplane_mode); 174 } else if (isUwbDisabledDueToRegulatory()) { 175 return mContext.getResources().getString( 176 R.string.uwb_settings_summary_no_uwb_regulatory); 177 } else { 178 return mContext.getResources().getString(R.string.uwb_settings_summary); 179 } 180 } 181 182 @Override getSliceHighlightMenuRes()183 public int getSliceHighlightMenuRes() { 184 return R.string.menu_key_connected_devices; 185 } 186 } 187 188