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

Commit 74b1da53 authored by Daniel Nishi's avatar Daniel Nishi
Browse files

Warn users before they change device name.

This ensures users know that the change they make will be visible
through Bluetooth and Wi-Fi Hotspot.

Fixes: 74981919
Test: Settings robotest

Merged-In: Ib27066f4a123dc472730d7e663adfb039b1e27d6
parent c2ec26b4
Loading
Loading
Loading
Loading
+50 −4
Original line number Diff line number Diff line
@@ -21,32 +21,49 @@ import android.content.Context;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import android.text.SpannedString;

import com.android.internal.annotations.VisibleForTesting;

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;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnCreate;
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;

public class DeviceNamePreferenceController extends BasePreferenceController
        implements ValidatedEditTextPreference.Validator, Preference.OnPreferenceChangeListener {
        implements ValidatedEditTextPreference.Validator,
                Preference.OnPreferenceChangeListener,
                LifecycleObserver,
                OnSaveInstanceState,
                OnCreate {
    private static final String PREF_KEY = "device_name";
    public static final int DEVICE_NAME_SET_WARNING_ID = 1;
    private static final String KEY_PENDING_DEVICE_NAME = "key_pending_device_name";
    private String mDeviceName;
    protected WifiManager mWifiManager;
    private final WifiDeviceNameTextValidator mWifiDeviceNameTextValidator;
    private ValidatedEditTextPreference mPreference;
    @Nullable
    private LocalBluetoothManager mBluetoothManager;
    private DeviceNamePreferenceHost mHost;
    private String mPendingDeviceName;

    public DeviceNamePreferenceController(Context context) {
        super(context, PREF_KEY);

        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        mWifiDeviceNameTextValidator = new WifiDeviceNameTextValidator();

        initializeDeviceName();
    }

@@ -85,9 +102,10 @@ public class DeviceNamePreferenceController extends BasePreferenceController

    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        mDeviceName = (String) newValue;
        setDeviceName(mDeviceName);
        preference.setSummary(getSummary());
        mPendingDeviceName = (String) newValue;
        if (mHost != null) {
            mHost.showDeviceNameWarningDialog(mPendingDeviceName);
        }
        return true;
    }

@@ -103,13 +121,25 @@ public class DeviceNamePreferenceController extends BasePreferenceController
        mBluetoothManager = localBluetoothManager;
    }

    public void confirmDeviceName() {
        if (mPendingDeviceName != null) {
            setDeviceName(mPendingDeviceName);
        }
    }

    public void setHost(DeviceNamePreferenceHost host) {
        mHost = host;
    }

    /**
     * This method presumes that security/validity checks have already been passed.
     */
    private void setDeviceName(String deviceName) {
        mDeviceName = deviceName;
        setSettingsGlobalDeviceName(deviceName);
        setBluetoothDeviceName(deviceName);
        setTetherSsidName(deviceName);
        mPreference.setSummary(getSummary());
    }

    private void setSettingsGlobalDeviceName(String deviceName) {
@@ -150,4 +180,20 @@ public class DeviceNamePreferenceController extends BasePreferenceController
        // TODO: If tether is running, turn off the AP and restart it after setting config.
        mWifiManager.setWifiApConfiguration(config);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        if (savedInstanceState != null) {
            mPendingDeviceName = savedInstanceState.getString(KEY_PENDING_DEVICE_NAME, null);
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        outState.putString(KEY_PENDING_DEVICE_NAME, mPendingDeviceName);
    }

    public interface DeviceNamePreferenceHost {
        void showDeviceNameWarningDialog(String deviceName);
    }
}
 No newline at end of file
+71 −0
Original line number Diff line number Diff line
/*
 * 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.aboutphone;

import android.app.AlertDialog;
import android.app.Dialog;
import android.app.FragmentManager;
import android.content.DialogInterface;
import android.os.Bundle;

import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;

/**
 * Warning dialog to let the user know where the device name will be shown before setting it.
 */
