4. public class CountryCodePicker extends Button {
private Resources flagResource;
public CountryCodePicker() {
setUIID("CountryCodePicker");
addActionListener(e -> showPickerForm());
String code = L10NManager.getInstance().getLocale();
if(code != null) {
String[] countryCodes;
if(code.length() == 2) {
countryCodes = COUNTRY_ISO2;
} else {
if(code.length() == 3) {
countryCodes = COUNTRY_ISO3;
} else {
return;
}
}
code = code.toUpperCase();
try {
flagResource = Resources.open("/flags.res");
} catch(IOException err) {
CountryCodePicker
The country code picker is a button subclass but it doesn’t really look like it
5. public class CountryCodePicker extends Button {
private Resources flagResource;
public CountryCodePicker() {
setUIID("CountryCodePicker");
addActionListener(e -> showPickerForm());
String code = L10NManager.getInstance().getLocale();
if(code != null) {
String[] countryCodes;
if(code.length() == 2) {
countryCodes = COUNTRY_ISO2;
} else {
if(code.length() == 3) {
countryCodes = COUNTRY_ISO3;
} else {
return;
}
}
code = code.toUpperCase();
try {
flagResource = Resources.open("/flags.res");
} catch(IOException err) {
CountryCodePicker
Because we set the UIID to the CountryCodePicker UIID
6. public class CountryCodePicker extends Button {
private Resources flagResource;
public CountryCodePicker() {
setUIID("CountryCodePicker");
addActionListener(e -> showPickerForm());
String code = L10NManager.getInstance().getLocale();
if(code != null) {
String[] countryCodes;
if(code.length() == 2) {
countryCodes = COUNTRY_ISO2;
} else {
if(code.length() == 3) {
countryCodes = COUNTRY_ISO3;
} else {
return;
}
}
code = code.toUpperCase();
try {
flagResource = Resources.open("/flags.res");
} catch(IOException err) {
CountryCodePicker
In this block of code we try to "guess" the current country based on the localization settings
7. public class CountryCodePicker extends Button {
private Resources flagResource;
public CountryCodePicker() {
setUIID("CountryCodePicker");
addActionListener(e -> showPickerForm());
String code = L10NManager.getInstance().getLocale();
if(code != null) {
String[] countryCodes;
if(code.length() == 2) {
countryCodes = COUNTRY_ISO2;
} else {
if(code.length() == 3) {
countryCodes = COUNTRY_ISO3;
} else {
return;
}
}
code = code.toUpperCase();
try {
flagResource = Resources.open("/flags.res");
} catch(IOException err) {
CountryCodePicker
The flags.res file is included in the SMSActivation cn1lib. It's a bit of an implementation detail so this code might break in the future if so we might need to copy the res
file from there or update the code.
8. try {
flagResource = Resources.open("/flags.res");
} catch(IOException err) {
Log.e(err);
}
Image blankIcon = Image.createImage(100, 70, 0);
for(int iter = 0 ; iter < countryCodes.length ; iter++) {
if(code.equals(countryCodes[iter])) {
setText("+" + COUNTRY_CODES[iter]);
setIcon(flagResource.getImage(COUNTRY_FLAGS[iter]));
if(getIcon() == null) {
setIcon(blankIcon);
}
return;
}
}
}
}
protected void showPickerForm() {
final Form f = getCurrentForm();
final Transition t = f.getTransitionOutAnimator();
CountryCodePicker
We don't have all the flags for all the countries. Without a blank icon the alignment might seem “broken" so it’s crucial to have this
9. setIcon(blankIcon);
}
return;
}
}
}
}
protected void showPickerForm() {
final Form f = getCurrentForm();
final Transition t = f.getTransitionOutAnimator();
f.setTransitionOutAnimator(CommonTransitions.createEmpty());
Form tf = new CountryPickerForm(this, flagResource);
tf.addShowListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
f.setTransitionOutAnimator(t);
f.removeShowListener(this);
}
});
tf.show();
}
CountryCodePicker
showPickerForm is useful for overriding. In the login form clicking this button should lead to a different form. However there is a lot more going on here…
The cover transition collides with the default slide transition producing a weird effect. So here we keep the transition instance. Remove the out transition so the cover
effect will work properly. We then bind a show listener that restores the old transition after we are done. That's important for when the user will click to move to the next
form..
14. public class LoginForm extends Form {
public LoginForm() {
super(new BorderLayout());
Label squareLogo = new Label("",
Resources.getGlobalResources().getImage("uber-logo.png"),
"SquareLogo") {
@Override
protected Dimension calcPreferredSize() {
Dimension size = super.calcPreferredSize();
size.setHeight(size.getWidth());
return size;
}
};
Container logo = BorderLayout.centerAbsolute(squareLogo);
logo.setUIID("LogoBackground");
add(CENTER, logo);
Label getMovingWithUber = new Label("Get moving with Uber", "GetMovingWithUber");
CountryCodePicker countryCodeButton = new CountryCodePicker() {
@Override
LoginForm
So without further ado, lets go to the login form code. This is the code of the first form you see when the Uber app launches. It’s a relatively simple class without too
many frills. Lets go over a few elements of note.
Initially I wrote the word `UBER`. Without the right font it looked "weird". Using an image for a logo is generally the best approach
15. public class LoginForm extends Form {
public LoginForm() {
super(new BorderLayout());
Label squareLogo = new Label("",
Resources.getGlobalResources().getImage("uber-logo.png"),
"SquareLogo") {
@Override
protected Dimension calcPreferredSize() {
Dimension size = super.calcPreferredSize();
size.setHeight(size.getWidth());
return size;
}
};
Container logo = BorderLayout.centerAbsolute(squareLogo);
logo.setUIID("LogoBackground");
add(CENTER, logo);
Label getMovingWithUber = new Label("Get moving with Uber", "GetMovingWithUber");
CountryCodePicker countryCodeButton = new CountryCodePicker() {
@Override
LoginForm
I want the logo to be square so height and width would be identical
16. public class LoginForm extends Form {
public LoginForm() {
super(new BorderLayout());
Label squareLogo = new Label("",
Resources.getGlobalResources().getImage("uber-logo.png"),
"SquareLogo") {
@Override
protected Dimension calcPreferredSize() {
Dimension size = super.calcPreferredSize();
size.setHeight(size.getWidth());
return size;
}
};
Container logo = BorderLayout.centerAbsolute(squareLogo);
logo.setUIID("LogoBackground");
add(CENTER, logo);
Label getMovingWithUber = new Label("Get moving with Uber", "GetMovingWithUber");
CountryCodePicker countryCodeButton = new CountryCodePicker() {
@Override
LoginForm
We place the entire tiled section and logo in the center of the form so they will take up the available space. We place the logo itself in the absolute center so it will float in
the middle
17. CountryCodePicker countryCodeButton = new CountryCodePicker() {
@Override
protected void showPickerForm() {
new EnterMobileNumberForm().show();
}
};
SpanButton phoneNumber = new SpanButton("Enter your mobile number",
"PhoneNumberHint");
phoneNumber.getTextComponent().setColumns(80);
phoneNumber.getTextComponent().setRows(2);
phoneNumber.getTextComponent().setGrowByContent(false);
phoneNumber.setUIID("Container");
phoneNumber.addActionListener(e -> new EnterMobileNumberForm().show());
Container phonePicking = BorderLayout.centerCenterEastWest(
phoneNumber,
null, countryCodeButton);
phonePicking.setUIID("Separator");
Button social = new Button("Or connect with social", "ConnectWithSocialButton");
social.addActionListener(e -> new FacebookOrGoogleLoginForm().show());
LoginForm
I override the behavior of the country picker button for consistency with the native Uber app
18. CountryCodePicker countryCodeButton = new CountryCodePicker() {
@Override
protected void showPickerForm() {
new EnterMobileNumberForm().show();
}
};
SpanButton phoneNumber = new SpanButton("Enter your mobile number",
"PhoneNumberHint");
phoneNumber.getTextComponent().setColumns(80);
phoneNumber.getTextComponent().setRows(2);
phoneNumber.getTextComponent().setGrowByContent(false);
phoneNumber.setUIID("Container");
phoneNumber.addActionListener(e -> new EnterMobileNumberForm().show());
Container phonePicking = BorderLayout.centerCenterEastWest(
phoneNumber,
null, countryCodeButton);
phonePicking.setUIID("Separator");
Button social = new Button("Or connect with social", "ConnectWithSocialButton");
social.addActionListener(e -> new FacebookOrGoogleLoginForm().show());
LoginForm
This looks like a text field but acts like a button in the naive app so I implemented it as such. Constraining the rows and size is important for proper layout
19. phoneNumber.addActionListener(e -> new EnterMobileNumberForm().show());
Container phonePicking = BorderLayout.centerCenterEastWest(
phoneNumber,
null, countryCodeButton);
phonePicking.setUIID("Separator");
Button social = new Button("Or connect with social", "ConnectWithSocialButton");
social.addActionListener(e -> new FacebookOrGoogleLoginForm().show());
add(SOUTH, BoxLayout.encloseY(getMovingWithUber, phonePicking, social));
}
@Override
protected boolean shouldPaintStatusBar() {
return false;
}
@Override
protected void initGlobalToolbar() {
}
}
LoginForm
The rest of the UI is relegated to the SOUTH of the form, this would have issues in landscape mode but since the app is portrait locked this shouldn't be a problem