OSDN Git Service

Make device discoverable in Connected Devices settings page
authortimhypeng <timhypeng@google.com>
Fri, 11 May 2018 07:41:10 +0000 (15:41 +0800)
committertim peng <timhypeng@google.com>
Tue, 22 May 2018 02:15:12 +0000 (02:15 +0000)
* Set preference title by bluetooth state
* Enable bluetooth discoverable mode in Connected device page
* Add more test cases for DiscoverableFooterPreferenceController

Bug: 79294219
Test: make -j50 RunSettingsRoboTests
Change-Id: I6d4f8ec3870c43bf48e9666eabd60068aa8950bb
Merged-In: I6d4f8ec3870c43bf48e9666eabd60068aa8950bb

src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java
src/com/android/settings/connecteddevice/DiscoverableFooterPreferenceController.java
tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragmentTest.java
tests/robotests/src/com/android/settings/connecteddevice/DiscoverableFooterPreferenceControllerTest.java

index 8495fa1..81ba65c 100644 (file)
@@ -64,15 +64,20 @@ public class ConnectedDeviceDashboardFragment extends DashboardFragment {
 
     @Override
     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
-        return buildPreferenceControllers(context);
+        return buildPreferenceControllers(context, getLifecycle());
     }
 
-    private static List<AbstractPreferenceController> buildPreferenceControllers(Context context) {
+    private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
+            Lifecycle lifecycle) {
         final List<AbstractPreferenceController> controllers = new ArrayList<>();
         final DiscoverableFooterPreferenceController discoverableFooterPreferenceController =
                 new DiscoverableFooterPreferenceController(context);
         controllers.add(discoverableFooterPreferenceController);
 
+        if (lifecycle != null) {
+            lifecycle.addObserver(discoverableFooterPreferenceController);
+        }
+
         return controllers;
     }
 
@@ -136,6 +141,12 @@ public class ConnectedDeviceDashboardFragment extends DashboardFragment {
                 }
 
                 @Override
+                public List<AbstractPreferenceController> createPreferenceControllers(Context
+                        context) {
+                    return buildPreferenceControllers(context, null /* lifecycle */);
+                }
+
+                @Override
                 public List<String> getNonIndexableKeys(Context context) {
                     List<String> keys = super.getNonIndexableKeys(context);
                     // Disable because they show dynamic data
index d8d2433..6d2b715 100644 (file)
 
 package com.android.settings.connecteddevice;
 
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.support.v7.preference.PreferenceScreen;
+import android.text.BidiFormatter;
+import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.bluetooth.AlwaysDiscoverable;
+import com.android.settings.bluetooth.Utils;
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.R;
+import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.widget.FooterPreference;
 import com.android.settingslib.widget.FooterPreferenceMixin;
 
 /**
  * Controller that shows and updates the bluetooth device name
  */
-public class DiscoverableFooterPreferenceController extends BasePreferenceController {
+public class DiscoverableFooterPreferenceController extends BasePreferenceController
+        implements LifecycleObserver, OnResume, OnPause {
     private static final String KEY = "discoverable_footer_preference";
 
-    private FooterPreference mPreference;
+    @VisibleForTesting
+    BroadcastReceiver mBluetoothChangedReceiver;
     private FooterPreferenceMixin mFooterPreferenceMixin;
+    private FooterPreference mPreference;
+    private LocalBluetoothManager mLocalManager;
+    private LocalBluetoothAdapter mLocalAdapter;
+    private AlwaysDiscoverable mAlwaysDiscoverable;
 
+    public DiscoverableFooterPreferenceController(Context context) {
+        super(context, KEY);
+        mLocalManager = Utils.getLocalBtManager(context);
+        if (mLocalManager == null) {
+            return;
+        }
+        mLocalAdapter = mLocalManager.getBluetoothAdapter();
+        mAlwaysDiscoverable = new AlwaysDiscoverable(context, mLocalAdapter);
+        initReceiver();
+    }
 
-    public DiscoverableFooterPreferenceController(Context context) { super(context, KEY); }
+    private void initReceiver() {
+        mBluetoothChangedReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (intent.getAction().equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+                    final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
+                            BluetoothAdapter.ERROR);
+                    updateFooterPreferenceTitle(state);
+                }
+            }
+        };
+    }
 
     public void init(DashboardFragment fragment) {
         mFooterPreferenceMixin = new FooterPreferenceMixin(fragment, fragment.getLifecycle());
     }
 
     @VisibleForTesting
