Commit 56258dbf authored by Tanmoy Mollik's avatar Tanmoy Mollik Committed by Commit Bot

[Android] Add Drawable badge support to ProfileDataCache

Currently ProfileDataCache doesn't support vector graphics. This cl replaces
the use of Bitmap with Drawable in ProfileDataCache.BadgeConfige so that
vector graphics can be provided to it. Also this cl adds a new factory
method in ProfileDataCache to create an instance of this class with a
provided badge resource id.

Bug: 1064957
Change-Id: I23e5417611787f5d3d17f1a2b81c195bcea781ab
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2130246
Commit-Queue: Tanmoy Mollik <triploblastic@chromium.org>
Reviewed-by: default avatarBoris Sazonov <bsazonov@chromium.org>
Cr-Commit-Position: refs/heads/master@{#758738}
parent 51f11043
......@@ -451,6 +451,7 @@ chrome_test_java_sources = [
"javatests/src/org/chromium/chrome/browser/signin/DummyAccountPickerTargetFragment.java",
"javatests/src/org/chromium/chrome/browser/signin/IdentityManagerIntegrationTest.java",
"javatests/src/org/chromium/chrome/browser/signin/ProfileDataCacheRenderTest.java",
"javatests/src/org/chromium/chrome/browser/signin/ProfileDataCacheWithBadgeRenderTest.java",
"javatests/src/org/chromium/chrome/browser/signin/SignOutDialogRenderTest.java",
"javatests/src/org/chromium/chrome/browser/signin/SigninFragmentTest.java",
"javatests/src/org/chromium/chrome/browser/signin/SigninHelperTest.java",
......
......@@ -157,6 +157,7 @@
<dimen name="badge_position_x">26dp</dimen>
<dimen name="badge_position_y">20dp</dimen>
<dimen name="badge_border_size">1.3dp</dimen>
<dimen name="badge_size">20dp</dimen>
<!-- First Run Experience dimensions -->
<dimen name="fre_content_margin">24dp</dimen>
......
......@@ -19,6 +19,7 @@ import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import androidx.annotation.DrawableRes;
import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
......@@ -58,30 +59,36 @@ public class ProfileDataCache implements ProfileDownloader.Observer, ProfileData
* a user avatar.
*/
public static class BadgeConfig {
private final Bitmap mBadge;
private final Drawable mBadge;
private final int mBadgeSize;
private final Point mPosition;
private final int mBorderSize;
/**
* @param badge Square badge bitmap to overlay on user avatar. Will be cropped to a circular
* one while overlaying.
* @param badge Square badge drawable to overlay on user avatar. Will be cropped to a
* circular one while overlaying.
* @param badgeSize Size of a side the square badge
* @param position Position of top left corner of a badge relative to top left corner of
* avatar.
* @param borderSize Size of a transparent border around badge.
*/
public BadgeConfig(Bitmap badge, Point position, int borderSize) {
assert badge.getHeight() == badge.getWidth();
public BadgeConfig(Drawable badge, int badgeSize, Point position, int borderSize) {
assert position != null;
mBadge = badge;
mBadgeSize = badgeSize;
mPosition = position;
mBorderSize = borderSize;
}
Bitmap getBadge() {
Drawable getBadge() {
return mBadge;
}
int getBadgeSize() {
return mBadgeSize;
}
Point getPosition() {
return mPosition;
}
......@@ -245,6 +252,40 @@ public class ProfileDataCache implements ProfileDownloader.Observer, ProfileData
return new BitmapDrawable(resources, output);
}
/**
* Returns a profile data cache object with the badgeResId provided. The badge is put with
* respect to R.dimen.user_picture_size. So this method only works with the user avatar of this
* size.
* @param context Context of the application to extract resources from
* @param badgeResId Resource id of the badge to be attached. If it is 0 then no badge is
* attached
*/
public static ProfileDataCache createProfileDataCache(
Context context, @DrawableRes int badgeResId) {
return createProfileDataCache(context, badgeResId,
AccountManagerFacadeProvider.getInstance().getProfileDataSource());
}
@VisibleForTesting
static ProfileDataCache createProfileDataCache(
Context context, @DrawableRes int badgeResId, ProfileDataSource profileDataSource) {
Resources resources = context.getResources();
int userPictureSize = resources.getDimensionPixelSize(R.dimen.user_picture_size);
if (badgeResId == 0) {
return new ProfileDataCache(context, userPictureSize, null, profileDataSource);
}
Drawable badge = AppCompatResources.getDrawable(context, badgeResId);
int badgePositionX = resources.getDimensionPixelOffset(R.dimen.badge_position_x);
int badgePositionY = resources.getDimensionPixelOffset(R.dimen.badge_position_y);
int badgeBorderSize = resources.getDimensionPixelSize(R.dimen.badge_border_size);
int badgeSize = resources.getDimensionPixelSize(R.dimen.badge_size);
return new ProfileDataCache(context, userPictureSize,
new BadgeConfig(badge, badgeSize, new Point(badgePositionX, badgePositionY),
badgeBorderSize),
profileDataSource);
}
private Drawable prepareAvatar(Bitmap bitmap) {
Drawable croppedAvatar = bitmap != null
? makeRoundAvatar(mContext.getResources(), bitmap, mImageSize)
......@@ -256,7 +297,7 @@ public class ProfileDataCache implements ProfileDownloader.Observer, ProfileData
}
private Drawable overlayBadgeOnUserPicture(Drawable userPicture) {
int badgeSize = mBadgeConfig.getBadge().getHeight();
int badgeSize = mBadgeConfig.getBadgeSize();
int badgedPictureWidth = Math.max(mBadgeConfig.getPosition().x + badgeSize, mImageSize);
int badgedPictureHeight = Math.max(mBadgeConfig.getPosition().y + badgeSize, mImageSize);
Bitmap badgedPicture = Bitmap.createBitmap(
......@@ -277,8 +318,10 @@ public class ProfileDataCache implements ProfileDownloader.Observer, ProfileData
badgeCenterX, badgeCenterY, badgeRadius + mBadgeConfig.getBorderSize(), paint);
// Draw the badge
canvas.drawBitmap(mBadgeConfig.getBadge(), mBadgeConfig.getPosition().x,
mBadgeConfig.getPosition().y, null);
Drawable badge = mBadgeConfig.getBadge();
badge.setBounds(mBadgeConfig.getPosition().x, mBadgeConfig.getPosition().y,
mBadgeConfig.getPosition().x + badgeSize, mBadgeConfig.getPosition().y + badgeSize);
badge.draw(canvas);
return new BitmapDrawable(mContext.getResources(), badgedPicture);
}
......
......@@ -7,9 +7,6 @@ package org.chromium.chrome.browser.signin;
import android.accounts.AccountManager;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.SystemClock;
......@@ -221,18 +218,9 @@ public abstract class SigninFragmentBase
mConsentTextTracker = new ConsentTextTracker(getResources());
ProfileDataCache.BadgeConfig badgeConfig = null;
if (ChildAccountStatus.isChild(mChildAccountStatus)) {
Bitmap badge =
BitmapFactory.decodeResource(getResources(), R.drawable.ic_account_child_20dp);
int badgePositionX = getResources().getDimensionPixelOffset(R.dimen.badge_position_x);
int badgePositionY = getResources().getDimensionPixelOffset(R.dimen.badge_position_y);
int badgeBorderSize = getResources().getDimensionPixelSize(R.dimen.badge_border_size);
badgeConfig = new ProfileDataCache.BadgeConfig(
badge, new Point(badgePositionX, badgePositionY), badgeBorderSize);
}
mProfileDataCache = new ProfileDataCache(getActivity(),
getResources().getDimensionPixelSize(R.dimen.user_picture_size), badgeConfig);
mProfileDataCache = ProfileDataCache.createProfileDataCache(getActivity(),
ChildAccountStatus.isChild(mChildAccountStatus) ? R.drawable.ic_account_child_20dp
: 0);
// By default this is set to true so that when system back button is pressed user action
// is recorded in onDestroy().
mRecordUndoSignin = true;
......
......@@ -10,9 +10,6 @@ import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.os.Build;
......@@ -112,18 +109,8 @@ public class AccountManagementFragment extends PreferenceFragmentCompat
SigninUtils.logEvent(ProfileAccountManagementMetrics.VIEW, mGaiaServiceType);
int avatarImageSize = getResources().getDimensionPixelSize(R.dimen.user_picture_size);
ProfileDataCache.BadgeConfig badgeConfig = null;
if (mProfile.isChild()) {
Bitmap badge =
BitmapFactory.decodeResource(getResources(), R.drawable.ic_account_child_20dp);
int badgePositionX = getResources().getDimensionPixelOffset(R.dimen.badge_position_x);
int badgePositionY = getResources().getDimensionPixelOffset(R.dimen.badge_position_y);
int badgeBorderSize = getResources().getDimensionPixelSize(R.dimen.badge_border_size);
badgeConfig = new ProfileDataCache.BadgeConfig(
badge, new Point(badgePositionX, badgePositionY), badgeBorderSize);
}
mProfileDataCache = new ProfileDataCache(getActivity(), avatarImageSize, badgeConfig);
mProfileDataCache = ProfileDataCache.createProfileDataCache(
getActivity(), mProfile.isChild() ? R.drawable.ic_account_child_20dp : 0);
}
@Override
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.signin;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.test.filters.MediumTest;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import androidx.annotation.DrawableRes;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.test.util.Feature;
import org.chromium.chrome.R;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.util.ChromeRenderTestRule;
import org.chromium.components.signin.ProfileDataSource;
import org.chromium.components.signin.test.util.FakeProfileDataSource;
import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.ui.test.util.DummyUiActivityTestCase;
import org.chromium.ui.widget.ChromeImageView;
import java.io.IOException;
/**
* Tests for ProfileDataCache with a badge. Leverages RenderTest instead of reimplementing
* bitmap comparison to simplify access to the compared images on buildbots (via result_details).
*/
@RunWith(ChromeJUnit4ClassRunner.class)
public class ProfileDataCacheWithBadgeRenderTest extends DummyUiActivityTestCase {
@Rule
public ChromeRenderTestRule mRenderTestRule = new ChromeRenderTestRule();
private FrameLayout mContentView;
private ImageView mImageView;
private FakeProfileDataSource mProfileDataSource;
private ProfileDataCache mProfileDataCache;
@Override
public void setUpTest() throws Exception {
super.setUpTest();
TestThreadUtils.runOnUiThreadBlocking(() -> {
Activity activity = getActivity();
mContentView = new FrameLayout(activity);
mImageView = new ChromeImageView(activity);
mContentView.addView(mImageView, ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
activity.setContentView(mContentView);
mProfileDataSource = new FakeProfileDataSource();
});
}
@Test
@MediumTest
@Feature("RenderTest")
public void testProfileDataCacheWithChildBadge() throws IOException {
setUpProfileDataCache(R.drawable.ic_account_child_20dp);
mRenderTestRule.render(mImageView, "profile_data_cache_with_child_badge");
}
private void setUpProfileDataCache(@DrawableRes int badgeResId) {
TestThreadUtils.runOnUiThreadBlocking(() -> {
mProfileDataCache = ProfileDataCache.createProfileDataCache(
getActivity(), badgeResId, mProfileDataSource);
// ProfileDataCache only populates the cache when an observer is added.
mProfileDataCache.addObserver(accountId -> {});
String accountName = "test@example.com";
ProfileDataSource.ProfileData profileData = new ProfileDataSource.ProfileData(
accountName, createAvatar(), "Full Name", "Given Name");
mProfileDataSource.setProfileData(accountName, profileData);
mImageView.setImageDrawable(
mProfileDataCache.getProfileDataOrDefault(accountName).getImage());
});
}
/**
* Creates a simple dummy bitmap to use as the avatar picture.
*/
private Bitmap createAvatar() {
final int avatarSize =
getActivity().getResources().getDimensionPixelSize(R.dimen.user_picture_size);
Assert.assertTrue("avatarSize must not be 0", avatarSize > 0);
Bitmap result = Bitmap.createBitmap(avatarSize, avatarSize, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
canvas.drawColor(Color.RED);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.BLUE);
canvas.drawCircle(0, 0, avatarSize, paint);
return result;
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment