Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

From Java to Kotlin Multiplatform

Jossi Wolf
December 11, 2019

From Java to Kotlin Multiplatform

Given at GDG Munich Android, Kotlin Usergroup Munich and Flutter Meetup Munich's joint special event 12/2019.

Jossi Wolf

December 11, 2019
Tweet

More Decks by Jossi Wolf

Other Decks in Programming

Transcript

  1. @jossiwolf internal fun calcRangeTicks() { var dx = (actual_maxx -

    actual_minx).toDouble() var dy = (actual_maxy - actual_miny).toDouble() val sw = getWidth() val sh = getHeight() val border = 1.09345 if (Math.abs(last_minx - actual_minx) + Math.abs(last_maxx - actual_maxx) > 0.1 * (actual_maxx - actual_minx)) { mTickX = calcTick(sw, dx).toFloat() dx = mTickX * Math.ceil(border * dx / mTickX) var tx = (actual_minx + actual_maxx - dx) / 2 tx = mTickX * Math.floor(tx / mTickX) minx = tx.toFloat() tx = (actual_minx.toDouble() + actual_maxx.toDouble() + dx) / 2 tx = mTickX * Math.ceil(tx / mTickX) maxx = tx.toFloat() last_minx = actual_minx last_maxx = actual_maxx } if (Math.abs(last_miny - actual_miny) + Math.abs(last_maxy - actual_maxy) > 0.1 * (actual_maxy - actual_miny)) { mTickY = calcTick(sh, dy).toFloat() dy = mTickY * Math.ceil(border * dy / mTickY) var ty = (actual_miny + actual_maxy - dy) / 2 ty = mTickY * Math.floor(ty / mTickY) miny = ty.toFloat() ty = (actual_miny.toDouble() + actual_maxy.toDouble() + dy) / 2 ty = mTickY * Math.ceil(ty / mTickY) maxy = ty.toFloat() last_miny = actual_miny last_maxy = actual_maxy } }
  2. @jossiwolf expect val platform: String fun main() { println("Running on

    $platform") } //JVM actual val platform = "JVM" //JS actual val platform = "JS"
  3. @jossiwolf abstract class Interpolator { static final int SPLINE =

    0; static final int LINEAR = 1; abstract void getPos(double t, double[] v); abstract void getPos(double t, float[] v); abstract double getPos(double t, int j); abstract void getSlope(double t, double[] v); abstract double getSlope(double t, int j); abstract double[] getTimePoints(); }
  4. @jossiwolf abstract class Interpolator { abstract val timePoints: DoubleArray abstract

    fun getPos(t: Double, v: DoubleArray) abstract fun getPos(t: Double, v: FloatArray) abstract fun getPos(t: Double, j: Int): Double abstract fun getSlope(t: Double, v: DoubleArray) abstract fun getSlope(t: Double, j: Int): Double }
  5. @jossiwolf private fun getP(time: Double): Double { var index =

    mPosition.binarySearch(time) var p = 0.0 if (index > 0) { p = 1.0 } else if (index != 0) { index = -index - 1 val m = (mPeriod[index] - mPeriod[index - 1]) / (mPosition[index] - mPosition[index - 1]) p = (mArea[index - 1] + (mPeriod[index - 1] - m * mPosition[index - 1]) * (time - mPosition[index - 1]) + m * (time * time - mPosition[index - 1] * mPosition[index - 1]) / 2) } return p }
  6. @jossiwolf private fun getP(time: Double): Double { val index =

    mPosition.binarySearch(time) return when (index > 0) { true -> 1.0 false -> { val searchIndex = -index - 1 val acceleration = calculateAcceleration(searchIndex) return getArea(acceleration) } } }
  7. @jossiwolf public class MainPanel extends JFrame { JTextArea myXmlOutput =

    new JTextArea(); JScrollPane myXmlScrollPane = new JScrollPane(myXmlOutput); JButton myPlayButton = new JButton("Play"); JComboBox<String> baseMovement = new JComboBox<>(AnimationPanel.MOVE_NAMES); JComboBox<String> duration = new JComboBox<>(AnimationPanel.DURATION); JComboBox<String> easing = new JComboBox<>(AnimationPanel.EASING_OPTIONS); JPanel main = new JPanel(new BorderLayout(5,5)); JPanel main1 = new JPanel(new BorderLayout(5,5)); JPanel main2 = new JPanel(new BorderLayout(5,5)); GridLayout myGraphLayout= new GridLayout(1, 1); JPanel myGraphs = new JPanel(myGraphLayout); JMenuBar topMenu= new JMenuBar(); JPanel base = new JPanel(new BorderLayout()); JTabbedPane myCycleEditTabs = new JTabbedPane(); CycleSetModel myCycleSetModel = new CycleSetModel(myXmlOutput); private CycleSetModel.Cycle createCycle() { return myCycleSetModel.createCycle(); } AnimationPanel animationPanel = new AnimationPanel(myCycleSetModel, myPlayButton); public static JButtoncreateTabbButton(String text) { JButton ret = new JButton(text); ret.setBorder(null); if (text == null) { ret.setIcon(UIManager.getIcon("InternalFrame.paletteCloseIcon")); } ret.setFocusPainted(false); ret.setContentAreaFilled(false); ret.setBorderPainted(true); ret.setBackground(null); ret.setHorizontalTextPosition(SwingConstants.LEFT); ret.setMargin(new Insets(0, 0, 0, 0)); return ret; } MainPanel() { super("Cycle Editor"); setBounds(new Rectangle(1000, 900)); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); myCycleSetModel.myMainPanel = this; CycleSetModel.CyclemyCycle; CycleEdit cycleEdit; myCycle = createCycle(); myGraphs.add(myCycle.myView); main.setBorder(new EmptyBorder(new Insets(5, 5, 5, 5))); main.add(main1,BorderLayout.CENTER); main.add(main2,BorderLayout.SOUTH); main2.add(animationPanel,BorderLayout.CENTER); main1.add(myGraphs,BorderLayout.CENTER); main2.add(myXmlScrollPane,BorderLayout.EAST); main1.add(base,BorderLayout.EAST); myXmlScrollPane.setPreferredSize(new Dimension(100, 300)); BasicArrowButtonnext = new BasicArrowButton(BasicArrowButton.EAST); BasicArrowButtonprev = new BasicArrowButton(BasicArrowButton.WEST); myCycle.myModel.delete= next; // add the first panel cycleEdit = new CycleEdit(myCycle.myView, myCycle.myModel, animationPanel); myCycle.myControl = cycleEdit; JScrollPane scrollPane = new JScrollPane(cycleEdit); myCycleEditTabs.add(scrollPane); cycleEdit.updateTabName(myCycle.myModel.getAttName()); // add the add more panel myCycleEditTabs.add(new JPanel(), "+"); JButton newTabbButton = createTabbButton("+"); newTabbButton.addActionListener(e -> createNewCycle()); myCycleEditTabs.setTabComponentAt(1, newTabbButton); base.add(myCycleEditTabs); JPanel bottomControls = new JPanel(); base.add(bottomControls, BorderLayout.SOUTH); myPlayButton.setText("XXXXX"); myPlayButton.setPreferredSize(myPlayButton.getPreferredSize()); myPlayButton.setText("Play"); bottomControls.add(myPlayButton); baseMovement .addActionListener((e) -> animationPanel.setMovement(baseMovement.getSelectedIndex())); bottomControls.add(baseMovement); duration.setSelectedIndex(AnimationPanel.DURATION.length - 1); duration.addActionListener((e) -> animationPanel.setDurationIndex(duration.getSelectedIndex())); bottomControls.add(duration); easing.addActionListener((e) -> animationPanel.setEasing((String) easing.getSelectedItem())); bottomControls.add(easing); myXmlScrollPane.setPreferredSize(base.getPreferredSize()); setContentPane(main); validate(); myCycle.myModel.update(); JMenu menu = new JMenu("File"); topMenu.add(menu); JMenuItem menuItem = new JMenuItem("parse xml", KeyEvent.VK_T); menuItem.addActionListener(e -> myCycle.myModelSet.parse()); menu.add(menuItem); menu = new JMenu("Examples"); topMenu.add(menu); for (int i = 0; i < KeyCycleExamples.all.length; i++) { String text = KeyCycleExamples.all[i][1]; int speed = Integer.parseInt(KeyCycleExamples.all[i][2]); int movement = Integer.parseInt(KeyCycleExamples.all[i][3]); int easingType = Integer.parseInt(KeyCycleExamples.all[i][4]); menuItem = new JMenuItem(KeyCycleExamples.all[i][0]); menuItem.addActionListener(e -> { myXmlOutput.setText(text); duration.setSelectedIndex(speed); baseMovement.setSelectedIndex(movement); easing.setSelectedIndex(easingType); myCycle.myModelSet.parse(); animationPanel.play(); }); menu.add(menuItem); } menu = new JMenu("Cycle"); menuItem = new JMenuItem("Add cycle", KeyEvent.VK_A); menuItem.addActionListener(e -> createNewCycle()); menu.add(menuItem); menuItem = new JMenuItem("Remove cycle", KeyEvent.VK_R); menuItem.addActionListener(e -> removeCurrentCycle()); menu.add(menuItem); topMenu.add(menu); menuItem = new JMenuItem("Play", KeyEvent.VK_P); menuItem.addActionListener(e -> animationPanel.play()); topMenu.add(menuItem); this.setJMenuBar(topMenu); } public CycleSetModel.CyclecreateNewCycle() { CycleSetModel.Cyclecycle = createCycle(); CycleEdit cycleEdit = new CycleEdit(cycle.myView, cycle.myModel, animationPanel); cycle.myControl = cycleEdit; cycleEdit.setRemoveCallback(e -> removeCycle(cycle)); int count = myCycleEditTabs.getTabCount(); myCycleEditTabs.insertTab("label", null, cycleEdit, "tooltip", count- 1); cycleEdit.updateTabName(cycle.myModel.getAttName()); myGraphs.add(cycle.myView); myGraphLayout.setRows(myCycleEditTabs.getTabCount() - 1); myGraphs.validate(); return cycle; } public static int getTabbIndex(JComponentcomponent) { Container tabb = component.getParent(); Component lastComponent = component; while (!(tabb instanceof JTabbedPane)) { lastComponent = tabb; tabb = tabb.getParent(); } return ((JTabbedPane) tabb).indexOfComponent(lastComponent); } void removeCurrentCycle() { int i = myCycleEditTabs.getSelectedIndex(); removeCycle(myCycleSetModel.myCycles.get(i)); } void removeCycle(CycleSetModel.Cycle cycle) { if (myCycleEditTabs.getTabCount() < 3) { // cant remove the last one return; } if (cycle.myControl != null) { myCycleEditTabs.remove(getTabbIndex(cycle.myControl)); } myGraphs.remove(cycle.myView); myGraphs.validate(); myGraphLayout.setRows(myCycleEditTabs.getTabCount() - 1); myCycleSetModel.removeCycle(cycle); myCycleEditTabs.setSelectedIndex(0); } public static void main(String[] arg) { MainPanel f = new MainPanel(); f.setVisible(true); } }
  8. @jossiwolf class CycleModel { public void addActionListener(ActionListener listener) public String[]

    getStrings() public void delete() public void add() public void setCycle(CycleView cycleView) public void update() public float getComputedValue(float v) public void setDot(float x, float y) public void setPos() public void setPeriod() public void setAmp() public void setOffset() void setMode() public void setSelected(int selectedIndex) public void setUIElements(JSlider pos, JSlider period, JSlider amp, JSlider off, JComboBox<String> mode) void updateUIelements() public void changeSelection(int delta) class ParseResults { void add() } public static String trimDp(String v) public void parseXML(String str) public String getKeyFrames() public void generateXML() String getAttName() public void setAttr(int selectedIndex) public void selectClosest(Point2D p) public void setTarget(JTextField target) }
  9. @jossiwolf class CycleModel { public void addActionListener(ActionListener listener) public String[]

    getStrings() public void delete() public void add() public void setCycle(CycleView cycleView) public void update() public float getComputedValue(float v) public void setDot(float x, float y) public void setPos() public void setPeriod() public void setAmp() public void setOffset() void setMode() public void setSelected(int selectedIndex) public void setUIElements(…) void updateUIelements() public void changeSelection(int delta) class ParseResults { void add() } public static String trimDp(String v) public void parseXML(String str) public String getKeyFrames() public void generateXML() String getAttName() public void setAttr(int selectedIndex) public void selectClosest(Point2D p) public void setTarget(JTextField target) }
  10. @jossiwolf class CycleModel { final int POS = 0; final

    int PERIOD = 1; final int AMP = 2; final int OFFSET = 3; public int selected = 3; ActionListener listener; CycleView mCycleView; CycleSetModel.Cycle myCycle; public JTextField mKeyCycleNo; private JComboBox<String> mMode; JButton delete, add; public JSlider mPos, mPeriod, mAmp, mOff; ... }
  11. @jossiwolf class CycleSetModel { MainPanel myMainPanel; JTextArea myXmlOutput; static class

    Cycle { CycleSetModel myModelSet; CycleModel myModel; CycleView myView; CycleEdit myControl; Cycle(CycleSetModel set) { myModelSet = set; myView = new CycleView(this); myModel = new CycleModel(this); } } CycleSetModel(JTextArea xmlOutput) { myXmlOutput = xmlOutput; }
  12. @jossiwolf class CycleModel { public void addActionListener(ActionListener listener) public String[]

    getStrings() public void delete() public void add() public void setCycle(CycleView cycleView) public void update() public float getComputedValue(float v) public void setDot(float x, float y) public void setPos() public void setPeriod() public void setAmp() public void setOffset() void setMode() public void setSelected(int selectedIndex) public void setUIElements(JSlider pos, JSlider period, JSlider amp, JSlider off, JComboBox<String> mode) void updateUIelements() public void changeSelection(int delta) class ParseResults { void add() } public static String trimDp(String v) public void parseXML(String str) public String getKeyFrames() public void generateXML() String getAttName() public void setAttr(int selectedIndex) public void selectClosest(Point2D p) public void setTarget(JTextField target) }
  13. @jossiwolf public void parseXML(String str) { try { InputStream inputStream

    = new ByteArrayInputStream(str.getBytes(Charset.forName("UTF-8"))); SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser saxParser = factory.newSAXParser(); ParseResults results = new ParseResults(); saxParser.parse(inputStream, new DefaultHandler(){ ... }); values = results.values; selected = values[POS].length / 2; mMode.setSelectedIndex(results.shape); mTarget.setText(results.target); mAttrIndex = results.valueType.ordinal(); update(); } catch (ParserConfigurationException e) { ... } }
  14. @jossiwolf # Don‘t convert UI & Repo # Free your

    Presentation layer of UI dependencies
  15. @jossiwolf fun updateUIelements() { inCallBack = true val max =

    calculateMax() val min = calculateMin() val middle = max / 2 mPos.setEnabled(middle) delete!!.setEnabled(middle) }
  16. @jossiwolf fun updateUIelements() { inCallBack = true val max =

    calculateMax() val min = calculateMin() val middle = max / 2 mPos.setEnabled(middle) delete!!.setEnabled(middle) }
  17. @jossiwolf suspend fun updateUIElements() { val min = calculateMin() val

    max = calculateMax() val isMiddle = ... val state = CycleViewState(isMiddle, min, max) stateFlow.emit(state) }
  18. @jossiwolf class CycleModel { // Couroutines are available in common

    modules val stateFlow = flow<CycleViewState> { … } }
  19. @jossiwolf class CycleActivity: AppCompatActivity() { private val cycleModel by lazy

    { CycleModel() } override fun onCreate() { cycleModel.stateFlow.collect(::render) } private suspend fun render(state: CycleViewState) { … } }
  20. @jossiwolf # Introduce a View State # Make your data

    flow unidirectional # Don‘t have hard coupling between layers