Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 78eb5a81 authored by timhypeng's avatar timhypeng Committed by tim peng
Browse files

Make device discoverable in Connected Devices settings page

* 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
parent 4f636b90
Loading
Loading
Loading
Loading
+13 −2
Original line number Diff line number Diff line
@@ -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;
    }

@@ -135,6 +140,12 @@ public class ConnectedDeviceDashboardFragment extends DashboardFragment {
                    return Arrays.asList(sir);
                }

                @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);
+80 −4
Original line number Diff line number Diff line
@@ -16,36 +16,79 @@

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
+2 −1
Original line number Diff line number Diff line
@@ -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;
+91 −7
Original line number Diff line number Diff line
@@ -16,18 +16,23 @@
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