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