Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Shai Almog - The Mythical Silver Bullet, How Co...

Shai Almog - The Mythical Silver Bullet, How Codename One Solves Mobility Differently

Riga Dev Day

January 22, 2015
Tweet

More Decks by Riga Dev Day

Other Decks in Programming

Transcript

  1. Introduction Devices Codename One Application Deployment Questions The Mythical Silver

    Bullet, How Codename One Solves Mobility Differently Shai Almog @Codename_One
  2. Introduction Devices Codename One Application Deployment Questions Who Am I?

    Shai Almog [email protected] Co-founder & CEO of Codename One JavaOne Rockstar, DZone MVB, JavaZone top speaker… Worked on mobile development tools since the 90’s at Sun Member of original WTK team, co-creator of LWUIT project Worked with Sun/Oracle, IBM, DoCoMo, Nokia, Samsung, Verizon, Sprint, Vodafone, Sony Ericsson and more Open source hacker, Java developer since 96 Over 20 years of professional software development experience
  3. Introduction Devices Codename One Application Deployment Questions What Is Codename

    One • Open source - Java for all devices • Supports iOS, Android & Windows Phone • Integrates with Eclipse, NetBeans & IntelliJ • GUI builder, theme editor, localization tools • API abstracting device • Designed for Tablets/Phones • Cloud based compilation
  4. Introduction Devices Codename One Application Deployment Questions Why Is Mobile

    Different • DPI - Density • iOS requires Mac, Windows Phone requires Windows • Changes • Certificates - provisioning
  5. Introduction Devices Codename One Application Deployment Questions Why Is Mobile

    Different • DPI - Density • iOS requires Mac, Windows Phone requires Windows • Changes • Certificates - provisioning
  6. Introduction Problem Codename One Competitors Market Final Words How Does

    It Work? In the case of iOS the Java bytecode is translated to C and compiled using Xcode
  7. Introduction Devices Codename One Application Deployment Questions The source code

    is in the Codename One SVN here: http://code.google.com/p/codenameone/source/ browse/trunk/Demos/ Don’t worry if you can’t follow or forget something… Everything will be posted in the Codename One blog after the conference!
  8. Introduction Devices Codename One Application Deployment Questions Generated Project Starting

    Point public void start() { if(current != null){ current.show(); return; } Form hi = new Form("Hi World"); hi.addComponent(new Label("Hi World")); hi.show(); }
  9. Introduction Devices Codename One Application Deployment Questions Generated Project Starting

    Point public void start() { if(current != null){ current.show(); return; } Form hi = new Form("Hi World"); hi.addComponent(new Label("Hi World")); hi.show(); } We have a component- container hierarchy Form is top level component
  10. Introduction Devices Codename One Application Deployment Questions Generated Project Starting

    Point public void start() { if(current != null){ current.show(); return; } Form hi = new Form("Hi World"); hi.addComponent(new Label("Hi World")); hi.show(); } We have a component- container hierarchy Form is top level component
  11. Introduction Devices Codename One Application Deployment Questions The New start()

    Method Form hi = new Form("Welcome"); BorderLayout bl = new BorderLayout(); bl.setCenterBehavior(BorderLayout.CENTER_BEHAVIOR_CENTER); hi.setLayout(bl); hi.setFormBottomPaddingEditingMode(true); ComponentGroup cg = new ComponentGroup(); final TextField name = new TextField(); name.setHint("What Is Your Name?"); final Button btn = new Button("Take Photo"); cg.addComponent(name); cg.addComponent(btn); btn.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { userPicture = captureRoundImage(); btn.setIcon(userPicture); } }); hi.addComponent(BorderLayout.CENTER, cg); Toolbar t = new Toolbar(); hi.setToolBar(t); t.addCommandToRightBar(new Command("Next") { @Override public void actionPerformed(ActionEvent evt) { userName = name.getText(); if(userName.length() == 0) { userName = "[Unnamed]"; } showSbaitso(); } }); hi.show();
  12. Introduction Devices Codename One Application Deployment Questions The New start()

    Method Form hi = new Form("Welcome"); BorderLayout bl = new BorderLayout(); bl.setCenterBehavior(BorderLayout.CENTER_BEHAVIOR_CENTER); hi.setLayout(bl); hi.setFormBottomPaddingEditingMode(true); ComponentGroup cg = new ComponentGroup(); final TextField name = new TextField(); name.setHint("What Is Your Name?"); final Button btn = new Button("Take Photo"); cg.addComponent(name); cg.addComponent(btn); btn.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { userPicture = captureRoundImage(); btn.setIcon(userPicture); } }); hi.addComponent(BorderLayout.CENTER, cg); Toolbar t = new Toolbar(); hi.setToolBar(t); t.addCommandToRightBar(new Command("Next") { @Override public void actionPerformed(ActionEvent evt) { userName = name.getText(); if(userName.length() == 0) { userName = "[Unnamed]"; } showSbaitso(); } }); hi.show(); The component group creates the rounded edges Action listener is invoked when the button is clicked Next moves to the next form which is the main app form
  13. Introduction Devices Codename One Application Deployment Questions The New start()

    Method Form hi = new Form("Welcome"); BorderLayout bl = new BorderLayout(); bl.setCenterBehavior(BorderLayout.CENTER_BEHAVIOR_CENTER); hi.setLayout(bl); hi.setFormBottomPaddingEditingMode(true); ComponentGroup cg = new ComponentGroup(); final TextField name = new TextField(); name.setHint("What Is Your Name?"); final Button btn = new Button("Take Photo"); cg.addComponent(name); cg.addComponent(btn); btn.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { userPicture = captureRoundImage(); btn.setIcon(userPicture); } }); hi.addComponent(BorderLayout.CENTER, cg); Toolbar t = new Toolbar(); hi.setToolBar(t); t.addCommandToRightBar(new Command("Next") { @Override public void actionPerformed(ActionEvent evt) { userName = name.getText(); if(userName.length() == 0) { userName = "[Unnamed]"; } showSbaitso(); } }); hi.show(); The component group creates the rounded edges Action listener is invoked when the button is clicked Next moves to the next form which is the main app form
  14. Introduction Devices Codename One Application Deployment Questions private void captureRoundImage(Label

    result) { try { int width = Display.getInstance().getDisplayWidth(); Image capturedImage = Image.createImage(Capture.capturePhoto(width, -1)); Image roundMask = Image.createImage(width, capturedImage.getHeight(), 0xff000000); Graphics gr = roundMask.getGraphics(); gr.setColor(0xffffff); gr.fillArc(0, 0, width, width, 0, 360); Object mask = roundMask.createMask(); capturedImage = capturedImage.applyMask(mask); result.setIcon(capturedImage); } catch(IOException err) { err.printStackTrace(); } } Creating A Picture (Avatar)
  15. Introduction Devices Codename One Application Deployment Questions private void captureRoundImage(Label

    result) { try { int width = Display.getInstance().getDisplayWidth(); Image capturedImage = Image.createImage(Capture.capturePhoto(width, -1)); Image roundMask = Image.createImage(width, capturedImage.getHeight(), 0xff000000); Graphics gr = roundMask.getGraphics(); gr.setColor(0xffffff); gr.fillArc(0, 0, width, width, 0, 360); Object mask = roundMask.createMask(); capturedImage = capturedImage.applyMask(mask); result.setIcon(capturedImage); } catch(IOException err) { err.printStackTrace(); } } Creating A Picture (Avatar) Capture a picture at device width with aspect ratio Create an image we can modify into a mask Apply the mask to the captured image and set to label
  16. Introduction Devices Codename One Application Deployment Questions private void captureRoundImage(Label

    result) { try { int width = Display.getInstance().getDisplayWidth(); Image capturedImage = Image.createImage(Capture.capturePhoto(width, -1)); Image roundMask = Image.createImage(width, capturedImage.getHeight(), 0xff000000); Graphics gr = roundMask.getGraphics(); gr.setColor(0xffffff); gr.fillArc(0, 0, width, width, 0, 360); Object mask = roundMask.createMask(); capturedImage = capturedImage.applyMask(mask); result.setIcon(capturedImage); } catch(IOException err) { err.printStackTrace(); } } Creating A Picture (Avatar) Capture a picture at device width with aspect ratio Create an image we can modify into a mask Apply the mask to the captured image and set to label
  17. Introduction Devices Codename One Application Deployment Questions void showSbaitso() {

    Form sb = new Form(); sb.setFormBottomPaddingEditingMode(true); Toolbar t = new Toolbar(); sb.setToolBar(t); final TextField searchField = new TextField(); searchField.setHint("Search For Answers..."); t.setTitleComponent(searchField); sb.setLayout(new BorderLayout()); final TextField ask = new TextField(); ask.setHint("Ask The Dr."); Container askContainer = new Container(new BorderLayout()); askContainer.addComponent(BorderLayout.CENTER, ask); Button askButton = new Button("Ask"); askContainer.addComponent(BorderLayout.EAST, askButton); sb.addComponent(BorderLayout.SOUTH, askContainer); final Container discussion = new Container(new BoxLayout(BoxLayout.Y_AXIS)); sb.addComponent(BorderLayout.CENTER, discussion); discussion.setScrollableY(true); sb.show(); The Main UI (part I)
  18. Introduction Devices Codename One Application Deployment Questions void showSbaitso() {

    Form sb = new Form(); sb.setFormBottomPaddingEditingMode(true); Toolbar t = new Toolbar(); sb.setToolBar(t); final TextField searchField = new TextField(); searchField.setHint("Search For Answers..."); t.setTitleComponent(searchField); sb.setLayout(new BorderLayout()); final TextField ask = new TextField(); ask.setHint("Ask The Dr."); Container askContainer = new Container(new BorderLayout()); askContainer.addComponent(BorderLayout.CENTER, ask); Button askButton = new Button("Ask"); askContainer.addComponent(BorderLayout.EAST, askButton); sb.addComponent(BorderLayout.SOUTH, askContainer); final Container discussion = new Container(new BoxLayout(BoxLayout.Y_AXIS)); sb.addComponent(BorderLayout.CENTER, discussion); discussion.setScrollableY(true); sb.show(); The Main UI (part I) Main chat window allows searching the discussion Questions/ Answers are placed in a box layout Y Notice the ask text field and ask button share a container
  19. Introduction Devices Codename One Application Deployment Questions Display.getInstance().callSerially(new Runnable() {

    public void run() { say(discussion, "HELLO " + userName +", MY NAME IS DOCTOR SBAITSO. \n\nI AM HERE TO HELP YOU.\n" + "SAY WHATEVER IS IN YOUR MIND FREELY," + "OUR CONVERSATION WILL BE KEPT IN STRICT CONFIDENCE.\n" + "MEMORY CONTENTS WILL BE WIPED OFF AFTER YOU LEAVE.", false); } }); searchField.addDataChangeListener(createSearchListener(searchField, discussion, askButton)); ActionListener askEvent = new ActionListener() { public void actionPerformed(ActionEvent evt) { String t = ask.getText(); if(t.length() > 0) { ask.setText(""); say(discussion, t, true); answer(t, discussion); } } }; ask.setDoneListener(askEvent); askButton.addActionListener(askEvent); } The Main UI (part II)
  20. Introduction Devices Codename One Application Deployment Questions Display.getInstance().callSerially(new Runnable() {

    public void run() { say(discussion, "HELLO " + userName +", MY NAME IS DOCTOR SBAITSO. \n\nI AM HERE TO HELP YOU.\n" + "SAY WHATEVER IS IN YOUR MIND FREELY," + "OUR CONVERSATION WILL BE KEPT IN STRICT CONFIDENCE.\n" + "MEMORY CONTENTS WILL BE WIPED OFF AFTER YOU LEAVE.", false); } }); searchField.addDataChangeListener(createSearchListener(searchField, discussion, askButton)); ActionListener askEvent = new ActionListener() { public void actionPerformed(ActionEvent evt) { String t = ask.getText(); if(t.length() > 0) { ask.setText(""); say(discussion, t, true); answer(t, discussion); } } }; ask.setDoneListener(askEvent); askButton.addActionListener(askEvent); } The Main UI (part II) We ask/ answer using the “say” method CallSerially postpones the question to the next cycle of the EDT We use data change for the search but done for ask
  21. Introduction Devices Codename One Application Deployment Questions Display.getInstance().callSerially(new Runnable() {

    public void run() { say(discussion, "HELLO " + userName +", MY NAME IS DOCTOR SBAITSO. \n\nI AM HERE TO HELP YOU.\n" + "SAY WHATEVER IS IN YOUR MIND FREELY," + "OUR CONVERSATION WILL BE KEPT IN STRICT CONFIDENCE.\n" + "MEMORY CONTENTS WILL BE WIPED OFF AFTER YOU LEAVE.", false); } }); searchField.addDataChangeListener(createSearchListener(searchField, discussion, askButton)); ActionListener askEvent = new ActionListener() { public void actionPerformed(ActionEvent evt) { String t = ask.getText(); if(t.length() > 0) { ask.setText(""); say(discussion, t, true); answer(t, discussion); } } }; ask.setDoneListener(askEvent); askButton.addActionListener(askEvent); } The Main UI (part II) We ask/ answer using the “say” method CallSerially postpones the question to the next cycle of the EDT We use data change for the search but done for ask
  22. Introduction Devices Codename One Application Deployment Questions void answer(String question,

    Container dest) { say(dest, AI.getResponse(question), false); } void say(Container destination, String text, boolean question) { SpanLabel t = new SpanLabel(text); t.setWidth(destination.getWidth()); t.setX(0); t.setHeight(t.getPreferredH()); if(question) { t.setY(Display.getInstance().getDisplayHeight()); t.setTextUIID("BubbleUser"); t.setIconPosition(BorderLayout.EAST); t.setIcon(userPicture); t.setTextBlockAlign(Component.RIGHT); } else { t.setY(0); t.setTextUIID("BubbleSbaitso"); t.setTextBlockAlign(Component.LEFT); } destination.addComponent(t); destination.animateLayoutAndWait(400); destination.scrollComponentToVisible(t); } Say & Answer
  23. Introduction Devices Codename One Application Deployment Questions void answer(String question,

    Container dest) { say(dest, AI.getResponse(question), false); } void say(Container destination, String text, boolean question) { SpanLabel t = new SpanLabel(text); t.setWidth(destination.getWidth()); t.setX(0); t.setHeight(t.getPreferredH()); if(question) { t.setY(Display.getInstance().getDisplayHeight()); t.setTextUIID("BubbleUser"); t.setIconPosition(BorderLayout.EAST); t.setIcon(userPicture); t.setTextBlockAlign(Component.RIGHT); } else { t.setY(0); t.setTextUIID("BubbleSbaitso"); t.setTextBlockAlign(Component.LEFT); } destination.addComponent(t); destination.animateLayoutAndWait(400); destination.scrollComponentToVisible(t); } Say & Answer Answer is just a convenience call to say Say just creates and styles a label notice the set UIID… After changing the UI we animate it into place
  24. Introduction Devices Codename One Application Deployment Questions void answer(String question,

    Container dest) { say(dest, AI.getResponse(question), false); } void say(Container destination, String text, boolean question) { SpanLabel t = new SpanLabel(text); t.setWidth(destination.getWidth()); t.setX(0); t.setHeight(t.getPreferredH()); if(question) { t.setY(Display.getInstance().getDisplayHeight()); t.setTextUIID("BubbleUser"); t.setIconPosition(BorderLayout.EAST); t.setIcon(userPicture); t.setTextBlockAlign(Component.RIGHT); } else { t.setY(0); t.setTextUIID("BubbleSbaitso"); t.setTextBlockAlign(Component.LEFT); } destination.addComponent(t); destination.animateLayoutAndWait(400); destination.scrollComponentToVisible(t); } Say & Answer Answer is just a convenience call to say Say just creates and styles a label notice the set UIID… After changing the UI we animate it into place
  25. Introduction Devices Codename One Application Deployment Questions public interface TTS

    extends NativeInterface { public void say(String text); } Native Interface
  26. Introduction Devices Codename One Application Deployment Questions public interface TTS

    extends NativeInterface { public void say(String text); } Native Interface Interface Extending NativeInterace Some limitations on method arguments
  27. Introduction Devices Codename One Application Deployment Questions public class TTSImpl

    { private TextToSpeech ttobj; public void say(String param) { if(ttobj == null) { ttobj = new TextToSpeech(AndroidNativeUtil.getActivity(), new TextToSpeech.OnInitListener() { @Override public void onInit(int status) { if (status != TextToSpeech.ERROR) { ttobj.setLanguage(Locale.UK); } } }); ttobj.speak(param, TextToSpeech.QUEUE_FLUSH, null); } } public boolean isSupported() { return true; } } Native Interface - Android
  28. Introduction Devices Codename One Application Deployment Questions public class TTSImpl

    { private TextToSpeech ttobj; public void say(String param) { if(ttobj == null) { ttobj = new TextToSpeech(AndroidNativeUtil.getActivity(), new TextToSpeech.OnInitListener() { @Override public void onInit(int status) { if (status != TextToSpeech.ERROR) { ttobj.setLanguage(Locale.UK); } } }); ttobj.speak(param, TextToSpeech.QUEUE_FLUSH, null); } } public boolean isSupported() { return true; } } Native Interface - Android Use native Android code unrestricted isSupported is defined in the base interface
  29. Introduction Devices Codename One Application Deployment Questions #import "com_codename1_demos_sbaitso_TTSImpl.h" #import

    <AVFoundation/AVFoundation.h> @implementation com_codename1_demos_sbaitso_TTSImpl -(void)say:(NSString*)param{ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; AVSpeechSynthesisVoice *voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"en-GB"]; AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:param]; AVSpeechSynthesizer *syn = [[[AVSpeechSynthesizer alloc] init]autorelease]; utterance.rate = 0; utterance.voice = voice; [syn speakUtterance:utterance]; [pool release]; } -(BOOL)isSupported{ return YES; } @end Native Interface - iOS
  30. Introduction Devices Codename One Application Deployment Questions #import "com_codename1_demos_sbaitso_TTSImpl.h" #import

    <AVFoundation/AVFoundation.h> @implementation com_codename1_demos_sbaitso_TTSImpl -(void)say:(NSString*)param{ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; AVSpeechSynthesisVoice *voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"en-GB"]; AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:param]; AVSpeechSynthesizer *syn = [[[AVSpeechSynthesizer alloc] init]autorelease]; utterance.rate = 0; utterance.voice = voice; [syn speakUtterance:utterance]; [pool release]; } -(BOOL)isSupported{ return YES; } @end Native Interface - iOS Use native iOS code unrestricted
  31. Introduction Devices Codename One Application Deployment Questions Q&A Thank You!

    Further Reading How Do I - codenameone.com/how-do-i.html Source code for Dr. Sbaitso - code.google.com/p/codenameone/source/browse/trunk/Demos Developer Guide - codenameone.com/developer-guide.html Discussion Forum - www.codenameone.com/discussion-forum.html Course (free for pro users) - udemy.com/codenameone101/ Source code/Issue tracker -code.google.com/p/codenameone/ JavaDocs - codenameone.googlecode.com/svn/trunk/CodenameOne/javadoc/index.html Blog - codenameone.com/blog Shai Almog @Codename_One http://www.codenameone.com/