SlideShare a Scribd company logo
1 of 30
Download to read offline
Creating a WhatsApp Clone - Part III
The next step is the other classes within the model package. The rest is relatively trivial after the Server class.
package com.codename1.whatsapp.model;
import java.util.List;
public interface ServerMessages {
public void connected();
public void disconnected();
public void messageReceived(ChatMessage m);
public void userTyping(String contactId);
public void messageViewed(String messageId, List<String> userIds);
}
ServerMessages
This is the callback interface we used within the Server class, it’s pretty trivial. I added some methods for future enhancement too. 

The first two methods inform the observer that the server is connected or disconnected.
package com.codename1.whatsapp.model;
import java.util.List;
public interface ServerMessages {
public void connected();
public void disconnected();
public void messageReceived(ChatMessage m);
public void userTyping(String contactId);
public void messageViewed(String messageId, List<String> userIds);
}
ServerMessages
Message received is invoked to update the UI based on the new incoming message
package com.codename1.whatsapp.model;
import java.util.List;
public interface ServerMessages {
public void connected();
public void disconnected();
public void messageReceived(ChatMessage m);
public void userTyping(String contactId);
public void messageViewed(String messageId, List<String> userIds);
}
ServerMessages
The last two callbacks aren’t really implemented but they allow us to update the UI if a user is typing in a chat. The message viewed event similarly indicates if a user
viewed the message. This can provide an indicator in the UI that the message was seen.
public class ChatContact implements PropertyBusinessObject {
public final Property<String, ChatContact> id = new Property<>("id");
public final Property<String, ChatContact> localId =
new Property<>("localId");
public final Property<String, ChatContact> phone =
new Property<>("phone");
public final Property<Image, ChatContact> photo =
new Property<>("photo", Image.class);
public final Property<String, ChatContact> name =
new Property<>("name");
public final Property<String, ChatContact> tagline =
new Property<>("tagline");
public final Property<String, ChatContact> token =
new Property<>("token");
public final SetProperty<String, ChatContact> members =
new SetProperty<>("members", String.class);
public final SetProperty<String, ChatContact> admins =
new SetProperty<>("admins", String.class);
public final LongProperty<ChatContact> muteUntil =
new LongProperty<>("muteUntil");
ChatContact
ChatContact is a PropertyBusinessObject that stores the content of a specific contact entry.
public class ChatContact implements PropertyBusinessObject {
public final Property<String, ChatContact> id = new Property<>("id");
public final Property<String, ChatContact> localId =
new Property<>("localId");
public final Property<String, ChatContact> phone =
new Property<>("phone");
public final Property<Image, ChatContact> photo =
new Property<>("photo", Image.class);
public final Property<String, ChatContact> name =
new Property<>("name");
public final Property<String, ChatContact> tagline =
new Property<>("tagline");
public final Property<String, ChatContact> token =
new Property<>("token");
public final SetProperty<String, ChatContact> members =
new SetProperty<>("members", String.class);
public final SetProperty<String, ChatContact> admins =
new SetProperty<>("admins", String.class);
public final LongProperty<ChatContact> muteUntil =
new LongProperty<>("muteUntil");
ChatContact
I chose to use unique id’s instead of using the phone as an id. This was something I was conflicted about. I eventually chose to use an ID which I think is more secure
overall. It would also support the option of changing a phone number in the future or using an email as the unique identifier
public class ChatContact implements PropertyBusinessObject {
public final Property<String, ChatContact> id = new Property<>("id");
public final Property<String, ChatContact> localId =
new Property<>("localId");
public final Property<String, ChatContact> phone =
new Property<>("phone");
public final Property<Image, ChatContact> photo =
new Property<>("photo", Image.class);
public final Property<String, ChatContact> name =
new Property<>("name");
public final Property<String, ChatContact> tagline =
new Property<>("tagline");
public final Property<String, ChatContact> token =
new Property<>("token");
public final SetProperty<String, ChatContact> members =
new SetProperty<>("members", String.class);
public final SetProperty<String, ChatContact> admins =
new SetProperty<>("admins", String.class);
public final LongProperty<ChatContact> muteUntil =
new LongProperty<>("muteUntil");
ChatContact
localId should map to the ID in the contacts. This allows us to refresh the contact details from the device address book in the future.
public class ChatContact implements PropertyBusinessObject {
public final Property<String, ChatContact> id = new Property<>("id");
public final Property<String, ChatContact> localId =
new Property<>("localId");
public final Property<String, ChatContact> phone =
new Property<>("phone");
public final Property<Image, ChatContact> photo =
new Property<>("photo", Image.class);
public final Property<String, ChatContact> name =
new Property<>("name");
public final Property<String, ChatContact> tagline =
new Property<>("tagline");
public final Property<String, ChatContact> token =
new Property<>("token");
public final SetProperty<String, ChatContact> members =
new SetProperty<>("members", String.class);
public final SetProperty<String, ChatContact> admins =
new SetProperty<>("admins", String.class);
public final LongProperty<ChatContact> muteUntil =
new LongProperty<>("muteUntil");
ChatContact
The phone property is pretty obvious
public class ChatContact implements PropertyBusinessObject {
public final Property<String, ChatContact> id = new Property<>("id");
public final Property<String, ChatContact> localId =
new Property<>("localId");
public final Property<String, ChatContact> phone =
new Property<>("phone");
public final Property<Image, ChatContact> photo =
new Property<>("photo", Image.class);
public final Property<String, ChatContact> name =
new Property<>("name");
public final Property<String, ChatContact> tagline =
new Property<>("tagline");
public final Property<String, ChatContact> token =
new Property<>("token");
public final SetProperty<String, ChatContact> members =
new SetProperty<>("members", String.class);
public final SetProperty<String, ChatContact> admins =
new SetProperty<>("admins", String.class);
public final LongProperty<ChatContact> muteUntil =
new LongProperty<>("muteUntil");
ChatContact
The photo property stores the picture of the contact, there is a lot to this property so I’ll discuss it in more detail soon.
public class ChatContact implements PropertyBusinessObject {
public final Property<String, ChatContact> id = new Property<>("id");
public final Property<String, ChatContact> localId =
new Property<>("localId");
public final Property<String, ChatContact> phone =
new Property<>("phone");
public final Property<Image, ChatContact> photo =
new Property<>("photo", Image.class);
public final Property<String, ChatContact> name =
new Property<>("name");
public final Property<String, ChatContact> tagline =
new Property<>("tagline");
public final Property<String, ChatContact> token =
new Property<>("token");
public final SetProperty<String, ChatContact> members =
new SetProperty<>("members", String.class);
public final SetProperty<String, ChatContact> admins =
new SetProperty<>("admins", String.class);
public final LongProperty<ChatContact> muteUntil =
new LongProperty<>("muteUntil");
ChatContact
These are the common attributes for name and tagline used in whatsapp. For simplicity I only used full name and ignored nuances such as first/last/middle etc.
public class ChatContact implements PropertyBusinessObject {
public final Property<String, ChatContact> id = new Property<>("id");
public final Property<String, ChatContact> localId =
new Property<>("localId");
public final Property<String, ChatContact> phone =
new Property<>("phone");
public final Property<Image, ChatContact> photo =
new Property<>("photo", Image.class);
public final Property<String, ChatContact> name =
new Property<>("name");
public final Property<String, ChatContact> tagline =
new Property<>("tagline");
public final Property<String, ChatContact> token =
new Property<>("token");
public final SetProperty<String, ChatContact> members =
new SetProperty<>("members", String.class);
public final SetProperty<String, ChatContact> admins =
new SetProperty<>("admins", String.class);
public final LongProperty<ChatContact> muteUntil =
new LongProperty<>("muteUntil");
ChatContact
The token is effectively our password to use the service. Since there is no login process a token is generated on the server as a key that allows us to use the service.
new Property<>("name");
public final Property<String, ChatContact> tagline =
new Property<>("tagline");
public final Property<String, ChatContact> token =
new Property<>("token");
public final SetProperty<String, ChatContact> members =
new SetProperty<>("members", String.class);
public final SetProperty<String, ChatContact> admins =
new SetProperty<>("admins", String.class);
public final LongProperty<ChatContact> muteUntil =
new LongProperty<>("muteUntil");
public final Property<String, ChatContact> createdBy =
new Property<>("createdBy");
public final Property<Date, ChatContact> creationDate =
new Property<>("creationDate", Date.class);
public final Property<Date, ChatContact> lastActivityTime =
new Property<>("lastActivityTime", Date.class);
public final ListProperty<ChatMessage, ChatContact> chats =
new ListProperty<>("chat", ChatMessage.class);
private final PropertyIndex idx = new PropertyIndex(this, "ChatContact",
id, localId, phone, photo, name, tagline, token, members, admins,
muteUntil, createdBy, creationDate, lastActivityTime, chats);
ChatContact
A chat contact can also serve as a group. I didn’t fully implement this logic but it’s wired almost everywhere. In this case we have two sets for members of the group and
the admins of the group. These sets would be empty for a typical user.
new Property<>("name");
public final Property<String, ChatContact> tagline =
new Property<>("tagline");
public final Property<String, ChatContact> token =
new Property<>("token");
public final SetProperty<String, ChatContact> members =
new SetProperty<>("members", String.class);
public final SetProperty<String, ChatContact> admins =
new SetProperty<>("admins", String.class);
public final LongProperty<ChatContact> muteUntil =
new LongProperty<>("muteUntil");
public final Property<String, ChatContact> createdBy =
new Property<>("createdBy");
public final Property<Date, ChatContact> creationDate =
new Property<>("creationDate", Date.class);
public final Property<Date, ChatContact> lastActivityTime =
new Property<>("lastActivityTime", Date.class);
public final ListProperty<ChatMessage, ChatContact> chats =
new ListProperty<>("chat", ChatMessage.class);
private final PropertyIndex idx = new PropertyIndex(this, "ChatContact",
id, localId, phone, photo, name, tagline, token, members, admins,
muteUntil, createdBy, creationDate, lastActivityTime, chats);
ChatContact
This property allows us to mute a chat contact so we won’t see notifications from that contact
new Property<>("name");
public final Property<String, ChatContact> tagline =
new Property<>("tagline");
public final Property<String, ChatContact> token =
new Property<>("token");
public final SetProperty<String, ChatContact> members =
new SetProperty<>("members", String.class);
public final SetProperty<String, ChatContact> admins =
new SetProperty<>("admins", String.class);
public final LongProperty<ChatContact> muteUntil =
new LongProperty<>("muteUntil");
public final Property<String, ChatContact> createdBy =
new Property<>("createdBy");
public final Property<Date, ChatContact> creationDate =
new Property<>("creationDate", Date.class);
public final Property<Date, ChatContact> lastActivityTime =
new Property<>("lastActivityTime", Date.class);
public final ListProperty<ChatMessage, ChatContact> chats =
new ListProperty<>("chat", ChatMessage.class);
private final PropertyIndex idx = new PropertyIndex(this, "ChatContact",
id, localId, phone, photo, name, tagline, token, members, admins,
muteUntil, createdBy, creationDate, lastActivityTime, chats);
ChatContact
If this is a group then it was created by a specific user. The id of that user should be listed here
new Property<>("name");
public final Property<String, ChatContact> tagline =
new Property<>("tagline");
public final Property<String, ChatContact> token =
new Property<>("token");
public final SetProperty<String, ChatContact> members =
new SetProperty<>("members", String.class);
public final SetProperty<String, ChatContact> admins =
new SetProperty<>("admins", String.class);
public final LongProperty<ChatContact> muteUntil =
new LongProperty<>("muteUntil");
public final Property<String, ChatContact> createdBy =
new Property<>("createdBy");
public final Property<Date, ChatContact> creationDate =
new Property<>("creationDate", Date.class);
public final Property<Date, ChatContact> lastActivityTime =
new Property<>("lastActivityTime", Date.class);
public final ListProperty<ChatMessage, ChatContact> chats =
new ListProperty<>("chat", ChatMessage.class);
private final PropertyIndex idx = new PropertyIndex(this, "ChatContact",
id, localId, phone, photo, name, tagline, token, members, admins,
muteUntil, createdBy, creationDate, lastActivityTime, chats);
ChatContact
The creation date is applicable to both groups and individual users
new Property<>("name");
public final Property<String, ChatContact> tagline =
new Property<>("tagline");
public final Property<String, ChatContact> token =
new Property<>("token");
public final SetProperty<String, ChatContact> members =
new SetProperty<>("members", String.class);
public final SetProperty<String, ChatContact> admins =
new SetProperty<>("admins", String.class);
public final LongProperty<ChatContact> muteUntil =
new LongProperty<>("muteUntil");
public final Property<String, ChatContact> createdBy =
new Property<>("createdBy");
public final Property<Date, ChatContact> creationDate =
new Property<>("creationDate", Date.class);
public final Property<Date, ChatContact> lastActivityTime =
new Property<>("lastActivityTime", Date.class);
public final ListProperty<ChatMessage, ChatContact> chats =
new ListProperty<>("chat", ChatMessage.class);
private final PropertyIndex idx = new PropertyIndex(this, "ChatContact",
id, localId, phone, photo, name, tagline, token, members, admins,
muteUntil, createdBy, creationDate, lastActivityTime, chats);
ChatContact
This is the timestamp of the last message we received from the given user. We saw this updated in the Server class. We use this to sort the chats by latest update
new Property<>("name");
public final Property<String, ChatContact> tagline =
new Property<>("tagline");
public final Property<String, ChatContact> token =
new Property<>("token");
public final SetProperty<String, ChatContact> members =
new SetProperty<>("members", String.class);
public final SetProperty<String, ChatContact> admins =
new SetProperty<>("admins", String.class);
public final LongProperty<ChatContact> muteUntil =
new LongProperty<>("muteUntil");
public final Property<String, ChatContact> createdBy =
new Property<>("createdBy");
public final Property<Date, ChatContact> creationDate =
new Property<>("creationDate", Date.class);
public final Property<Date, ChatContact> lastActivityTime =
new Property<>("lastActivityTime", Date.class);
public final ListProperty<ChatMessage, ChatContact> chats =
new ListProperty<>("chat", ChatMessage.class);
private final PropertyIndex idx = new PropertyIndex(this, "ChatContact",
id, localId, phone, photo, name, tagline, token, members, admins,
muteUntil, createdBy, creationDate, lastActivityTime, chats);
ChatContact
ChatMessage is the property business object that contains the content of the message. Here we store the actual chats we had with the contact or group.
new Property<>("createdBy");
public final Property<Date, ChatContact> creationDate =
new Property<>("creationDate", Date.class);
public final Property<Date, ChatContact> lastActivityTime =
new Property<>("lastActivityTime", Date.class);
public final ListProperty<ChatMessage, ChatContact> chats =
new ListProperty<>("chat", ChatMessage.class);
private final PropertyIndex idx = new PropertyIndex(this, "ChatContact",
id, localId, phone, photo, name, tagline, token, members, admins,
muteUntil, createdBy, creationDate, lastActivityTime, chats);
@Override
public PropertyIndex getPropertyIndex() {
return idx;
}
public ChatContact() {
idx.setExcludeFromJSON(photo, true);
}
private static final int SMALL_IMAGE = 0;
private static final int LARGE_IMAGE = 1;
private static final float[] IMAGE_SIZES = {4f, 6.5f};
ChatContact
As I mentioned before photo isn’t stored in JSON when we save the contact to keep the size low. We save the contact image in a separate file and don't want too much
noise here.
return idx;
}
public ChatContact() {
idx.setExcludeFromJSON(photo, true);
}
private static final int SMALL_IMAGE = 0;
private static final int LARGE_IMAGE = 1;
private static final float[] IMAGE_SIZES = {4f, 6.5f};
private static final Image[] maskImage = new Image[2];
private static final Object[] maskObject = new Object[2];
private static final EncodedImage[] placeholder = new EncodedImage[2];
private static Image createMaskImage(int size) {
Image m = Image.createImage(size, size, 0);
Graphics g = m.getGraphics();
g.setAntiAliased(true);
g.setColor(0xffffff);
g.fillArc(0, 0, size - 1, size -1, 0, 360);
return m;
}
private Image createPlaceholder(int size) {
ChatContact
The app has two thumbnail images. One is slightly smaller than the other and both are rounded. To keep the code generic I use arrays with the detail and then use two
sizes. The small size maps to the 0 offset in the array and the large size maps to the one offset.
return idx;
}
public ChatContact() {
idx.setExcludeFromJSON(photo, true);
}
private static final int SMALL_IMAGE = 0;
private static final int LARGE_IMAGE = 1;
private static final float[] IMAGE_SIZES = {4f, 6.5f};
private static final Image[] maskImage = new Image[2];
private static final Object[] maskObject = new Object[2];
private static final EncodedImage[] placeholder = new EncodedImage[2];
private static Image createMaskImage(int size) {
Image m = Image.createImage(size, size, 0);
Graphics g = m.getGraphics();
g.setAntiAliased(true);
g.setColor(0xffffff);
g.fillArc(0, 0, size - 1, size -1, 0, 360);
return m;
}
private Image createPlaceholder(int size) {
ChatContact
Here are the sizes of these two images in millimeters
return idx;
}
public ChatContact() {
idx.setExcludeFromJSON(photo, true);
}
private static final int SMALL_IMAGE = 0;
private static final int LARGE_IMAGE = 1;
private static final float[] IMAGE_SIZES = {4f, 6.5f};
private static final Image[] maskImage = new Image[2];
private static final Object[] maskObject = new Object[2];
private static final EncodedImage[] placeholder = new EncodedImage[2];
private static Image createMaskImage(int size) {
Image m = Image.createImage(size, size, 0);
Graphics g = m.getGraphics();
g.setAntiAliased(true);
g.setColor(0xffffff);
g.fillArc(0, 0, size - 1, size -1, 0, 360);
return m;
}
private Image createPlaceholder(int size) {
ChatContact
Images are masked to these sizes, masking allows us to round an image in this case
return idx;
}
public ChatContact() {
idx.setExcludeFromJSON(photo, true);
}
private static final int SMALL_IMAGE = 0;
private static final int LARGE_IMAGE = 1;
private static final float[] IMAGE_SIZES = {4f, 6.5f};
private static final Image[] maskImage = new Image[2];
private static final Object[] maskObject = new Object[2];
private static final EncodedImage[] placeholder = new EncodedImage[2];
private static Image createMaskImage(int size) {
Image m = Image.createImage(size, size, 0);
Graphics g = m.getGraphics();
g.setAntiAliased(true);
g.setColor(0xffffff);
g.fillArc(0, 0, size - 1, size -1, 0, 360);
return m;
}
private Image createPlaceholder(int size) {
ChatContact
We generate placeholder images which are used when an image is unavailable
return idx;
}
public ChatContact() {
idx.setExcludeFromJSON(photo, true);
}
private static final int SMALL_IMAGE = 0;
private static final int LARGE_IMAGE = 1;
private static final float[] IMAGE_SIZES = {4f, 6.5f};
private static final Image[] maskImage = new Image[2];
private static final Object[] maskObject = new Object[2];
private static final EncodedImage[] placeholder = new EncodedImage[2];
private static Image createMaskImage(int size) {
Image m = Image.createImage(size, size, 0);
Graphics g = m.getGraphics();
g.setAntiAliased(true);
g.setColor(0xffffff);
g.fillArc(0, 0, size - 1, size -1, 0, 360);
return m;
}
private Image createPlaceholder(int size) {
ChatContact
This method creates a mask image of a given size in pixels a mask image uses black pixels to represent transparency and white pixels to represent the visible opacity. So
where we draw a black rectangle image with a white circle in the center. When we apply this mask to an image only the portion represented by the white circle will
remain.
Graphics g = m.getGraphics();
g.setAntiAliased(true);
g.setColor(0xffffff);
g.fillArc(0, 0, size - 1, size -1, 0, 360);
return m;
}
private Image createPlaceholder(int size) {
Image i = Image.createImage(size, size, 0xffe2e7ea);
Graphics g = i.getGraphics();
g.setAntiAliased(true);
g.setAntiAliasedText(true);
g.setColor(0xffffff);
Font fnt = FontImage.getMaterialDesignFont().
derive(size, Font.STYLE_PLAIN);
g.setFont(fnt);
String s = "" + FontImage.MATERIAL_PERSON;
g.drawString(s, size / 2 - fnt.stringWidth(s) / 2, 0);
return i;
}
private Image getImage(int offset) {
if(maskImage[offset] == null) {
ChatContact
The placeholder image is used when no image is defined, again we create this based on a size in pixels. We create a gray image then draw on it using white. We use the
material font to draw the image of a person onto this image
g.drawString(s, size / 2 - fnt.stringWidth(s) / 2, 0);
return i;
}
private Image getImage(int offset) {
if(maskImage[offset] == null) {
maskImage[offset] =
createMaskImage(convertToPixels(IMAGE_SIZES[offset]));
maskObject[offset] = maskImage[offset].createMask();
Image i = createPlaceholder(
convertToPixels(IMAGE_SIZES[offset]));
placeholder[offset] = EncodedImage.createFromImage(
i.applyMask(maskObject[offset]), false);
}
if(photo.get() == null) {
return placeholder[offset];
}
return photo.get().
fill(maskImage[offset].getWidth(),
maskImage[offset].getHeight()).
applyMask(maskObject[offset]);
}
ChatContact
This method gets the image represented by the contact. In theory I could have used the photo property and overridden get() to implement this. I thought this is a simpler
approach.
g.drawString(s, size / 2 - fnt.stringWidth(s) / 2, 0);
return i;
}
private Image getImage(int offset) {
if(maskImage[offset] == null) {
maskImage[offset] =
createMaskImage(convertToPixels(IMAGE_SIZES[offset]));
maskObject[offset] = maskImage[offset].createMask();
Image i = createPlaceholder(
convertToPixels(IMAGE_SIZES[offset]));
placeholder[offset] = EncodedImage.createFromImage(
i.applyMask(maskObject[offset]), false);
}
if(photo.get() == null) {
return placeholder[offset];
}
return photo.get().
fill(maskImage[offset].getWidth(),
maskImage[offset].getHeight()).
applyMask(maskObject[offset]);
}
ChatContact
Here we lazily initialize the arrays of the mask images for large or small images.
g.drawString(s, size / 2 - fnt.stringWidth(s) / 2, 0);
return i;
}
private Image getImage(int offset) {
if(maskImage[offset] == null) {
maskImage[offset] =
createMaskImage(convertToPixels(IMAGE_SIZES[offset]));
maskObject[offset] = maskImage[offset].createMask();
Image i = createPlaceholder(
convertToPixels(IMAGE_SIZES[offset]));
placeholder[offset] = EncodedImage.createFromImage(
i.applyMask(maskObject[offset]), false);
}
if(photo.get() == null) {
return placeholder[offset];
}
return photo.get().
fill(maskImage[offset].getWidth(),
maskImage[offset].getHeight()).
applyMask(maskObject[offset]);
}
ChatContact
We create the mask images then convert the mask image to mask object. Finally we create the placeholder image
g.drawString(s, size / 2 - fnt.stringWidth(s) / 2, 0);
return i;
}
private Image getImage(int offset) {
if(maskImage[offset] == null) {
maskImage[offset] =
createMaskImage(convertToPixels(IMAGE_SIZES[offset]));
maskObject[offset] = maskImage[offset].createMask();
Image i = createPlaceholder(
convertToPixels(IMAGE_SIZES[offset]));
placeholder[offset] = EncodedImage.createFromImage(
i.applyMask(maskObject[offset]), false);
}
if(photo.get() == null) {
return placeholder[offset];
}
return photo.get().
fill(maskImage[offset].getWidth(),
maskImage[offset].getHeight()).
applyMask(maskObject[offset]);
}
ChatContact
If the photo is null I return the placeholder image instead of using the photo object
g.drawString(s, size / 2 - fnt.stringWidth(s) / 2, 0);
return i;
}
private Image getImage(int offset) {
if(maskImage[offset] == null) {
maskImage[offset] =
createMaskImage(convertToPixels(IMAGE_SIZES[offset]));
maskObject[offset] = maskImage[offset].createMask();
Image i = createPlaceholder(
convertToPixels(IMAGE_SIZES[offset]));
placeholder[offset] = EncodedImage.createFromImage(
i.applyMask(maskObject[offset]), false);
}
if(photo.get() == null) {
return placeholder[offset];
}
return photo.get().
fill(maskImage[offset].getWidth(),
maskImage[offset].getHeight()).
applyMask(maskObject[offset]);
}
ChatContact
Otherwise we fill the image into the size of the mask and apply the mask to create a round object. Fill scales the image so it’s cropped while filling the exact boundaries
given. It doesn’t distort the aspect ratio of the image like a typical scale operation would
applyMask(maskObject[offset]);
}
private Image smallIcon;
private Image largeIcon;
public Image getSmallIcon() {
if(smallIcon != null) {
return smallIcon;
}
smallIcon = getImage(SMALL_IMAGE);
return smallIcon;
}
public Image getLargeIcon() {
if(largeIcon != null) {
return largeIcon;
}
largeIcon = getImage(LARGE_IMAGE);
return largeIcon;
}
}
ChatContact
The final public methods and variables cache the small & large image. Appropriately they are the publicly exposed API’s for this functionality

More Related Content

Similar to Creating a Whatsapp Clone - Part III - Transcript.pdf

Project: Call Center Management
Project: Call Center ManagementProject: Call Center Management
Project: Call Center Managementpritamkumar
 
Creating a Whatsapp Clone - Part II - Transcript.pdf
Creating a Whatsapp Clone - Part II - Transcript.pdfCreating a Whatsapp Clone - Part II - Transcript.pdf
Creating a Whatsapp Clone - Part II - Transcript.pdfShaiAlmog1
 
Creating a Facebook Clone - Part XL - Transcript.pdf
Creating a Facebook Clone - Part XL - Transcript.pdfCreating a Facebook Clone - Part XL - Transcript.pdf
Creating a Facebook Clone - Part XL - Transcript.pdfShaiAlmog1
 
Creating a Facebook Clone - Part XIV - Transcript.pdf
Creating a Facebook Clone - Part XIV - Transcript.pdfCreating a Facebook Clone - Part XIV - Transcript.pdf
Creating a Facebook Clone - Part XIV - Transcript.pdfShaiAlmog1
 
Use the code below from the previous assignment that we need to exte.pdf
Use the code below from the previous assignment that we need to exte.pdfUse the code below from the previous assignment that we need to exte.pdf
Use the code below from the previous assignment that we need to exte.pdfsales87
 
Creating a Whatsapp Clone - Part XI - Transcript.pdf
Creating a Whatsapp Clone - Part XI - Transcript.pdfCreating a Whatsapp Clone - Part XI - Transcript.pdf
Creating a Whatsapp Clone - Part XI - Transcript.pdfShaiAlmog1
 
Creating a Facebook Clone - Part X - Transcript.pdf
Creating a Facebook Clone - Part X - Transcript.pdfCreating a Facebook Clone - Part X - Transcript.pdf
Creating a Facebook Clone - Part X - Transcript.pdfShaiAlmog1
 
So here is the code from the previous assignment that we need to ext.pdf
So here is the code from the previous assignment that we need to ext.pdfSo here is the code from the previous assignment that we need to ext.pdf
So here is the code from the previous assignment that we need to ext.pdfleolight2
 
Creating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part II.pdfCreating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part II.pdfShaiAlmog1
 
Creating a Facebook Clone - Part XL.pdf
Creating a Facebook Clone - Part XL.pdfCreating a Facebook Clone - Part XL.pdf
Creating a Facebook Clone - Part XL.pdfShaiAlmog1
 
Java beginners meetup: Introduction to class and application design
Java beginners meetup: Introduction to class and application designJava beginners meetup: Introduction to class and application design
Java beginners meetup: Introduction to class and application designPatrick Kostjens
 
Creating a Facebook Clone - Part XI.pdf
Creating a Facebook Clone - Part XI.pdfCreating a Facebook Clone - Part XI.pdf
Creating a Facebook Clone - Part XI.pdfShaiAlmog1
 
Need help on creating code using cart. The output has to show multip.pdf
Need help on creating code using cart. The output has to show multip.pdfNeed help on creating code using cart. The output has to show multip.pdf
Need help on creating code using cart. The output has to show multip.pdfmeerobertsonheyde608
 
GWT Training - Session 3/3
GWT Training - Session 3/3GWT Training - Session 3/3
GWT Training - Session 3/3Faiz Bashir
 
Creating a Facebook Clone - Part XIV.pdf
Creating a Facebook Clone - Part XIV.pdfCreating a Facebook Clone - Part XIV.pdf
Creating a Facebook Clone - Part XIV.pdfShaiAlmog1
 
Struts 2 + Spring
Struts 2 + SpringStruts 2 + Spring
Struts 2 + SpringBryan Hsueh
 
Diving in the Flex Data Binding Waters
Diving in the Flex Data Binding WatersDiving in the Flex Data Binding Waters
Diving in the Flex Data Binding Watersmichael.labriola
 
Angular 2 Component Communication - Talk by Rob McDiarmid
Angular 2 Component Communication - Talk by Rob McDiarmidAngular 2 Component Communication - Talk by Rob McDiarmid
Angular 2 Component Communication - Talk by Rob McDiarmidAmrita Chopra
 

Similar to Creating a Whatsapp Clone - Part III - Transcript.pdf (20)

Project: Call Center Management
Project: Call Center ManagementProject: Call Center Management
Project: Call Center Management
 
Creating a Whatsapp Clone - Part II - Transcript.pdf
Creating a Whatsapp Clone - Part II - Transcript.pdfCreating a Whatsapp Clone - Part II - Transcript.pdf
Creating a Whatsapp Clone - Part II - Transcript.pdf
 
Creating a Facebook Clone - Part XL - Transcript.pdf
Creating a Facebook Clone - Part XL - Transcript.pdfCreating a Facebook Clone - Part XL - Transcript.pdf
Creating a Facebook Clone - Part XL - Transcript.pdf
 
Creating a Facebook Clone - Part XIV - Transcript.pdf
Creating a Facebook Clone - Part XIV - Transcript.pdfCreating a Facebook Clone - Part XIV - Transcript.pdf
Creating a Facebook Clone - Part XIV - Transcript.pdf
 
Use the code below from the previous assignment that we need to exte.pdf
Use the code below from the previous assignment that we need to exte.pdfUse the code below from the previous assignment that we need to exte.pdf
Use the code below from the previous assignment that we need to exte.pdf
 
Week 12 code
Week 12 codeWeek 12 code
Week 12 code
 
Creating a Whatsapp Clone - Part XI - Transcript.pdf
Creating a Whatsapp Clone - Part XI - Transcript.pdfCreating a Whatsapp Clone - Part XI - Transcript.pdf
Creating a Whatsapp Clone - Part XI - Transcript.pdf
 
Creating a Facebook Clone - Part X - Transcript.pdf
Creating a Facebook Clone - Part X - Transcript.pdfCreating a Facebook Clone - Part X - Transcript.pdf
Creating a Facebook Clone - Part X - Transcript.pdf
 
So here is the code from the previous assignment that we need to ext.pdf
So here is the code from the previous assignment that we need to ext.pdfSo here is the code from the previous assignment that we need to ext.pdf
So here is the code from the previous assignment that we need to ext.pdf
 
Creating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part II.pdfCreating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part II.pdf
 
Creating a Facebook Clone - Part XL.pdf
Creating a Facebook Clone - Part XL.pdfCreating a Facebook Clone - Part XL.pdf
Creating a Facebook Clone - Part XL.pdf
 
Java beginners meetup: Introduction to class and application design
Java beginners meetup: Introduction to class and application designJava beginners meetup: Introduction to class and application design
Java beginners meetup: Introduction to class and application design
 
Creating a Facebook Clone - Part XI.pdf
Creating a Facebook Clone - Part XI.pdfCreating a Facebook Clone - Part XI.pdf
Creating a Facebook Clone - Part XI.pdf
 
Need help on creating code using cart. The output has to show multip.pdf
Need help on creating code using cart. The output has to show multip.pdfNeed help on creating code using cart. The output has to show multip.pdf
Need help on creating code using cart. The output has to show multip.pdf
 
GWT Training - Session 3/3
GWT Training - Session 3/3GWT Training - Session 3/3
GWT Training - Session 3/3
 
Creating a Facebook Clone - Part XIV.pdf
Creating a Facebook Clone - Part XIV.pdfCreating a Facebook Clone - Part XIV.pdf
Creating a Facebook Clone - Part XIV.pdf
 
Struts 2 + Spring
Struts 2 + SpringStruts 2 + Spring
Struts 2 + Spring
 
Diving in the Flex Data Binding Waters
Diving in the Flex Data Binding WatersDiving in the Flex Data Binding Waters
Diving in the Flex Data Binding Waters
 
Android introduction by vidya topa
Android introduction by vidya topaAndroid introduction by vidya topa
Android introduction by vidya topa
 
Angular 2 Component Communication - Talk by Rob McDiarmid
Angular 2 Component Communication - Talk by Rob McDiarmidAngular 2 Component Communication - Talk by Rob McDiarmid
Angular 2 Component Communication - Talk by Rob McDiarmid
 

More from ShaiAlmog1

The Duck Teaches Learn to debug from the masters. Local to production- kill ...
The Duck Teaches  Learn to debug from the masters. Local to production- kill ...The Duck Teaches  Learn to debug from the masters. Local to production- kill ...
The Duck Teaches Learn to debug from the masters. Local to production- kill ...ShaiAlmog1
 
create-netflix-clone-06-client-ui.pdf
create-netflix-clone-06-client-ui.pdfcreate-netflix-clone-06-client-ui.pdf
create-netflix-clone-06-client-ui.pdfShaiAlmog1
 
create-netflix-clone-01-introduction_transcript.pdf
create-netflix-clone-01-introduction_transcript.pdfcreate-netflix-clone-01-introduction_transcript.pdf
create-netflix-clone-01-introduction_transcript.pdfShaiAlmog1
 
create-netflix-clone-02-server_transcript.pdf
create-netflix-clone-02-server_transcript.pdfcreate-netflix-clone-02-server_transcript.pdf
create-netflix-clone-02-server_transcript.pdfShaiAlmog1
 
create-netflix-clone-04-server-continued_transcript.pdf
create-netflix-clone-04-server-continued_transcript.pdfcreate-netflix-clone-04-server-continued_transcript.pdf
create-netflix-clone-04-server-continued_transcript.pdfShaiAlmog1
 
create-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdfcreate-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdfShaiAlmog1
 
create-netflix-clone-06-client-ui_transcript.pdf
create-netflix-clone-06-client-ui_transcript.pdfcreate-netflix-clone-06-client-ui_transcript.pdf
create-netflix-clone-06-client-ui_transcript.pdfShaiAlmog1
 
create-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdfcreate-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdfShaiAlmog1
 
create-netflix-clone-04-server-continued.pdf
create-netflix-clone-04-server-continued.pdfcreate-netflix-clone-04-server-continued.pdf
create-netflix-clone-04-server-continued.pdfShaiAlmog1
 
create-netflix-clone-05-client-model_transcript.pdf
create-netflix-clone-05-client-model_transcript.pdfcreate-netflix-clone-05-client-model_transcript.pdf
create-netflix-clone-05-client-model_transcript.pdfShaiAlmog1
 
create-netflix-clone-03-server_transcript.pdf
create-netflix-clone-03-server_transcript.pdfcreate-netflix-clone-03-server_transcript.pdf
create-netflix-clone-03-server_transcript.pdfShaiAlmog1
 
create-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdfcreate-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdfShaiAlmog1
 
create-netflix-clone-05-client-model.pdf
create-netflix-clone-05-client-model.pdfcreate-netflix-clone-05-client-model.pdf
create-netflix-clone-05-client-model.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part V - Transcript.pdf
Creating a Whatsapp Clone - Part V - Transcript.pdfCreating a Whatsapp Clone - Part V - Transcript.pdf
Creating a Whatsapp Clone - Part V - Transcript.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part I - Transcript.pdf
Creating a Whatsapp Clone - Part I - Transcript.pdfCreating a Whatsapp Clone - Part I - Transcript.pdf
Creating a Whatsapp Clone - Part I - Transcript.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part VI.pdf
Creating a Whatsapp Clone - Part VI.pdfCreating a Whatsapp Clone - Part VI.pdf
Creating a Whatsapp Clone - Part VI.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part VII.pdf
Creating a Whatsapp Clone - Part VII.pdfCreating a Whatsapp Clone - Part VII.pdf
Creating a Whatsapp Clone - Part VII.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part V.pdf
Creating a Whatsapp Clone - Part V.pdfCreating a Whatsapp Clone - Part V.pdf
Creating a Whatsapp Clone - Part V.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part X - Transcript.pdf
Creating a Whatsapp Clone - Part X - Transcript.pdfCreating a Whatsapp Clone - Part X - Transcript.pdf
Creating a Whatsapp Clone - Part X - Transcript.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part X.pdf
Creating a Whatsapp Clone - Part X.pdfCreating a Whatsapp Clone - Part X.pdf
Creating a Whatsapp Clone - Part X.pdfShaiAlmog1
 

More from ShaiAlmog1 (20)

The Duck Teaches Learn to debug from the masters. Local to production- kill ...
The Duck Teaches  Learn to debug from the masters. Local to production- kill ...The Duck Teaches  Learn to debug from the masters. Local to production- kill ...
The Duck Teaches Learn to debug from the masters. Local to production- kill ...
 
create-netflix-clone-06-client-ui.pdf
create-netflix-clone-06-client-ui.pdfcreate-netflix-clone-06-client-ui.pdf
create-netflix-clone-06-client-ui.pdf
 
create-netflix-clone-01-introduction_transcript.pdf
create-netflix-clone-01-introduction_transcript.pdfcreate-netflix-clone-01-introduction_transcript.pdf
create-netflix-clone-01-introduction_transcript.pdf
 
create-netflix-clone-02-server_transcript.pdf
create-netflix-clone-02-server_transcript.pdfcreate-netflix-clone-02-server_transcript.pdf
create-netflix-clone-02-server_transcript.pdf
 
create-netflix-clone-04-server-continued_transcript.pdf
create-netflix-clone-04-server-continued_transcript.pdfcreate-netflix-clone-04-server-continued_transcript.pdf
create-netflix-clone-04-server-continued_transcript.pdf
 
create-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdfcreate-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdf
 
create-netflix-clone-06-client-ui_transcript.pdf
create-netflix-clone-06-client-ui_transcript.pdfcreate-netflix-clone-06-client-ui_transcript.pdf
create-netflix-clone-06-client-ui_transcript.pdf
 
create-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdfcreate-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdf
 
create-netflix-clone-04-server-continued.pdf
create-netflix-clone-04-server-continued.pdfcreate-netflix-clone-04-server-continued.pdf
create-netflix-clone-04-server-continued.pdf
 
create-netflix-clone-05-client-model_transcript.pdf
create-netflix-clone-05-client-model_transcript.pdfcreate-netflix-clone-05-client-model_transcript.pdf
create-netflix-clone-05-client-model_transcript.pdf
 
create-netflix-clone-03-server_transcript.pdf
create-netflix-clone-03-server_transcript.pdfcreate-netflix-clone-03-server_transcript.pdf
create-netflix-clone-03-server_transcript.pdf
 
create-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdfcreate-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdf
 
create-netflix-clone-05-client-model.pdf
create-netflix-clone-05-client-model.pdfcreate-netflix-clone-05-client-model.pdf
create-netflix-clone-05-client-model.pdf
 
Creating a Whatsapp Clone - Part V - Transcript.pdf
Creating a Whatsapp Clone - Part V - Transcript.pdfCreating a Whatsapp Clone - Part V - Transcript.pdf
Creating a Whatsapp Clone - Part V - Transcript.pdf
 
Creating a Whatsapp Clone - Part I - Transcript.pdf
Creating a Whatsapp Clone - Part I - Transcript.pdfCreating a Whatsapp Clone - Part I - Transcript.pdf
Creating a Whatsapp Clone - Part I - Transcript.pdf
 
Creating a Whatsapp Clone - Part VI.pdf
Creating a Whatsapp Clone - Part VI.pdfCreating a Whatsapp Clone - Part VI.pdf
Creating a Whatsapp Clone - Part VI.pdf
 
Creating a Whatsapp Clone - Part VII.pdf
Creating a Whatsapp Clone - Part VII.pdfCreating a Whatsapp Clone - Part VII.pdf
Creating a Whatsapp Clone - Part VII.pdf
 
Creating a Whatsapp Clone - Part V.pdf
Creating a Whatsapp Clone - Part V.pdfCreating a Whatsapp Clone - Part V.pdf
Creating a Whatsapp Clone - Part V.pdf
 
Creating a Whatsapp Clone - Part X - Transcript.pdf
Creating a Whatsapp Clone - Part X - Transcript.pdfCreating a Whatsapp Clone - Part X - Transcript.pdf
Creating a Whatsapp Clone - Part X - Transcript.pdf
 
Creating a Whatsapp Clone - Part X.pdf
Creating a Whatsapp Clone - Part X.pdfCreating a Whatsapp Clone - Part X.pdf
Creating a Whatsapp Clone - Part X.pdf
 

Recently uploaded

Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...apidays
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Victor Rentea
 
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Orbitshub
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processorsdebabhi2
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProduct Anonymous
 
Ransomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfRansomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfOverkill Security
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MIND CTI
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024The Digital Insurer
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Zilliz
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDropbox
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoffsammart93
 
Spring Boot vs Quarkus the ultimate battle - DevoxxUK
Spring Boot vs Quarkus the ultimate battle - DevoxxUKSpring Boot vs Quarkus the ultimate battle - DevoxxUK
Spring Boot vs Quarkus the ultimate battle - DevoxxUKJago de Vreede
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWERMadyBayot
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FMESafe Software
 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...apidays
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...Zilliz
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century educationjfdjdjcjdnsjd
 

Recently uploaded (20)

Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
 
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Ransomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfRansomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdf
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Spring Boot vs Quarkus the ultimate battle - DevoxxUK
Spring Boot vs Quarkus the ultimate battle - DevoxxUKSpring Boot vs Quarkus the ultimate battle - DevoxxUK
Spring Boot vs Quarkus the ultimate battle - DevoxxUK
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 

Creating a Whatsapp Clone - Part III - Transcript.pdf

