Merge "Add a rename dialog for paired Bluetooth devices" into oc-dr1-dev
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
index c81e1ee..2445619 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
@@ -20,6 +20,11 @@
import android.bluetooth.BluetoothDevice;
import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.VisibleForTesting;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
@@ -36,15 +41,42 @@
public static final String KEY_DEVICE_ADDRESS = "device_address";
private static final String TAG = "BTDeviceDetailsFrg";
+ @VisibleForTesting
+ static int EDIT_DEVICE_NAME_ITEM_ID = Menu.FIRST;
+
private String mDeviceAddress;
+ private LocalBluetoothManager mManager;
+ private CachedBluetoothDevice mCachedDevice;
public BluetoothDeviceDetailsFragment() {
super(DISALLOW_CONFIG_BLUETOOTH);
}
+ @VisibleForTesting
+ LocalBluetoothManager getLocalBluetoothManager(Context context) {
+ return Utils.getLocalBtManager(context);
+ }
+
+ @VisibleForTesting
+ CachedBluetoothDevice getCachedDevice(String deviceAddress) {
+ BluetoothDevice remoteDevice =
+ mManager.getBluetoothAdapter().getRemoteDevice(deviceAddress);
+ return mManager.getCachedDeviceManager().findDevice(remoteDevice);
+ }
+
+ public static BluetoothDeviceDetailsFragment newInstance(String deviceAddress) {
+ Bundle args = new Bundle(1);
+ args.putString(KEY_DEVICE_ADDRESS, deviceAddress);
+ BluetoothDeviceDetailsFragment fragment = new BluetoothDeviceDetailsFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
@Override
public void onAttach(Context context) {
mDeviceAddress = getArguments().getString(KEY_DEVICE_ADDRESS);
+ mManager = getLocalBluetoothManager(context);
+ mCachedDevice = getCachedDevice(mDeviceAddress);
super.onAttach(context);
}
@@ -64,20 +96,36 @@
}
@Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ MenuItem item = menu.add(0, EDIT_DEVICE_NAME_ITEM_ID, 0, R.string.bluetooth_rename_button);
+ item.setIcon(R.drawable.ic_mode_edit);
+ item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ super.onCreateOptionsMenu(menu, inflater);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem menuItem) {
+ if (menuItem.getItemId() == EDIT_DEVICE_NAME_ITEM_ID) {
+ RemoteDeviceNameDialogFragment.newInstance(mCachedDevice).show(
+ getFragmentManager(), RemoteDeviceNameDialogFragment.TAG);
+ return true;
+ }
+ return super.onOptionsItemSelected(menuItem);
+ }
+
+ @Override
protected List<PreferenceController> getPreferenceControllers(Context context) {
ArrayList<PreferenceController> controllers = new ArrayList<>();
- LocalBluetoothManager manager = Utils.getLocalBtManager(context);
- BluetoothDevice remoteDevice = manager.getBluetoothAdapter().getRemoteDevice(
- mDeviceAddress);
- CachedBluetoothDevice device = manager.getCachedDeviceManager().findDevice(remoteDevice);
- if (device != null) {
+
+ if (mCachedDevice != null) {
Lifecycle lifecycle = getLifecycle();
- controllers.add(new BluetoothDetailsHeaderController(context, this, device, lifecycle));
- controllers.add(new BluetoothDetailsButtonsController(context, this, device,
+ controllers.add(new BluetoothDetailsHeaderController(context, this, mCachedDevice,
lifecycle));
- controllers.add(new BluetoothDetailsProfilesController(context, this, manager, device,
+ controllers.add(new BluetoothDetailsButtonsController(context, this, mCachedDevice,
lifecycle));
- controllers.add(new BluetoothDetailsMacAddressController(context, this, device,
+ controllers.add(new BluetoothDetailsProfilesController(context, this, mManager,
+ mCachedDevice, lifecycle));
+ controllers.add(new BluetoothDetailsMacAddressController(context, this, mCachedDevice,
lifecycle));
}
return controllers;
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceNamePreferenceController.java b/src/com/android/settings/bluetooth/BluetoothDeviceNamePreferenceController.java
index baab3fc..7e826c8 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceNamePreferenceController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceNamePreferenceController.java
@@ -37,7 +37,6 @@
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.core.lifecycle.events.OnResume;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
@@ -111,7 +110,8 @@
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (KEY_DEVICE_NAME.equals(preference.getKey())) {
- new BluetoothNameDialogFragment().show(mFragment.getFragmentManager(), "rename device");
+ LocalDeviceNameDialogFragment.newInstance()
+ .show(mFragment.getFragmentManager(), LocalDeviceNameDialogFragment.TAG);
return true;
}
diff --git a/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java b/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java
index 484d4b3..e415b47 100644
--- a/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java
+++ b/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java
@@ -18,19 +18,13 @@
import android.app.AlertDialog;
import android.app.Dialog;
-import android.app.DialogFragment;
-import android.bluetooth.BluetoothAdapter;
-import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.res.Configuration;
import android.os.Bundle;
import android.text.Editable;
import android.text.InputFilter;
import android.text.TextWatcher;
-import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
@@ -40,25 +34,19 @@
import android.widget.EditText;
import android.widget.TextView;
-import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
-import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
-import com.android.settingslib.bluetooth.LocalBluetoothManager;
/**
- * Dialog fragment for renaming the local Bluetooth device.
+ * Dialog fragment for renaming a Bluetooth device.
*/
-public final class BluetoothNameDialogFragment extends InstrumentedDialogFragment
+abstract class BluetoothNameDialogFragment extends InstrumentedDialogFragment
implements TextWatcher {
private static final int BLUETOOTH_NAME_MAX_LENGTH_BYTES = 248;
private AlertDialog mAlertDialog;
private Button mOkButton;
- // accessed from inner class (not private to avoid thunks)
- static final String TAG = "BluetoothNameDialogFragment";
- LocalBluetoothAdapter mLocalAdapter;
EditText mDeviceNameView;
// This flag is set when the name is updated by code, to distinguish from user changes
@@ -71,63 +59,43 @@
private static final String KEY_NAME = "device_name";
private static final String KEY_NAME_EDITED = "device_name_edited";
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED)) {
- updateDeviceName();
- } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED) &&
- (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR) ==
- BluetoothAdapter.STATE_ON)) {
- updateDeviceName();
- }
- }
- };
+ /**
+ * @return the title to use for the dialog.
+ */
+ abstract protected int getDialogTitle();
- @Override
- public int getMetricsCategory() {
- return MetricsProto.MetricsEvent.DIALOG_BLUETOOTH_RENAME;
- }
+ /**
+ * @return the current name used for this device.
+ */
+ abstract protected String getDeviceName();
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- LocalBluetoothManager localManager = Utils.getLocalBtManager(getActivity());
- mLocalAdapter = localManager.getBluetoothAdapter();
- }
+ /**
+ * Set the device to the given name.
+ * @param deviceName the name to use
+ */
+ abstract protected void setDeviceName(String deviceName);
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
- String deviceName = mLocalAdapter.getName();
+ String deviceName = getDeviceName();
if (savedInstanceState != null) {
deviceName = savedInstanceState.getString(KEY_NAME, deviceName);
mDeviceNameEdited = savedInstanceState.getBoolean(KEY_NAME_EDITED, false);
}
- mAlertDialog = new AlertDialog.Builder(getActivity())
- .setTitle(R.string.bluetooth_rename_device)
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
+ .setTitle(getDialogTitle())
.setView(createDialogView(deviceName))
- .setPositiveButton(R.string.bluetooth_rename_button,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- String deviceName = mDeviceNameView.getText().toString();
- setDeviceName(deviceName);
- }
- })
- .setNegativeButton(android.R.string.cancel, null)
- .create();
+ .setPositiveButton(R.string.bluetooth_rename_button, (dialog, which) -> {
+ setDeviceName(mDeviceNameView.getText().toString());
+ })
+ .setNegativeButton(android.R.string.cancel, null);
+ mAlertDialog = builder.create();
mAlertDialog.getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
return mAlertDialog;
}
- private void setDeviceName(String deviceName) {
- Log.d(TAG, "Setting device name to " + deviceName);
- mLocalAdapter.setName(deviceName);
- }
-
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putString(KEY_NAME, mDeviceNameView.getText().toString());
@@ -174,23 +142,14 @@
mOkButton = mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE);
mOkButton.setEnabled(mDeviceNameEdited); // Ok button enabled after user edits
}
- IntentFilter filter = new IntentFilter();
- filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
- filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
- getActivity().registerReceiver(mReceiver, filter);
- }
-
- @Override
- public void onPause() {
- super.onPause();
- getActivity().unregisterReceiver(mReceiver);
}
void updateDeviceName() {
- if (mLocalAdapter != null && mLocalAdapter.isEnabled()) {
+ String name = getDeviceName();
+ if (name != null) {
mDeviceNameUpdated = true;
mDeviceNameEdited = false;
- mDeviceNameView.setText(mLocalAdapter.getName());
+ mDeviceNameView.setText(name);
}
}
diff --git a/src/com/android/settings/bluetooth/BluetoothSettings.java b/src/com/android/settings/bluetooth/BluetoothSettings.java
index 4703031..85eb23e 100644
--- a/src/com/android/settings/bluetooth/BluetoothSettings.java
+++ b/src/com/android/settings/bluetooth/BluetoothSettings.java
@@ -316,7 +316,6 @@
// New version - uses a separate screen.
args.putString(BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS,
device.getDevice().getAddress());
- BluetoothDeviceDetailsFragment fragment = new BluetoothDeviceDetailsFragment();
final SettingsActivity activity =
(SettingsActivity) BluetoothSettings.this.getActivity();
activity.startPreferencePanel(this,
diff --git a/src/com/android/settings/bluetooth/LocalDeviceNameDialogFragment.java b/src/com/android/settings/bluetooth/LocalDeviceNameDialogFragment.java
new file mode 100644
index 0000000..029b974
--- /dev/null
+++ b/src/com/android/settings/bluetooth/LocalDeviceNameDialogFragment.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2017 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.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+/** Provides a dialog for changing the advertised name of the local bluetooth adapter. */
+public class LocalDeviceNameDialogFragment extends BluetoothNameDialogFragment {
+ public static final String TAG = "LocalAdapterName";
+ private LocalBluetoothAdapter mLocalAdapter;
+
+ public static LocalDeviceNameDialogFragment newInstance() {
+ return new LocalDeviceNameDialogFragment();
+ }
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED.equals(action) ||
+ (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action) &&
+ intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)
+ == BluetoothAdapter.STATE_ON)) {
+ updateDeviceName();
+ }
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ LocalBluetoothManager localManager = Utils.getLocalBtManager(getActivity());
+ mLocalAdapter = localManager.getBluetoothAdapter();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+ filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
+ getActivity().registerReceiver(mReceiver, filter);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ getActivity().unregisterReceiver(mReceiver);
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsProto.MetricsEvent.DIALOG_BLUETOOTH_RENAME;
+ }
+
+ @Override
+ protected int getDialogTitle() {
+ return R.string.bluetooth_rename_device;
+ }
+
+ @Override
+ protected String getDeviceName() {
+ if (mLocalAdapter != null && mLocalAdapter.isEnabled()) {
+ return mLocalAdapter.getName();
+ }
+ return null;
+ }
+
+ @Override
+ protected void setDeviceName(String deviceName) {
+ mLocalAdapter.setName(deviceName);
+ }
+}
diff --git a/src/com/android/settings/bluetooth/RemoteDeviceNameDialogFragment.java b/src/com/android/settings/bluetooth/RemoteDeviceNameDialogFragment.java
new file mode 100644
index 0000000..4e5acefe
--- /dev/null
+++ b/src/com/android/settings/bluetooth/RemoteDeviceNameDialogFragment.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 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.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.os.Bundle;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+/** Provides a dialog for changing the display name of a remote bluetooth device. */
+public class RemoteDeviceNameDialogFragment extends BluetoothNameDialogFragment {
+ public static final String TAG = "RemoteDeviceName";
+ private static final String KEY_CACHED_DEVICE_ADDRESS = "cached_device";
+
+ private CachedBluetoothDevice mDevice;
+
+ public static RemoteDeviceNameDialogFragment newInstance(CachedBluetoothDevice device) {
+ Bundle args = new Bundle(1);
+ args.putString(KEY_CACHED_DEVICE_ADDRESS, device.getDevice().getAddress());
+ RemoteDeviceNameDialogFragment fragment = new RemoteDeviceNameDialogFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @VisibleForTesting
+ CachedBluetoothDevice getDevice(Context context) {
+ String deviceAddress = getArguments().getString(KEY_CACHED_DEVICE_ADDRESS);
+ LocalBluetoothManager manager = Utils.getLocalBtManager(context);
+ BluetoothDevice device = manager.getBluetoothAdapter().getRemoteDevice(deviceAddress);
+ return manager.getCachedDeviceManager().findDevice(device);
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ mDevice = getDevice(context);
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsProto.MetricsEvent.DIALOG_BLUETOOTH_PAIRED_DEVICE_RENAME;
+ }
+
+ @Override
+ protected int getDialogTitle() {
+ return R.string.bluetooth_device_name;
+ }
+
+ @Override
+ protected String getDeviceName() {
+ if (mDevice != null) {
+ return mDevice.getName();
+ }
+ return null;
+ }
+
+ @Override
+ protected void setDeviceName(String deviceName) {
+ if (mDevice != null) {
+ mDevice.setName(deviceName);
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java
new file mode 100644
index 0000000..7bd1203
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2017 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.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.fakes.RoboMenu;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class BluetoothDeviceDetailsFragmentTest {
+ private BluetoothDeviceDetailsFragment mFragment;
+ private Context mContext;
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private CachedBluetoothDevice mCachedDevice;
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private LocalBluetoothManager mLocalManager;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ FakeFeatureFactory.setupForTest(mContext);
+
+ String deviceAddress = "55:66:77:88:99:AA";
+ mFragment = spy(BluetoothDeviceDetailsFragment.newInstance(deviceAddress));
+ doReturn(mLocalManager).when(mFragment).getLocalBluetoothManager(any());
+ doReturn(mCachedDevice).when(mFragment).getCachedDevice(any());
+
+ when(mCachedDevice.getDevice().getAddress()).thenReturn(deviceAddress);
+ Bundle args = new Bundle();
+ args.putString(BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS, deviceAddress);
+ mFragment.setArguments(args);
+ mFragment.onAttach(mContext);
+ }
+
+ @Test
+ public void renameControlGetsAdded() {
+ RoboMenu menu = new RoboMenu(mContext);
+ MenuInflater inflater = new MenuInflater(mContext);
+ mFragment.onCreateOptionsMenu(menu, inflater);
+ MenuItem item = menu.getItem(0);
+ assertThat(item.getTitle()).isEqualTo(mContext.getString(R.string.bluetooth_rename_button));
+ assertThat(item.getIcon()).isEqualTo(mContext.getDrawable(R.drawable.ic_mode_edit));
+ }
+
+ @Test
+ public void renameControlClicked() {
+ RoboMenu menu = new RoboMenu(mContext);
+ MenuInflater inflater = new MenuInflater(mContext);
+ mFragment.onCreateOptionsMenu(menu, inflater);
+ MenuItem item = menu.getItem(0);
+ assertThat(item.getItemId()).isEqualTo(
+ BluetoothDeviceDetailsFragment.EDIT_DEVICE_NAME_ITEM_ID);
+
+ FragmentManager fragmentManager = mock(FragmentManager.class);
+ when(mFragment.getFragmentManager()).thenReturn(fragmentManager);
+ FragmentTransaction ft = mock(FragmentTransaction.class);
+ when(fragmentManager.beginTransaction()).thenReturn(ft);
+
+ ArgumentCaptor<Fragment> captor = ArgumentCaptor.forClass(Fragment.class);
+ mFragment.onOptionsItemSelected(item);
+ verify(ft).add(captor.capture(), eq(RemoteDeviceNameDialogFragment.TAG));
+ RemoteDeviceNameDialogFragment dialog = (RemoteDeviceNameDialogFragment) captor.getValue();
+ assertThat(dialog).isNotNull();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/RemoteDeviceNameDialogFragmentTest.java b/tests/robotests/src/com/android/settings/bluetooth/RemoteDeviceNameDialogFragmentTest.java
new file mode 100644
index 0000000..2d7f215
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/bluetooth/RemoteDeviceNameDialogFragmentTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2017 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.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.widget.Button;
+import android.widget.EditText;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowDialog;
+import org.robolectric.util.FragmentTestUtil;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class RemoteDeviceNameDialogFragmentTest {
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private CachedBluetoothDevice mCachedDevice;
+
+ private RemoteDeviceNameDialogFragment mFragment;
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ FakeFeatureFactory.setupForTest(mContext);
+
+ String deviceAddress = "55:66:77:88:99:AA";
+ when(mCachedDevice.getDevice().getAddress()).thenReturn(deviceAddress);
+ mFragment = spy(RemoteDeviceNameDialogFragment.newInstance(mCachedDevice));
+ doReturn(mCachedDevice).when(mFragment).getDevice(any());
+ }
+
+ /**
+ * Helper method to set the mock device's name and show the dialog.
+ *
+ * @param deviceName what name to set
+ * @return the dialog created
+ */
+ AlertDialog startDialog(String deviceName) {
+ when(mCachedDevice.getName()).thenReturn(deviceName);
+ FragmentTestUtil.startFragment(mFragment);
+ return (AlertDialog) ShadowDialog.getLatestDialog();
+ }
+
+ @Test
+ public void deviceNameDisplayIsCorrect() {
+ String deviceName = "ABC Corp Headphones";
+ AlertDialog dialog = startDialog(deviceName);
+ EditText editText = (EditText) dialog.findViewById(R.id.edittext);
+ assertThat(editText.getText().toString()).isEqualTo(deviceName);
+
+ // Make sure that the "rename" button isn't enabled since the text hasn't changed yet, but
+ // the "cancel" button should be enabled.
+ Button positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
+ assertThat(positiveButton.isEnabled()).isFalse();
+ Button negativeButton = dialog.getButton(DialogInterface.BUTTON_NEGATIVE);
+ assertThat(negativeButton.isEnabled()).isTrue();
+ }
+
+ @Test
+ public void deviceNameEditSucceeds() {
+ String deviceNameInitial = "ABC Corp Headphones";
+ String deviceNameModified = "My Headphones";
+ AlertDialog dialog = startDialog(deviceNameInitial);
+
+ // Before modifying the text the "rename" button should be disabled but the cancel button
+ // should be enabled.
+ Button positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
+ Button negativeButton = dialog.getButton(DialogInterface.BUTTON_NEGATIVE);
+ assertThat(negativeButton.isEnabled()).isTrue();
+ assertThat(positiveButton.isEnabled()).isFalse();
+
+ // Once we modify the text, the positive button should be clickable, and clicking it should
+ // cause a call to change the name.
+ EditText editText = (EditText) dialog.findViewById(R.id.edittext);
+ editText.setText(deviceNameModified);
+ assertThat(positiveButton.isEnabled()).isTrue();
+ positiveButton.performClick();
+ verify(mCachedDevice).setName(deviceNameModified);
+ }
+
+ @Test
+ public void deviceNameEditThenCancelDoesntRename() {
+ String deviceNameInitial = "ABC Corp Headphones";
+ String deviceNameModified = "My Headphones";
+ AlertDialog dialog = startDialog(deviceNameInitial);
+
+ // Modifying the text but then hitting cancel should not cause the name to change.
+ Button negativeButton = dialog.getButton(DialogInterface.BUTTON_NEGATIVE);
+ assertThat(negativeButton.isEnabled()).isTrue();
+ EditText editText = (EditText) dialog.findViewById(R.id.edittext);
+ editText.setText(deviceNameModified);
+ negativeButton.performClick();
+ verify(mCachedDevice, never()).setName(anyString());
+ }
+}