-    void init(FooterPreferenceMixin footerPreferenceMixin, FooterPreference preference) {
+    void init(FooterPreferenceMixin footerPreferenceMixin, FooterPreference preference,
+            AlwaysDiscoverable alwaysDiscoverable) {
         mFooterPreferenceMixin = footerPreferenceMixin;
         mPreference = preference;
+        mAlwaysDiscoverable = alwaysDiscoverable;
     }
 
     @Override
@@ -66,4 +109,37 @@ public class DiscoverableFooterPreferenceController extends BasePreferenceContro
         mPreference.setKey(KEY);
         screen.addPreference(mPreference);
     }
+
+    @Override
+    public void onResume() {
+        mContext.registerReceiver(mBluetoothChangedReceiver,
+                new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
+        mAlwaysDiscoverable.start();
+        updateFooterPreferenceTitle(mLocalAdapter.getState());
+    }
+
+    @Override
+    public void onPause() {
+        mContext.unregisterReceiver(mBluetoothChangedReceiver);
+        mAlwaysDiscoverable.stop();
+    }
+
+    private void updateFooterPreferenceTitle (int bluetoothState) {
+        if (bluetoothState == BluetoothAdapter.STATE_ON) {
+            mPreference.setTitle(getPreferenceTitle());
+        } else {
+            mPreference.setTitle(R.string.bluetooth_off_footer);
+        }
+    }
+
+    private CharSequence getPreferenceTitle() {
+        final String deviceName = mLocalAdapter.getName();
+        if (TextUtils.isEmpty(deviceName)) {
+            return null;
+        }
+
+        return TextUtils.expandTemplate(
+                mContext.getText(R.string.bluetooth_device_name_summary),
+                BidiFormatter.getInstance().unicodeWrap(deviceName));
+    }
 }
\ No newline at end of file
index 9064179..fd1ec15 100644 (file)
@@ -32,6 +32,7 @@ import android.provider.SearchIndexableResource;
 import com.android.settings.R;
 import com.android.settings.nfc.NfcPreferenceController;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
 import com.android.settings.testutils.shadow.ShadowBluetoothPan;
 import com.android.settings.testutils.shadow.ShadowConnectivityManager;
 import com.android.settings.testutils.shadow.ShadowUserManager;
