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.util.Log; 25 import android.widget.CompoundButton; 26 import android.widget.CompoundButton.OnCheckedChangeListener; 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 38 import java.io.IOException; 39 40 public class NfcPreferenceController extends TogglePreferenceController 41 implements LifecycleObserver, OnResume, OnPause, OnCheckedChangeListener { 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 = screen.findPreference(getPreferenceKey()); 62 mPreference.addOnSwitchChangeListener(this); 63 mNfcEnabler = new NfcEnabler(mContext, mPreference); 64 } 65 66 @Override onCheckedChanged(CompoundButton buttonView, boolean isChecked)67 public void onCheckedChanged(CompoundButton buttonView, 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 getSliceHighlightMenuRes()107 public int getSliceHighlightMenuRes() { 108 return R.string.menu_key_connected_devices; 109 } 110 111 @Override getBackgroundWorkerClass()112 public Class<? extends SliceBackgroundWorker> getBackgroundWorkerClass() { 113 return NfcSliceWorker.class; 114 } 115 116 @Override onResume()117 public void onResume() { 118 if (mNfcEnabler != null) { 119 mNfcEnabler.resume(); 120 } 121 } 122 123 @Override onPause()124 public void onPause() { 125 if (mNfcEnabler != null) { 126 mNfcEnabler.pause(); 127 } 128 } 129 130 /** 131 * Listener for background changes to NFC. 132 * 133 * <p> 134 * Listen to broadcasts from {@link NfcAdapter}. The worker will call notify changed on the 135 * NFC Slice only when the following extras are present in the broadcast: 136 * <ul> 137 * <li>{@link NfcAdapter#STATE_ON}</li> 138 * <li>{@link NfcAdapter#STATE_OFF}</li> 139 * </ul> 140 */ 141 public static class NfcSliceWorker extends SliceBackgroundWorker<Void> { 142 143 private static final String TAG = "NfcSliceWorker"; 144 145 private static final IntentFilter NFC_FILTER = 146 new IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED); 147 148 private NfcUpdateReceiver mUpdateObserver; 149 NfcSliceWorker(Context context, Uri uri)150 public NfcSliceWorker(Context context, Uri uri) { 151 super(context, uri); 152 mUpdateObserver = new NfcUpdateReceiver(this); 153 } 154 155 @Override onSlicePinned()156 protected void onSlicePinned() { 157 getContext().registerReceiver(mUpdateObserver, NFC_FILTER); 158 } 159 160 @Override onSliceUnpinned()161 protected void onSliceUnpinned() { 162 getContext().unregisterReceiver(mUpdateObserver); 163 } 164 165 @Override close()166 public void close() throws IOException { 167 mUpdateObserver = null; 168 } 169 updateSlice()170 public void updateSlice() { 171 notifySliceChange(); 172 } 173 174 public class NfcUpdateReceiver extends BroadcastReceiver { 175 176 private final int NO_EXTRA = -1; 177 178 private final NfcSliceWorker mSliceBackgroundWorker; 179 NfcUpdateReceiver(NfcSliceWorker sliceWorker)180 public NfcUpdateReceiver(NfcSliceWorker sliceWorker) { 181 mSliceBackgroundWorker = sliceWorker; 182 } 183 184 @Override onReceive(Context context, Intent intent)185 public void onReceive(Context context, Intent intent) { 186 final int nfcStateExtra = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE, 187 NO_EXTRA); 188 189 // Do nothing if state change is empty, or an intermediate step. 190 if ((nfcStateExtra == NO_EXTRA) 191 || (nfcStateExtra == NfcAdapter.STATE_TURNING_ON) 192 || (nfcStateExtra == NfcAdapter.STATE_TURNING_OFF)) { 193 Log.d(TAG, "Transitional update, dropping broadcast"); 194 return; 195 } 196 197 Log.d(TAG, "Nfc broadcast received, updating Slice."); 198 mSliceBackgroundWorker.updateSlice(); 199 } 200 } 201 } 202 } 203