Merge "Add fp icon with background shape"
diff --git a/color-check-baseline.xml b/color-check-baseline.xml
index 7ab18a4..3424e76 100644
--- a/color-check-baseline.xml
+++ b/color-check-baseline.xml
@@ -1541,6 +1541,22 @@
         priority="4"
         summary="Using hardcoded color"
         explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+        errorLine1="    <color name="bt_outline_color">#1f000000</color> <!-- icon outline color -->"
+        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="res/values/colors.xml"
+            line="160"
+            column="5"/>
+    </issue>
+
+    <issue
+        id="HardCodedColor"
+        severity="Error"
+        message="Avoid using hardcoded color"
+        category="Correctness"
+        priority="4"
+        summary="Using hardcoded color"
+        explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
         errorLine1="                android:background=&quot;@color/lock_pattern_background&quot; />"
         errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 59ed4c4..c333edb 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -156,4 +156,6 @@
     <color name="bt_color_bg_5">#fdcfe8</color> <!-- Material Pink 100 -->
     <color name="bt_color_bg_6">#e9d2fd</color> <!-- Material Purple 100 -->
     <color name="bt_color_bg_7">#cbf0f8</color> <!-- Material Cyan 100 -->
+
+    <color name="bt_outline_color">#1f000000</color> <!-- icon outline color -->
 </resources>
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 248fb81..4a65bc9 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -404,4 +404,13 @@
 
     <!-- Wi-Fi DPP fragment icon size -->
     <dimen name="wifi_dpp_fragment_icon_width_height">48dp</dimen>
+
+    <!-- Size of nearby icon -->
+    <dimen name="bt_nearby_icon_size">24dp</dimen>
+
+    <!-- Stroke size of adaptive outline -->
+    <dimen name="adaptive_outline_stroke">1dp</dimen>
+
+    <!-- Elevation of bluetooth icon -->
+    <dimen name="bt_icon_elevation">4dp</dimen>
 </resources>
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java
index 3860352..fe73a2e 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java
@@ -80,8 +80,10 @@
 
     @Override
     protected void refresh() {
-        setHeaderProperties();
-        mHeaderController.done(mFragment.getActivity(), true /* rebindActions */);
+        if (isAvailable()) {
+            setHeaderProperties();
+            mHeaderController.done(mFragment.getActivity(), true /* rebindActions */);
+        }
     }
 
     @Override
diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
index 8d727eb..4a7ca27 100644
--- a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
+++ b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
@@ -164,6 +164,8 @@
         final ImageView imageView = (ImageView) view.findViewById(android.R.id.icon);
         if (imageView != null) {
             imageView.setContentDescription(contentDescription);
+            imageView.setElevation(
+                    getContext().getResources().getDimension(R.dimen.bt_icon_elevation));
         }
         super.onBindViewHolder(view);
     }
diff --git a/src/com/android/settings/bluetooth/Utils.java b/src/com/android/settings/bluetooth/Utils.java
index e02ddc4..ecf40b6 100755
--- a/src/com/android/settings/bluetooth/Utils.java
+++ b/src/com/android/settings/bluetooth/Utils.java
@@ -22,8 +22,11 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.res.Resources;
+import android.graphics.Bitmap;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.provider.MediaStore;
 import android.provider.Settings;
 import android.util.Log;
 import android.util.Pair;
@@ -33,14 +36,18 @@
 import androidx.appcompat.app.AlertDialog;
 
 import com.android.settings.R;
+import com.android.settings.homepage.AdaptiveIconShapeDrawable;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.widget.AdaptiveIcon;
+import com.android.settings.widget.AdaptiveOutlineDrawable;
 import com.android.settingslib.bluetooth.BluetoothUtils;
 import com.android.settingslib.bluetooth.BluetoothUtils.ErrorListener;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.LocalBluetoothManager.BluetoothManagerCallback;
 
