OSDN Git Service

Refactor nfc preference controller
authorChihhang Chuang <chihhangchuang@google.com>
Thu, 29 Mar 2018 09:26:04 +0000 (17:26 +0800)
committerChihhang Chuang <chihhangchuang@google.com>
Thu, 24 May 2018 02:31:44 +0000 (10:31 +0800)
- Remove BaseNfcPreferenceController.
- NfcPreferenceController inherit from TogglePreferenceController.
- AndroidBeamPreferenceController inherit from BasePreferenceController.
- Override getIntentFilter in NfcPreferenceController to listen changes.
- Add an API (hasAsyncUpdate) into BasePreferenceController to
distinguish the setting which is updated asynchronously.

Change-Id: I1abe4410169e305a0d6106e24c54e7f2e763fc91
Merged-In: I7c9c48ea7f1ad01a02524beabf9d30baa3db891f
Fixes: 67997761
Fixes: 74887543
Test: RunSettingsRoboTests

19 files changed:
res/xml/connected_devices_advanced.xml
src/com/android/settings/connecteddevice/AdvancedConnectedDeviceController.java
src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragment.java
src/com/android/settings/core/BasePreferenceController.java
src/com/android/settings/nfc/AndroidBeamEnabler.java
src/com/android/settings/nfc/AndroidBeamPreferenceController.java
src/com/android/settings/nfc/BaseNfcPreferenceController.java [deleted file]
src/com/android/settings/nfc/NfcAirplaneModeObserver.java [new file with mode: 0644]
src/com/android/settings/nfc/NfcEnabler.java
src/com/android/settings/nfc/NfcPreferenceController.java
src/com/android/settings/slices/SliceBroadcastReceiver.java
tests/robotests/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceControllerTest.java
tests/robotests/src/com/android/settings/core/BasePreferenceControllerTest.java
tests/robotests/src/com/android/settings/nfc/AndroidBeamPreferenceControllerTest.java
tests/robotests/src/com/android/settings/nfc/NfcAirplaneModeObserverTest.java [new file with mode: 0644]
tests/robotests/src/com/android/settings/nfc/NfcPreferenceControllerTest.java
tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java
tests/robotests/src/com/android/settings/testutils/FakeToggleController.java
tests/robotests/src/com/android/settings/testutils/shadow/ShadowNfcAdapter.java

index f81fd79..41d3e9b 100644 (file)
         android:title="@string/nfc_quick_toggle_title"
         android:icon="@drawable/ic_nfc"
         android:summary="@string/nfc_quick_toggle_summary"
+        settings:controller="com.android.settings.nfc.NfcPreferenceController"
         android:order="-7"/>
 
     <com.android.settingslib.RestrictedPreference
         android:fragment="com.android.settings.nfc.AndroidBeam"
         android:key="android_beam_settings"
         android:title="@string/android_beam_settings_title"
+        settings:controller="com.android.settings.nfc.AndroidBeamPreferenceController"
         android:icon="@drawable/ic_android"
         android:order="-6"/>
 