public class DeviceNameWarningDialog extends InstrumentedDialogFragment
        implements DialogInterface.OnClickListener {

    public static final String TAG = "DeviceNameWarningDlg";

    public static void show(MyDeviceInfoFragment host) {
        final FragmentManager manager = host.getActivity().getFragmentManager();
        if (manager.findFragmentByTag(TAG) != null) {
            return;
        }

        final DeviceNameWarningDialog dialog = new DeviceNameWarningDialog();
        dialog.setTargetFragment(host, 0 /* requestCode */);
        dialog.show(manager, TAG);
    }

    @Override
    public int getMetricsCategory() {
        return MetricsProto.MetricsEvent.DIALOG_ENABLE_DEVELOPMENT_OPTIONS;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return new AlertDialog.Builder(getActivity())
                .setTitle(R.string.my_device_info_device_name_preference_title)
                .setMessage(R.string.about_phone_device_name_warning)
                .setCancelable(false)
                .setPositiveButton(com.android.internal.R.string.ok, this)
                .setNegativeButton(com.android.internal.R.string.cancel, this)
                .create();
    }

    @Override
    public void onClick(DialogInterface dialog, int which) {
        final MyDeviceInfoFragment host = (MyDeviceInfoFragment) getTargetFragment();
        if (which == DialogInterface.BUTTON_POSITIVE) {
            host.onSetDeviceNameConfirm();
        }
    }
}
+21 −4
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ 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;
import android.content.Intent;
import android.content.pm.UserInfo;
@@ -60,7 +59,8 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class MyDeviceInfoFragment extends DashboardFragment {
public class MyDeviceInfoFragment extends DashboardFragment
        implements DeviceNamePreferenceController.DeviceNamePreferenceHost {
    private static final String LOG_TAG = "MyDeviceInfoFragment";

    private static final String KEY_MY_DEVICE_INFO_HEADER = "my_device_info_header";
@@ -98,8 +98,11 @@ public class MyDeviceInfoFragment extends DashboardFragment {
                getLifecycle());
    }

    private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
            Activity activity, Fragment fragment, Lifecycle lifecycle) {
    private static List<AbstractPreferenceController> buildPreferenceControllers(
            Context context,
            Activity activity,
            MyDeviceInfoFragment fragment,
            Lifecycle lifecycle) {
        final List<AbstractPreferenceController> controllers = new ArrayList<>();
        controllers.add(new EmergencyInfoPreferenceController(context));
        controllers.add(new PhoneNumberPreferenceController(context));
@@ -107,6 +110,10 @@ public class MyDeviceInfoFragment extends DashboardFragment {
        DeviceNamePreferenceController deviceNamePreferenceController =
                new DeviceNamePreferenceController(context);
        deviceNamePreferenceController.setLocalBluetoothManager(getLocalBtManager(context));
        deviceNamePreferenceController.setHost(fragment);
        if (lifecycle != null) {
            lifecycle.addObserver(deviceNamePreferenceController);
        }
        controllers.add(deviceNamePreferenceController);
        controllers.add(new SimStatusPreferenceController(context, fragment));
        controllers.add(new DeviceModelPreferenceController(context, fragment));
@@ -162,6 +169,16 @@ public class MyDeviceInfoFragment extends DashboardFragment {
        controller.done(context, true /* rebindActions */);
    }

    @Override
    public void showDeviceNameWarningDialog(String deviceName) {
        DeviceNameWarningDialog.show(this);
    }

    public void onSetDeviceNameConfirm() {
        final DeviceNamePreferenceController controller = use(DeviceNamePreferenceController.class);
        controller.confirmDeviceName();
    }

    private static class SummaryProvider implements SummaryLoader.SummaryProvider {

        private final SummaryLoader mSummaryLoader;
+28 −2
Original line number Diff line number Diff line
@@ -17,8 +17,10 @@
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.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@@ -84,8 +86,8 @@ public class DeviceNamePreferenceControllerTest {

    @Test
    public void constructor_deviceNameLoadedIfSet() {
        Settings.Global
            .putString(mContext.getContentResolver(), Settings.Global.DEVICE_NAME, "Test");
        Settings.Global.putString(
                mContext.getContentResolver(), Settings.Global.DEVICE_NAME, "Test");
        mController = new DeviceNamePreferenceController(mContext);
        mController.setLocalBluetoothManager(mBluetoothManager);
        assertThat(mController.getSummary()).isEqualTo("Test");
@@ -103,6 +105,8 @@ public class DeviceNamePreferenceControllerTest {

    @Test
    public void setDeviceName_preferenceUpdatedWhenDeviceNameUpdated() {
        forceAcceptDeviceName();
        mController.displayPreference(mScreen);
        mController.onPreferenceChange(mPreference, TESTING_STRING);

        assertThat(mPreference.getSummary()).isEqualTo(TESTING_STRING);
@@ -110,6 +114,8 @@ public class DeviceNamePreferenceControllerTest {

    @Test
    public void setDeviceName_bluetoothNameUpdatedWhenDeviceNameUpdated() {
        forceAcceptDeviceName();
        mController.displayPreference(mScreen);
        mController.onPreferenceChange(mPreference, TESTING_STRING);

        verify(mBluetoothAdapter).setName(eq(TESTING_STRING));
@@ -117,6 +123,8 @@ public class DeviceNamePreferenceControllerTest {

    @Test
    public void setDeviceName_wifiTetherNameUpdatedWhenDeviceNameUpdated() {
        forceAcceptDeviceName();
        mController.displayPreference(mScreen);
        mController.onPreferenceChange(mPreference, TESTING_STRING);

        ArgumentCaptor<WifiConfiguration> captor = ArgumentCaptor.forClass(WifiConfiguration.class);
@@ -131,4 +139,22 @@ public class DeviceNamePreferenceControllerTest {
        assertThat(mPreference.getText()).isEqualTo(Build.MODEL);
    }

    @Test
    public void setDeviceName_ignoresIfCancelPressed() {
        mController.displayPreference(mScreen);
        mController.onPreferenceChange(mPreference, TESTING_STRING);

        verify(mBluetoothAdapter, never()).setName(eq(TESTING_STRING));
    }

    private void forceAcceptDeviceName() {
        mController.setHost(
                new DeviceNamePreferenceController.DeviceNamePreferenceHost() {
                    @Override
                    public void showDeviceNameWarningDialog(String deviceName) {
                        mController.confirmDeviceName();
                    }
                });
    }

}
+48 −0
Original line number Diff line number Diff line
package com.android.settings.deviceinfo.deviceinfo;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;

import android.content.DialogInterface;

import com.android.settings.deviceinfo.aboutphone.DeviceNameWarningDialog;
import com.android.settings.deviceinfo.aboutphone.MyDeviceInfoFragment;
import com.android.settings.testutils.SettingsRobolectricTestRunner;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.android.controller.FragmentController;

@RunWith(SettingsRobolectricTestRunner.class)
public class DeviceNameWarningDialogTest {
    DeviceNameWarningDialog mDialog;

    @Test
    public void onClick_okSetsName() {
        final FragmentController<DeviceNameWarningDialog> fragmentController =
                Robolectric.buildFragment(DeviceNameWarningDialog.class);
        final DeviceNameWarningDialog fragment = spy(fragmentController.get());
        final MyDeviceInfoFragment deviceInfoFragment = mock(MyDeviceInfoFragment.class);
        fragment.setTargetFragment(deviceInfoFragment, 0);
        fragmentController.create().start().resume();
        fragment.onClick(null, DialogInterface.BUTTON_POSITIVE);

        verify(deviceInfoFragment).onSetDeviceNameConfirm();
    }

    @Test
    public void onClick_cancelDoesNothing() {
        final FragmentController<DeviceNameWarningDialog> fragmentController =
                Robolectric.buildFragment(DeviceNameWarningDialog.class);
        final DeviceNameWarningDialog fragment = spy(fragmentController.get());
        final MyDeviceInfoFragment deviceInfoFragment = mock(MyDeviceInfoFragment.class);
        fragment.setTargetFragment(deviceInfoFragment, 0);
        fragmentController.create().start().resume();
        fragment.onClick(null, DialogInterface.BUTTON_NEGATIVE);

        verify(deviceInfoFragment, never()).onSetDeviceNameConfirm();
    }
}