-
Notifications
You must be signed in to change notification settings - Fork 553
Show avatar with username initials. #372
Show avatar with username initials. #372
Conversation
Since it is a helper, we don't need to instantiate the RocketChatUserAvatar to each use. This commit adds methods to get a drawable with username initials.
|
||
return TextDrawable.builder() | ||
.beginConfig() | ||
.useFont(Typeface.SANS_SERIF) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will it use the default one from the OS?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will be rendered as expected on Application, since the Typeface
was added by the Android platform since API level 1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
0xFFF44336.toInt(), 0xFFE91E63.toInt(), 0xFF9C27B0.toInt(), 0xFF673AB7.toInt(), 0xFF3F51B5.toInt(), | ||
0xFF2196F3.toInt(), 0xFF03A9F4.toInt(), 0xFF00BCD4.toInt(), 0xFF009688.toInt(), 0xFF4CAF50.toInt(), | ||
0xFF8BC34A.toInt(), 0xFFCDDC39.toInt(), 0xFFFFC107.toInt(), 0xFFFF9800.toInt(), 0xFFFF5722.toInt(), | ||
0xFF795548.toInt(), 0xFF9E9E9E.toInt(), 0xFF607D8B.toInt()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about creating unit tests for this class? You could test a few example names with a few different usernames. On iOS I tested a bunch of different cases: https://github.com/RocketChat/Rocket.Chat.iOS/blob/develop/Rocket.ChatTests/Views/AvatarViewSpec.swift.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Of course! I'll do unit tests! Thank you guy for putting those thoughts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
} | ||
} | ||
}); | ||
return contentType; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This most likely will be null, since you are doing an Asynchronous request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To get the contentType
we need to do a network call - blocking operation, so we need to do an async task, right? There are the following possibilities:
- Get the
contentType
using theHttpURLConnection
with theAsyncTask
. - Get the
contentType
using theHttpURLConnection
withRxJava
. - Get the
contentType
using theOkHttp
which is currently used on RC app -OkHttp
is a built-in response cache, a better cookie model, a better headers model and a better call model.
So, how we should handle this requirement?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should not do this, if we really need to get the Content-Type, we will need to implement our own Image loading library, like Fresco or Picasso, to download, cache and display it...
Doing a lot of HEAD
s here will make a lot of useless requests on fling, we are not caching the result...
Ideally this should be handled on the Image library, on Fresco, or switch to some that already supports SVG.
* @see getUri | ||
*/ | ||
fun getImageFormat(uri: String): String { | ||
return OkHttpHelper.getContentType(uri) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should have this information from the backend, each time we show an Avatar doing a HEAD (network operation) operation and a GET seems very expensive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I pay attention w/ it, so to get the image format the requested method is HEAD
and not GET
. Take a look.
https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html:
The HEAD method is identical to GET except that the server MUST NOT return a message-body in the response. The metainformation contained in the HTTP headers in response to a HEAD request SHOULD be identical to the information sent in response to a GET request. This method can be used for obtaining metainformation about the entity implied by the request without transferring the entity-body itself. This method is often used for testing hypertext links for validity, accessibility, and recent modification.
private static OkHttpClient httpClientForUploadFile; | ||
private static OkHttpClient httpClientForDownloadFile; | ||
private static OkHttpClient httpClientForWS; | ||
private static String contentType; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will not work as expected...
First time you call getContentType()
it will probably return null
, since it is a async request.
Next time you call it, you will probably get the contentType from the previous request, which may be right or not.
On a list flinging, there will be dozens of requests overwriting contentType
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First time you call getContentType() it will probably return null, since it is a async request.
See the comment below.
On a list flinging, there will be dozens of requests overwriting contentType
I agree w/ you. There is a way to avoid it, but it involves involves a disabled support. See this comment.
* @param username The username. | ||
* @return A string with username initials. | ||
*/ | ||
fun getUsernameInitials(username: String): String { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function doesn't conform with the documentation, it handles only separation by "." and not by spaces
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@luciofm What did you mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the javadoc (for kotlin 😄) says one thing and the function does another..
@@ -14,6 +14,7 @@ | |||
public abstract class AbstractMessageViewHolder extends ModelViewHolder<PairedMessage> { | |||
protected final RocketChatAvatar avatar; | |||
protected final ImageView userNotFoundAvatarImageView; | |||
protected final ImageView imageViewSvgUserAvatar; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to use a separate ImageView? This should probably be handled on RocketChatAvatar.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need, because RocketChatAvatar
is a class which returns a SimpleDraweeView. There is a setImageDrawable()
method on SimpleDraweeView but it is deprecated (trying to use it you'll get the messed avatars). Also, from Fresco:
Don’t set images directly on a DraweeView
Currently DraweeView is a subclass of Android’s ImageView. This has various methods to set an image (such as setImageBitmap, setImageDrawable)
If you set an image directly, you will completely lose your DraweeHierarchy, and will not get any results from the image pipeline.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My suggestion is to work with Fresco, and implement support for SVG directly on it...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean, adding SVG support for Fresco
userNotFoundAvatarImageView.visibility = View.GONE | ||
rocketChatAvatarWidget.visibility = View.VISIBLE | ||
val userAvatarUri = RocketChatUserAvatar.getUri(hostname, username) | ||
val userAvatarImageContentType = OkHttpHelper.getContentType(userAvatarUri) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As commented before, this will not work consistently.
@filipedelimabrito Why don't you implement it the way iOS does? Here's the structure of the AvatarView, basically:
This way, while the image is loading the imageView is transparent and the ViewInitials is the one that's being present to the user. If ImageView is loaded/cached, it will show the ImageView instead of ViewInitials. |
That's I said before to you @rafaelks. We can set a placeholder image w/ username initials and if the image from backend is loaded successful then we show it. Note that the images w/ username initials are loaded from the backend, but it isn't build correctly - until now (see RocketChat/Rocket.Chat#7572). As the chatroom background is white we can't see the username initials which are white too (the background isn't load because the SVG is build in a wrong way). We are supporting the SVG rendering on RC app (I added it on some PR times ago). We simply can disable the SVG support to set the avatar w/ username initials and Fresco will do the rest (loading images that are not in SVG format, and setting the placeholder w/ username initials when the images are in SVG format - or, when the loading from server fails). See this comment also, can be useful. |
…mabrito/Rocket.Chat.Android into avatar-with-username-initials
@@ -26,7 +31,7 @@ public static void initialize(Context context, OkHttpClient okHttpClient) { | |||
ImagePipelineConfig imagePipelineConfig = OkHttpImagePipelineConfigFactory | |||
.newBuilder(context, okHttpClient) | |||
.setRequestListeners(listeners) | |||
.setImageDecoderConfig(CustomImageFormatConfigurator.createImageDecoderConfig()) | |||
// .setImageDecoderConfig(CustomImageFormatConfigurator.createImageDecoderConfig()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we remove this commented code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we need to plan to support SVG image in the future I think that we could leave this commented code. If not, we can remove it (and the CustomImageFormatConfigurator and SvgDecoder class too).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have github... We can always look the code history to recover this...
@@ -16,6 +20,12 @@ | |||
private final RocketChatAvatar avatar; | |||
private final ImageView status; | |||
|
|||
private static final int[] COLORS = new int[] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you repeating the colors here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, for the same reason bellow.
:(
.buildRoundRect(getUsernameInitials(username), getUserAvatarBackgroundColor(username), round); | ||
} | ||
|
||
private String getUsernameInitials(String username) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method seems to be duplicated...?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes :(
I just put it as before (w/ some the correct modifications). This class belongs to another module, there is the possibility to add the :app
dependency to this module to avoid that duplication, but I really don't know if this is the correct (in the terms of design, architecture...). So I just did like before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually think the widgets should live on the app module... unless they are meant to be reusable, them they should live on another repository...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just small change in your method docs.
} | ||
|
||
/** | ||
* Returns a string with the username initials. For example: username John Doe returns JD initials. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you should change this docs to something that will work. In your method John Doe
will not become JD
.
@@ -0,0 +1,22 @@ | |||
import chat.rocket.android.widget.helper.UserAvatarHelper |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tests should live in the same package (and directory structure) as the class you are testing...
xmlns:fresco="http://schemas.android.com/apk/res-auto" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Format it with 4 spaces indentation, please.
@@ -58,7 +58,7 @@ object UserAvatarHelper { | |||
} | |||
|
|||
/** | |||
* Returns a string with the username initials. For example: username John Doe returns JD initials. | |||
* Returns a string with the username initials. For example: username John.Doe returns JD initials. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
LGTM, what about you @luciofm? |
@@ -48,4 +63,28 @@ public static OkHttpClient getClientForWebSocket() { | |||
} | |||
return httpClientForWS; | |||
} | |||
|
|||
public static String getContentType(String uri) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you remove this method that is not used anymore.
@RocketChat/android
Closes #369