+import java.io.IOException;
+
 /**
  * Utils is a helper class that contains constants for various
  * Android resource IDs, debug logging flags, and static methods
@@ -165,6 +172,7 @@
         if (bluetoothDevice == null) {
             return false;
         }
+
         return Boolean.parseBoolean(bluetoothDevice.getMetadata(key));
     }
 
@@ -193,7 +201,36 @@
             CachedBluetoothDevice cachedDevice) {
         final Pair<Drawable, String> pair = BluetoothUtils.getBtClassDrawableWithDescription(
                 context, cachedDevice);
+        final boolean untetheredHeadset = Utils.getBooleanMetaData(cachedDevice.getDevice(),
+                BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET);
+        final int iconSize = context.getResources().getDimensionPixelSize(
+                R.dimen.bt_nearby_icon_size);
         final Resources resources = context.getResources();
+
+        // Deal with untethered headset
+        if (untetheredHeadset) {
+            final String uriString = Utils.getStringMetaData(cachedDevice.getDevice(),
+                    BluetoothDevice.METADATA_MAIN_ICON);
+            final Uri iconUri = uriString != null ? Uri.parse(uriString) : null;
+            if (iconUri != null) {
+                try {
+                    final Bitmap bitmap = MediaStore.Images.Media.getBitmap(
+                            context.getContentResolver(), iconUri);
+                    if (bitmap != null) {
+                        final Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, iconSize,
+                                iconSize, false);
+                        bitmap.recycle();
+                        final AdaptiveOutlineDrawable drawable = new AdaptiveOutlineDrawable(
+                                resources, resizedBitmap);
+                        return new Pair<>(drawable, pair.second);
+                    }
+                } catch (IOException e) {
+                    Log.e(TAG, "Failed to get drawable for: " + iconUri, e);
+                }
+            }
+        }
+
+        // Deal with normal headset
         final int[] iconFgColors = resources.getIntArray(R.array.bt_icon_fg_colors);
         final int[] iconBgColors = resources.getIntArray(R.array.bt_icon_bg_colors);
 
diff --git a/src/com/android/settings/widget/AdaptiveOutlineDrawable.java b/src/com/android/settings/widget/AdaptiveOutlineDrawable.java
new file mode 100644
index 0000000..84c4e2f
--- /dev/null
+++ b/src/com/android/settings/widget/AdaptiveOutlineDrawable.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019 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.widget;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.DrawableWrapper;
+import android.util.PathParser;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.R;
+import com.android.settings.homepage.AdaptiveIconShapeDrawable;
+
+/**
+ * Adaptive outline drawable with white plain background color and black outline
+ */
+public class AdaptiveOutlineDrawable extends DrawableWrapper {
+    @VisibleForTesting
+    final Paint mOutlinePaint;
+    private Path mPath;
+    private final int mInsetPx;
+    private final Bitmap mBitmap;
+
+    public AdaptiveOutlineDrawable(Resources resources, Bitmap bitmap) {
+        super(new AdaptiveIconShapeDrawable(resources));
+
+        getDrawable().setTint(Color.WHITE);
+        mPath = new Path(PathParser.createPathFromPathData(
+                resources.getString(com.android.internal.R.string.config_icon_mask)));
+        mOutlinePaint = new Paint();
+        mOutlinePaint.setColor(resources.getColor(R.color.bt_outline_color, null));
+        mOutlinePaint.setStyle(Paint.Style.STROKE);
+        mOutlinePaint.setStrokeWidth(resources.getDimension(R.dimen.adaptive_outline_stroke));
+        mOutlinePaint.setAntiAlias(true);
+
+        mInsetPx = resources
+                .getDimensionPixelSize(R.dimen.dashboard_tile_foreground_image_inset);
+        mBitmap = bitmap;
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        super.draw(canvas);
+        final Rect bounds = getBounds();
+        final float pathSize = AdaptiveIconDrawable.MASK_SIZE;
+
+        final float scaleX = (bounds.right - bounds.left) / pathSize;
+        final float scaleY = (bounds.bottom - bounds.top) / pathSize;
+
+        final int count = canvas.save();
+        canvas.scale(scaleX, scaleY);
+        // Draw outline
+        canvas.drawPath(mPath, mOutlinePaint);
+        canvas.restoreToCount(count);
+
+        // Draw the foreground icon
+        canvas.drawBitmap(mBitmap, bounds.left + mInsetPx, bounds.top + mInsetPx, null);
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return mBitmap.getHeight() + 2 * mInsetPx;
+    }
+
+    @Override
+    public int getIntrinsicWidth() {
+        return mBitmap.getWidth() + 2 * mInsetPx;
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/UtilsTest.java b/tests/robotests/src/com/android/settings/bluetooth/UtilsTest.java
index 51e559f..a956518 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/UtilsTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/UtilsTest.java
@@ -29,6 +29,8 @@
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.widget.AdaptiveIcon;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
@@ -39,6 +41,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
 
 @RunWith(RobolectricTestRunner.class)
 public class UtilsTest {
@@ -51,6 +54,8 @@
     private Context mContext;
     @Mock
     private BluetoothDevice mBluetoothDevice;
+    @Mock
+    private CachedBluetoothDevice mCachedBluetoothDevice;
 
     private MetricsFeatureProvider mMetricsFeatureProvider;
 
@@ -107,4 +112,16 @@
                 BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET)).isEqualTo(true);
     }
 
+    @Test
+    public void getBtRainbowDrawableWithDescription_normalHeadset_returnAdaptiveIcon() {
+        when(mBluetoothDevice.getMetadata(
+                BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET)).thenReturn("false");
+        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+        when(mCachedBluetoothDevice.getAddress()).thenReturn("1f:aa:bb");
+
+        assertThat(Utils.getBtRainbowDrawableWithDescription(RuntimeEnvironment.application,
+                mCachedBluetoothDevice).first).isInstanceOf(
+                AdaptiveIcon.class);
+    }
+
 }
diff --git a/tests/robotests/src/com/android/settings/widget/AdaptiveOutlineDrawableTest.java b/tests/robotests/src/com/android/settings/widget/AdaptiveOutlineDrawableTest.java
new file mode 100644
index 0000000..f21fc34
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/widget/AdaptiveOutlineDrawableTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 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.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.res.Resources;
+import android.graphics.Paint;
+
+import com.android.settings.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class AdaptiveOutlineDrawableTest {
+
+    @Test
+    public void constructor_initPaint() {
+        final Resources resources = RuntimeEnvironment.application.getResources();
+        final AdaptiveOutlineDrawable drawable = new AdaptiveOutlineDrawable(resources, null);
+
+        assertThat(drawable.mOutlinePaint.getStyle()).isEqualTo(Paint.Style.STROKE);
+        assertThat(drawable.mOutlinePaint.getStrokeWidth()).isWithin(0.01f).of(
+                resources.getDimension(R.dimen.adaptive_outline_stroke));
+    }
+
+}