diff --git a/packages/SettingsLib/AdaptiveIcon/Android.bp b/packages/SettingsLib/AdaptiveIcon/Android.bp
new file mode 100644
index 0000000000000..7f4442deecd6b
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/Android.bp
@@ -0,0 +1,13 @@
+android_library {
+ name: "SettingsLibAdaptiveIcon",
+
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+
+ static_libs: [
+ "androidx.annotation_annotation",
+ "SettingsLibTile"
+ ],
+
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/AdaptiveIcon/AndroidManifest.xml b/packages/SettingsLib/AdaptiveIcon/AndroidManifest.xml
new file mode 100644
index 0000000000000..256b8f3ea4772
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/AndroidManifest.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
diff --git a/packages/SettingsLib/AdaptiveIcon/res/values/colors.xml b/packages/SettingsLib/AdaptiveIcon/res/values/colors.xml
new file mode 100644
index 0000000000000..76d106a067dd2
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/res/values/colors.xml
@@ -0,0 +1,21 @@
+
+
+
+
+ #1A73E8
+
+ #1f000000
+
diff --git a/packages/SettingsLib/AdaptiveIcon/res/values/dimens.xml b/packages/SettingsLib/AdaptiveIcon/res/values/dimens.xml
new file mode 100644
index 0000000000000..7f5b58c48abbe
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/res/values/dimens.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+ 6dp
+
+
+ 1dp
+
\ No newline at end of file
diff --git a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java
new file mode 100644
index 0000000000000..fc93650a6b85d
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java
@@ -0,0 +1,130 @@
+/*
+ * 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.settingslib.widget;
+
+import static androidx.annotation.VisibleForTesting.NONE;
+
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_HINT;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.os.Bundle;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settingslib.drawer.Tile;
+
+/**
+ * Adaptive icon that can set background color
+ */
+public class AdaptiveIcon extends LayerDrawable {
+
+ private static final String TAG = "AdaptiveHomepageIcon";
+
+ @VisibleForTesting(otherwise = NONE)
+ int mBackgroundColor = -1;
+ private AdaptiveConstantState mAdaptiveConstantState;
+
+ public AdaptiveIcon(Context context, Drawable foreground) {
+ super(new Drawable[]{
+ new AdaptiveIconShapeDrawable(context.getResources()),
+ foreground
+ });
+ final int insetPx = context.getResources()
+ .getDimensionPixelSize(R.dimen.dashboard_tile_foreground_image_inset);
+ setLayerInset(1 /* index */, insetPx, insetPx, insetPx, insetPx);
+ mAdaptiveConstantState = new AdaptiveConstantState(context, foreground);
+ }
+
+ /**
+ * According {@code tile} metaData to set background color
+ */
+ public void setBackgroundColor(Context context, Tile tile) {
+ final Bundle metaData = tile.getMetaData();
+ try {
+ if (metaData != null) {
+ // Load from bg.argb first
+ int bgColor = metaData.getInt(META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB,
+ 0 /* default */);
+ // Not found, load from bg.hint
+ if (bgColor == 0) {
+ final int colorRes = metaData.getInt(META_DATA_PREFERENCE_ICON_BACKGROUND_HINT,
+ 0 /* default */);
+ if (colorRes != 0) {
+ bgColor = context.getPackageManager()
+ .getResourcesForApplication(tile.getPackageName())
+ .getColor(colorRes, null /* theme */);
+ }
+ }
+ // If found anything, use it.
+ if (bgColor != 0) {
+ setBackgroundColor(bgColor);
+ return;
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Failed to set background color for " + tile.getPackageName());
+ }
+ setBackgroundColor(context.getColor(R.color.homepage_generic_icon_background));
+ }
+
+ /**
+ * Set background color by {@code color}
+ */
+ public void setBackgroundColor(int color) {
+ mBackgroundColor = color;
+ getDrawable(0).setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
+ Log.d(TAG, "Setting background color " + mBackgroundColor);
+ mAdaptiveConstantState.mColor = color;
+ }
+
+ @Override
+ public ConstantState getConstantState() {
+ return mAdaptiveConstantState;
+ }
+
+ @VisibleForTesting
+ static class AdaptiveConstantState extends ConstantState {
+ Context mContext;
+ Drawable mDrawable;
+ int mColor;
+
+ AdaptiveConstantState(Context context, Drawable drawable) {
+ this.mContext = context;
+ this.mDrawable = drawable;
+ }
+
+ @Override
+ public Drawable newDrawable() {
+ final AdaptiveIcon
+ icon = new AdaptiveIcon(mContext, mDrawable);
+ icon.setBackgroundColor(mColor);
+
+ return icon;
+ }
+
+ @Override
+ public int getChangingConfigurations() {
+ return 0;
+ }
+ }
+}
diff --git a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIconShapeDrawable.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIconShapeDrawable.java
new file mode 100644
index 0000000000000..4d7610cf97b68
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIconShapeDrawable.java
@@ -0,0 +1,59 @@
+/*
+ * 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.settingslib.widget;
+
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.graphics.Path;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.PathShape;
+import android.util.AttributeSet;
+import android.util.PathParser;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * Draws a filled {@link ShapeDrawable} using the path from {@link AdaptiveIconDrawable}.
+ */
+public class AdaptiveIconShapeDrawable extends ShapeDrawable {
+ public AdaptiveIconShapeDrawable() {
+ super();
+ }
+
+ public AdaptiveIconShapeDrawable(Resources resources) {
+ super();
+ init(resources);
+ }
+
+ @Override
+ public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ throws XmlPullParserException, IOException {
+ super.inflate(r, parser, attrs, theme);
+ init(r);
+ }
+
+ private void init(Resources resources) {
+ final float pathSize = AdaptiveIconDrawable.MASK_SIZE;
+ final Path path = new Path(PathParser.createPathFromPathData(
+ resources.getString(com.android.internal.R.string.config_icon_mask)));
+ setShape(new PathShape(path, pathSize, pathSize));
+ }
+}
diff --git a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
new file mode 100644
index 0000000000000..1c65bc248961d
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
@@ -0,0 +1,87 @@
+/*
+ * 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.settingslib.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;
+
+/**
+ * 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/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 730e9e134e3df..b532621cd6179 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -22,6 +22,7 @@ android_library {
"SettingsLibEntityHeaderWidgets",
"SettingsLibBarChartPreference",
"SettingsLibProgressBar",
+ "SettingsLibAdaptiveIcon",
],
// ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/Tile/Android.bp b/packages/SettingsLib/Tile/Android.bp
new file mode 100644
index 0000000000000..bf16ef317fd8a
--- /dev/null
+++ b/packages/SettingsLib/Tile/Android.bp
@@ -0,0 +1,11 @@
+android_library {
+ name: "SettingsLibTile",
+
+ srcs: ["src/**/*.java"],
+
+ static_libs: [
+ "androidx.annotation_annotation",
+ ],
+
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/Tile/AndroidManifest.xml b/packages/SettingsLib/Tile/AndroidManifest.xml
new file mode 100644
index 0000000000000..b13532e2a5fd8
--- /dev/null
+++ b/packages/SettingsLib/Tile/AndroidManifest.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/DashboardCategory.java
similarity index 93%
rename from packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
rename to packages/SettingsLib/Tile/src/com/android/settingslib/drawer/DashboardCategory.java
index a3dda658bec72..7b062b1ac9a9a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/DashboardCategory.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (C) 2015 The Android Open Source Project
+/*
+ * 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
+ * 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,
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package com.android.settingslib.drawer;
import static java.lang.String.CASE_INSENSITIVE_ORDER;
@@ -26,6 +25,9 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+/**
+ * The category for handle {@link Tile}
+ */
public class DashboardCategory implements Parcelable {
/**
@@ -67,18 +69,30 @@ public class DashboardCategory implements Parcelable {
return result;
}
+ /**
+ * Add tile
+ */
public synchronized void addTile(Tile tile) {
mTiles.add(tile);
}
+ /**
+ * Remove tile
+ */
public synchronized void removeTile(int n) {
mTiles.remove(n);
}
+ /**
+ * Get size of tile
+ */
public int getTilesCount() {
return mTiles.size();
}
+ /**
+ * Get tile
+ */
public Tile getTile(int n) {
return mTiles.get(n);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
similarity index 95%
rename from packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
rename to packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
index d28b00a7ed398..5108efbdc2169 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (C) 2015 The Android Open Source Project
+/*
+ * 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
+ * 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,
@@ -84,8 +84,8 @@ public class Tile implements Parcelable {
mActivityPackage = in.readString();
mActivityName = in.readString();
mIntent = new Intent().setClassName(mActivityPackage, mActivityName);
- final int N = in.readInt();
- for (int i = 0; i < N; i++) {
+ final int number = in.readInt();
+ for (int i = 0; i < number; i++) {
userHandle.add(UserHandle.CREATOR.createFromParcel(in));
}
mCategory = in.readString();
@@ -101,9 +101,9 @@ public class Tile implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mActivityPackage);
dest.writeString(mActivityName);
- final int N = userHandle.size();
- dest.writeInt(N);
- for (int i = 0; i < N; i++) {
+ final int size = userHandle.size();
+ dest.writeInt(size);
+ for (int i = 0; i < size; i++) {
userHandle.get(i).writeToParcel(dest, flags);
}
dest.writeString(mCategory);
@@ -151,6 +151,9 @@ public class Tile implements Parcelable {
}
}
+ /**
+ * Check whether title has order.
+ */
public boolean hasOrder() {
return mMetaData.containsKey(META_DATA_KEY_ORDER)
&& mMetaData.get(META_DATA_KEY_ORDER) instanceof Integer;
@@ -262,6 +265,9 @@ public class Tile implements Parcelable {
}
}
+ /**
+ * Check whether title has key.
+ */
public boolean hasKey() {
return mMetaData != null && mMetaData.containsKey(META_DATA_PREFERENCE_KEYHINT);
}
@@ -361,9 +367,12 @@ public class Tile implements Parcelable {
}
};
+ /**
+ * Check whether title is only have primary profile
+ */
public boolean isPrimaryProfileOnly() {
- String profile = mMetaData != null ?
- mMetaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL;
+ String profile = mMetaData != null
+ ? mMetaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL;
profile = (profile != null ? profile : PROFILE_ALL);
return TextUtils.equals(profile, PROFILE_PRIMARY);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
similarity index 98%
rename from packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
rename to packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
index 91892abdfb446..31925ab64ec3a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package com.android.settingslib.drawer;
@@ -39,6 +39,10 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+/**
+ * Utils is a helper class that contains profile key, meta data, settings action
+ * and static methods for get icon or text from uri.
+ */
public class TileUtils {
private static final boolean DEBUG_TIMING = false;
diff --git a/packages/SettingsLib/res/drawable/ic_media_device.xml b/packages/SettingsLib/res/drawable/ic_media_device.xml
new file mode 100644
index 0000000000000..5a6aeb4a8e237
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_media_device.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index ed3c11cd3ca3a..39c55fd1925cb 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -603,4 +603,26 @@
- 3
- 3
+
+
+ - @color/bt_color_icon_1
+ - @color/bt_color_icon_2
+ - @color/bt_color_icon_3
+ - @color/bt_color_icon_4
+ - @color/bt_color_icon_5
+ - @color/bt_color_icon_6
+ - @color/bt_color_icon_7
+
+
+
+
+ - @color/bt_color_bg_1
+ - @color/bt_color_bg_2
+ - @color/bt_color_bg_3
+ - @color/bt_color_bg_4
+ - @color/bt_color_bg_5
+ - @color/bt_color_bg_6
+ - @color/bt_color_bg_7
+
+
diff --git a/packages/SettingsLib/res/values/colors.xml b/packages/SettingsLib/res/values/colors.xml
index 66bbb3a6c890f..4b91bbb8d8dc1 100644
--- a/packages/SettingsLib/res/values/colors.xml
+++ b/packages/SettingsLib/res/values/colors.xml
@@ -19,4 +19,20 @@
@*android:color/tertiary_device_default_settings
#64000000
+
+ #48a50e0e
+ #480d652d
+ #48e37400
+ #48b06000
+ #489c166b
+ #48681da8
+ #48007b83
+
+ #fad2cf
+ #ceead6
+ #feefc3
+ #fedfc8
+ #fdcfe8
+ #e9d2fd
+ #cbf0f8
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index a9c5061f6d873..2cb9d4b14aa75 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -91,6 +91,7 @@
0.9dp
-
+
+ 24dp
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index bb8c8a6768ed0..867efb4082588 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -1,18 +1,30 @@
package com.android.settingslib.bluetooth;
import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
+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.util.Log;
import android.util.Pair;
import androidx.annotation.DrawableRes;
import com.android.settingslib.R;
+import com.android.settingslib.widget.AdaptiveIcon;
+import com.android.settingslib.widget.AdaptiveOutlineDrawable;
+import java.io.IOException;
import java.util.List;
public class BluetoothUtils {
+ private static final String TAG = "BluetoothUtils";
+
public static final boolean V = false; // verbose logging
public static final boolean D = true; // regular logging
@@ -112,4 +124,68 @@ public class BluetoothUtils {
public static Drawable getBluetoothDrawable(Context context, @DrawableRes int resId) {
return context.getDrawable(resId);
}
+
+ /**
+ * Get colorful bluetooth icon with description
+ */
+ public static Pair getBtRainbowDrawableWithDescription(Context context,
+ CachedBluetoothDevice cachedDevice) {
+ final Pair pair = BluetoothUtils.getBtClassDrawableWithDescription(
+ context, cachedDevice);
+ final BluetoothDevice bluetoothDevice = cachedDevice.getDevice();
+ final boolean untetheredHeadset = bluetoothDevice != null
+ ? Boolean.parseBoolean(bluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET))
+ : false;
+ 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 = bluetoothDevice != null
+ ? bluetoothDevice.getMetadata(BluetoothDevice.METADATA_MAIN_ICON)
+ : null;
+ 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);
+ }
+ }
+ }
+
+ return new Pair<>(buildBtRainbowDrawable(context,
+ pair.first, cachedDevice.getAddress().hashCode()), pair.second);
+ }
+
+ /**
+ * Build Bluetooth device icon with rainbow
+ */
+ public static Drawable buildBtRainbowDrawable(Context context, Drawable drawable,
+ int hashCode) {
+ final Resources resources = context.getResources();
+
+ // 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);
+
+ // get color index based on mac address
+ final int index = Math.abs(hashCode % iconBgColors.length);
+ drawable.setColorFilter(iconFgColors[index], PorterDuff.Mode.SRC_ATOP);
+ final Drawable adaptiveIcon = new AdaptiveIcon(context, drawable);
+ ((AdaptiveIcon) adaptiveIcon).setBackgroundColor(iconBgColors[index]);
+
+ return adaptiveIcon;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index 3092b9960c7ed..2711e31759577 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -18,8 +18,11 @@ package com.android.settingslib.media;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
+import android.graphics.drawable.Drawable;
import android.util.Log;
+import android.util.Pair;
+import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
/**
@@ -48,9 +51,10 @@ public class BluetoothMediaDevice extends MediaDevice {
}
@Override
- public int getIcon() {
- //TODO(b/117129183): This is not final icon for bluetooth device, just for demo.
- return com.android.internal.R.drawable.ic_bt_headphones_a2dp;
+ public Drawable getIcon() {
+ final Pair pair = BluetoothUtils
+ .getBtRainbowDrawableWithDescription(mContext, mCachedDevice);
+ return pair.first;
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index 95f3d3d0f7693..732e8dba3e44b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -16,10 +16,14 @@
package com.android.settingslib.media;
import android.content.Context;
+import android.graphics.drawable.Drawable;
import android.widget.Toast;
import androidx.mediarouter.media.MediaRouter;
+import com.android.settingslib.R;
+import com.android.settingslib.bluetooth.BluetoothUtils;
+
/**
* InfoMediaDevice extends MediaDevice to represents wifi device.
*/
@@ -46,9 +50,10 @@ public class InfoMediaDevice extends MediaDevice {
}
@Override
- public int getIcon() {
- //TODO(b/121083246): This is not final icon for cast device, just for demo.
- return com.android.internal.R.drawable.ic_settings_print;
+ public Drawable getIcon() {
+ //TODO(b/120669861): Return remote device icon uri once api is ready.
+ return BluetoothUtils.buildBtRainbowDrawable(mContext,
+ mContext.getDrawable(R.drawable.ic_media_device), getId().hashCode());
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 9b9e80310c18d..53a852069478e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -16,6 +16,7 @@
package com.android.settingslib.media;
import android.content.Context;
+import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import androidx.annotation.IntDef;
@@ -70,11 +71,11 @@ public abstract class MediaDevice implements Comparable {
public abstract String getSummary();
/**
- * Get resource id of MediaDevice.
+ * Get icon of MediaDevice.
*
- * @return resource id of MediaDevice.
+ * @return drawable of icon.
*/
- public abstract int getIcon();
+ public abstract Drawable getIcon();
/**
* Get unique ID that represent MediaDevice
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 8c3fcc077edcd..af91c34641945 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -16,10 +16,12 @@
package com.android.settingslib.media;
import android.content.Context;
+import android.graphics.drawable.Drawable;
import android.util.Log;
import com.android.settingslib.R;
import com.android.settingslib.bluetooth.A2dpProfile;
+import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.HearingAidProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
@@ -56,8 +58,9 @@ public class PhoneMediaDevice extends MediaDevice {
}
@Override
- public int getIcon() {
- return R.drawable.ic_smartphone;
+ public Drawable getIcon() {
+ return BluetoothUtils.buildBtRainbowDrawable(mContext,
+ mContext.getDrawable(R.drawable.ic_smartphone), getId().hashCode());
}
@Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index b713e08eb67e6..b228cf7c10c6d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -15,15 +15,20 @@
*/
package com.android.settingslib.bluetooth;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.Pair;
+import com.android.settingslib.widget.AdaptiveIcon;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -38,6 +43,9 @@ public class BluetoothUtilsTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private CachedBluetoothDevice mCachedBluetoothDevice;
+ @Mock
+ private BluetoothDevice mBluetoothDevice;
+
private Context mContext;
@Before
@@ -66,4 +74,16 @@ public class BluetoothUtilsTest {
verify(mContext).getDrawable(com.android.internal.R.drawable.ic_bt_laptop);
}
+
+ @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(BluetoothUtils.getBtRainbowDrawableWithDescription(
+ RuntimeEnvironment.application,
+ mCachedBluetoothDevice).first).isInstanceOf(AdaptiveIcon.class);
+ }
}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java
new file mode 100644
index 0000000000000..ed6b9b0a135e3
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java
@@ -0,0 +1,139 @@
+/*
+ * 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.settingslib.widget;
+
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_HINT;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Icon;
+import android.graphics.drawable.ShapeDrawable;
+import android.os.Bundle;
+
+import com.android.settingslib.R;
+import com.android.settingslib.drawer.CategoryKey;
+import com.android.settingslib.drawer.Tile;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class AdaptiveIconTest {
+
+ private Context mContext;
+ private ActivityInfo mActivityInfo;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mActivityInfo = new ActivityInfo();
+ mActivityInfo.packageName = mContext.getPackageName();
+ mActivityInfo.name = "class";
+ mActivityInfo.metaData = new Bundle();
+ }
+
+ @Test
+ public void createIcon_shouldSetBackgroundAndInset() {
+ final AdaptiveIcon icon =
+ new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
+
+ assertThat(icon.getNumberOfLayers()).isEqualTo(2);
+ assertThat(icon.getDrawable(0)).isInstanceOf(AdaptiveIconShapeDrawable.class);
+ }
+
+ @Test
+ public void setBackgroundColor_shouldUpdateColorFilter() {
+ final AdaptiveIcon icon =
+ spy(new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK)));
+ final ShapeDrawable background = mock(ShapeDrawable.class);
+ when(icon.getDrawable(0)).thenReturn(background);
+
+ icon.setBackgroundColor(Color.BLUE);
+
+ verify(background).setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_ATOP);
+ }
+
+ @Test
+ public void setBackgroundColor_externalTileWithBackgroundColorRawValue_shouldUpdateIcon() {
+ final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
+ mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB, 0xff0000);
+ doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update))
+ .when(tile).getIcon(mContext);
+ final AdaptiveIcon icon =
+ new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
+
+ icon.setBackgroundColor(mContext, tile);
+ assertThat(icon.mBackgroundColor).isEqualTo(0xff0000);
+ }
+
+ @Test
+ public void setBackgroundColor_tileWithoutBackgroundColor_shouldSetDefaultBackgroundColor() {
+ final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
+ doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update))
+ .when(tile).getIcon(mContext);
+ final AdaptiveIcon icon = new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
+
+ icon.setBackgroundColor(mContext, tile);
+
+ assertThat(icon.mBackgroundColor)
+ .isEqualTo(mContext.getColor(R.color.homepage_generic_icon_background));
+ }
+
+ @Test
+ public void onBindTile_externalTileWithBackgroundColorHint_shouldUpdateIcon() {
+ final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
+ mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_HINT,
+ R.color.bt_outline_color);
+ doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update))
+ .when(tile).getIcon(mContext);
+
+ final AdaptiveIcon icon =
+ new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
+ icon.setBackgroundColor(mContext, tile);
+
+ assertThat(icon.mBackgroundColor)
+ .isEqualTo(mContext.getColor(R.color.bt_outline_color));
+ }
+
+ @Test
+ public void getConstantState_returnCorrectState() {
+ final AdaptiveIcon icon =
+ new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
+ icon.setBackgroundColor(Color.YELLOW);
+
+ final AdaptiveIcon.AdaptiveConstantState state =
+ (AdaptiveIcon.AdaptiveConstantState) icon.getConstantState();
+
+ assertThat(state.mColor).isEqualTo(Color.YELLOW);
+ assertThat(state.mContext).isEqualTo(mContext);
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveOutlineDrawableTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveOutlineDrawableTest.java
new file mode 100644
index 0000000000000..71d55bc3de2ab
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/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.settingslib.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.res.Resources;
+import android.graphics.Paint;
+
+import com.android.settingslib.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));
+ }
+
+}