OSDN Git Service

Fix null pointer crash in BT renaming dialog
authorjackqdyulei <jackqdyulei@google.com>
Mon, 17 Sep 2018 20:25:36 +0000 (13:25 -0700)
committerjackqdyulei <jackqdyulei@google.com>
Tue, 18 Sep 2018 21:41:37 +0000 (14:41 -0700)
The dialog may become null after onDestroy has been invoked, so
we need to catch this case.

This CL also moves the listener outside to make it easy to test.

Change-Id: I4ce640c5bdaf1f201f9fecb14b3e5e38e10d4b79
Fixes: 115679393
Test: RunSettingsRoboTests

src/com/android/settings/bluetooth/BluetoothNameDialogFragment.java
tests/robotests/src/com/android/settings/bluetooth/BluetoothNameDialogFragmentTest.java [new file with mode: 0644]

index 260b4a8..74c39b6 100644 (file)
@@ -34,6 +34,7 @@ import android.widget.Button;
 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 @@ import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
  * 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 @@ abstract class BluetoothNameDialogFragment extends InstrumentedDialogFragment
     // 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 @@ abstract class BluetoothNameDialogFragment extends InstrumentedDialogFragment
         }
         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 (file)
index 0000000..350ec9e
--- /dev/null
@@ -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;
+        }
+    }
+
+}