Merge changes from topics "apply_fast_pair_icon", "bt_color_icon"

* changes:
  According Bluetooth device type to get icon
  Add method to get rainbow bt icon
  Separate library from SettingsLib
This commit is contained in:
TreeHugger Robot
2019-03-31 07:03:03 +00:00
committed by Android (Google) Code Review
25 changed files with 811 additions and 28 deletions

View File

@@ -0,0 +1,13 @@
android_library {
name: "SettingsLibAdaptiveIcon",
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
static_libs: [
"androidx.annotation_annotation",
"SettingsLibTile"
],
min_sdk_version: "21",
}

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.settingslib.widget">
<uses-sdk android:minSdkVersion="21" />
</manifest>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<resources>
<color name="homepage_generic_icon_background">#1A73E8</color>
<color name="bt_outline_color">#1f000000</color> <!-- icon outline color -->
</resources>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<resources>
<!-- Dashboard foreground image inset (from background edge to foreground edge) -->
<dimen name="dashboard_tile_foreground_image_inset">6dp</dimen>
<!-- Stroke size of adaptive outline -->
<dimen name="adaptive_outline_stroke">1dp</dimen>
</resources>

View File

@@ -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;
}
}
}

View File

@@ -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));
}
}

View File

@@ -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;
}
}

View File

@@ -22,6 +22,7 @@ android_library {
"SettingsLibEntityHeaderWidgets",
"SettingsLibBarChartPreference",
"SettingsLibProgressBar",
"SettingsLibAdaptiveIcon",
],
// ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES

View File

@@ -0,0 +1,11 @@
android_library {
name: "SettingsLibTile",
srcs: ["src/**/*.java"],
static_libs: [
"androidx.annotation_annotation",
],
min_sdk_version: "21",
}

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.settingslib.drawer">
<uses-sdk android:minSdkVersion="21" />
</manifest>

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportWidth="24"
android:viewportHeight="24"
android:width="24dp"
android:height="24dp"
android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="#00000000"
android:fillAlpha=".1"
android:pathData="M0 0h24v24H0z" />
<path
android:fillColor="#00000000"
android:pathData="M0 0h24v24H0z" />
<path
android:fillColor="#000000"
android:pathData="M21 3H3c-1.1 0-2 0.9-2 2v3h2V5h18v14h-7v2h7c1.1 0 2 -0.9 2-2V5c0-1.1 -0.9-2-2-2zM1 18v3h3c0-1.66-1.34-3-3-3zm0-4v2c2.76 0 5 2.24 5 5h2c0-3.87-3.13-7-7-7zm0-4v2c4.97 0 9 4.03 9 9h2c0-6.08-4.93-11-11-11z" />
</vector>

View File

@@ -603,4 +603,26 @@
<item>3</item><item>3</item>
</array>
<!-- Bluetooth icon foreground colors -->
<integer-array name="bt_icon_fg_colors">
<item>@color/bt_color_icon_1</item>
<item>@color/bt_color_icon_2</item>
<item>@color/bt_color_icon_3</item>
<item>@color/bt_color_icon_4</item>
<item>@color/bt_color_icon_5</item>
<item>@color/bt_color_icon_6</item>
<item>@color/bt_color_icon_7</item>
</integer-array>
<!-- Bluetooth icon background colors -->
<integer-array name="bt_icon_bg_colors">
<item>@color/bt_color_bg_1</item>
<item>@color/bt_color_bg_2</item>
<item>@color/bt_color_bg_3</item>
<item>@color/bt_color_bg_4</item>
<item>@color/bt_color_bg_5</item>
<item>@color/bt_color_bg_6</item>
<item>@color/bt_color_bg_7</item>
</integer-array>
</resources>

View File

@@ -19,4 +19,20 @@
<color name="usage_graph_dots">@*android:color/tertiary_device_default_settings</color>
<color name="list_divider_color">#64000000</color>
<color name="bt_color_icon_1">#48a50e0e</color> <!-- 72% Material Red 900 -->
<color name="bt_color_icon_2">#480d652d</color> <!-- 72% Material Green 900 -->
<color name="bt_color_icon_3">#48e37400</color> <!-- 72% Material Yellow 900 -->
<color name="bt_color_icon_4">#48b06000</color> <!-- 72% Material Orange 900 -->
<color name="bt_color_icon_5">#489c166b</color> <!-- 72% Material Pink 900 -->
<color name="bt_color_icon_6">#48681da8</color> <!-- 72% Material Purple 900 -->
<color name="bt_color_icon_7">#48007b83</color> <!-- 72% Material Cyan 900 -->
<color name="bt_color_bg_1">#fad2cf</color> <!-- Material Red 100 -->
<color name="bt_color_bg_2">#ceead6</color> <!-- Material Green 100 -->
<color name="bt_color_bg_3">#feefc3</color> <!-- Material Yellow 100 -->
<color name="bt_color_bg_4">#fedfc8</color> <!-- Material Orange 100 -->
<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 -->
</resources>

View File

@@ -91,6 +91,7 @@
<!-- How far to inset the rounded edges -->
<dimen name="stat_sys_mobile_signal_circle_inset">0.9dp</dimen>
<!-- Size of nearby icon -->
<dimen name="bt_nearby_icon_size">24dp</dimen>
</resources>

View File

@@ -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<Drawable, String> getBtRainbowDrawableWithDescription(Context context,
CachedBluetoothDevice cachedDevice) {
final Pair<Drawable, String> 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;
}
}

View File

@@ -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<Drawable, String> pair = BluetoothUtils
.getBtRainbowDrawableWithDescription(mContext, mCachedDevice);
return pair.first;
}
@Override

View File

@@ -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

View File

@@ -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<MediaDevice> {
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

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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));
}
}