Merge "Add device name to About Phone page."
diff --git a/res/xml/my_device_info.xml b/res/xml/my_device_info.xml
index 673b2a5..4988b16 100644
--- a/res/xml/my_device_info.xml
+++ b/res/xml/my_device_info.xml
@@ -30,7 +30,7 @@
<!-- Account name -->
<Preference
- android:key="account"
+ android:key="branded_account"
android:order="1"
android:title="@string/my_device_info_account_preference_title"
android:summary="@string/summary_placeholder"/>
@@ -43,7 +43,7 @@
android:summary="@string/summary_placeholder"/>
<!-- Device name -->
- <Preference
+ <com.android.settings.widget.ValidatedEditTextPreference
android:key="device_name"
android:order="3"
android:title="@string/my_device_info_device_name_preference_title"
diff --git a/src/com/android/settings/bluetooth/BluetoothLengthDeviceNameFilter.java b/src/com/android/settings/bluetooth/BluetoothLengthDeviceNameFilter.java
new file mode 100644
index 0000000..cdf5310
--- /dev/null
+++ b/src/com/android/settings/bluetooth/BluetoothLengthDeviceNameFilter.java
@@ -0,0 +1,28 @@
+/*
+ * 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.bluetooth;
+
+/**
+ * Filter to max the length of a Bluetotoh device name to 248 bytes, as defined by the spec.
+ */
+public class BluetoothLengthDeviceNameFilter extends Utf8ByteLengthFilter {
+ private static final int BLUETOOTH_NAME_MAX_LENGTH_BYTES = 248;
+
+ public BluetoothLengthDeviceNameFilter() {
+ super(BLUETOOTH_NAME_MAX_LENGTH_BYTES);
+ }
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java b/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java
index 576e656..134bb97 100644
--- a/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java
+++ b/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java
@@ -43,8 +43,6 @@
*/
abstract class BluetoothNameDialogFragment extends InstrumentedDialogFragment
implements TextWatcher {
- private static final int BLUETOOTH_NAME_MAX_LENGTH_BYTES = 248;
-
private AlertDialog mAlertDialog;
private Button mOkButton;
@@ -109,7 +107,7 @@
View view = layoutInflater.inflate(R.layout.dialog_edittext, null);
mDeviceNameView = (EditText) view.findViewById(R.id.edittext);
mDeviceNameView.setFilters(new InputFilter[] {
- new Utf8ByteLengthFilter(BLUETOOTH_NAME_MAX_LENGTH_BYTES)
+ new BluetoothLengthDeviceNameFilter()
});
mDeviceNameView.setText(deviceName); // set initial value before adding listener
if (!TextUtils.isEmpty(deviceName)) {
diff --git a/src/com/android/settings/bluetooth/Utf8ByteLengthFilter.java b/src/com/android/settings/bluetooth/Utf8ByteLengthFilter.java
index bae6e56..ab49818 100644
--- a/src/com/android/settings/bluetooth/Utf8ByteLengthFilter.java
+++ b/src/com/android/settings/bluetooth/Utf8ByteLengthFilter.java
@@ -37,7 +37,7 @@
* pairs are encoded as 4 bytes, with the caveat that the maximum
* length will be constrained more conservatively than necessary.
*/
-class Utf8ByteLengthFilter implements InputFilter {
+public class Utf8ByteLengthFilter implements InputFilter {
private final int mMaxBytes;
Utf8ByteLengthFilter(int maxBytes) {
diff --git a/src/com/android/settings/deviceinfo/BrandedAccountPreferenceController.java b/src/com/android/settings/deviceinfo/BrandedAccountPreferenceController.java
index 5565e3d..c968d25 100644
--- a/src/com/android/settings/deviceinfo/BrandedAccountPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/BrandedAccountPreferenceController.java
@@ -31,7 +31,7 @@
import com.android.settings.overlay.FeatureFactory;
public class BrandedAccountPreferenceController extends BasePreferenceController {
- private static final String KEY_PREFERENCE_TITLE = "account";
+ private static final String KEY_PREFERENCE_TITLE = "branded_account";
private final Account[] mAccounts;
public BrandedAccountPreferenceController(Context context) {
diff --git a/src/com/android/settings/deviceinfo/DeviceNamePreferenceController.java b/src/com/android/settings/deviceinfo/DeviceNamePreferenceController.java
new file mode 100644
index 0000000..0f1dea1
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/DeviceNamePreferenceController.java
@@ -0,0 +1,151 @@
+/*
+ * 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.deviceinfo;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.os.Build;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.SpannedString;
+
+import com.android.settings.bluetooth.BluetoothLengthDeviceNameFilter;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.widget.ValidatedEditTextPreference;
+import com.android.settings.wifi.tether.WifiDeviceNameTextValidator;
+import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+public class DeviceNamePreferenceController extends BasePreferenceController
+ implements ValidatedEditTextPreference.Validator, Preference.OnPreferenceChangeListener {
+ private static final String PREF_KEY = "device_name";
+ private String mDeviceName;
+ protected WifiManager mWifiManager;
+ private final WifiDeviceNameTextValidator mWifiDeviceNameTextValidator;
+ private ValidatedEditTextPreference mPreference;
+ @Nullable
+ private LocalBluetoothManager mBluetoothManager;
+
+ public DeviceNamePreferenceController(Context context) {
+ super(context, PREF_KEY);
+ mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ mWifiDeviceNameTextValidator = new WifiDeviceNameTextValidator();
+ initializeDeviceName();
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = (ValidatedEditTextPreference) screen.findPreference(PREF_KEY);
+ mPreference.setSummary(getSummary());
+ mPreference.setValidator(this);
+ }
+
+ private void initializeDeviceName() {
+ mDeviceName = Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.DEVICE_NAME);
+ if (mDeviceName == null) {
+ mDeviceName = Build.MODEL;
+ }
+ }
+
+ @Override
+ public String getSummary() {
+ return mDeviceName;
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return PREF_KEY;
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ mDeviceName = (String) newValue;
+ setDeviceName(mDeviceName);
+ preference.setSummary(getSummary());
+ return true;
+ }
+
+ @Override
+ public boolean isTextValid(String deviceName) {
+ // BluetoothNameDialogFragment describes BT name filter as a 248 bytes long cap.
+ // Given the restrictions presented by the SSID name filter (32 char), I don't believe it is
+ // possible to construct an SSID that is not a valid Bluetooth name.
+ return mWifiDeviceNameTextValidator.isTextValid(deviceName);
+ }
+
+ public void setLocalBluetoothManager(LocalBluetoothManager localBluetoothManager) {
+ mBluetoothManager = localBluetoothManager;
+ }
+
+ /**
+ * This method presumes that security/validity checks have already been passed.
+ */
+ private void setDeviceName(String deviceName) {
+ setSettingsGlobalDeviceName(deviceName);
+ setBluetoothDeviceName(deviceName);
+ setTetherSsidName(deviceName);
+ }
+
+ private void setSettingsGlobalDeviceName(String deviceName) {
+ Settings.Global.putString(mContext.getContentResolver(), Settings.Global.DEVICE_NAME,
+ deviceName);
+ }
+
+ private void setBluetoothDeviceName(String deviceName) {
+ // Bluetooth manager doesn't exist for certain devices.
+ if (mBluetoothManager == null) {
+ return;
+ }
+
+ final LocalBluetoothAdapter localBluetoothAdapter = mBluetoothManager.getBluetoothAdapter();
+ if (localBluetoothAdapter != null) {
+ localBluetoothAdapter.setName(getFilteredBluetoothString(deviceName));
+ }
+ }
+
+ /**
+ * Using a UTF8ByteLengthFilter, we can filter a string to be compliant with the Bluetooth spec.
+ * For more information, see {@link com.android.settings.bluetooth.BluetoothNameDialogFragment}.
+ */
+ private static final String getFilteredBluetoothString(final String deviceName) {
+ CharSequence filteredSequence = new BluetoothLengthDeviceNameFilter().filter(deviceName, 0, deviceName.length(),
+ new SpannedString(""),
+ 0, 0);
+ // null -> use the original
+ if (filteredSequence == null) {
+ return deviceName;
+ }
+ return filteredSequence.toString();
+ }
+
+ private void setTetherSsidName(String deviceName) {
+ final WifiConfiguration config = mWifiManager.getWifiApConfiguration();
+ config.SSID = deviceName;
+ // TODO: If tether is running, turn off the AP and restart it after setting config.
+ mWifiManager.setWifiApConfiguration(config);
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/deviceinfo/aboutphone/MyDeviceInfoFragment.java b/src/com/android/settings/deviceinfo/aboutphone/MyDeviceInfoFragment.java
index a301807..04e7fde 100644
--- a/src/com/android/settings/deviceinfo/aboutphone/MyDeviceInfoFragment.java
+++ b/src/com/android/settings/deviceinfo/aboutphone/MyDeviceInfoFragment.java
@@ -16,6 +16,8 @@
package com.android.settings.deviceinfo.aboutphone;
+import static com.android.settings.bluetooth.Utils.getLocalBtManager;
+
import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
@@ -54,6 +56,7 @@
import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settings.deviceinfo.DeviceNamePreferenceController;
import java.util.ArrayList;
import java.util.Arrays;
@@ -102,6 +105,10 @@
final List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new PhoneNumberPreferenceController(context));
controllers.add(new BrandedAccountPreferenceController(context));
+ DeviceNamePreferenceController deviceNamePreferenceController =
+ new DeviceNamePreferenceController(context);
+ deviceNamePreferenceController.setLocalBluetoothManager(getLocalBtManager(context));
+ controllers.add(deviceNamePreferenceController);
controllers.add(new SimStatusPreferenceController(context, fragment));
controllers.add(new DeviceModelPreferenceController(context, fragment));
controllers.add(new ImeiInfoPreferenceController(context, fragment));
@@ -117,7 +124,6 @@
controllers.add(new FccEquipmentIdPreferenceController(context));
controllers.add(
new BuildNumberPreferenceController(context, activity, fragment, lifecycle));
- // TODO: Add preference controller for getting the device name.
return controllers;
}
diff --git a/src/com/android/settings/wifi/tether/WifiDeviceNameTextValidator.java b/src/com/android/settings/wifi/tether/WifiDeviceNameTextValidator.java
new file mode 100644
index 0000000..e766e32
--- /dev/null
+++ b/src/com/android/settings/wifi/tether/WifiDeviceNameTextValidator.java
@@ -0,0 +1,30 @@
+/*
+ * 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.wifi.tether;
+
+import com.android.settings.widget.ValidatedEditTextPreference;
+import com.android.settings.wifi.WifiUtils;
+
+/**
+ * Validates a text field for a valid Wi-Fi SSID name.
+ */
+public class WifiDeviceNameTextValidator implements ValidatedEditTextPreference.Validator {
+ @Override
+ public boolean isTextValid(String value) {
+ return !WifiUtils.isSSIDTooLong(value) && !WifiUtils.isSSIDTooShort(value);
+ }
+}
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceController.java
index b563e53..d7cb441 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceController.java
@@ -35,10 +35,12 @@
static final String DEFAULT_SSID = "AndroidAP";
private String mSSID;
+ private WifiDeviceNameTextValidator mWifiDeviceNameTextValidator;
public WifiTetherSSIDPreferenceController(Context context,
OnTetherConfigUpdateListener listener) {
super(context, listener);
+ mWifiDeviceNameTextValidator = new WifiDeviceNameTextValidator();
}
@Override
@@ -70,7 +72,7 @@
@Override
public boolean isTextValid(String value) {
- return !WifiUtils.isSSIDTooLong(value) && !WifiUtils.isSSIDTooShort(value);
+ return mWifiDeviceNameTextValidator.isTextValid(value);
}
public String getSSID() {
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/DeviceNamePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/DeviceNamePreferenceControllerTest.java
new file mode 100644
index 0000000..4ff79ca
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/deviceinfo/DeviceNamePreferenceControllerTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.deviceinfo;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.os.Build;
+import android.provider.Settings;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.widget.ValidatedEditTextPreference;
+import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
+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.shadows.ShadowApplication;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class DeviceNamePreferenceControllerTest {
+ private static final String TESTING_STRING = "Testing";
+
+ @Mock
+ private LocalBluetoothAdapter mBluetoothAdapter;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private LocalBluetoothManager mBluetoothManager;
+ @Mock
+ private WifiManager mWifiManager;
+ @Mock
+ private PreferenceScreen mScreen;
+ private ValidatedEditTextPreference mPreference;
+ private DeviceNamePreferenceController mController;
+ private Context mContext;
+
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ ShadowApplication shadowApplication = ShadowApplication.getInstance();
+ shadowApplication.setSystemService(Context.WIFI_SERVICE, mWifiManager);
+ mContext = shadowApplication.getApplicationContext();
+ mPreference = new ValidatedEditTextPreference(mContext);
+ when(mBluetoothManager.getBluetoothAdapter()).thenReturn(mBluetoothAdapter);
+ when(mScreen.findPreference(anyString())).thenReturn(mPreference);
+ final WifiConfiguration configuration = new WifiConfiguration();
+ configuration.SSID = "test-ap";
+ when(mWifiManager.getWifiApConfiguration()).thenReturn(configuration);
+
+ mController = new DeviceNamePreferenceController(mContext);
+ mController.setLocalBluetoothManager(mBluetoothManager);
+ }
+
+ @Test
+ public void constructor_defaultDeviceNameIsModelName() {
+ assertThat(mController.getSummary()).isEqualTo(Build.MODEL);
+ }
+
+ @Test
+ public void constructor_deviceNameLoadedIfSet() {
+ Settings.Global.putString(mContext.getContentResolver(), Settings.Global.DEVICE_NAME,
+ "Test");
+ mController = new DeviceNamePreferenceController(mContext);
+ mController.setLocalBluetoothManager(mBluetoothManager);
+ assertThat(mController.getSummary()).isEqualTo("Test");
+ }
+
+ @Test
+ public void isTextValid_nameUnder33CharactersIsValid() {
+ assertThat(mController.isTextValid("12345678901234567890123456789012")).isTrue();
+ }
+
+ @Test
+ public void isTextValid_nameTooLongIsInvalid() {
+ assertThat(mController.isTextValid("123456789012345678901234567890123")).isFalse();
+ }
+
+ @Test
+ public void setDeviceName_preferenceUpdatedWhenDeviceNameUpdated() {
+ mController.onPreferenceChange(mPreference, TESTING_STRING);
+
+ assertThat(mPreference.getSummary()).isEqualTo(TESTING_STRING);
+ }
+
+ @Test
+ public void setDeviceName_bluetoothNameUpdatedWhenDeviceNameUpdated() {
+ mController.onPreferenceChange(mPreference, TESTING_STRING);
+
+ verify(mBluetoothAdapter).setName(eq(TESTING_STRING));
+ }
+
+ @Test
+ public void setDeviceName_wifiTetherNameUpdatedWhenDeviceNameUpdated() {
+ mController.onPreferenceChange(mPreference, TESTING_STRING);
+
+ ArgumentCaptor<WifiConfiguration> captor = ArgumentCaptor.forClass(WifiConfiguration.class);
+ verify(mWifiManager).setWifiApConfiguration(captor.capture());
+ assertThat(captor.getValue().SSID).isEqualTo(TESTING_STRING);
+ }
+}