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 = [ ...@@ -451,6 +451,7 @@ chrome_test_java_sources = [
"javatests/src/org/chromium/chrome/browser/signin/DummyAccountPickerTargetFragment.java", "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/IdentityManagerIntegrationTest.java",
"javatests/src/org/chromium/chrome/browser/signin/ProfileDataCacheRenderTest.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/SignOutDialogRenderTest.java",
"javatests/src/org/chromium/chrome/browser/signin/SigninFragmentTest.java", "javatests/src/org/chromium/chrome/browser/signin/SigninFragmentTest.java",
"javatests/src/org/chromium/chrome/browser/signin/SigninHelperTest.java", "javatests/src/org/chromium/chrome/browser/signin/SigninHelperTest.java",
......
...@@ -157,6 +157,7 @@ ...@@ -157,6 +157,7 @@
<dimen name="badge_position_x">26dp</dimen> <dimen name="badge_position_x">26dp</dimen>
<dimen name="badge_position_y">20dp</dimen> <dimen name="badge_position_y">20dp</dimen>
<dimen name="badge_border_size">1.3dp</dimen> <dimen name="badge_border_size">1.3dp</dimen>
<dimen name="badge_size">20dp</dimen>
<!-- First Run Experience dimensions --> <!-- First Run Experience dimensions -->
<dimen name="fre_content_margin">24dp</dimen> <dimen name="fre_content_margin">24dp</dimen>
......
...@@ -19,6 +19,7 @@ import android.graphics.Rect; ...@@ -19,6 +19,7 @@ import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import androidx.annotation.DrawableRes;
import androidx.annotation.MainThread; import androidx.annotation.MainThread;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.Px; import androidx.annotation.Px;
...@@ -58,30 +59,36 @@ public class ProfileDataCache implements ProfileDownloader.Observer, ProfileData ...@@ -58,30 +59,36 @@ public class ProfileDataCache implements ProfileDownloader.Observer, ProfileData
* a user avatar. * a user avatar.
*/ */
public static class BadgeConfig { public static class BadgeConfig {
private final Bitmap mBadge; private final Drawable mBadge;
private final int mBadgeSize;
private final Point mPosition; private final Point mPosition;
private final int mBorderSize; private final int mBorderSize;
/** /**
* @param badge Square badge bitmap to overlay on user avatar. Will be cropped to a circular * @param badge Square badge drawable to overlay on user avatar. Will be cropped to a
* one while overlaying. * 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 * @param position Position of top left corner of a badge relative to top left corner of
* avatar. * avatar.
* @param borderSize Size of a transparent border around badge. * @param borderSize Size of a transparent border around badge.
*/ */
public BadgeConfig(Bitmap badge, Point position, int borderSize) { public BadgeConfig(Drawable badge, int badgeSize, Point position, int borderSize) {
assert badge.getHeight() == badge.getWidth();
assert position != null; assert position != null;
mBadge = badge; mBadge = badge;
mBadgeSize = badgeSize;
mPosition = position; mPosition = position;
mBorderSize = borderSize; mBorderSize = borderSize;
} }
Bitmap getBadge() { Drawable getBadge() {
return mBadge; return mBadge;
} }
int getBadgeSize() {
return mBadgeSize;
}
Point getPosition() { Point getPosition() {
return mPosition; return mPosition;
} }
...@@ -245,6 +252,40 @@ public class ProfileDataCache implements ProfileDownloader.Observer, ProfileData ...@@ -245,6 +252,40 @@ public class ProfileDataCache implements ProfileDownloader.Observer, ProfileData
return new BitmapDrawable(resources, output); 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) { private Drawable prepareAvatar(Bitmap bitmap) {
Drawable croppedAvatar = bitmap != null Drawable croppedAvatar = bitmap != null
? makeRoundAvatar(mContext.getResources(), bitmap, mImageSize) ? makeRoundAvatar(mContext.getResources(), bitmap, mImageSize)
...@@ -256,7 +297,7 @@ public class ProfileDataCache implements ProfileDownloader.Observer, ProfileData ...@@ -256,7 +297,7 @@ public class ProfileDataCache implements ProfileDownloader.Observer, ProfileData
} }
private Drawable overlayBadgeOnUserPicture(Drawable userPicture) { private Drawable overlayBadgeOnUserPicture(Drawable userPicture) {
int badgeSize = mBadgeConfig.getBadge().getHeight(); int badgeSize = mBadgeConfig.getBadgeSize();
int badgedPictureWidth = Math.max(mBadgeConfig.getPosition().x + badgeSize, mImageSize); int badgedPictureWidth = Math.max(mBadgeConfig.getPosition().x + badgeSize, mImageSize);
int badgedPictureHeight = Math.max(mBadgeConfig.getPosition().y + badgeSize, mImageSize); int badgedPictureHeight = Math.max(mBadgeConfig.getPosition().y + badgeSize, mImageSize);
Bitmap badgedPicture = Bitmap.createBitmap( Bitmap badgedPicture = Bitmap.createBitmap(
...@@ -277,8 +318,10 @@ public class ProfileDataCache implements ProfileDownloader.Observer, ProfileData ...@@ -277,8 +318,10 @@ public class ProfileDataCache implements ProfileDownloader.Observer, ProfileData
badgeCenterX, badgeCenterY, badgeRadius + mBadgeConfig.getBorderSize(), paint); badgeCenterX, badgeCenterY, badgeRadius + mBadgeConfig.getBorderSize(), paint);
// Draw the badge // Draw the badge
canvas.drawBitmap(mBadgeConfig.getBadge(), mBadgeConfig.getPosition().x, Drawable badge = mBadgeConfig.getBadge();
mBadgeConfig.getPosition().y, null); 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); return new BitmapDrawable(mContext.getResources(), badgedPicture);
} }
......
...@@ -7,9 +7,6 @@ package org.chromium.chrome.browser.signin; ...@@ -7,9 +7,6 @@ package org.chromium.chrome.browser.signin;
import android.accounts.AccountManager; import android.accounts.AccountManager;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.os.SystemClock; import android.os.SystemClock;
...@@ -221,18 +218,9 @@ public abstract class SigninFragmentBase ...@@ -221,18 +218,9 @@ public abstract class SigninFragmentBase
mConsentTextTracker = new ConsentTextTracker(getResources()); mConsentTextTracker = new ConsentTextTracker(getResources());
ProfileDataCache.BadgeConfig badgeConfig = null; mProfileDataCache = ProfileDataCache.createProfileDataCache(getActivity(),
if (ChildAccountStatus.isChild(mChildAccountStatus)) { ChildAccountStatus.isChild(mChildAccountStatus) ? R.drawable.ic_account_child_20dp
Bitmap badge = : 0);
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);
// By default this is set to true so that when system back button is pressed user action // By default this is set to true so that when system back button is pressed user action
// is recorded in onDestroy(). // is recorded in onDestroy().
mRecordUndoSignin = true; mRecordUndoSignin = true;
......
...@@ -10,9 +10,6 @@ import android.app.Dialog; ...@@ -10,9 +10,6 @@ import android.app.Dialog;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Build; import android.os.Build;
...@@ -112,18 +109,8 @@ public class AccountManagementFragment extends PreferenceFragmentCompat ...@@ -112,18 +109,8 @@ public class AccountManagementFragment extends PreferenceFragmentCompat
SigninUtils.logEvent(ProfileAccountManagementMetrics.VIEW, mGaiaServiceType); SigninUtils.logEvent(ProfileAccountManagementMetrics.VIEW, mGaiaServiceType);
int avatarImageSize = getResources().getDimensionPixelSize(R.dimen.user_picture_size); mProfileDataCache = ProfileDataCache.createProfileDataCache(
ProfileDataCache.BadgeConfig badgeConfig = null; getActivity(), mProfile.isChild() ? R.drawable.ic_account_child_20dp : 0);
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);
} }
@Override @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