Merge "Fix null pointer crash in BT renaming dialog"
diff --git a/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java b/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java
index 260b4a8..74c39b6 100644
--- a/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java
+++ b/src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java
@@ -34,6 +34,7 @@
 import android.widget.EditText;
 import android.widget.TextView;
 
+import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.app.AlertDialog;
 
 import com.android.settings.R;
@@ -43,8 +44,14 @@
  * Dialog fragment for renaming a Bluetooth device.
  */
 abstract class BluetoothNameDialogFragment extends InstrumentedDialogFragment
-        implements TextWatcher {
-    private AlertDialog mAlertDialog;
+        implements TextWatcher, TextView.OnEditorActionListener {
+
+    // Key to save the edited name and edit status for restoring after rotation
+    private static final String KEY_NAME = "device_name";
+    private static final String KEY_NAME_EDITED = "device_name_edited";
+
+    @VisibleForTesting
+    AlertDialog mAlertDialog;
     private Button mOkButton;
 
     EditText mDeviceNameView;
@@ -55,10 +62,6 @@
     // This flag is set when the user edits the name (preserved on rotation)
     private boolean mDeviceNameEdited;
 
-    // Key to save the edited name and edit status for restoring after rotation
-    private static final String KEY_NAME = "device_name";
-    private static final String KEY_NAME_EDITED = "device_name_edited";
-
     /**
      * @return the title to use for the dialog.
      */
@@ -123,22 +126,24 @@
         }
         mDeviceNameView.addTextChangedListener(this);
         com.android.settings.Utils.setEditTextCursorPosition(mDeviceNameView);
-        mDeviceNameView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
-            @Override
-            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
-                if (actionId == EditorInfo.IME_ACTION_DONE) {
-                    setDeviceName(v.getText().toString());
-                    mAlertDialog.dismiss();
-                    return true;    // action handled
-                } else {
-                    return false;   // not handled
-                }
-            }
-        });
+        mDeviceNameView.setOnEditorActionListener(this);
         return view;
     }
 
     @Override
+    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+        if (actionId == EditorInfo.IME_ACTION_DONE) {
+            setDeviceName(v.getText().toString());
+            if (mAlertDialog != null && mAlertDialog.isShowing()) {
+                mAlertDialog.dismiss();
+            }
+            return true;    // action handled
+        } else {
+            return false;   // not handled
+        }
+    }
+
+    @Override
     public void onDestroy() {
         super.onDestroy();
         mAlertDialog = null;
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothNameDialogFragmentTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothNameDialogFragmentTest.java
new file mode 100644
index 0000000..350ec9e
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothNameDialogFragmentTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.view.inputmethod.EditorInfo;
+import android.widget.TextView;
+
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class BluetoothNameDialogFragmentTest {
+
+    private TestBluetoothNameDialogFragment mBluetoothNameDialogFragment;
+    private TextView mTextView;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mBluetoothNameDialogFragment = new TestBluetoothNameDialogFragment();
+        mTextView = new TextView(RuntimeEnvironment.application);
+    }
+
+    @Test
+    public void onEditorAction_dialogNull_shouldNotCrash() {
+        mBluetoothNameDialogFragment.mAlertDialog = null;
+
+        // Should not crash
+        assertThat(
+                mBluetoothNameDialogFragment.onEditorAction(mTextView, EditorInfo.IME_ACTION_DONE,
+                        null)).isTrue();
+    }
+
+
+    /**
+     * Test fragment for {@link BluetoothNameDialogFragment} to test common methods
+     */
+    public static class TestBluetoothNameDialogFragment extends BluetoothNameDialogFragment {
+
+        @Override
+        protected int getDialogTitle() {
+            return 0;
+        }
+
+        @Override
+        protected String getDeviceName() {
+            return null;
+        }
+
+        @Override
+        protected void setDeviceName(String deviceName) {
+
+        }
+
+        @Override
+        public int getMetricsCategory() {
+            return 0;
+        }
+    }
+
+}