index eb93eb1..3976c92 100644 (file)
@@ -50,7 +50,7 @@ public class AdvancedConnectedDeviceController extends BasePreferenceController
      */
     public static int getConnectedDevicesSummaryResourceId(Context context) {
         final NfcPreferenceController nfcPreferenceController =
-                new NfcPreferenceController(context);
+                new NfcPreferenceController(context, NfcPreferenceController.KEY_TOGGLE_NFC);
 
         return getConnectedDevicesSummaryResourceId(nfcPreferenceController,
                 isDrivingModeAvailable(context));
index a64a430..9795e9f 100644 (file)
@@ -24,7 +24,6 @@ import com.android.settings.R;
 import com.android.settings.bluetooth.BluetoothFilesPreferenceController;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.nfc.AndroidBeamPreferenceController;
-import com.android.settings.nfc.NfcPreferenceController;
 import com.android.settings.print.PrintSettingPreferenceController;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settingslib.core.AbstractPreferenceController;
@@ -70,25 +69,15 @@ public class AdvancedConnectedDeviceDashboardFragment extends DashboardFragment
             Lifecycle lifecycle) {
         final List<AbstractPreferenceController> controllers = new ArrayList<>();
 
-        final AndroidBeamPreferenceController beamPreferenceController =
-                new AndroidBeamPreferenceController(context);
-        controllers.add(beamPreferenceController);
-
         controllers.add(new BluetoothFilesPreferenceController(context));
         controllers.add(new BluetoothOnWhileDrivingPreferenceController(context));
 
         final PrintSettingPreferenceController printerController =
                 new PrintSettingPreferenceController(context);
-        final NfcPreferenceController nfcPreferenceController =
-                new NfcPreferenceController(context);
 
         if (lifecycle != null) {
-            lifecycle.addObserver(beamPreferenceController);
             lifecycle.addObserver(printerController);
-            lifecycle.addObserver(nfcPreferenceController);
         }
-
-        controllers.add(nfcPreferenceController);
         controllers.add(printerController);
 
         return controllers;
index c2d1cf5..efd9448 100644 (file)
@@ -247,6 +247,16 @@ public abstract class BasePreferenceController extends AbstractPreferenceControl
     }
 
     /**
+     * @return {@code true} if the setting update asynchronously.
+     * <p>
+     * For example, a Wifi controller would return true, because it needs to update the radio
+     * and wait for it to turn on.
+     */
+    public boolean hasAsyncUpdate() {
+        return false;
+    }
+
+    /**
      * Updates non-indexable keys for search provider.
      *
      * Called by SearchIndexProvider#getNonIndexableKeys
index 66e42b6..1808775 100644 (file)
@@ -20,7 +20,6 @@ import android.content.Context;
 import android.nfc.NfcAdapter;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.support.v7.preference.Preference;
 
 import com.android.settings.R;
 import com.android.settingslib.RestrictedLockUtils;
@@ -36,18 +35,14 @@ public class AndroidBeamEnabler extends BaseNfcEnabler {
 
     public AndroidBeamEnabler(Context context, RestrictedPreference preference) {
         super(context);
-
         mPreference = preference;
-
         mBeamDisallowedBySystem = RestrictedLockUtils.hasBaseUserRestriction(context,
                 UserManager.DISALLOW_OUTGOING_BEAM, UserHandle.myUserId());
-
         if (!isNfcAvailable()) {
             // NFC is not supported
             mPreference.setEnabled(false);
             return;
         }
-
         if (mBeamDisallowedBySystem) {
             mPreference.setEnabled(false);
         }
index 6ae7fe5..b4026de 100644 (file)
 package com.android.settings.nfc;
 
 import android.content.Context;
+import android.nfc.NfcAdapter;
+import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
 
-import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.core.BasePreferenceController;
 import com.android.settingslib.RestrictedPreference;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnPause;
 import com.android.settingslib.core.lifecycle.events.OnResume;
 
-public class AndroidBeamPreferenceController extends BaseNfcPreferenceController {
+import java.util.List;
+
+public class AndroidBeamPreferenceController extends BasePreferenceController
+        implements LifecycleObserver, OnResume, OnPause {
 
     public static final String KEY_ANDROID_BEAM_SETTINGS = "android_beam_settings";
+    private final NfcAdapter mNfcAdapter;
+    private AndroidBeamEnabler mAndroidBeamEnabler;
+    private NfcAirplaneModeObserver mAirplaneModeObserver;
 
-    public AndroidBeamPreferenceController(Context context) {
-        super(context);
+    public AndroidBeamPreferenceController(Context context, String key) {
+        super(context, key);
+        mNfcAdapter = NfcAdapter.getDefaultAdapter(context);
     }
 
     @Override
     public void displayPreference(PreferenceScreen screen) {
         super.displayPreference(screen);
         if (!isAvailable()) {
+            mAndroidBeamEnabler = null;
             return;
         }
 
-        mNfcEnabler = new AndroidBeamEnabler(mContext, (RestrictedPreference) mPreference);
+        final RestrictedPreference restrictedPreference =
+                (RestrictedPreference) screen.findPreference(getPreferenceKey());
+        mAndroidBeamEnabler = new AndroidBeamEnabler(mContext, restrictedPreference);
+
+        // Manually set dependencies for NFC when not toggleable.
+        if (!NfcPreferenceController.isToggleableInAirplaneMode(mContext)) {
+            mAirplaneModeObserver = new NfcAirplaneModeObserver(mContext, mNfcAdapter,
+                    (Preference) restrictedPreference);
+        }
     }
 
     @Override
-    public String getPreferenceKey() {
-        return KEY_ANDROID_BEAM_SETTINGS;
+    @AvailabilityStatus
+    public int getAvailabilityStatus() {
+        return mNfcAdapter != null
+                ? AVAILABLE
+                : UNSUPPORTED_ON_DEVICE;
+    }
+
+    @Override
+    public void onResume() {
+        if (mAirplaneModeObserver != null) {
+            mAirplaneModeObserver.register();
+        }
+        if (mAndroidBeamEnabler != null) {
+            mAndroidBeamEnabler.resume();
+        }
+    }
+
+    @Override
+    public void onPause() {
+        if (mAirplaneModeObserver != null) {
+            mAirplaneModeObserver.unregister();
+        }
+        if (mAndroidBeamEnabler != null) {
+            mAndroidBeamEnabler.pause();
+        }
     }
 }
diff --git a/src/com/android/settings/nfc/BaseNfcPreferenceController.java b/src/com/android/settings/nfc/BaseNfcPreferenceController.java
deleted file mode 100644 (file)
index 33d75fa..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settings.nfc;
-
-import android.content.Context;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.nfc.NfcAdapter;
-import android.nfc.NfcManager;
-import android.os.Handler;
-import android.provider.Settings;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceScreen;
-
-import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.core.lifecycle.events.OnPause;
-import com.android.settingslib.core.lifecycle.events.OnResume;
-
-import java.util.List;
-
-public abstract class BaseNfcPreferenceController extends AbstractPreferenceController
-        implements PreferenceControllerMixin, LifecycleObserver, OnResume, OnPause {
-
-    protected BaseNfcEnabler mNfcEnabler;
-    private NfcAdapter mNfcAdapter;
-    private int mAirplaneMode;
-    private AirplaneModeObserver mAirplaneModeObserver;
-    protected Preference mPreference;
-
-    public BaseNfcPreferenceController(Context context) {
-        super(context);
-        mNfcAdapter = NfcAdapter.getDefaultAdapter(context);
-    }
-
-    @Override
-    public void displayPreference(PreferenceScreen screen) {
-        super.displayPreference(screen);
-
-        if (!isAvailable()) {
-            mNfcEnabler = null;
-            return;
-        }
-
-        mPreference = screen.findPreference(getPreferenceKey());
-
-        // Manually set dependencies for NFC when not toggleable.
-        if (!isToggleableInAirplaneMode(mContext)) {
-            mAirplaneModeObserver = new AirplaneModeObserver();
-            updateNfcPreference();
-        }
-    }
-
-    @Override
-    public void updateNonIndexableKeys(List<String> keys) {
-        if (!isAvailable()) {
-            keys.add(getPreferenceKey());
-        }
-    }
-
-    @Override
-    public boolean isAvailable() {
-        return mNfcAdapter != null;
-    }
-
-    public abstract String getPreferenceKey();
-
-    @Override
-    public void onResume() {
-        if (mAirplaneModeObserver != null) {
-            mAirplaneModeObserver.register();
-        }
-        if (mNfcEnabler != null) {
-            mNfcEnabler.resume();
-        }
-    }
-
-    @Override
-    public void onPause() {
-        if (mAirplaneModeObserver != null) {
-            mAirplaneModeObserver.unregister();
-        }
-        if (mNfcEnabler != null) {
-            mNfcEnabler.pause();
-        }
-    }
-
-    private void updateNfcPreference() {
-        final int airplaneMode = Settings.Global.getInt(
-                mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, mAirplaneMode);
-        if (airplaneMode == mAirplaneMode) {
-            return;
-        }
-        mAirplaneMode = airplaneMode;
-        boolean toggleable = mAirplaneMode != 1;
-        if (toggleable) {
-            mNfcAdapter.enable();
-        } else {
-            mNfcAdapter.disable();
-        }
-        mPreference.setEnabled(toggleable);
-    }
-
-    public static boolean isToggleableInAirplaneMode(Context context) {
-        String toggleable = Settings.Global.getString(context.getContentResolver(),
-                Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
-        return toggleable != null && toggleable.contains(Settings.Global.RADIO_NFC);
-    }
-
-    private final class AirplaneModeObserver extends ContentObserver {
-        private final Uri AIRPLANE_MODE_URI =
-                Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON);
-
-        private AirplaneModeObserver() {
-            super(new Handler());
-        }
-
-        public void register() {
-            mContext.getContentResolver().registerContentObserver(AIRPLANE_MODE_URI, false, this);
-        }
-
-        public void unregister() {
-            mContext.getContentResolver().unregisterContentObserver(this);
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            super.onChange(selfChange, uri);
-            updateNfcPreference();
-        }
-    }
-
-}
diff --git a/src/com/android/settings/nfc/NfcAirplaneModeObserver.java b/src/com/android/settings/nfc/NfcAirplaneModeObserver.java
new file mode 100644 (file)
index 0000000..723d814
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.nfc;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.nfc.NfcAdapter;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+
+/**
+ * NfcAirplaneModeObserver is a helper to manage the Nfc on/off when airplane mode status
+ * is changed.
+ */
+public class NfcAirplaneModeObserver extends ContentObserver {
+
+    private final Context mContext;
+    private final NfcAdapter mNfcAdapter;
+    private final Preference mPreference;
+    private int mAirplaneMode;
+
+    @VisibleForTesting
+    final static Uri AIRPLANE_MODE_URI =
+            Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON);
+
+    public NfcAirplaneModeObserver(Context context, NfcAdapter nfcAdapter, Preference preference) {
+        super(new Handler(Looper.getMainLooper()));
+        mContext = context;
+        mNfcAdapter = nfcAdapter;
+        mPreference = preference;
+        updateNfcPreference();
+    }
+
+    public void register() {
+        mContext.getContentResolver().registerContentObserver(AIRPLANE_MODE_URI, false, this);
+    }
+
+    public void unregister() {
+        mContext.getContentResolver().unregisterContentObserver(this);
+    }
+
+    @Override
+    public void onChange(boolean selfChange, Uri uri) {
+        super.onChange(selfChange, uri);
+        updateNfcPreference();
+    }
+
+    private void updateNfcPreference() {
+        final int airplaneMode = Settings.Global.getInt(
+                mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, mAirplaneMode);
+        if (airplaneMode == mAirplaneMode) {
+            return;
+        }
+
+        mAirplaneMode = airplaneMode;
+        boolean toggleable = mAirplaneMode != 1;
+        if (toggleable) {
+            mNfcAdapter.enable();
+        } else {
+            mNfcAdapter.disable();
+        }
+        mPreference.setEnabled(toggleable);
+    }
+}
index 29cef99..f39a0a1 100644 (file)
@@ -18,52 +18,20 @@ package com.android.settings.nfc;
 
 import android.content.Context;
 import android.nfc.NfcAdapter;
