Fix the device on "Media devices" not updated issue

- This CL uses new CachedBluetoothDevice callback instance
  in construct to avoid unregister wrong callback.
- This CL uses flag to make sure preference will register callback
  on onAttached() when preference remove callback on
  onPrepareForRemoval() or onDetached().
- Update test case

Bug: 168682778
Bug: 157653997
Test: make -j42 RunSettingsRoboTests
Change-Id: I7a0d9f5332153ee80634e191847b84cd7c380b7d
Merged-In: I7a0d9f5332153ee80634e191847b84cd7c380b7d
(cherry picked from commit cafeab28136320345e12ccfb8e9bf2def10635d8)
diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
index 2e4654c..d927121 100644
--- a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
+++ b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
@@ -52,8 +52,7 @@
  * BluetoothDevicePreference is the preference type used to display each remote
  * Bluetooth device in the Bluetooth Settings screen.
  */
-public final class BluetoothDevicePreference extends GearPreference implements
-        CachedBluetoothDevice.Callback {
+public final class BluetoothDevicePreference extends GearPreference {
     private static final String TAG = "BluetoothDevicePref";
 
     private static int sDimAlpha = Integer.MIN_VALUE;
@@ -77,10 +76,20 @@
     private AlertDialog mDisconnectDialog;
     private String contentDescription = null;
     private boolean mHideSecondTarget = false;
+    private boolean mIsCallbackRemoved = false;
     @VisibleForTesting
     boolean mNeedNotifyHierarchyChanged = false;
     /* Talk-back descriptions for various BT icons */
     Resources mResources;
+    final BluetoothDevicePreferenceCallback mCallback;
+
+    private class BluetoothDevicePreferenceCallback implements CachedBluetoothDevice.Callback {
+
+        @Override
+        public void onDeviceAttributesChanged() {
+            onPreferenceAttributesChanged();
+        }
+    }
 
     public BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice,
             boolean showDeviceWithoutNames, @SortType int type) {
@@ -96,10 +105,12 @@
         }
 
         mCachedDevice = cachedDevice;
+        mCallback = new BluetoothDevicePreferenceCallback();
+        mCachedDevice.registerCallback(mCallback);
         mCurrentTime = System.currentTimeMillis();
         mType = type;
 
-        onDeviceAttributesChanged();
+        onPreferenceAttributesChanged();
     }
 
     public void setNeedNotifyHierarchyChanged(boolean needNotifyHierarchyChanged) {
@@ -126,6 +137,10 @@
     @Override
     protected void onPrepareForRemoval() {
         super.onPrepareForRemoval();
+        if (!mIsCallbackRemoved) {
+            mCachedDevice.unregisterCallback(mCallback);
+            mIsCallbackRemoved = true;
+        }
         if (mDisconnectDialog != null) {
             mDisconnectDialog.dismiss();
             mDisconnectDialog = null;
@@ -135,13 +150,20 @@
     @Override
     public void onAttached() {
         super.onAttached();
-        mCachedDevice.registerCallback(this);
+        if (mIsCallbackRemoved) {
+            mCachedDevice.registerCallback(mCallback);
+            mIsCallbackRemoved = false;
+        }
+        onPreferenceAttributesChanged();
     }
 
     @Override
     public void onDetached() {
         super.onDetached();
-        mCachedDevice.unregisterCallback(this);
+        if (!mIsCallbackRemoved) {
+            mCachedDevice.unregisterCallback(mCallback);
+            mIsCallbackRemoved = true;
+        }
     }
 
     public CachedBluetoothDevice getBluetoothDevice() {
@@ -152,7 +174,7 @@
         mHideSecondTarget = hideSecondTarget;
     }
 
-    public void onDeviceAttributesChanged() {
+    private void onPreferenceAttributesChanged() {
         /*
          * The preference framework takes care of making sure the value has
          * changed before proceeding. It will also call notifyChanged() if
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java
index f12e06e..c87cc25 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java
@@ -17,10 +17,12 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -35,7 +37,6 @@
 import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-import com.android.settingslib.testutils.DrawableTestHelper;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -261,4 +262,19 @@
         assertThat(mPreferenceList.get(2).getCachedDevice().getAddress())
                 .isEqualTo(preference3.getCachedDevice().getAddress());
     }
+
+    @Test
+    public void onAttached_callbackNotRemoved_doNotRegisterCallback() {
+        mPreference.onAttached();
+
+        verify(mCachedBluetoothDevice, never()).unregisterCallback(any());
+    }
+
+    @Test
+    public void onAttached_callbackRemoved_registerCallback() {
+        mPreference.onPrepareForRemoval();
+        mPreference.onAttached();
+
+        verify(mCachedBluetoothDevice, times(2)).registerCallback(any());
+    }
 }