diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java index c81e1eef428..244561919a6 100644 --- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java +++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java @@ -20,6 +20,11 @@ import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH; 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 class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment 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); } @@ -63,21 +95,37 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment return R.xml.bluetooth_device_details_fragment; } + @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 getPreferenceControllers(Context context) { ArrayList 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 baab3fcf88b..7e826c8ab3e 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.LocalBluetoothAdapter; 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 @@ public class BluetoothDeviceNamePreferenceController extends PreferenceControlle @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 484d4b3d5da..e415b47c1a8 100644 --- a/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java +++ b/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java @@ -18,19 +18,13 @@ package com.android.settings.bluetooth; 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.Button; 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 @@ public final class BluetoothNameDialogFragment extends InstrumentedDialogFragmen 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 @@ public final class BluetoothNameDialogFragment extends InstrumentedDialogFragmen 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 47030315f8c..85eb23ea056 100644 --- a/src/com/android/settings/bluetooth/BluetoothSettings.java +++ b/src/com/android/settings/bluetooth/BluetoothSettings.java @@ -316,7 +316,6 @@ public class BluetoothSettings extends DeviceListPreferenceFragment implements I // 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 00000000000..029b974d201 --- /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 00000000000..4e5acefe616 --- /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 00000000000..7bd120390ac --- /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 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 00000000000..2d7f215689f --- /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()); + } +}