1 /* 2 * Copyright (C) 2016 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 package com.android.settings.nfc; 17 18 import android.content.BroadcastReceiver; 19 import android.content.Context; 20 import android.content.Intent; 21 import android.content.IntentFilter; 22 import android.net.Uri; 23 import android.nfc.NfcAdapter; 24 import android.provider.Settings; 25 import android.util.Log; 26 import android.widget.Switch; 27 28 import androidx.preference.PreferenceScreen; 29 30 import com.android.settings.R; 31 import com.android.settings.core.TogglePreferenceController; 32 import com.android.settings.slices.SliceBackgroundWorker; 33 import com.android.settingslib.core.lifecycle.LifecycleObserver; 34 import com.android.settingslib.core.lifecycle.events.OnPause; 35 import com.android.settingslib.core.lifecycle.events.OnResume; 36 import com.android.settingslib.widget.MainSwitchPreference; 37 import com.android.settingslib.widget.OnMainSwitchChangeListener; 38 39 import java.io.IOException; 40 41 public class NfcPreferenceController extends TogglePreferenceController 42 implements LifecycleObserver, OnResume, OnPause, OnMainSwitchChangeListener { 43 44 public static final String KEY_TOGGLE_NFC = "toggle_nfc"; 45 private final NfcAdapter mNfcAdapter; 46 private NfcEnabler mNfcEnabler; 47 private MainSwitchPreference mPreference; 48 NfcPreferenceController(Context context, String key)49 public NfcPreferenceController(Context context, String key) { 50 super(context, key); 51 mNfcAdapter = NfcAdapter.getDefaultAdapter(context); 52 } 53 54 @Override displayPreference(PreferenceScreen screen)55 public void displayPreference(PreferenceScreen screen) { 56 super.displayPreference(screen); 57 if (!isAvailable()) { 58 mNfcEnabler = null; 59 return; 60 } 61 62 mPreference = screen.findPreference(getPreferenceKey()); 63 mPreference.addOnSwitchChangeListener(this); 64 mNfcEnabler = new NfcEnabler(mContext, mPreference); 65 } 66 67 @Override onSwitchChanged(Switch switchView, boolean isChecked)68 public void onSwitchChanged(Switch switchView, boolean isChecked) { 69 if (isChecked != mNfcAdapter.isEnabled()) { 70 setChecked(isChecked); 71 } 72 } 73 74 @Override isChecked()75 public boolean isChecked() { 76 return mNfcAdapter.isEnabled(); 77 } 78 79 @Override setChecked(boolean isChecked)80 public boolean setChecked(boolean isChecked) { 81 if (isChecked) { 82 mNfcAdapter.enable(); 83 } else { 84 mNfcAdapter.disable(); 85 } 86 return true; 87 } 88 89 @Override 90 @AvailabilityStatus getAvailabilityStatus()91 public int getAvailabilityStatus() { 92 return mNfcAdapter != null 93 ? AVAILABLE 94 : UNSUPPORTED_ON_DEVICE; 95 } 96 97 @Override hasAsyncUpdate()98 public boolean hasAsyncUpdate() { 99 return true; 100 } 101 102 @Override isPublicSlice()103 public boolean isPublicSlice() { 104 return true; 105 } 106 107 @Override getSliceHighlightMenuRes()108 public int getSliceHighlightMenuRes() { 109 return R.string.menu_key_connected_devices; 110 } 111 112 @Override getBackgroundWorkerClass()113 public Class<? extends SliceBackgroundWorker> getBackgroundWorkerClass() { 114 return NfcSliceWorker.class; 115 } 116 117 @Override onResume()118 public void onResume() { 119 if (mNfcEnabler != null) { 120 mNfcEnabler.resume(); 121 } 122 } 123 124 @Override onPause()125 public void onPause() { 126 if (mNfcEnabler != null) { 127 mNfcEnabler.pause(); 128 } 129 } 130 shouldTurnOffNFCInAirplaneMode(Context context)131 public static boolean shouldTurnOffNFCInAirplaneMode(Context context) { 132 final String airplaneModeRadios = Settings.Global.getString(context.getContentResolver(), 133 Settings.Global.AIRPLANE_MODE_RADIOS); 134 return airplaneModeRadios != null && airplaneModeRadios.contains(Settings.Global.RADIO_NFC); 135 } 136 isToggleableInAirplaneMode(Context context)137 public static boolean isToggleableInAirplaneMode(Context context) { 138 final String toggleable = Settings.Global.getString(context.getContentResolver(), 139 Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS); 140 return toggleable != null && toggleable.contains(Settings.Global.RADIO_NFC); 141 } 142 143 /** 144 * Listener for background changes to NFC. 145 * 146 * <p> 147 * Listen to broadcasts from {@link NfcAdapter}. The worker will call notify changed on the 148 * NFC Slice only when the following extras are present in the broadcast: 149 * <ul> 150 * <li>{@link NfcAdapter#STATE_ON}</li> 151 * <li>{@link NfcAdapter#STATE_OFF}</li> 152 * </ul> 153 */ 154 public static class NfcSliceWorker extends SliceBackgroundWorker<Void> { 155 156 private static final String TAG = "NfcSliceWorker"; 157 158 private static final IntentFilter NFC_FILTER = 159 new IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED); 160 161 private NfcUpdateReceiver mUpdateObserver; 162 NfcSliceWorker(Context context, Uri uri)163 public NfcSliceWorker(Context context, Uri uri) { 164 super(context, uri); 165 mUpdateObserver = new NfcUpdateReceiver(this); 166 } 167 168 @Override onSlicePinned()169 protected void onSlicePinned() { 170 getContext().registerReceiver(mUpdateObserver, NFC_FILTER); 171 } 172 173 @Override onSliceUnpinned()174 protected void onSliceUnpinned() { 175 getContext().unregisterReceiver(mUpdateObserver); 176 } 177 178 @Override close()179 public void close() throws IOException { 180 mUpdateObserver = null; 181 } 182 updateSlice()183 public void updateSlice() { 184 notifySliceChange(); 185 } 186 187 public class NfcUpdateReceiver extends BroadcastReceiver { 188 189 private final int NO_EXTRA = -1; 190 191 private final NfcSliceWorker mSliceBackgroundWorker; 192 NfcUpdateReceiver(NfcSliceWorker sliceWorker)193 public NfcUpdateReceiver(NfcSliceWorker sliceWorker) { 194 mSliceBackgroundWorker = sliceWorker; 195 } 196 197 @Override onReceive(Context context, Intent intent)198 public void onReceive(Context context, Intent intent) { 199 final int nfcStateExtra = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE, 200 NO_EXTRA); 201 202 // Do nothing if state change is empty, or an intermediate step. 203 if ((nfcStateExtra == NO_EXTRA) 204 || (nfcStateExtra == NfcAdapter.STATE_TURNING_ON) 205 || (nfcStateExtra == NfcAdapter.STATE_TURNING_OFF)) { 206 Log.d(TAG, "Transitional update, dropping broadcast"); 207 return; 208 } 209 210 Log.d(TAG, "Nfc broadcast received, updating Slice."); 211 mSliceBackgroundWorker.updateSlice(); 212 } 213 } 214 } 215 } 216