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