@@ -48,7 +49,7 @@ import java.util.List;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(shadows = {ShadowBluetoothPan.class, ShadowUserManager.class,
-        ShadowConnectivityManager.class})
+        ShadowConnectivityManager.class, ShadowBluetoothAdapter.class})
 public class ConnectedDeviceDashboardFragmentTest {
     @Mock
     private PackageManager mPackageManager;
index 5664d27..8cad0bf 100644 (file)
 package com.android.settings.connecteddevice;
 
 import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.support.v7.preference.PreferenceScreen;
+import android.text.BidiFormatter;
+import android.text.TextUtils;
 
 import com.android.settings.core.BasePreferenceController;
+import com.android.settings.bluetooth.AlwaysDiscoverable;
+import com.android.settings.R;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
 import com.android.settings.testutils.shadow.ShadowBluetoothPan;
@@ -41,32 +46,49 @@ import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.Shadows;
 import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import java.util.ArrayList;
+import java.util.List;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(shadows = {ShadowBluetoothPan.class, ShadowBluetoothAdapter.class,
         ShadowLocalBluetoothAdapter.class})
 public class DiscoverableFooterPreferenceControllerTest {
+    private static final String DEVICE_NAME = "device name";
+    private static final String KEY = "discoverable_footer_preference";
+
     @Mock
     private PackageManager mPackageManager;
     @Mock
     private PreferenceScreen mScreen;
     @Mock
     private FooterPreferenceMixin mFooterPreferenceMixin;
+    @Mock
+    private AlwaysDiscoverable mAlwaysDiscoverable;
 
     private Context mContext;
-    private DiscoverableFooterPreferenceController mDiscoverableFooterPreferenceController;
     private FooterPreference mPreference;
+    private DiscoverableFooterPreferenceController mDiscoverableFooterPreferenceController;
+    private BroadcastReceiver mBluetoothChangedReceiver;
+    private ShadowApplication mShadowApplication;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        mShadowApplication = Shadows.shadowOf(RuntimeEnvironment.application);
         mContext = spy(RuntimeEnvironment.application);
+
         doReturn(mPackageManager).when(mContext).getPackageManager();
         mDiscoverableFooterPreferenceController =
                 new DiscoverableFooterPreferenceController(mContext);
         mPreference = spy(new FooterPreference(mContext));
-        mDiscoverableFooterPreferenceController.init(mFooterPreferenceMixin, mPreference);
+        mDiscoverableFooterPreferenceController.init(mFooterPreferenceMixin, mPreference,
+                mAlwaysDiscoverable);
+        mBluetoothChangedReceiver = mDiscoverableFooterPreferenceController
+                .mBluetoothChangedReceiver;
     }
 
     @Test
@@ -78,7 +100,7 @@ public class DiscoverableFooterPreferenceControllerTest {
     }
 
     @Test
-    public void getAvailabilityStatus_BluetoothFeature_returnSupported() {
+    public void getAvailabilityStatus_BluetoothFeature_returnAvailable() {
         when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)).thenReturn(true);
 
         assertThat(mDiscoverableFooterPreferenceController.getAvailabilityStatus()).isEqualTo(
@@ -90,7 +112,69 @@ public class DiscoverableFooterPreferenceControllerTest {
         when(mFooterPreferenceMixin.createFooterPreference()).thenReturn(mPreference);
         mDiscoverableFooterPreferenceController.displayPreference(mScreen);
 
-        verify(mPreference).setKey(anyString());
+        verify(mPreference).setKey(KEY);
         verify(mScreen).addPreference(mPreference);
     }
-}
+
+    @Test
+    public void onResume() {
+        mDiscoverableFooterPreferenceController.onResume();
+        assertThat(getRegisteredBroadcastReceivers()).contains(mBluetoothChangedReceiver);
+        verify(mAlwaysDiscoverable).start();
+    }
+
+    @Test
+    public void onPause() {
+        mDiscoverableFooterPreferenceController.onResume();
+        mDiscoverableFooterPreferenceController.onPause();
+
+        assertThat(getRegisteredBroadcastReceivers()).doesNotContain(mBluetoothChangedReceiver);
+        verify(mAlwaysDiscoverable).stop();
+    }
+
+    @Test
+    public void onBluetoothStateChanged_bluetoothOn_updateTitle() {
+        ShadowLocalBluetoothAdapter.setName(DEVICE_NAME);
+        sendBluetoothStateChangedIntent(BluetoothAdapter.STATE_ON);
+
+        assertThat(mPreference.getTitle()).isEqualTo(generateTitle(DEVICE_NAME));
+    }
+
+    @Test
+    public void onBluetoothStateChanged_bluetoothOff_updateTitle(){
+        ShadowLocalBluetoothAdapter.setName(DEVICE_NAME);
+        sendBluetoothStateChangedIntent(BluetoothAdapter.STATE_OFF);
+
+        assertThat(mPreference.getTitle()).isEqualTo(generateTitle(null));
+    }
+
+    private CharSequence generateTitle(String deviceName) {
+        if (deviceName == null) {
+            return mContext.getString(R.string.bluetooth_off_footer);
+
+        } else {
+            return TextUtils.expandTemplate(
+                    mContext.getText(R.string.bluetooth_device_name_summary),
+                    BidiFormatter.getInstance().unicodeWrap(deviceName));
+        }
+    }
+
+    private void sendBluetoothStateChangedIntent(int state) {
+        Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
+        intent.putExtra(BluetoothAdapter.EXTRA_STATE, state);
+        mBluetoothChangedReceiver.onReceive(mContext, intent);
+    }
+
+    /**
+     * Return a list of all the registered broadcast receivers
+     */
+    private List<BroadcastReceiver> getRegisteredBroadcastReceivers() {
+        List<BroadcastReceiver> registeredBroadcastReceivers = new ArrayList();
+        List<ShadowApplication.Wrapper> registeredReceivers =
+                mShadowApplication.getRegisteredReceivers();
+        for (ShadowApplication.Wrapper wrapper : registeredReceivers) {
+            registeredBroadcastReceivers.add(wrapper.getBroadcastReceiver());
+        }
+        return registeredBroadcastReceivers;
+    }
+}
\ No newline at end of file