SlideShare a Scribd company logo
1 of 27
Download to read offline
Creating a Facebook Clone - Part XXXVI
A big missing piece is the settings code.
© Codename One 2017 all rights reserved
One of the important things we need to expose is the ability to customize information about the user. Ideally this should be as flexible as possible so when we add
additional attributes to track they can be incorporated right into the UI.

I simplified a lot of the UI Facebook had and tried to focus on two big ticket items:

The Image Pickers & the generic user settings
public class ImagePicker {
private String fileName;
private EncodedImage img;
private int mode = GALLERY_IMAGE;
public ImagePicker() {
}
public ImagePicker(int mode) {
this.mode = mode;
}
public void pickImage(SuccessCallback<EncodedImage> onPick) {
Display.getInstance().openGallery(e -> {
if(e == null) {
return;
}
String s = (String)e.getSource();
if(s != null) {
try(InputStream i = openFileInputStream(s);) {
img = EncodedImage.create(i, (int)getFileLength(s));
fileName = s;
onPick.onSucess(img);
} catch(IOException err) {
ImagePicker
I've encapsulated image picking in the ImagePicker class, this is convenient as we might want to make it more elaborate in the future. At the moment it simply picks from
the device gallery.

We don't need a class in terms of implementation. It's there to encapsulate the meta-data of the image in a single operation
public class ImagePicker {
private String fileName;
private EncodedImage img;
private int mode = GALLERY_IMAGE;
public ImagePicker() {
}
public ImagePicker(int mode) {
this.mode = mode;
}
public void pickImage(SuccessCallback<EncodedImage> onPick) {
Display.getInstance().openGallery(e -> {
if(e == null) {
return;
}
String s = (String)e.getSource();
if(s != null) {
try(InputStream i = openFileInputStream(s);) {
img = EncodedImage.create(i, (int)getFileLength(s));
fileName = s;
onPick.onSucess(img);
} catch(IOException err) {
ImagePicker
The filename is useful for the upload process as we maintain that meta-data in the server for reference
public class ImagePicker {
private String fileName;
private EncodedImage img;
private int mode = GALLERY_IMAGE;
public ImagePicker() {
}
public ImagePicker(int mode) {
this.mode = mode;
}
public void pickImage(SuccessCallback<EncodedImage> onPick) {
Display.getInstance().openGallery(e -> {
if(e == null) {
return;
}
String s = (String)e.getSource();
if(s != null) {
try(InputStream i = openFileInputStream(s);) {
img = EncodedImage.create(i, (int)getFileLength(s));
fileName = s;
onPick.onSucess(img);
} catch(IOException err) {
ImagePicker
openGallery launches the OS native picker code in this case using image mode
private int mode = GALLERY_IMAGE;
public ImagePicker() {
}
public ImagePicker(int mode) {
this.mode = mode;
}
public void pickImage(SuccessCallback<EncodedImage> onPick) {
Display.getInstance().openGallery(e -> {
if(e == null) {
return;
}
String s = (String)e.getSource();
if(s != null) {
try(InputStream i = openFileInputStream(s);) {
img = EncodedImage.create(i, (int)getFileLength(s));
fileName = s;
onPick.onSucess(img);
} catch(IOException err) {
Log.e(err);
}
}
}, mode);
}
public void upload(SuccessCallback<String> mediaId) {
ServerAPI.uploadMedia("image/jpeg", "photo", "public", fileName,
ImagePicker
We keep the fileName & the image object
this.mode = mode;
}
public void pickImage(SuccessCallback<EncodedImage> onPick) {
Display.getInstance().openGallery(e -> {
if(e == null) {
return;
}
String s = (String)e.getSource();
if(s != null) {
try(InputStream i = openFileInputStream(s);) {
img = EncodedImage.create(i, (int)getFileLength(s));
fileName = s;
onPick.onSucess(img);
} catch(IOException err) {
Log.e(err);
}
}
}, mode);
}
public void upload(SuccessCallback<String> mediaId) {
ServerAPI.uploadMedia("image/jpeg", "photo", "public", fileName,
img.getImageData(), mediaId);
}
}
ImagePicker
This class encapsulates the upload feature too so the filename can remain encapsulated. That's pretty simple, we could possibly do more by offering an option to launch
the camera etc.
public class SettingsForm extends Form {
private Label cover = new Label(" ", "LoginTitle");
private Label avatar = new Label("", "LabelFrame");
private Button changeCover =
new Button(MATERIAL_CAMERA_ALT, "CameraLabel");
private Button changeAvatar =
new Button(MATERIAL_CAMERA_ALT, "CameraLabel");
public SettingsForm() {
super("", BoxLayout.y());
Form previous = getCurrentForm();
getToolbar().setBackCommand("Back", e -> previous.showBack());
User me = ServerAPI.me();
avatar.setIcon(me.getAvatar(12));
if(me.cover.get() != null) {
me.fetchCoverImage(i -> {
cover.getAllStyles().setBgImage(i);
repaint();
});
}
changeAvatar.addActionListener(e -> pickAvatar(avatar));
SettingsForm
The settings UI is encapsulated in the SettingsForm class.
public class SettingsForm extends Form {
private Label cover = new Label(" ", "LoginTitle");
private Label avatar = new Label("", "LabelFrame");
private Button changeCover =
new Button(MATERIAL_CAMERA_ALT, "CameraLabel");
private Button changeAvatar =
new Button(MATERIAL_CAMERA_ALT, "CameraLabel");
public SettingsForm() {
super("", BoxLayout.y());
Form previous = getCurrentForm();
getToolbar().setBackCommand("Back", e -> previous.showBack());
User me = ServerAPI.me();
avatar.setIcon(me.getAvatar(12));
if(me.cover.get() != null) {
me.fetchCoverImage(i -> {
cover.getAllStyles().setBgImage(i);
repaint();
});
}
changeAvatar.addActionListener(e -> pickAvatar(avatar));
SettingsForm
cover & avatar represent the two images in the settings UI, the background cover image and avatar image
public class SettingsForm extends Form {
private Label cover = new Label(" ", "LoginTitle");
private Label avatar = new Label("", "LabelFrame");
private Button changeCover =
new Button(MATERIAL_CAMERA_ALT, "CameraLabel");
private Button changeAvatar =
new Button(MATERIAL_CAMERA_ALT, "CameraLabel");
public SettingsForm() {
super("", BoxLayout.y());
Form previous = getCurrentForm();
getToolbar().setBackCommand("Back", e -> previous.showBack());
User me = ServerAPI.me();
avatar.setIcon(me.getAvatar(12));
if(me.cover.get() != null) {
me.fetchCoverImage(i -> {
cover.getAllStyles().setBgImage(i);
repaint();
});
}
changeAvatar.addActionListener(e -> pickAvatar(avatar));
SettingsForm
The two change fields represent the camera change buttons that allow us to replace the avatar images
new Button(MATERIAL_CAMERA_ALT, "CameraLabel");
private Button changeAvatar =
new Button(MATERIAL_CAMERA_ALT, "CameraLabel");
public SettingsForm() {
super("", BoxLayout.y());
Form previous = getCurrentForm();
getToolbar().setBackCommand("Back", e -> previous.showBack());
User me = ServerAPI.me();
avatar.setIcon(me.getAvatar(12));
if(me.cover.get() != null) {
me.fetchCoverImage(i -> {
cover.getAllStyles().setBgImage(i);
repaint();
});
}
changeAvatar.addActionListener(e -> pickAvatar(avatar));
changeCover.addActionListener(e -> pickCover(cover));
Container coverContainer = LayeredLayout.encloseIn(
cover,
FlowLayout.encloseRightBottom(changeCover)
);
coverContainer.setUIID("SettingsMargin");
Container avatarContainer = LayeredLayout.encloseIn(
avatar,
SettingsForm
Next lets explore the constructor code, it's mostly standard and creates the main settings UI for replacing the images. Here we bind the back button behavior
new Button(MATERIAL_CAMERA_ALT, "CameraLabel");
private Button changeAvatar =
new Button(MATERIAL_CAMERA_ALT, "CameraLabel");
public SettingsForm() {
super("", BoxLayout.y());
Form previous = getCurrentForm();
getToolbar().setBackCommand("Back", e -> previous.showBack());
User me = ServerAPI.me();
avatar.setIcon(me.getAvatar(12));
if(me.cover.get() != null) {
me.fetchCoverImage(i -> {
cover.getAllStyles().setBgImage(i);
repaint();
});
}
changeAvatar.addActionListener(e -> pickAvatar(avatar));
changeCover.addActionListener(e -> pickCover(cover));
Container coverContainer = LayeredLayout.encloseIn(
cover,
FlowLayout.encloseRightBottom(changeCover)
);
coverContainer.setUIID("SettingsMargin");
Container avatarContainer = LayeredLayout.encloseIn(
avatar,
SettingsForm
The current avatar is set into the avatar label
new Button(MATERIAL_CAMERA_ALT, "CameraLabel");
private Button changeAvatar =
new Button(MATERIAL_CAMERA_ALT, "CameraLabel");
public SettingsForm() {
super("", BoxLayout.y());
Form previous = getCurrentForm();
getToolbar().setBackCommand("Back", e -> previous.showBack());
User me = ServerAPI.me();
avatar.setIcon(me.getAvatar(12));
if(me.cover.get() != null) {
me.fetchCoverImage(i -> {
cover.getAllStyles().setBgImage(i);
repaint();
});
}
changeAvatar.addActionListener(e -> pickAvatar(avatar));
changeCover.addActionListener(e -> pickCover(cover));
Container coverContainer = LayeredLayout.encloseIn(
cover,
FlowLayout.encloseRightBottom(changeCover)
);
coverContainer.setUIID("SettingsMargin");
Container avatarContainer = LayeredLayout.encloseIn(
avatar,
SettingsForm
If a cover image is set we fetch the image asynchronously
new Button(MATERIAL_CAMERA_ALT, "CameraLabel");
private Button changeAvatar =
new Button(MATERIAL_CAMERA_ALT, "CameraLabel");
public SettingsForm() {
super("", BoxLayout.y());
Form previous = getCurrentForm();
getToolbar().setBackCommand("Back", e -> previous.showBack());
User me = ServerAPI.me();
avatar.setIcon(me.getAvatar(12));
if(me.cover.get() != null) {
me.fetchCoverImage(i -> {
cover.getAllStyles().setBgImage(i);
repaint();
});
}
changeAvatar.addActionListener(e -> pickAvatar(avatar));
changeCover.addActionListener(e -> pickCover(cover));
Container coverContainer = LayeredLayout.encloseIn(
cover,
FlowLayout.encloseRightBottom(changeCover)
);
coverContainer.setUIID("SettingsMargin");
Container avatarContainer = LayeredLayout.encloseIn(
avatar,
SettingsForm
Both image buttons delegate to picker methods so we can replace that logic in the future
super("", BoxLayout.y());
Form previous = getCurrentForm();
getToolbar().setBackCommand("Back", e -> previous.showBack());
User me = ServerAPI.me();
avatar.setIcon(me.getAvatar(12));
if(me.cover.get() != null) {
me.fetchCoverImage(i -> {
cover.getAllStyles().setBgImage(i);
repaint();
});
}
changeAvatar.addActionListener(e -> pickAvatar(avatar));
changeCover.addActionListener(e -> pickCover(cover));
Container coverContainer = LayeredLayout.encloseIn(
cover,
FlowLayout.encloseRightBottom(changeCover)
);
coverContainer.setUIID("SettingsMargin");
Container avatarContainer = LayeredLayout.encloseIn(
avatar,
FlowLayout.encloseRightBottom(changeAvatar)
);
add(LayeredLayout.encloseIn(
coverContainer,
FlowLayout.encloseCenterBottom(avatarContainer)
SettingsForm
The cover label places the change button on top using a LayeredLayout & wraps the button in a flow layout so it will align to the bottom right
super("", BoxLayout.y());
Form previous = getCurrentForm();
getToolbar().setBackCommand("Back", e -> previous.showBack());
User me = ServerAPI.me();
avatar.setIcon(me.getAvatar(12));
if(me.cover.get() != null) {
me.fetchCoverImage(i -> {
cover.getAllStyles().setBgImage(i);
repaint();
});
}
changeAvatar.addActionListener(e -> pickAvatar(avatar));
changeCover.addActionListener(e -> pickCover(cover));
Container coverContainer = LayeredLayout.encloseIn(
cover,
FlowLayout.encloseRightBottom(changeCover)
);
coverContainer.setUIID("SettingsMargin");
Container avatarContainer = LayeredLayout.encloseIn(
avatar,
FlowLayout.encloseRightBottom(changeAvatar)
);
add(LayeredLayout.encloseIn(
coverContainer,
FlowLayout.encloseCenterBottom(avatarContainer)
SettingsForm
We do the exact same thing with the avatar image and its picker button
});
}
changeAvatar.addActionListener(e -> pickAvatar(avatar));
changeCover.addActionListener(e -> pickCover(cover));
Container coverContainer = LayeredLayout.encloseIn(
cover,
FlowLayout.encloseRightBottom(changeCover)
);
coverContainer.setUIID("SettingsMargin");
Container avatarContainer = LayeredLayout.encloseIn(
avatar,
FlowLayout.encloseRightBottom(changeAvatar)
);
add(LayeredLayout.encloseIn(
coverContainer,
FlowLayout.encloseCenterBottom(avatarContainer)
));
add(new Label(me.fullName(), "CenterLargeThinLabel"));
add(createButtonBar());
}
private Container createButtonBar() {
Button activity = new Button(MATERIAL_HISTORY, "CleanButton");
Button settings = new Button(MATERIAL_SETTINGS, "CleanButton");
Button viewAs = new Button(MATERIAL_ACCOUNT_CIRCLE, "CleanButton");
SettingsForm
We then wrap both of those LayeredLayout’s in another layered layout and the avatar in a FlowLayout to align it to the bottom center region.

The LayeredLayout usage raises the obvious question of why not use one layered layout instead of three?

That would be hard to accomplish with the picker button. We would have a hard time aligning the picker button of the avatar to the bottom right corner since the parent
layout would be too big.
FlowLayout.encloseCenterBottom(avatarContainer)
));
add(new Label(me.fullName(), "CenterLargeThinLabel"));
add(createButtonBar());
}
private Container createButtonBar() {
Button activity = new Button(MATERIAL_HISTORY, "CleanButton");
Button settings = new Button(MATERIAL_SETTINGS, "CleanButton");
Button viewAs = new Button(MATERIAL_ACCOUNT_CIRCLE, "CleanButton");
Button more = new Button(MATERIAL_MORE_HORIZ, "CleanButton");
settings.addActionListener(e ->
showUserEditForm(ServerAPI.me()));
return GridLayout.encloseIn(4, activity, settings, viewAs, more);
}
private void pickAvatar(Label l) {
ImagePicker p = new ImagePicker();
p.pickImage(img -> {
p.upload(mediaId -> {
ServerAPI.me().avatar.set(mediaId);
ServerAPI.setAvatar(mediaId);
Image temp = img.fill(l.getIcon().getWidth(),
l.getIcon().getHeight());
temp = temp.applyMask(ServerAPI.me().
SettingsForm
Next up are the buttons we create at the bottom of the UI as mentioned at the end of the constructor.

The button bar are just 4 buttons in a horizontal grid, we only map the settings to functionality of full editing in showUserEditForm
Button viewAs = new Button(MATERIAL_ACCOUNT_CIRCLE, "CleanButton");
Button more = new Button(MATERIAL_MORE_HORIZ, "CleanButton");
settings.addActionListener(e ->
showUserEditForm(ServerAPI.me()));
return GridLayout.encloseIn(4, activity, settings, viewAs, more);
}
private void pickAvatar(Label l) {
ImagePicker p = new ImagePicker();
p.pickImage(img -> {
p.upload(mediaId -> {
ServerAPI.me().avatar.set(mediaId);
ServerAPI.setAvatar(mediaId);
Image temp = img.fill(l.getIcon().getWidth(),
l.getIcon().getHeight());
temp = temp.applyMask(ServerAPI.me().
getAvatarMask(temp.getWidth()).createMask());
l.setIcon(temp);
});
});
}
private void pickCover(Label l) {
ImagePicker p = new ImagePicker();
p.pickImage(img -> {
p.upload(mediaId -> {
SettingsForm
ImagePicker does most of the heavy lifting here we launch it and get an image object back if it proceeded
Button viewAs = new Button(MATERIAL_ACCOUNT_CIRCLE, "CleanButton");
Button more = new Button(MATERIAL_MORE_HORIZ, "CleanButton");
settings.addActionListener(e ->
showUserEditForm(ServerAPI.me()));
return GridLayout.encloseIn(4, activity, settings, viewAs, more);
}
private void pickAvatar(Label l) {
ImagePicker p = new ImagePicker();
p.pickImage(img -> {
p.upload(mediaId -> {
ServerAPI.me().avatar.set(mediaId);
ServerAPI.setAvatar(mediaId);
Image temp = img.fill(l.getIcon().getWidth(),
l.getIcon().getHeight());
temp = temp.applyMask(ServerAPI.me().
getAvatarMask(temp.getWidth()).createMask());
l.setIcon(temp);
});
});
}
private void pickCover(Label l) {
ImagePicker p = new ImagePicker();
p.pickImage(img -> {
p.upload(mediaId -> {
SettingsForm
We upload the image and get a callback with the media id value
Button viewAs = new Button(MATERIAL_ACCOUNT_CIRCLE, "CleanButton");
Button more = new Button(MATERIAL_MORE_HORIZ, "CleanButton");
settings.addActionListener(e ->
showUserEditForm(ServerAPI.me()));
return GridLayout.encloseIn(4, activity, settings, viewAs, more);
}
private void pickAvatar(Label l) {
ImagePicker p = new ImagePicker();
p.pickImage(img -> {
p.upload(mediaId -> {
ServerAPI.me().avatar.set(mediaId);
ServerAPI.setAvatar(mediaId);
Image temp = img.fill(l.getIcon().getWidth(),
l.getIcon().getHeight());
temp = temp.applyMask(ServerAPI.me().
getAvatarMask(temp.getWidth()).createMask());
l.setIcon(temp);
});
});
}
private void pickCover(Label l) {
ImagePicker p = new ImagePicker();
p.pickImage(img -> {
p.upload(mediaId -> {
SettingsForm
The avatar is set both locally & remotely so we don't need a server refresh call
Button viewAs = new Button(MATERIAL_ACCOUNT_CIRCLE, "CleanButton");
Button more = new Button(MATERIAL_MORE_HORIZ, "CleanButton");
settings.addActionListener(e ->
showUserEditForm(ServerAPI.me()));
return GridLayout.encloseIn(4, activity, settings, viewAs, more);
}
private void pickAvatar(Label l) {
ImagePicker p = new ImagePicker();
p.pickImage(img -> {
p.upload(mediaId -> {
ServerAPI.me().avatar.set(mediaId);
ServerAPI.setAvatar(mediaId);
Image temp = img.fill(l.getIcon().getWidth(),
l.getIcon().getHeight());
temp = temp.applyMask(ServerAPI.me().
getAvatarMask(temp.getWidth()).createMask());
l.setIcon(temp);
});
});
}
private void pickCover(Label l) {
ImagePicker p = new ImagePicker();
p.pickImage(img -> {
p.upload(mediaId -> {
SettingsForm
We do refresh the icon though and make sure to shape it as it was before
temp = temp.applyMask(ServerAPI.me().
getAvatarMask(temp.getWidth()).createMask());
l.setIcon(temp);
});
});
}
private void pickCover(Label l) {
ImagePicker p = new ImagePicker();
p.pickImage(img -> {
p.upload(mediaId -> {
ServerAPI.me().cover.set(mediaId);
ServerAPI.setCover(mediaId);
l.getAllStyles().setBgImage(img);
});
});
}
@Override
protected void initGlobalToolbar() {
Toolbar tb = new Toolbar(true);
tb.setUIID("Container");
setToolbar(tb);
}
SettingsForm
Cover picking is very similar but has simpler code as we don't need to shape the resulting image & have a simpler API
p.pickImage(img -> {
p.upload(mediaId -> {
ServerAPI.me().cover.set(mediaId);
ServerAPI.setCover(mediaId);
l.getAllStyles().setBgImage(img);
});
});
}
@Override
protected void initGlobalToolbar() {
Toolbar tb = new Toolbar(true);
tb.setUIID("Container");
setToolbar(tb);
}
private void showUserEditForm(User me) {
InstantUI iu = new InstantUI();
iu.excludeProperties(me.authtoken, me.avatar, me.cover,
me.friendRequests, me.friends, me.peopleYouMayKnow, me.id,
me.password, me.birthday);
iu.setMultiChoiceLabels(me.gender, "Male", "Female", "Other");
iu.setMultiChoiceValues(me.gender, "Male", "Female", "Other");
Container cnt = iu.createEditUI(me, true);
cnt.setUIID("PaddedContainer");
SettingsForm
Next we override this method to have a custom toolbar
p.pickImage(img -> {
p.upload(mediaId -> {
ServerAPI.me().cover.set(mediaId);
ServerAPI.setCover(mediaId);
l.getAllStyles().setBgImage(img);
});
});
}
@Override
protected void initGlobalToolbar() {
Toolbar tb = new Toolbar(true);
tb.setUIID("Container");
setToolbar(tb);
}
private void showUserEditForm(User me) {
InstantUI iu = new InstantUI();
iu.excludeProperties(me.authtoken, me.avatar, me.cover,
me.friendRequests, me.friends, me.peopleYouMayKnow, me.id,
me.password, me.birthday);
iu.setMultiChoiceLabels(me.gender, "Male", "Female", "Other");
iu.setMultiChoiceValues(me.gender, "Male", "Female", "Other");
Container cnt = iu.createEditUI(me, true);
cnt.setUIID("PaddedContainer");
SettingsForm
The toolbar is created using the "layered" mode (the true argument) which makes the content run under it
p.pickImage(img -> {
p.upload(mediaId -> {
ServerAPI.me().cover.set(mediaId);
ServerAPI.setCover(mediaId);
l.getAllStyles().setBgImage(img);
});
});
}
@Override
protected void initGlobalToolbar() {
Toolbar tb = new Toolbar(true);
tb.setUIID("Container");
setToolbar(tb);
}
private void showUserEditForm(User me) {
InstantUI iu = new InstantUI();
iu.excludeProperties(me.authtoken, me.avatar, me.cover,
me.friendRequests, me.friends, me.peopleYouMayKnow, me.id,
me.password, me.birthday);
iu.setMultiChoiceLabels(me.gender, "Male", "Female", "Other");
iu.setMultiChoiceValues(me.gender, "Male", "Female", "Other");
Container cnt = iu.createEditUI(me, true);
cnt.setUIID("PaddedContainer");
SettingsForm
We use the Container UIID for the `Toolbar` to make it transparent
Toolbar tb = new Toolbar(true);
tb.setUIID("Container");
setToolbar(tb);
}
private void showUserEditForm(User me) {
InstantUI iu = new InstantUI();
iu.excludeProperties(me.authtoken, me.avatar, me.cover,
me.friendRequests, me.friends, me.peopleYouMayKnow, me.id,
me.password, me.birthday);
iu.setMultiChoiceLabels(me.gender, "Male", "Female", "Other");
iu.setMultiChoiceValues(me.gender, "Male", "Female", "Other");
Container cnt = iu.createEditUI(me, true);
cnt.setUIID("PaddedContainer");
cnt.setScrollableY(true);
Form edit = new Form("Edit", new BorderLayout());
edit.add(BorderLayout.CENTER, cnt);
edit.getToolbar().setBackCommand("Back", e -> {
showBack();
UiBinding.unbind(me);
callSerially(() -> ServerAPI.update(me));
});
edit.show();
}
}
SettingsForm
Finally the last piece of code is the showUserEditForm(). Since there is still a lot to cover here I’ll continue in the next lesson

More Related Content

Similar to Facebook Clone Settings Code

Creating an Uber Clone - Part XXXX.pdf
Creating an Uber Clone - Part XXXX.pdfCreating an Uber Clone - Part XXXX.pdf
Creating an Uber Clone - Part XXXX.pdfShaiAlmog1
 
i am unsure how to make the button below the image instead of on it- a.pdf
i am unsure how to make the button below the image instead of on it- a.pdfi am unsure how to make the button below the image instead of on it- a.pdf
i am unsure how to make the button below the image instead of on it- a.pdfMattU5mLambertq
 
Creating an Uber Clone - Part XXIV.pdf
Creating an Uber Clone - Part XXIV.pdfCreating an Uber Clone - Part XXIV.pdf
Creating an Uber Clone - Part XXIV.pdfShaiAlmog1
 
This is Java,I am currently stumped on how to add a scoreboard for.pdf
This is Java,I am currently stumped on how to add a scoreboard for.pdfThis is Java,I am currently stumped on how to add a scoreboard for.pdf
This is Java,I am currently stumped on how to add a scoreboard for.pdfanjandavid
 
Creating a Facebook Clone - Part XLVI - Transcript.pdf
Creating a Facebook Clone - Part XLVI - Transcript.pdfCreating a Facebook Clone - Part XLVI - Transcript.pdf
Creating a Facebook Clone - Part XLVI - Transcript.pdfShaiAlmog1
 
JSLab. Алексей Волков. "React на практике"
JSLab. Алексей Волков. "React на практике"JSLab. Алексей Волков. "React на практике"
JSLab. Алексей Волков. "React на практике"GeeksLab Odessa
 
Creating a Facebook Clone - Part XLI.pdf
Creating a Facebook Clone - Part XLI.pdfCreating a Facebook Clone - Part XLI.pdf
Creating a Facebook Clone - Part XLI.pdfShaiAlmog1
 
Gutenberg sous le capot, modules réutilisables
Gutenberg sous le capot, modules réutilisablesGutenberg sous le capot, modules réutilisables
Gutenberg sous le capot, modules réutilisablesRiad Benguella
 
i need a remove button to remove the image from my favourites- however.pdf
i need a remove button to remove the image from my favourites- however.pdfi need a remove button to remove the image from my favourites- however.pdf
i need a remove button to remove the image from my favourites- however.pdfshreeaadithyaacellso
 
MySQL User Conference 2009: Python and MySQL
MySQL User Conference 2009: Python and MySQLMySQL User Conference 2009: Python and MySQL
MySQL User Conference 2009: Python and MySQLTed Leung
 
Modify the following source code so that when the mouse is clicked w.pdf
Modify the following source code so that when the mouse is clicked w.pdfModify the following source code so that when the mouse is clicked w.pdf
Modify the following source code so that when the mouse is clicked w.pdfarorastores
 
I phone勉強会 (2011.11.23)
I phone勉強会 (2011.11.23)I phone勉強会 (2011.11.23)
I phone勉強会 (2011.11.23)Katsumi Kishikawa
 
Initial UI Mockup - Part 3.pdf
Initial UI Mockup - Part 3.pdfInitial UI Mockup - Part 3.pdf
Initial UI Mockup - Part 3.pdfShaiAlmog1
 
Intro programacion funcional
Intro programacion funcionalIntro programacion funcional
Intro programacion funcionalNSCoder Mexico
 
Capture image on eye blink
Capture image on eye blinkCapture image on eye blink
Capture image on eye blinkInnovationM
 
Neues aus dem Tindergarten: Auswertung "privater" APIs mit Apache Ignite
Neues aus dem Tindergarten: Auswertung "privater" APIs mit Apache IgniteNeues aus dem Tindergarten: Auswertung "privater" APIs mit Apache Ignite
Neues aus dem Tindergarten: Auswertung "privater" APIs mit Apache IgniteQAware GmbH
 

Similar to Facebook Clone Settings Code (20)

Creating an Uber Clone - Part XXXX.pdf
Creating an Uber Clone - Part XXXX.pdfCreating an Uber Clone - Part XXXX.pdf
Creating an Uber Clone - Part XXXX.pdf
 
i am unsure how to make the button below the image instead of on it- a.pdf
i am unsure how to make the button below the image instead of on it- a.pdfi am unsure how to make the button below the image instead of on it- a.pdf
i am unsure how to make the button below the image instead of on it- a.pdf
 
Creating an Uber Clone - Part XXIV.pdf
Creating an Uber Clone - Part XXIV.pdfCreating an Uber Clone - Part XXIV.pdf
Creating an Uber Clone - Part XXIV.pdf
 
This is Java,I am currently stumped on how to add a scoreboard for.pdf
This is Java,I am currently stumped on how to add a scoreboard for.pdfThis is Java,I am currently stumped on how to add a scoreboard for.pdf
This is Java,I am currently stumped on how to add a scoreboard for.pdf
 
Creating a Facebook Clone - Part XLVI - Transcript.pdf
Creating a Facebook Clone - Part XLVI - Transcript.pdfCreating a Facebook Clone - Part XLVI - Transcript.pdf
Creating a Facebook Clone - Part XLVI - Transcript.pdf
 
JSLab. Алексей Волков. "React на практике"
JSLab. Алексей Волков. "React на практике"JSLab. Алексей Волков. "React на практике"
JSLab. Алексей Волков. "React на практике"
 
Creating a Facebook Clone - Part XLI.pdf
Creating a Facebook Clone - Part XLI.pdfCreating a Facebook Clone - Part XLI.pdf
Creating a Facebook Clone - Part XLI.pdf
 
Gutenberg sous le capot, modules réutilisables
Gutenberg sous le capot, modules réutilisablesGutenberg sous le capot, modules réutilisables
Gutenberg sous le capot, modules réutilisables
 
i need a remove button to remove the image from my favourites- however.pdf
i need a remove button to remove the image from my favourites- however.pdfi need a remove button to remove the image from my favourites- however.pdf
i need a remove button to remove the image from my favourites- however.pdf
 
MySQL User Conference 2009: Python and MySQL
MySQL User Conference 2009: Python and MySQLMySQL User Conference 2009: Python and MySQL
MySQL User Conference 2009: Python and MySQL
 
Clean Javascript
Clean JavascriptClean Javascript
Clean Javascript
 
Modify the following source code so that when the mouse is clicked w.pdf
Modify the following source code so that when the mouse is clicked w.pdfModify the following source code so that when the mouse is clicked w.pdf
Modify the following source code so that when the mouse is clicked w.pdf
 
662305 LAB13
662305 LAB13662305 LAB13
662305 LAB13
 
I phone勉強会 (2011.11.23)
I phone勉強会 (2011.11.23)I phone勉強会 (2011.11.23)
I phone勉強会 (2011.11.23)
 
Initial UI Mockup - Part 3.pdf
Initial UI Mockup - Part 3.pdfInitial UI Mockup - Part 3.pdf
Initial UI Mockup - Part 3.pdf
 
Backbone Basics with Examples
Backbone Basics with ExamplesBackbone Basics with Examples
Backbone Basics with Examples
 
Wicket 6
Wicket 6Wicket 6
Wicket 6
 
Intro programacion funcional
Intro programacion funcionalIntro programacion funcional
Intro programacion funcional
 
Capture image on eye blink
Capture image on eye blinkCapture image on eye blink
Capture image on eye blink
 
Neues aus dem Tindergarten: Auswertung "privater" APIs mit Apache Ignite
Neues aus dem Tindergarten: Auswertung "privater" APIs mit Apache IgniteNeues aus dem Tindergarten: Auswertung "privater" APIs mit Apache Ignite
Neues aus dem Tindergarten: Auswertung "privater" APIs mit Apache Ignite
 

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 II.pdf
Creating a Whatsapp Clone - Part II.pdfCreating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part II.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part IX - Transcript.pdf
Creating a Whatsapp Clone - Part IX - Transcript.pdfCreating a Whatsapp Clone - Part IX - Transcript.pdf
Creating a Whatsapp Clone - Part IX - Transcript.pdfShaiAlmog1
 
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 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 IV - Transcript.pdf
Creating a Whatsapp Clone - Part IV - Transcript.pdfCreating a Whatsapp Clone - Part IV - Transcript.pdf
Creating a Whatsapp Clone - Part IV - Transcript.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part IV.pdf
Creating a Whatsapp Clone - Part IV.pdfCreating a Whatsapp Clone - Part IV.pdf
Creating a Whatsapp Clone - Part IV.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
 

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 II.pdf
Creating a Whatsapp Clone - Part II.pdfCreating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part II.pdf
 
Creating a Whatsapp Clone - Part IX - Transcript.pdf
Creating a Whatsapp Clone - Part IX - Transcript.pdfCreating a Whatsapp Clone - Part IX - Transcript.pdf
Creating a Whatsapp Clone - Part IX - Transcript.pdf
 
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 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 IV - Transcript.pdf
Creating a Whatsapp Clone - Part IV - Transcript.pdfCreating a Whatsapp Clone - Part IV - Transcript.pdf
Creating a Whatsapp Clone - Part IV - Transcript.pdf
 
Creating a Whatsapp Clone - Part IV.pdf
Creating a Whatsapp Clone - Part IV.pdfCreating a Whatsapp Clone - Part IV.pdf
Creating a Whatsapp Clone - Part IV.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
 

Recently uploaded

Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersThousandEyes
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?XfilesPro
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
Hyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your Budget
Hyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your BudgetHyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your Budget
Hyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your BudgetEnjoy Anytime
 
Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxOnBoard
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphNeo4j
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Allon Mureinik
 
Snow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter RoadsSnow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter RoadsHyundai Motor Group
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhisoniya singh
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Alan Dix
 
Next-generation AAM aircraft unveiled by Supernal, S-A2
Next-generation AAM aircraft unveiled by Supernal, S-A2Next-generation AAM aircraft unveiled by Supernal, S-A2
Next-generation AAM aircraft unveiled by Supernal, S-A2Hyundai Motor Group
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 

Recently uploaded (20)

Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping Elbows
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
Hyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your Budget
Hyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your BudgetHyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your Budget
Hyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your Budget
 
Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptx
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)
 
Snow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter RoadsSnow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter Roads
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
 
Next-generation AAM aircraft unveiled by Supernal, S-A2
Next-generation AAM aircraft unveiled by Supernal, S-A2Next-generation AAM aircraft unveiled by Supernal, S-A2
Next-generation AAM aircraft unveiled by Supernal, S-A2
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 

Facebook Clone Settings Code

  • 1. Creating a Facebook Clone - Part XXXVI A big missing piece is the settings code.
  • 2. © Codename One 2017 all rights reserved One of the important things we need to expose is the ability to customize information about the user. Ideally this should be as flexible as possible so when we add additional attributes to track they can be incorporated right into the UI. I simplified a lot of the UI Facebook had and tried to focus on two big ticket items: The Image Pickers & the generic user settings
  • 3. public class ImagePicker { private String fileName; private EncodedImage img; private int mode = GALLERY_IMAGE; public ImagePicker() { } public ImagePicker(int mode) { this.mode = mode; } public void pickImage(SuccessCallback<EncodedImage> onPick) { Display.getInstance().openGallery(e -> { if(e == null) { return; } String s = (String)e.getSource(); if(s != null) { try(InputStream i = openFileInputStream(s);) { img = EncodedImage.create(i, (int)getFileLength(s)); fileName = s; onPick.onSucess(img); } catch(IOException err) { ImagePicker I've encapsulated image picking in the ImagePicker class, this is convenient as we might want to make it more elaborate in the future. At the moment it simply picks from the device gallery. We don't need a class in terms of implementation. It's there to encapsulate the meta-data of the image in a single operation
  • 4. public class ImagePicker { private String fileName; private EncodedImage img; private int mode = GALLERY_IMAGE; public ImagePicker() { } public ImagePicker(int mode) { this.mode = mode; } public void pickImage(SuccessCallback<EncodedImage> onPick) { Display.getInstance().openGallery(e -> { if(e == null) { return; } String s = (String)e.getSource(); if(s != null) { try(InputStream i = openFileInputStream(s);) { img = EncodedImage.create(i, (int)getFileLength(s)); fileName = s; onPick.onSucess(img); } catch(IOException err) { ImagePicker The filename is useful for the upload process as we maintain that meta-data in the server for reference
  • 5. public class ImagePicker { private String fileName; private EncodedImage img; private int mode = GALLERY_IMAGE; public ImagePicker() { } public ImagePicker(int mode) { this.mode = mode; } public void pickImage(SuccessCallback<EncodedImage> onPick) { Display.getInstance().openGallery(e -> { if(e == null) { return; } String s = (String)e.getSource(); if(s != null) { try(InputStream i = openFileInputStream(s);) { img = EncodedImage.create(i, (int)getFileLength(s)); fileName = s; onPick.onSucess(img); } catch(IOException err) { ImagePicker openGallery launches the OS native picker code in this case using image mode
  • 6. private int mode = GALLERY_IMAGE; public ImagePicker() { } public ImagePicker(int mode) { this.mode = mode; } public void pickImage(SuccessCallback<EncodedImage> onPick) { Display.getInstance().openGallery(e -> { if(e == null) { return; } String s = (String)e.getSource(); if(s != null) { try(InputStream i = openFileInputStream(s);) { img = EncodedImage.create(i, (int)getFileLength(s)); fileName = s; onPick.onSucess(img); } catch(IOException err) { Log.e(err); } } }, mode); } public void upload(SuccessCallback<String> mediaId) { ServerAPI.uploadMedia("image/jpeg", "photo", "public", fileName, ImagePicker We keep the fileName & the image object
  • 7. this.mode = mode; } public void pickImage(SuccessCallback<EncodedImage> onPick) { Display.getInstance().openGallery(e -> { if(e == null) { return; } String s = (String)e.getSource(); if(s != null) { try(InputStream i = openFileInputStream(s);) { img = EncodedImage.create(i, (int)getFileLength(s)); fileName = s; onPick.onSucess(img); } catch(IOException err) { Log.e(err); } } }, mode); } public void upload(SuccessCallback<String> mediaId) { ServerAPI.uploadMedia("image/jpeg", "photo", "public", fileName, img.getImageData(), mediaId); } } ImagePicker This class encapsulates the upload feature too so the filename can remain encapsulated. That's pretty simple, we could possibly do more by offering an option to launch the camera etc.
  • 8. public class SettingsForm extends Form { private Label cover = new Label(" ", "LoginTitle"); private Label avatar = new Label("", "LabelFrame"); private Button changeCover = new Button(MATERIAL_CAMERA_ALT, "CameraLabel"); private Button changeAvatar = new Button(MATERIAL_CAMERA_ALT, "CameraLabel"); public SettingsForm() { super("", BoxLayout.y()); Form previous = getCurrentForm(); getToolbar().setBackCommand("Back", e -> previous.showBack()); User me = ServerAPI.me(); avatar.setIcon(me.getAvatar(12)); if(me.cover.get() != null) { me.fetchCoverImage(i -> { cover.getAllStyles().setBgImage(i); repaint(); }); } changeAvatar.addActionListener(e -> pickAvatar(avatar)); SettingsForm The settings UI is encapsulated in the SettingsForm class.
  • 9. public class SettingsForm extends Form { private Label cover = new Label(" ", "LoginTitle"); private Label avatar = new Label("", "LabelFrame"); private Button changeCover = new Button(MATERIAL_CAMERA_ALT, "CameraLabel"); private Button changeAvatar = new Button(MATERIAL_CAMERA_ALT, "CameraLabel"); public SettingsForm() { super("", BoxLayout.y()); Form previous = getCurrentForm(); getToolbar().setBackCommand("Back", e -> previous.showBack()); User me = ServerAPI.me(); avatar.setIcon(me.getAvatar(12)); if(me.cover.get() != null) { me.fetchCoverImage(i -> { cover.getAllStyles().setBgImage(i); repaint(); }); } changeAvatar.addActionListener(e -> pickAvatar(avatar)); SettingsForm cover & avatar represent the two images in the settings UI, the background cover image and avatar image
  • 10. public class SettingsForm extends Form { private Label cover = new Label(" ", "LoginTitle"); private Label avatar = new Label("", "LabelFrame"); private Button changeCover = new Button(MATERIAL_CAMERA_ALT, "CameraLabel"); private Button changeAvatar = new Button(MATERIAL_CAMERA_ALT, "CameraLabel"); public SettingsForm() { super("", BoxLayout.y()); Form previous = getCurrentForm(); getToolbar().setBackCommand("Back", e -> previous.showBack()); User me = ServerAPI.me(); avatar.setIcon(me.getAvatar(12)); if(me.cover.get() != null) { me.fetchCoverImage(i -> { cover.getAllStyles().setBgImage(i); repaint(); }); } changeAvatar.addActionListener(e -> pickAvatar(avatar)); SettingsForm The two change fields represent the camera change buttons that allow us to replace the avatar images
  • 11. new Button(MATERIAL_CAMERA_ALT, "CameraLabel"); private Button changeAvatar = new Button(MATERIAL_CAMERA_ALT, "CameraLabel"); public SettingsForm() { super("", BoxLayout.y()); Form previous = getCurrentForm(); getToolbar().setBackCommand("Back", e -> previous.showBack()); User me = ServerAPI.me(); avatar.setIcon(me.getAvatar(12)); if(me.cover.get() != null) { me.fetchCoverImage(i -> { cover.getAllStyles().setBgImage(i); repaint(); }); } changeAvatar.addActionListener(e -> pickAvatar(avatar)); changeCover.addActionListener(e -> pickCover(cover)); Container coverContainer = LayeredLayout.encloseIn( cover, FlowLayout.encloseRightBottom(changeCover) ); coverContainer.setUIID("SettingsMargin"); Container avatarContainer = LayeredLayout.encloseIn( avatar, SettingsForm Next lets explore the constructor code, it's mostly standard and creates the main settings UI for replacing the images. Here we bind the back button behavior
  • 12. new Button(MATERIAL_CAMERA_ALT, "CameraLabel"); private Button changeAvatar = new Button(MATERIAL_CAMERA_ALT, "CameraLabel"); public SettingsForm() { super("", BoxLayout.y()); Form previous = getCurrentForm(); getToolbar().setBackCommand("Back", e -> previous.showBack()); User me = ServerAPI.me(); avatar.setIcon(me.getAvatar(12)); if(me.cover.get() != null) { me.fetchCoverImage(i -> { cover.getAllStyles().setBgImage(i); repaint(); }); } changeAvatar.addActionListener(e -> pickAvatar(avatar)); changeCover.addActionListener(e -> pickCover(cover)); Container coverContainer = LayeredLayout.encloseIn( cover, FlowLayout.encloseRightBottom(changeCover) ); coverContainer.setUIID("SettingsMargin"); Container avatarContainer = LayeredLayout.encloseIn( avatar, SettingsForm The current avatar is set into the avatar label
  • 13. new Button(MATERIAL_CAMERA_ALT, "CameraLabel"); private Button changeAvatar = new Button(MATERIAL_CAMERA_ALT, "CameraLabel"); public SettingsForm() { super("", BoxLayout.y()); Form previous = getCurrentForm(); getToolbar().setBackCommand("Back", e -> previous.showBack()); User me = ServerAPI.me(); avatar.setIcon(me.getAvatar(12)); if(me.cover.get() != null) { me.fetchCoverImage(i -> { cover.getAllStyles().setBgImage(i); repaint(); }); } changeAvatar.addActionListener(e -> pickAvatar(avatar)); changeCover.addActionListener(e -> pickCover(cover)); Container coverContainer = LayeredLayout.encloseIn( cover, FlowLayout.encloseRightBottom(changeCover) ); coverContainer.setUIID("SettingsMargin"); Container avatarContainer = LayeredLayout.encloseIn( avatar, SettingsForm If a cover image is set we fetch the image asynchronously
  • 14. new Button(MATERIAL_CAMERA_ALT, "CameraLabel"); private Button changeAvatar = new Button(MATERIAL_CAMERA_ALT, "CameraLabel"); public SettingsForm() { super("", BoxLayout.y()); Form previous = getCurrentForm(); getToolbar().setBackCommand("Back", e -> previous.showBack()); User me = ServerAPI.me(); avatar.setIcon(me.getAvatar(12)); if(me.cover.get() != null) { me.fetchCoverImage(i -> { cover.getAllStyles().setBgImage(i); repaint(); }); } changeAvatar.addActionListener(e -> pickAvatar(avatar)); changeCover.addActionListener(e -> pickCover(cover)); Container coverContainer = LayeredLayout.encloseIn( cover, FlowLayout.encloseRightBottom(changeCover) ); coverContainer.setUIID("SettingsMargin"); Container avatarContainer = LayeredLayout.encloseIn( avatar, SettingsForm Both image buttons delegate to picker methods so we can replace that logic in the future
  • 15. super("", BoxLayout.y()); Form previous = getCurrentForm(); getToolbar().setBackCommand("Back", e -> previous.showBack()); User me = ServerAPI.me(); avatar.setIcon(me.getAvatar(12)); if(me.cover.get() != null) { me.fetchCoverImage(i -> { cover.getAllStyles().setBgImage(i); repaint(); }); } changeAvatar.addActionListener(e -> pickAvatar(avatar)); changeCover.addActionListener(e -> pickCover(cover)); Container coverContainer = LayeredLayout.encloseIn( cover, FlowLayout.encloseRightBottom(changeCover) ); coverContainer.setUIID("SettingsMargin"); Container avatarContainer = LayeredLayout.encloseIn( avatar, FlowLayout.encloseRightBottom(changeAvatar) ); add(LayeredLayout.encloseIn( coverContainer, FlowLayout.encloseCenterBottom(avatarContainer) SettingsForm The cover label places the change button on top using a LayeredLayout & wraps the button in a flow layout so it will align to the bottom right
  • 16. super("", BoxLayout.y()); Form previous = getCurrentForm(); getToolbar().setBackCommand("Back", e -> previous.showBack()); User me = ServerAPI.me(); avatar.setIcon(me.getAvatar(12)); if(me.cover.get() != null) { me.fetchCoverImage(i -> { cover.getAllStyles().setBgImage(i); repaint(); }); } changeAvatar.addActionListener(e -> pickAvatar(avatar)); changeCover.addActionListener(e -> pickCover(cover)); Container coverContainer = LayeredLayout.encloseIn( cover, FlowLayout.encloseRightBottom(changeCover) ); coverContainer.setUIID("SettingsMargin"); Container avatarContainer = LayeredLayout.encloseIn( avatar, FlowLayout.encloseRightBottom(changeAvatar) ); add(LayeredLayout.encloseIn( coverContainer, FlowLayout.encloseCenterBottom(avatarContainer) SettingsForm We do the exact same thing with the avatar image and its picker button
  • 17. }); } changeAvatar.addActionListener(e -> pickAvatar(avatar)); changeCover.addActionListener(e -> pickCover(cover)); Container coverContainer = LayeredLayout.encloseIn( cover, FlowLayout.encloseRightBottom(changeCover) ); coverContainer.setUIID("SettingsMargin"); Container avatarContainer = LayeredLayout.encloseIn( avatar, FlowLayout.encloseRightBottom(changeAvatar) ); add(LayeredLayout.encloseIn( coverContainer, FlowLayout.encloseCenterBottom(avatarContainer) )); add(new Label(me.fullName(), "CenterLargeThinLabel")); add(createButtonBar()); } private Container createButtonBar() { Button activity = new Button(MATERIAL_HISTORY, "CleanButton"); Button settings = new Button(MATERIAL_SETTINGS, "CleanButton"); Button viewAs = new Button(MATERIAL_ACCOUNT_CIRCLE, "CleanButton"); SettingsForm We then wrap both of those LayeredLayout’s in another layered layout and the avatar in a FlowLayout to align it to the bottom center region. The LayeredLayout usage raises the obvious question of why not use one layered layout instead of three? That would be hard to accomplish with the picker button. We would have a hard time aligning the picker button of the avatar to the bottom right corner since the parent layout would be too big.
  • 18. FlowLayout.encloseCenterBottom(avatarContainer) )); add(new Label(me.fullName(), "CenterLargeThinLabel")); add(createButtonBar()); } private Container createButtonBar() { Button activity = new Button(MATERIAL_HISTORY, "CleanButton"); Button settings = new Button(MATERIAL_SETTINGS, "CleanButton"); Button viewAs = new Button(MATERIAL_ACCOUNT_CIRCLE, "CleanButton"); Button more = new Button(MATERIAL_MORE_HORIZ, "CleanButton"); settings.addActionListener(e -> showUserEditForm(ServerAPI.me())); return GridLayout.encloseIn(4, activity, settings, viewAs, more); } private void pickAvatar(Label l) { ImagePicker p = new ImagePicker(); p.pickImage(img -> { p.upload(mediaId -> { ServerAPI.me().avatar.set(mediaId); ServerAPI.setAvatar(mediaId); Image temp = img.fill(l.getIcon().getWidth(), l.getIcon().getHeight()); temp = temp.applyMask(ServerAPI.me(). SettingsForm Next up are the buttons we create at the bottom of the UI as mentioned at the end of the constructor. The button bar are just 4 buttons in a horizontal grid, we only map the settings to functionality of full editing in showUserEditForm
  • 19. Button viewAs = new Button(MATERIAL_ACCOUNT_CIRCLE, "CleanButton"); Button more = new Button(MATERIAL_MORE_HORIZ, "CleanButton"); settings.addActionListener(e -> showUserEditForm(ServerAPI.me())); return GridLayout.encloseIn(4, activity, settings, viewAs, more); } private void pickAvatar(Label l) { ImagePicker p = new ImagePicker(); p.pickImage(img -> { p.upload(mediaId -> { ServerAPI.me().avatar.set(mediaId); ServerAPI.setAvatar(mediaId); Image temp = img.fill(l.getIcon().getWidth(), l.getIcon().getHeight()); temp = temp.applyMask(ServerAPI.me(). getAvatarMask(temp.getWidth()).createMask()); l.setIcon(temp); }); }); } private void pickCover(Label l) { ImagePicker p = new ImagePicker(); p.pickImage(img -> { p.upload(mediaId -> { SettingsForm ImagePicker does most of the heavy lifting here we launch it and get an image object back if it proceeded
  • 20. Button viewAs = new Button(MATERIAL_ACCOUNT_CIRCLE, "CleanButton"); Button more = new Button(MATERIAL_MORE_HORIZ, "CleanButton"); settings.addActionListener(e -> showUserEditForm(ServerAPI.me())); return GridLayout.encloseIn(4, activity, settings, viewAs, more); } private void pickAvatar(Label l) { ImagePicker p = new ImagePicker(); p.pickImage(img -> { p.upload(mediaId -> { ServerAPI.me().avatar.set(mediaId); ServerAPI.setAvatar(mediaId); Image temp = img.fill(l.getIcon().getWidth(), l.getIcon().getHeight()); temp = temp.applyMask(ServerAPI.me(). getAvatarMask(temp.getWidth()).createMask()); l.setIcon(temp); }); }); } private void pickCover(Label l) { ImagePicker p = new ImagePicker(); p.pickImage(img -> { p.upload(mediaId -> { SettingsForm We upload the image and get a callback with the media id value
  • 21. Button viewAs = new Button(MATERIAL_ACCOUNT_CIRCLE, "CleanButton"); Button more = new Button(MATERIAL_MORE_HORIZ, "CleanButton"); settings.addActionListener(e -> showUserEditForm(ServerAPI.me())); return GridLayout.encloseIn(4, activity, settings, viewAs, more); } private void pickAvatar(Label l) { ImagePicker p = new ImagePicker(); p.pickImage(img -> { p.upload(mediaId -> { ServerAPI.me().avatar.set(mediaId); ServerAPI.setAvatar(mediaId); Image temp = img.fill(l.getIcon().getWidth(), l.getIcon().getHeight()); temp = temp.applyMask(ServerAPI.me(). getAvatarMask(temp.getWidth()).createMask()); l.setIcon(temp); }); }); } private void pickCover(Label l) { ImagePicker p = new ImagePicker(); p.pickImage(img -> { p.upload(mediaId -> { SettingsForm The avatar is set both locally & remotely so we don't need a server refresh call
  • 22. Button viewAs = new Button(MATERIAL_ACCOUNT_CIRCLE, "CleanButton"); Button more = new Button(MATERIAL_MORE_HORIZ, "CleanButton"); settings.addActionListener(e -> showUserEditForm(ServerAPI.me())); return GridLayout.encloseIn(4, activity, settings, viewAs, more); } private void pickAvatar(Label l) { ImagePicker p = new ImagePicker(); p.pickImage(img -> { p.upload(mediaId -> { ServerAPI.me().avatar.set(mediaId); ServerAPI.setAvatar(mediaId); Image temp = img.fill(l.getIcon().getWidth(), l.getIcon().getHeight()); temp = temp.applyMask(ServerAPI.me(). getAvatarMask(temp.getWidth()).createMask()); l.setIcon(temp); }); }); } private void pickCover(Label l) { ImagePicker p = new ImagePicker(); p.pickImage(img -> { p.upload(mediaId -> { SettingsForm We do refresh the icon though and make sure to shape it as it was before
  • 23. temp = temp.applyMask(ServerAPI.me(). getAvatarMask(temp.getWidth()).createMask()); l.setIcon(temp); }); }); } private void pickCover(Label l) { ImagePicker p = new ImagePicker(); p.pickImage(img -> { p.upload(mediaId -> { ServerAPI.me().cover.set(mediaId); ServerAPI.setCover(mediaId); l.getAllStyles().setBgImage(img); }); }); } @Override protected void initGlobalToolbar() { Toolbar tb = new Toolbar(true); tb.setUIID("Container"); setToolbar(tb); } SettingsForm Cover picking is very similar but has simpler code as we don't need to shape the resulting image & have a simpler API
  • 24. p.pickImage(img -> { p.upload(mediaId -> { ServerAPI.me().cover.set(mediaId); ServerAPI.setCover(mediaId); l.getAllStyles().setBgImage(img); }); }); } @Override protected void initGlobalToolbar() { Toolbar tb = new Toolbar(true); tb.setUIID("Container"); setToolbar(tb); } private void showUserEditForm(User me) { InstantUI iu = new InstantUI(); iu.excludeProperties(me.authtoken, me.avatar, me.cover, me.friendRequests, me.friends, me.peopleYouMayKnow, me.id, me.password, me.birthday); iu.setMultiChoiceLabels(me.gender, "Male", "Female", "Other"); iu.setMultiChoiceValues(me.gender, "Male", "Female", "Other"); Container cnt = iu.createEditUI(me, true); cnt.setUIID("PaddedContainer"); SettingsForm Next we override this method to have a custom toolbar
  • 25. p.pickImage(img -> { p.upload(mediaId -> { ServerAPI.me().cover.set(mediaId); ServerAPI.setCover(mediaId); l.getAllStyles().setBgImage(img); }); }); } @Override protected void initGlobalToolbar() { Toolbar tb = new Toolbar(true); tb.setUIID("Container"); setToolbar(tb); } private void showUserEditForm(User me) { InstantUI iu = new InstantUI(); iu.excludeProperties(me.authtoken, me.avatar, me.cover, me.friendRequests, me.friends, me.peopleYouMayKnow, me.id, me.password, me.birthday); iu.setMultiChoiceLabels(me.gender, "Male", "Female", "Other"); iu.setMultiChoiceValues(me.gender, "Male", "Female", "Other"); Container cnt = iu.createEditUI(me, true); cnt.setUIID("PaddedContainer"); SettingsForm The toolbar is created using the "layered" mode (the true argument) which makes the content run under it
  • 26. p.pickImage(img -> { p.upload(mediaId -> { ServerAPI.me().cover.set(mediaId); ServerAPI.setCover(mediaId); l.getAllStyles().setBgImage(img); }); }); } @Override protected void initGlobalToolbar() { Toolbar tb = new Toolbar(true); tb.setUIID("Container"); setToolbar(tb); } private void showUserEditForm(User me) { InstantUI iu = new InstantUI(); iu.excludeProperties(me.authtoken, me.avatar, me.cover, me.friendRequests, me.friends, me.peopleYouMayKnow, me.id, me.password, me.birthday); iu.setMultiChoiceLabels(me.gender, "Male", "Female", "Other"); iu.setMultiChoiceValues(me.gender, "Male", "Female", "Other"); Container cnt = iu.createEditUI(me, true); cnt.setUIID("PaddedContainer"); SettingsForm We use the Container UIID for the `Toolbar` to make it transparent
  • 27. Toolbar tb = new Toolbar(true); tb.setUIID("Container"); setToolbar(tb); } private void showUserEditForm(User me) { InstantUI iu = new InstantUI(); iu.excludeProperties(me.authtoken, me.avatar, me.cover, me.friendRequests, me.friends, me.peopleYouMayKnow, me.id, me.password, me.birthday); iu.setMultiChoiceLabels(me.gender, "Male", "Female", "Other"); iu.setMultiChoiceValues(me.gender, "Male", "Female", "Other"); Container cnt = iu.createEditUI(me, true); cnt.setUIID("PaddedContainer"); cnt.setScrollableY(true); Form edit = new Form("Edit", new BorderLayout()); edit.add(BorderLayout.CENTER, cnt); edit.getToolbar().setBackCommand("Back", e -> { showBack(); UiBinding.unbind(me); callSerially(() -> ServerAPI.update(me)); }); edit.show(); } } SettingsForm Finally the last piece of code is the showUserEditForm(). Since there is still a lot to cover here I’ll continue in the next lesson