  • 1. Creating a WhatsApp Clone - Part III The next step is the other classes within the model package. The rest is relatively trivial after the Server class.
  • 2. package com.codename1.whatsapp.model; import java.util.List; public interface ServerMessages { public void connected(); public void disconnected(); public void messageReceived(ChatMessage m); public void userTyping(String contactId); public void messageViewed(String messageId, List<String> userIds); } ServerMessages This is the callback interface we used within the Server class, it’s pretty trivial. I added some methods for future enhancement too. The first two methods inform the observer that the server is connected or disconnected.
  • 3. package com.codename1.whatsapp.model; import java.util.List; public interface ServerMessages { public void connected(); public void disconnected(); public void messageReceived(ChatMessage m); public void userTyping(String contactId); public void messageViewed(String messageId, List<String> userIds); } ServerMessages Message received is invoked to update the UI based on the new incoming message
  • 4. package com.codename1.whatsapp.model; import java.util.List; public interface ServerMessages { public void connected(); public void disconnected(); public void messageReceived(ChatMessage m); public void userTyping(String contactId); public void messageViewed(String messageId, List<String> userIds); } ServerMessages The last two callbacks aren’t really implemented but they allow us to update the UI if a user is typing in a chat. The message viewed event similarly indicates if a user viewed the message. This can provide an indicator in the UI that the message was seen.
  • 5. public class ChatContact implements PropertyBusinessObject { public final Property<String, ChatContact> id = new Property<>("id"); public final Property<String, ChatContact> localId = new Property<>("localId"); public final Property<String, ChatContact> phone = new Property<>("phone"); public final Property<Image, ChatContact> photo = new Property<>("photo", Image.class); public final Property<String, ChatContact> name = new Property<>("name"); public final Property<String, ChatContact> tagline = new Property<>("tagline"); public final Property<String, ChatContact> token = new Property<>("token"); public final SetProperty<String, ChatContact> members = new SetProperty<>("members", String.class); public final SetProperty<String, ChatContact> admins = new SetProperty<>("admins", String.class); public final LongProperty<ChatContact> muteUntil = new LongProperty<>("muteUntil"); ChatContact ChatContact is a PropertyBusinessObject that stores the content of a specific contact entry.
  • 6. public class ChatContact implements PropertyBusinessObject { public final Property<String, ChatContact> id = new Property<>("id"); public final Property<String, ChatContact> localId = new Property<>("localId"); public final Property<String, ChatContact> phone = new Property<>("phone"); public final Property<Image, ChatContact> photo = new Property<>("photo", Image.class); public final Property<String, ChatContact> name = new Property<>("name"); public final Property<String, ChatContact> tagline = new Property<>("tagline"); public final Property<String, ChatContact> token = new Property<>("token"); public final SetProperty<String, ChatContact> members = new SetProperty<>("members", String.class); public final SetProperty<String, ChatContact> admins = new SetProperty<>("admins", String.class); public final LongProperty<ChatContact> muteUntil = new LongProperty<>("muteUntil"); ChatContact I chose to use unique id’s instead of using the phone as an id. This was something I was conflicted about. I eventually chose to use an ID which I think is more secure overall. It would also support the option of changing a phone number in the future or using an email as the unique identifier
  • 7. public class ChatContact implements PropertyBusinessObject { public final Property<String, ChatContact> id = new Property<>("id"); public final Property<String, ChatContact> localId = new Property<>("localId"); public final Property<String, ChatContact> phone = new Property<>("phone"); public final Property<Image, ChatContact> photo = new Property<>("photo", Image.class); public final Property<String, ChatContact> name = new Property<>("name"); public final Property<String, ChatContact> tagline = new Property<>("tagline"); public final Property<String, ChatContact> token = new Property<>("token"); public final SetProperty<String, ChatContact> members = new SetProperty<>("members", String.class); public final SetProperty<String, ChatContact> admins = new SetProperty<>("admins", String.class); public final LongProperty<ChatContact> muteUntil = new LongProperty<>("muteUntil"); ChatContact localId should map to the ID in the contacts. This allows us to refresh the contact details from the device address book in the future.
  • 8. public class ChatContact implements PropertyBusinessObject { public final Property<String, ChatContact> id = new Property<>("id"); public final Property<String, ChatContact> localId = new Property<>("localId"); public final Property<String, ChatContact> phone = new Property<>("phone"); public final Property<Image, ChatContact> photo = new Property<>("photo", Image.class); public final Property<String, ChatContact> name = new Property<>("name"); public final Property<String, ChatContact> tagline = new Property<>("tagline"); public final Property<String, ChatContact> token = new Property<>("token"); public final SetProperty<String, ChatContact> members = new SetProperty<>("members", String.class); public final SetProperty<String, ChatContact> admins = new SetProperty<>("admins", String.class); public final LongProperty<ChatContact> muteUntil = new LongProperty<>("muteUntil"); ChatContact The phone property is pretty obvious
  • 9. public class ChatContact implements PropertyBusinessObject { public final Property<String, ChatContact> id = new Property<>("id"); public final Property<String, ChatContact> localId = new Property<>("localId"); public final Property<String, ChatContact> phone = new Property<>("phone"); public final Property<Image, ChatContact> photo = new Property<>("photo", Image.class); public final Property<String, ChatContact> name = new Property<>("name"); public final Property<String, ChatContact> tagline = new Property<>("tagline"); public final Property<String, ChatContact> token = new Property<>("token"); public final SetProperty<String, ChatContact> members = new SetProperty<>("members", String.class); public final SetProperty<String, ChatContact> admins = new SetProperty<>("admins", String.class); public final LongProperty<ChatContact> muteUntil = new LongProperty<>("muteUntil"); ChatContact The photo property stores the picture of the contact, there is a lot to this property so I’ll discuss it in more detail soon.
  • 10. public class ChatContact implements PropertyBusinessObject { public final Property<String, ChatContact> id = new Property<>("id"); public final Property<String, ChatContact> localId = new Property<>("localId"); public final Property<String, ChatContact> phone = new Property<>("phone"); public final Property<Image, ChatContact> photo = new Property<>("photo", Image.class); public final Property<String, ChatContact> name = new Property<>("name"); public final Property<String, ChatContact> tagline = new Property<>("tagline"); public final Property<String, ChatContact> token = new Property<>("token"); public final SetProperty<String, ChatContact> members = new SetProperty<>("members", String.class); public final SetProperty<String, ChatContact> admins = new SetProperty<>("admins", String.class); public final LongProperty<ChatContact> muteUntil = new LongProperty<>("muteUntil"); ChatContact These are the common attributes for name and tagline used in whatsapp. For simplicity I only used full name and ignored nuances such as first/last/middle etc.
  • 11. public class ChatContact implements PropertyBusinessObject { public final Property<String, ChatContact> id = new Property<>("id"); public final Property<String, ChatContact> localId = new Property<>("localId"); public final Property<String, ChatContact> phone = new Property<>("phone"); public final Property<Image, ChatContact> photo = new Property<>("photo", Image.class); public final Property<String, ChatContact> name = new Property<>("name"); public final Property<String, ChatContact> tagline = new Property<>("tagline"); public final Property<String, ChatContact> token = new Property<>("token"); public final SetProperty<String, ChatContact> members = new SetProperty<>("members", String.class); public final SetProperty<String, ChatContact> admins = new SetProperty<>("admins", String.class); public final LongProperty<ChatContact> muteUntil = new LongProperty<>("muteUntil"); ChatContact The token is effectively our password to use the service. Since there is no login process a token is generated on the server as a key that allows us to use the service.
  • 12. new Property<>("name"); public final Property<String, ChatContact> tagline = new Property<>("tagline"); public final Property<String, ChatContact> token = new Property<>("token"); public final SetProperty<String, ChatContact> members = new SetProperty<>("members", String.class); public final SetProperty<String, ChatContact> admins = new SetProperty<>("admins", String.class); public final LongProperty<ChatContact> muteUntil = new LongProperty<>("muteUntil"); public final Property<String, ChatContact> createdBy = new Property<>("createdBy"); public final Property<Date, ChatContact> creationDate = new Property<>("creationDate", Date.class); public final Property<Date, ChatContact> lastActivityTime = new Property<>("lastActivityTime", Date.class); public final ListProperty<ChatMessage, ChatContact> chats = new ListProperty<>("chat", ChatMessage.class); private final PropertyIndex idx = new PropertyIndex(this, "ChatContact", id, localId, phone, photo, name, tagline, token, members, admins, muteUntil, createdBy, creationDate, lastActivityTime, chats); ChatContact A chat contact can also serve as a group. I didn’t fully implement this logic but it’s wired almost everywhere. In this case we have two sets for members of the group and the admins of the group. These sets would be empty for a typical user.
  • 13. new Property<>("name"); public final Property<String, ChatContact> tagline = new Property<>("tagline"); public final Property<String, ChatContact> token = new Property<>("token"); public final SetProperty<String, ChatContact> members = new SetProperty<>("members", String.class); public final SetProperty<String, ChatContact> admins = new SetProperty<>("admins", String.class); public final LongProperty<ChatContact> muteUntil = new LongProperty<>("muteUntil"); public final Property<String, ChatContact> createdBy = new Property<>("createdBy"); public final Property<Date, ChatContact> creationDate = new Property<>("creationDate", Date.class); public final Property<Date, ChatContact> lastActivityTime = new Property<>("lastActivityTime", Date.class); public final ListProperty<ChatMessage, ChatContact> chats = new ListProperty<>("chat", ChatMessage.class); private final PropertyIndex idx = new PropertyIndex(this, "ChatContact", id, localId, phone, photo, name, tagline, token, members, admins, muteUntil, createdBy, creationDate, lastActivityTime, chats); ChatContact This property allows us to mute a chat contact so we won’t see notifications from that contact
  • 14. new Property<>("name"); public final Property<String, ChatContact> tagline = new Property<>("tagline"); public final Property<String, ChatContact> token = new Property<>("token"); public final SetProperty<String, ChatContact> members = new SetProperty<>("members", String.class); public final SetProperty<String, ChatContact> admins = new SetProperty<>("admins", String.class); public final LongProperty<ChatContact> muteUntil = new LongProperty<>("muteUntil"); public final Property<String, ChatContact> createdBy = new Property<>("createdBy"); public final Property<Date, ChatContact> creationDate = new Property<>("creationDate", Date.class); public final Property<Date, ChatContact> lastActivityTime = new Property<>("lastActivityTime", Date.class); public final ListProperty<ChatMessage, ChatContact> chats = new ListProperty<>("chat", ChatMessage.class); private final PropertyIndex idx = new PropertyIndex(this, "ChatContact", id, localId, phone, photo, name, tagline, token, members, admins, muteUntil, createdBy, creationDate, lastActivityTime, chats); ChatContact If this is a group then it was created by a specific user. The id of that user should be listed here
  • 15. new Property<>("name"); public final Property<String, ChatContact> tagline = new Property<>("tagline"); public final Property<String, ChatContact> token = new Property<>("token"); public final SetProperty<String, ChatContact> members = new SetProperty<>("members", String.class); public final SetProperty<String, ChatContact> admins = new SetProperty<>("admins", String.class); public final LongProperty<ChatContact> muteUntil = new LongProperty<>("muteUntil"); public final Property<String, ChatContact> createdBy = new Property<>("createdBy"); public final Property<Date, ChatContact> creationDate = new Property<>("creationDate", Date.class); public final Property<Date, ChatContact> lastActivityTime = new Property<>("lastActivityTime", Date.class); public final ListProperty<ChatMessage, ChatContact> chats = new ListProperty<>("chat", ChatMessage.class); private final PropertyIndex idx = new PropertyIndex(this, "ChatContact", id, localId, phone, photo, name, tagline, token, members, admins, muteUntil, createdBy, creationDate, lastActivityTime, chats); ChatContact The creation date is applicable to both groups and individual users
  • 16. new Property<>("name"); public final Property<String, ChatContact> tagline = new Property<>("tagline"); public final Property<String, ChatContact> token = new Property<>("token"); public final SetProperty<String, ChatContact> members = new SetProperty<>("members", String.class); public final SetProperty<String, ChatContact> admins = new SetProperty<>("admins", String.class); public final LongProperty<ChatContact> muteUntil = new LongProperty<>("muteUntil"); public final Property<String, ChatContact> createdBy = new Property<>("createdBy"); public final Property<Date, ChatContact> creationDate = new Property<>("creationDate", Date.class); public final Property<Date, ChatContact> lastActivityTime = new Property<>("lastActivityTime", Date.class); public final ListProperty<ChatMessage, ChatContact> chats = new ListProperty<>("chat", ChatMessage.class); private final PropertyIndex idx = new PropertyIndex(this, "ChatContact", id, localId, phone, photo, name, tagline, token, members, admins, muteUntil, createdBy, creationDate, lastActivityTime, chats); ChatContact This is the timestamp of the last message we received from the given user. We saw this updated in the Server class. We use this to sort the chats by latest update
  • 17. new Property<>("name"); public final Property<String, ChatContact> tagline = new Property<>("tagline"); public final Property<String, ChatContact> token = new Property<>("token"); public final SetProperty<String, ChatContact> members = new SetProperty<>("members", String.class); public final SetProperty<String, ChatContact> admins = new SetProperty<>("admins", String.class); public final LongProperty<ChatContact> muteUntil = new LongProperty<>("muteUntil"); public final Property<String, ChatContact> createdBy = new Property<>("createdBy"); public final Property<Date, ChatContact> creationDate = new Property<>("creationDate", Date.class); public final Property<Date, ChatContact> lastActivityTime = new Property<>("lastActivityTime", Date.class); public final ListProperty<ChatMessage, ChatContact> chats = new ListProperty<>("chat", ChatMessage.class); private final PropertyIndex idx = new PropertyIndex(this, "ChatContact", id, localId, phone, photo, name, tagline, token, members, admins, muteUntil, createdBy, creationDate, lastActivityTime, chats); ChatContact ChatMessage is the property business object that contains the content of the message. Here we store the actual chats we had with the contact or group.
  • 18. new Property<>("createdBy"); public final Property<Date, ChatContact> creationDate = new Property<>("creationDate", Date.class); public final Property<Date, ChatContact> lastActivityTime = new Property<>("lastActivityTime", Date.class); public final ListProperty<ChatMessage, ChatContact> chats = new ListProperty<>("chat", ChatMessage.class); private final PropertyIndex idx = new PropertyIndex(this, "ChatContact", id, localId, phone, photo, name, tagline, token, members, admins, muteUntil, createdBy, creationDate, lastActivityTime, chats); @Override public PropertyIndex getPropertyIndex() { return idx; } public ChatContact() { idx.setExcludeFromJSON(photo, true); } private static final int SMALL_IMAGE = 0; private static final int LARGE_IMAGE = 1; private static final float[] IMAGE_SIZES = {4f, 6.5f}; ChatContact As I mentioned before photo isn’t stored in JSON when we save the contact to keep the size low. We save the contact image in a separate file and don't want too much noise here.
  • 19. return idx; } public ChatContact() { idx.setExcludeFromJSON(photo, true); } private static final int SMALL_IMAGE = 0; private static final int LARGE_IMAGE = 1; private static final float[] IMAGE_SIZES = {4f, 6.5f}; private static final Image[] maskImage = new Image[2]; private static final Object[] maskObject = new Object[2]; private static final EncodedImage[] placeholder = new EncodedImage[2]; private static Image createMaskImage(int size) { Image m = Image.createImage(size, size, 0); Graphics g = m.getGraphics(); g.setAntiAliased(true); g.setColor(0xffffff); g.fillArc(0, 0, size - 1, size -1, 0, 360); return m; } private Image createPlaceholder(int size) { ChatContact The app has two thumbnail images. One is slightly smaller than the other and both are rounded. To keep the code generic I use arrays with the detail and then use two sizes. The small size maps to the 0 offset in the array and the large size maps to the one offset.
  • 20. return idx; } public ChatContact() { idx.setExcludeFromJSON(photo, true); } private static final int SMALL_IMAGE = 0; private static final int LARGE_IMAGE = 1; private static final float[] IMAGE_SIZES = {4f, 6.5f}; private static final Image[] maskImage = new Image[2]; private static final Object[] maskObject = new Object[2]; private static final EncodedImage[] placeholder = new EncodedImage[2]; private static Image createMaskImage(int size) { Image m = Image.createImage(size, size, 0); Graphics g = m.getGraphics(); g.setAntiAliased(true); g.setColor(0xffffff); g.fillArc(0, 0, size - 1, size -1, 0, 360); return m; } private Image createPlaceholder(int size) { ChatContact Here are the sizes of these two images in millimeters
  • 21. return idx; } public ChatContact() { idx.setExcludeFromJSON(photo, true); } private static final int SMALL_IMAGE = 0; private static final int LARGE_IMAGE = 1; private static final float[] IMAGE_SIZES = {4f, 6.5f}; private static final Image[] maskImage = new Image[2]; private static final Object[] maskObject = new Object[2]; private static final EncodedImage[] placeholder = new EncodedImage[2]; private static Image createMaskImage(int size) { Image m = Image.createImage(size, size, 0); Graphics g = m.getGraphics(); g.setAntiAliased(true); g.setColor(0xffffff); g.fillArc(0, 0, size - 1, size -1, 0, 360); return m; } private Image createPlaceholder(int size) { ChatContact Images are masked to these sizes, masking allows us to round an image in this case
  • 22. return idx; } public ChatContact() { idx.setExcludeFromJSON(photo, true); } private static final int SMALL_IMAGE = 0; private static final int LARGE_IMAGE = 1; private static final float[] IMAGE_SIZES = {4f, 6.5f}; private static final Image[] maskImage = new Image[2]; private static final Object[] maskObject = new Object[2]; private static final EncodedImage[] placeholder = new EncodedImage[2]; private static Image createMaskImage(int size) { Image m = Image.createImage(size, size, 0); Graphics g = m.getGraphics(); g.setAntiAliased(true); g.setColor(0xffffff); g.fillArc(0, 0, size - 1, size -1, 0, 360); return m; } private Image createPlaceholder(int size) { ChatContact We generate placeholder images which are used when an image is unavailable
  • 23. return idx; } public ChatContact() { idx.setExcludeFromJSON(photo, true); } private static final int SMALL_IMAGE = 0; private static final int LARGE_IMAGE = 1; private static final float[] IMAGE_SIZES = {4f, 6.5f}; private static final Image[] maskImage = new Image[2]; private static final Object[] maskObject = new Object[2]; private static final EncodedImage[] placeholder = new EncodedImage[2]; private static Image createMaskImage(int size) { Image m = Image.createImage(size, size, 0); Graphics g = m.getGraphics(); g.setAntiAliased(true); g.setColor(0xffffff); g.fillArc(0, 0, size - 1, size -1, 0, 360); return m; } private Image createPlaceholder(int size) { ChatContact This method creates a mask image of a given size in pixels a mask image uses black pixels to represent transparency and white pixels to represent the visible opacity. So where we draw a black rectangle image with a white circle in the center. When we apply this mask to an image only the portion represented by the white circle will remain.
  • 24. Graphics g = m.getGraphics(); g.setAntiAliased(true); g.setColor(0xffffff); g.fillArc(0, 0, size - 1, size -1, 0, 360); return m; } private Image createPlaceholder(int size) { Image i = Image.createImage(size, size, 0xffe2e7ea); Graphics g = i.getGraphics(); g.setAntiAliased(true); g.setAntiAliasedText(true); g.setColor(0xffffff); Font fnt = FontImage.getMaterialDesignFont(). derive(size, Font.STYLE_PLAIN); g.setFont(fnt); String s = "" + FontImage.MATERIAL_PERSON; g.drawString(s, size / 2 - fnt.stringWidth(s) / 2, 0); return i; } private Image getImage(int offset) { if(maskImage[offset] == null) { ChatContact The placeholder image is used when no image is defined, again we create this based on a size in pixels. We create a gray image then draw on it using white. We use the material font to draw the image of a person onto this image
  • 25. g.drawString(s, size / 2 - fnt.stringWidth(s) / 2, 0); return i; } private Image getImage(int offset) { if(maskImage[offset] == null) { maskImage[offset] = createMaskImage(convertToPixels(IMAGE_SIZES[offset])); maskObject[offset] = maskImage[offset].createMask(); Image i = createPlaceholder( convertToPixels(IMAGE_SIZES[offset])); placeholder[offset] = EncodedImage.createFromImage( i.applyMask(maskObject[offset]), false); } if(photo.get() == null) { return placeholder[offset]; } return photo.get(). fill(maskImage[offset].getWidth(), maskImage[offset].getHeight()). applyMask(maskObject[offset]); } ChatContact This method gets the image represented by the contact. In theory I could have used the photo property and overridden get() to implement this. I thought this is a simpler approach.
  • 26. g.drawString(s, size / 2 - fnt.stringWidth(s) / 2, 0); return i; } private Image getImage(int offset) { if(maskImage[offset] == null) { maskImage[offset] = createMaskImage(convertToPixels(IMAGE_SIZES[offset])); maskObject[offset] = maskImage[offset].createMask(); Image i = createPlaceholder( convertToPixels(IMAGE_SIZES[offset])); placeholder[offset] = EncodedImage.createFromImage( i.applyMask(maskObject[offset]), false); } if(photo.get() == null) { return placeholder[offset]; } return photo.get(). fill(maskImage[offset].getWidth(), maskImage[offset].getHeight()). applyMask(maskObject[offset]); } ChatContact Here we lazily initialize the arrays of the mask images for large or small images.
  • 27. g.drawString(s, size / 2 - fnt.stringWidth(s) / 2, 0); return i; } private Image getImage(int offset) { if(maskImage[offset] == null) { maskImage[offset] = createMaskImage(convertToPixels(IMAGE_SIZES[offset])); maskObject[offset] = maskImage[offset].createMask(); Image i = createPlaceholder( convertToPixels(IMAGE_SIZES[offset])); placeholder[offset] = EncodedImage.createFromImage( i.applyMask(maskObject[offset]), false); } if(photo.get() == null) { return placeholder[offset]; } return photo.get(). fill(maskImage[offset].getWidth(), maskImage[offset].getHeight()). applyMask(maskObject[offset]); } ChatContact We create the mask images then convert the mask image to mask object. Finally we create the placeholder image
  • 28. g.drawString(s, size / 2 - fnt.stringWidth(s) / 2, 0); return i; } private Image getImage(int offset) { if(maskImage[offset] == null) { maskImage[offset] = createMaskImage(convertToPixels(IMAGE_SIZES[offset])); maskObject[offset] = maskImage[offset].createMask(); Image i = createPlaceholder( convertToPixels(IMAGE_SIZES[offset])); placeholder[offset] = EncodedImage.createFromImage( i.applyMask(maskObject[offset]), false); } if(photo.get() == null) { return placeholder[offset]; } return photo.get(). fill(maskImage[offset].getWidth(), maskImage[offset].getHeight()). applyMask(maskObject[offset]); } ChatContact If the photo is null I return the placeholder image instead of using the photo object
  • 29. g.drawString(s, size / 2 - fnt.stringWidth(s) / 2, 0); return i; } private Image getImage(int offset) { if(maskImage[offset] == null) { maskImage[offset] = createMaskImage(convertToPixels(IMAGE_SIZES[offset])); maskObject[offset] = maskImage[offset].createMask(); Image i = createPlaceholder( convertToPixels(IMAGE_SIZES[offset])); placeholder[offset] = EncodedImage.createFromImage( i.applyMask(maskObject[offset]), false); } if(photo.get() == null) { return placeholder[offset]; } return photo.get(). fill(maskImage[offset].getWidth(), maskImage[offset].getHeight()). applyMask(maskObject[offset]); } ChatContact Otherwise we fill the image into the size of the mask and apply the mask to create a round object. Fill scales the image so it’s cropped while filling the exact boundaries given. It doesn’t distort the aspect ratio of the image like a typical scale operation would
  • 30. applyMask(maskObject[offset]); } private Image smallIcon; private Image largeIcon; public Image getSmallIcon() { if(smallIcon != null) { return smallIcon; } smallIcon = getImage(SMALL_IMAGE); return smallIcon; } public Image getLargeIcon() { if(largeIcon != null) { return largeIcon; } largeIcon = getImage(LARGE_IMAGE); return largeIcon; } } ChatContact The final public methods and variables cache the small & large image. Appropriately they are the publicly exposed API’s for this functionality