-import android.support.v7.preference.Preference;
 import android.support.v14.preference.SwitchPreference;
 
 /**
  * NfcEnabler is a helper to manage the Nfc on/off checkbox preference. It turns on/off Nfc
  * and ensures the summary of the preference reflects the current state.
  */
-public class NfcEnabler extends BaseNfcEnabler implements Preference.OnPreferenceChangeListener {
+public class NfcEnabler extends BaseNfcEnabler {
     private final SwitchPreference mPreference;
 
     public NfcEnabler(Context context, SwitchPreference preference) {
         super(context);
-
         mPreference = preference;
     }
 
-    public void resume() {
-        super.resume();
-        if (isNfcAvailable()) {
-            mPreference.setOnPreferenceChangeListener(this);
-        }
-    }
-
-    public void pause() {
-        super.pause();
-        if (isNfcAvailable()) {
-            mPreference.setOnPreferenceChangeListener(null);
-        }
-    }
-
-    public boolean onPreferenceChange(Preference preference, Object value) {
-        // Turn NFC on/off
-
-        final boolean desiredState = (Boolean) value;
-        mPreference.setChecked(desiredState);
-        mPreference.setEnabled(false);
-
-        if (desiredState) {
-            mNfcAdapter.enable();
-        } else {
-            mNfcAdapter.disable();
-        }
-
-        return false;
-    }
-
     @Override
     protected void handleNfcStateChanged(int newState) {
         switch (newState) {
index a0678e0..a5d7c76 100644 (file)
 package com.android.settings.nfc;
 
 import android.content.Context;
+import android.content.IntentFilter;
+import android.nfc.NfcAdapter;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
 import android.support.v14.preference.SwitchPreference;
 
-import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.core.TogglePreferenceController;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnPause;
 import com.android.settingslib.core.lifecycle.events.OnResume;
 
+import java.util.List;
 
-public class NfcPreferenceController extends BaseNfcPreferenceController {
+public class NfcPreferenceController extends TogglePreferenceController
+        implements LifecycleObserver, OnResume, OnPause {
 
     public static final String KEY_TOGGLE_NFC = "toggle_nfc";
+    private final NfcAdapter mNfcAdapter;
+    private NfcEnabler mNfcEnabler;
+    private NfcAirplaneModeObserver mAirplaneModeObserver;
 
-    public NfcPreferenceController(Context context) {
-        super(context);
+    public NfcPreferenceController(Context context, String key) {
+        super(context, key);
+        mNfcAdapter = NfcAdapter.getDefaultAdapter(context);
     }
 
     @Override
     public void displayPreference(PreferenceScreen screen) {
         super.displayPreference(screen);
         if (!isAvailable()) {
+            mNfcEnabler = null;
             return;
         }
 
-        mNfcEnabler = new NfcEnabler(mContext, (SwitchPreference) mPreference);
+        final SwitchPreference switchPreference =
+                (SwitchPreference) screen.findPreference(getPreferenceKey());
+
+        mNfcEnabler = new NfcEnabler(mContext, switchPreference);
+
+        // Manually set dependencies for NFC when not toggleable.
+        if (!isToggleableInAirplaneMode(mContext)) {
+            mAirplaneModeObserver = new NfcAirplaneModeObserver(mContext,
+                    mNfcAdapter, (Preference) switchPreference);
+        }
+    }
+
+    @Override
+    public boolean isChecked() {
+        return mNfcAdapter.isEnabled();
+    }
+
+    @Override
+    public boolean setChecked(boolean isChecked) {
+        if (isChecked) {
+            mNfcAdapter.enable();
+        } else {
+            mNfcAdapter.disable();
+        }
+        return true;
+    }
+
+    @Override
+    @AvailabilityStatus
+    public int getAvailabilityStatus() {
+        return mNfcAdapter != null
+                ? AVAILABLE
+                : UNSUPPORTED_ON_DEVICE;
+    }
+
+    @Override
+    public IntentFilter getIntentFilter() {
+        final IntentFilter filter = new IntentFilter();
+        filter.addAction(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED);
+        filter.addAction(NfcAdapter.EXTRA_ADAPTER_STATE);
+        return filter;
+    }
+
+    @Override
+    public boolean hasAsyncUpdate() {
+        return true;
+    }
+
+    @Override
+    public boolean isSliceable() {
+        return TextUtils.equals(getPreferenceKey(), KEY_TOGGLE_NFC);
+    }
+
+    @Override
+    public void onResume() {
+        if (mAirplaneModeObserver != null) {
+            mAirplaneModeObserver.register();
+        }
+        if (mNfcEnabler != null) {
+            mNfcEnabler.resume();
+        }
     }
 
     @Override
-    public String getPreferenceKey() {
-        return KEY_TOGGLE_NFC;
+    public void onPause() {
+        if (mAirplaneModeObserver != null) {
+            mAirplaneModeObserver.unregister();
+        }
+        if (mNfcEnabler != null) {
+            mNfcEnabler.pause();
+        }
+    }
+
+    public static boolean isToggleableInAirplaneMode(Context context) {
+        final String toggleable = Settings.Global.getString(context.getContentResolver(),
+                Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
+        return toggleable != null && toggleable.contains(Settings.Global.RADIO_NFC);
     }
 }
index 80e3e3c..213bf00 100644 (file)
@@ -106,7 +106,9 @@ public class SliceBroadcastReceiver extends BroadcastReceiver {
 
         if (!controller.isAvailable()) {
             Log.w(TAG, "Can't update " + key + " since the setting is unavailable");
-            updateUri(context, key, isPlatformSlice);
+            if (!controller.hasAsyncUpdate()) {
+                updateUri(context, key, isPlatformSlice);
+            }
             return;
         }
 
@@ -115,7 +117,9 @@ public class SliceBroadcastReceiver extends BroadcastReceiver {
         final TogglePreferenceController toggleController = (TogglePreferenceController) controller;
         toggleController.setChecked(isChecked);
         logSliceValueChange(context, key, isChecked ? 1 : 0);
-        updateUri(context, key, isPlatformSlice);
+        if (!controller.hasAsyncUpdate()) {
+            updateUri(context, key, isPlatformSlice);
+        }
     }
 
     private void handleSliderAction(Context context, String key, int newPosition,
index ceea81e..ce55802 100644 (file)
@@ -55,7 +55,8 @@ public class AdvancedConnectedDeviceControllerTest {
 
         mContext = spy(RuntimeEnvironment.application);
         mContentResolver = mContext.getContentResolver();
-        mNfcController = new NfcPreferenceController(mContext);
+        mNfcController = new NfcPreferenceController(mContext,
+                NfcPreferenceController.KEY_TOGGLE_NFC);
         mShadowNfcAdapter = shadowOf(ShadowNfcAdapter.getNfcAdapter(mContext));
     }
 
index eec4e37..ad2dabb 100644 (file)
@@ -135,6 +135,11 @@ public class BasePreferenceControllerTest {
     }
 
     @Test
+    public void hasAsyncUpdate_shouldReturnFalse() {
+        assertThat(mPreferenceController.hasAsyncUpdate()).isFalse();
+    }
+
+    @Test
     public void settingAvailable_disabledOnDisplayPreference_preferenceEnabled() {
         final PreferenceScreen screen = mock(PreferenceScreen.class);
         final Preference preference = new Preference(mContext);
index df5bb64..8232afe 100644 (file)
@@ -72,7 +72,8 @@ public class AndroidBeamPreferenceControllerTest {
                 UserManager.DISALLOW_OUTGOING_BEAM, UserHandle.myUserId())).thenReturn(false);
         when(NfcAdapter.getDefaultAdapter(mContext)).thenReturn(mNfcAdapter);
 
-        mAndroidBeamController = new AndroidBeamPreferenceController(mContext);
+        mAndroidBeamController = new AndroidBeamPreferenceController(mContext,
+                AndroidBeamPreferenceController.KEY_ANDROID_BEAM_SETTINGS);
         mAndroidBeamPreference = new RestrictedPreference(RuntimeEnvironment.application);
         when(mScreen.findPreference(mAndroidBeamController.getPreferenceKey())).thenReturn(
                 mAndroidBeamPreference);
diff --git a/tests/robotests/src/com/android/settings/nfc/NfcAirplaneModeObserverTest.java b/tests/robotests/src/com/android/settings/nfc/NfcAirplaneModeObserverTest.java
new file mode 100644 (file)
index 0000000..b6d28d1
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.nfc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.nfc.NfcAdapter;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.support.v14.preference.SwitchPreference;
+
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.ShadowNfcAdapter;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(shadows = {ShadowNfcAdapter.class})
+public class NfcAirplaneModeObserverTest {
+
+    Context mContext;
+    private NfcAdapter mNfcAdapter;
+    private SwitchPreference mNfcPreference;
+    private NfcAirplaneModeObserver mNfcAirplaneModeObserver;
+
+    @Before
+    public void setUp() {
+        mContext = ShadowApplication.getInstance().getApplicationContext();
+        mNfcAdapter = NfcAdapter.getDefaultAdapter(mContext);
+
+        mNfcPreference = new SwitchPreference(RuntimeEnvironment.application);
+
+        mNfcAirplaneModeObserver = new NfcAirplaneModeObserver(mContext, mNfcAdapter,
+                (Preference) mNfcPreference);
+    }
+
+    @Test
+    public void NfcAirplaneModeObserver_airplaneOn_shouldDisableNfc() {
+        ReflectionHelpers.setField(mNfcAirplaneModeObserver,
+                "mAirplaneMode", 0);
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.AIRPLANE_MODE_ON, 1);
+
+        mNfcAirplaneModeObserver.onChange(false,
+                NfcAirplaneModeObserver.AIRPLANE_MODE_URI);
+
+        assertThat(mNfcAdapter.isEnabled()).isFalse();
+        assertThat(mNfcPreference.isEnabled()).isFalse();
+    }
+
+    @Test
+    public void NfcAirplaneModeObserver_airplaneOff_shouldEnableNfc() {
+        ReflectionHelpers.setField(mNfcAirplaneModeObserver,
+                "mAirplaneMode", 1);
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.AIRPLANE_MODE_ON, 0);
+
+        mNfcAirplaneModeObserver.onChange(false,
+                NfcAirplaneModeObserver.AIRPLANE_MODE_URI);
+
+        assertThat(mNfcAdapter.isEnabled()).isTrue();
+        assertThat(mNfcPreference.isEnabled()).isTrue();
+    }
+}
index 802e199..008d158 100644 (file)
@@ -19,6 +19,7 @@ package com.android.settings.nfc;
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -26,8 +27,8 @@ import android.nfc.NfcAdapter;
 import android.nfc.NfcManager;
 import android.os.UserManager;
 import android.provider.Settings;
-import android.support.v14.preference.SwitchPreference;
 import android.support.v7.preference.PreferenceScreen;
+import android.support.v14.preference.SwitchPreference;
 
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
@@ -68,8 +69,10 @@ public class NfcPreferenceControllerTest {
         when(mContext.getSystemService(Context.NFC_SERVICE)).thenReturn(mManager);
         when(NfcAdapter.getDefaultAdapter(mContext)).thenReturn(mNfcAdapter);
 
-        mNfcController = new NfcPreferenceController(mContext);
+        mNfcController = new NfcPreferenceController(mContext,
+                NfcPreferenceController.KEY_TOGGLE_NFC);
         mNfcPreference = new SwitchPreference(RuntimeEnvironment.application);
+
         when(mScreen.findPreference(mNfcController.getPreferenceKey())).thenReturn(mNfcPreference);
 
         Settings.Global.putString(mContext.getContentResolver(),
@@ -83,15 +86,17 @@ public class NfcPreferenceControllerTest {
     }
 
     @Test
-    public void isAvailable_hasNfc_shouldReturnTrue() {
+    public void getAvailabilityStatus_hasNfc_shouldReturnAvailable() {
         when(mNfcAdapter.isEnabled()).thenReturn(true);
-        assertThat(mNfcController.isAvailable()).isTrue();
+        assertThat(mNfcController.getAvailabilityStatus())
+                .isEqualTo(NfcPreferenceController.AVAILABLE);
     }
 
     @Test
-    public void isAvailable_noNfcAdapter_shouldReturnFalse() {
+    public void getAvailabilityStatus_noNfcAdapter_shouldReturnDisabledUnsupported() {
         ReflectionHelpers.setField(mNfcController, "mNfcAdapter", null);
-        assertThat(mNfcController.isAvailable()).isFalse();
+        assertThat(mNfcController.getAvailabilityStatus())
+                .isEqualTo(NfcPreferenceController.UNSUPPORTED_ON_DEVICE);
     }
 
     @Test
@@ -157,4 +162,46 @@ public class NfcPreferenceControllerTest {
 
         assertThat(keys).hasSize(1);
     }
+    @Test
+    public void setChecked_True_nfcShouldEnable() {
+        mNfcController.setChecked(true);
+        mNfcController.onResume();
+
+        verify(mNfcAdapter).enable();
+    }
+
+    @Test
+    public void setChecked_False_nfcShouldDisable() {
+        mNfcController.setChecked(false);
+        mNfcController.onResume();
+
+        verify(mNfcAdapter).disable();
+    }
+
+    @Test
+    public void hasAsyncUpdate_shouldReturnTrue() {
+        assertThat(mNfcController.hasAsyncUpdate()).isTrue();
+    }
+
+    @Test
+    public void isToggleableInAirplaneMode_containNfc_shouldReturnTrue() {
+        Settings.Global.putString(mContext.getContentResolver(),
+                Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS,
+                Settings.Global.RADIO_NFC);
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.AIRPLANE_MODE_ON, 1);
+
+        assertThat(NfcPreferenceController.isToggleableInAirplaneMode(mContext)).isTrue();
+    }
+
+    @Test
+    public void isToggleableInAirplaneMode_withoutNfc_shouldReturnFalse() {
+        Settings.Global.putString(mContext.getContentResolver(),
+                Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS,
+                "null");
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.AIRPLANE_MODE_ON, 1);
+
+        assertThat(NfcPreferenceController.isToggleableInAirplaneMode(mContext)).isFalse();
+    }
 }
index f03f88e..5f0bc96 100644 (file)
@@ -23,6 +23,7 @@ import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
@@ -126,6 +127,60 @@ public class SliceBroadcastReceiverTest {
     }
 
     @Test
+    public void toggleUpdate_synchronously_notifyChange_should_be_called() {
+        // Monitor the ContentResolver
+        final ContentResolver resolver = spy(mContext.getContentResolver());
+        doReturn(resolver).when(mContext).getContentResolver();
+
+        final String key = "key";
+        mSearchFeatureProvider.getSearchIndexableResources().getProviderValues().clear();
+        insertSpecialCase(key);
+
+        FakeToggleController fakeToggleController = new FakeToggleController(mContext, key);
+        fakeToggleController.setChecked(true);
+        // Set the toggle setting update synchronously.
+        fakeToggleController.setAsyncUpdate(false);
+        Intent intent = new Intent(SettingsSliceProvider.ACTION_TOGGLE_CHANGED);
+        intent.putExtra(SettingsSliceProvider.EXTRA_SLICE_KEY, key);
+
+        assertThat(fakeToggleController.isChecked()).isTrue();
+
+        // Toggle setting
+        mReceiver.onReceive(mContext, intent);
+
+        assertThat(fakeToggleController.isChecked()).isFalse();
+
+        final Uri expectedUri = SliceBuilderUtils.getUri(
+                SettingsSlicesContract.PATH_SETTING_ACTION + "/" + key, false);
+        verify(resolver).notifyChange(eq(expectedUri), eq(null));
+    }
+
+    @Test
+    public void toggleUpdate_asynchronously_notifyChange_should_not_be_called() {
+        // Monitor the ContentResolver
+        final ContentResolver resolver = spy(mContext.getContentResolver());
+        doReturn(resolver).when(mContext).getContentResolver();
+
+        final String key = "key";
+        mSearchFeatureProvider.getSearchIndexableResources().getProviderValues().clear();
+        insertSpecialCase(key);
+
+        FakeToggleController fakeToggleController = new FakeToggleController(mContext, key);
+        fakeToggleController.setChecked(true);
+        // Set the toggle setting update asynchronously.
+        fakeToggleController.setAsyncUpdate(true);
+        Intent intent = new Intent(SettingsSliceProvider.ACTION_TOGGLE_CHANGED);
+        intent.putExtra(SettingsSliceProvider.EXTRA_SLICE_KEY, key);
+
+        assertThat(fakeToggleController.isChecked()).isTrue();
+
+        // Toggle setting
+        mReceiver.onReceive(mContext, intent);
+
+        verify(resolver, never()).notifyChange(null, null);
+    }
+
+    @Test
     public void onReceive_sliderChanged() {
         final String key = "key";
         final int position = FakeSliderController.MAX_STEPS - 1;
index 680a04d..8e408f0 100644 (file)
@@ -36,6 +36,8 @@ public class FakeToggleController extends TogglePreferenceController {
     private final int ON = 1;
     private final int OFF = 0;
 
+    private boolean mIsAsyncUpdate = false;
+
     public FakeToggleController(Context context, String preferenceKey) {
         super(context, preferenceKey);
     }
@@ -67,4 +69,13 @@ public class FakeToggleController extends TogglePreferenceController {
     public boolean isSliceable() {
         return true;
     }
+
+    @Override
+    public boolean hasAsyncUpdate() {
+        return mIsAsyncUpdate;
+    }
+
+    public void setAsyncUpdate(boolean isAsyncUpdate) {
+        mIsAsyncUpdate = isAsyncUpdate;
+    }
 }
index e4421ee..07c8e54 100644 (file)
@@ -33,6 +33,7 @@ import org.robolectric.util.ReflectionHelpers.ClassParameter;
 @Implements(NfcAdapter.class)
 public class ShadowNfcAdapter {
     private static boolean sReaderModeEnabled;
+    private boolean mIsNfcEnabled = false;
 
     @Implementation
     public void enableReaderMode(Activity activity, NfcAdapter.ReaderCallback callback, int flags,
@@ -46,6 +47,23 @@ public class ShadowNfcAdapter {
                 NfcAdapter.class, ClassParameter.from(Context.class, context));
     }
 
+    @Implementation
+    public boolean isEnabled() {
+        return mIsNfcEnabled;
+    }
+
+    @Implementation
+    public boolean enable() {
+        mIsNfcEnabled = true;
+        return true;
+    }
+
+    @Implementation
+    public boolean disable() {
+        mIsNfcEnabled = false;
+        return true;
+    }
+
     public static boolean isReaderModeEnabled() {
         return sReaderModeEnabled;
     }