diff --git a/.gitignore b/.gitignore index 0ea567bf74..4a541eca81 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,8 @@ *.skin *.jar *.class +*.pyc +**/__pycache__/ **/private/* **/.idea/* **/build/* @@ -91,3 +93,6 @@ dependency-reduced-pom.xml /docs/website/.cache/ /docs/website/.venv/ /docs/website/.venv-pagefind/ +/docs/website/.venv-video-refresh/ +/docs/website/.youtube-oauth-token.json +/docs/website/client_secret.json diff --git a/docs/website/content/courses/course-01-java-for-mobile-devices/001-introduction.md b/docs/website/content/courses/course-01-java-for-mobile-devices/001-introduction.md index 45e4320bbe..73e566ed7a 100644 --- a/docs/website/content/courses/course-01-java-for-mobile-devices/001-introduction.md +++ b/docs/website/content/courses/course-01-java-for-mobile-devices/001-introduction.md @@ -9,7 +9,24 @@ module_order: 1 lesson_order: 1 weight: 1 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "An introduction to the course and the modern Codename One workflow." --- > Module 1: Course Lessons + +This course is about learning how mobile applications are built in Codename One without treating the framework as magic. The goal is not just to get an app on screen. It is to understand what mobile development asks of you, how Codename One fits into that world, and how to build habits that will still make sense once the project grows beyond a toy example. + +If you are starting fresh today, the right place to begin is with the current Maven-based workflow. Create projects with [Initializr](/initializr/), open them in the IDE you already use, and rely on the generated project structure instead of the legacy plugin-based setup shown in some of the older course material. That modern setup is simpler, easier to version, and much closer to the way current Codename One projects are maintained. + +The early lessons in this course focus on the fundamentals that keep coming back throughout real projects: how mobile devices differ from desktop environments, how a Codename One application is structured, how layouts and styling work, why the event dispatch thread matters, and how device builds fit into the development loop. Those are the things that make the rest of the framework easier to understand. + +You should expect some of the videos in this course to show older tooling. When that happens, use the written lesson as the current source of truth. The important concepts are still worth learning, but the practical workflow has evolved. In particular, modern projects usually start with Maven, use CSS as the primary styling workflow, and use l10n property bundles for localization instead of treating the designer and resource editor as the center of the project. + +The best way to use this course is to keep a project open while you read. Run the simulator often, change one thing at a time, and make sure you understand why the framework behaves the way it does before moving on. That feedback loop is what turns the lessons into working knowledge. + +## Further Reading + +- [Getting Started](/getting-started/) +- [Initializr](/initializr/) +- [Developer Guide](/developer-guide/) +- [Hello World](/hello-world/) diff --git a/docs/website/content/courses/course-01-java-for-mobile-devices/002-creating-a-hello-world-app.md b/docs/website/content/courses/course-01-java-for-mobile-devices/002-creating-a-hello-world-app.md index 92970505e1..c0cac505cf 100644 --- a/docs/website/content/courses/course-01-java-for-mobile-devices/002-creating-a-hello-world-app.md +++ b/docs/website/content/courses/course-01-java-for-mobile-devices/002-creating-a-hello-world-app.md @@ -9,12 +9,33 @@ module_order: 1 lesson_order: 2 weight: 2 is_course_lesson: true -description: "Note: This is the old approach. I suggest checking out the newer approach here." +description: "Create a first Codename One application using the current Maven-based workflow." --- - > Module 1: Course Lessons - -Note: This is the old approach. I suggest checking out the newer approach here. +This lesson shows how to build a small but complete Codename One application: an app that starts cleanly, shows a form, responds to input, and is ready to be sent to a device once the basics are working. {{< youtube 73d65cvyQv4 >}} + +The best first project is a very small one. [Create a Maven-based Codename One application using the initializr](/initializr/), open it in your IDE, and spend a few minutes understanding the generated app before you start customizing it. The video covers the same first milestone, but this is one place where it is out of date because it starts with the old IDE plugin flow instead of the current Maven-based setup. At this stage you are not trying to design the final architecture of your product. You are trying to learn the basic rhythm of a Codename One app and confirm that your development environment is working. + +One of the first things worth getting right is the package name. In Codename One it becomes part of the app's identity and eventually touches signing, native packaging, and store submission. Choose a stable reverse-domain package name at the beginning instead of treating it as a temporary placeholder. + +Once the project is generated, the application class shows you the lifecycle that every Codename One app follows. `init()` is where one-time application setup belongs. `start()` is where the first UI is created and shown. `stop()` is called when the app goes into the background, and `destroy()` is there for shutdown cleanup when needed. That lifecycle is still the backbone of the framework, and understanding it early saves a lot of confusion later. + +The actual hello world UI should stay simple. Create a form, add a button, attach an action listener, and show a dialog or some other small response when the user taps it. That single exercise teaches several important things at once. It shows how a form is displayed, how components are added to it, how event listeners are wired, and how the UI responds to interaction. Once that works, you have a real application, even if it is still visually plain. + +Run that first version in the simulator and use it as your main feedback loop. The simulator is still the fastest place to confirm that the lifecycle is behaving correctly and that the UI looks sane across different device skins. Only after that loop feels stable should you send a native build. For most teams Android is the easiest first checkpoint. iOS usually comes later because certificates and provisioning need to be in place before the build becomes useful. + +Once the app is running, the next step is usually to improve the look and feel. This is one place where the video is out of date. It moves toward the older theme designer and resource-editor workflow. For most current Codename One projects, CSS is the better default for styling and l10n property bundles are the better default for localization. The designer and resource editor still exist, but they are no longer the workflow most new projects should start with. + +By the time you have a form on screen, a button responding to taps, and a simulator session you can trust, you already have the foundation you need. From there you can style the app with CSS, add localization with property bundles, and start growing the project without having to relearn the basics later. + +## Further Reading + +- [Getting Started](/getting-started/) +- [Hello World](/hello-world/) +- [Development Environment](/development-environment/) +- [Build Server](/build-server/) +- [Themeing](/themeing/) +- [Moving To Maven](/blog/moving-to-maven/) diff --git a/docs/website/content/courses/course-01-java-for-mobile-devices/003-core-concepts-of-mobile-development.md b/docs/website/content/courses/course-01-java-for-mobile-devices/003-core-concepts-of-mobile-development.md index 21551278fd..46a35075cf 100644 --- a/docs/website/content/courses/course-01-java-for-mobile-devices/003-core-concepts-of-mobile-development.md +++ b/docs/website/content/courses/course-01-java-for-mobile-devices/003-core-concepts-of-mobile-development.md @@ -9,9 +9,30 @@ module_order: 1 lesson_order: 3 weight: 3 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Understand the mobile constraints that shape layout, images, memory usage, and device builds." --- - > Module 1: Course Lessons {{< youtube hwsWdhJHl8Q >}} + +Mobile development feels different from desktop development because the constraints are different. Screens vary wildly in size and density, memory is tighter, interaction is touch-first, and native platforms impose rules around packaging, signing, and distribution. If those constraints feel annoying at first, that is normal. The key is to design with them instead of fighting them. + +The first concept to get comfortable with is the difference between resolution and density. Resolution tells you how many pixels are on a screen. Density tells you how tightly packed those pixels are. Two devices can have similar resolutions and still look very different because one has far more pixels per inch. That is why a UI that seems fine on one device can suddenly look too small, blurry, or badly balanced on another. In Codename One, this is one of the reasons layouts matter so much. Hard-coded sizes age badly across real devices. + +Images are where density becomes expensive. If you ship only one low-resolution asset, it will look soft or pixelated on modern devices. If you ship only very large assets, the app pays in download size and memory use. The practical answer is to be selective. Use vector-friendly approaches where you can, especially for icons, and use raster images only where they genuinely add value. Codename One's multi-image support exists to help match assets to device density, but that should not become an excuse to dump huge image libraries into the app. + +For icons and simple symbolic graphics, built-in material icons and font-based icons are often the better choice. They scale cleanly, work well with theme colors, and avoid the asset explosion that comes from exporting multiple bitmap versions of the same shape. For photos and more detailed artwork, regular image assets still make sense, but you should think carefully about where the sharpest version is really needed. + +Another mobile concept that surprises people is that shipping an app is not just a matter of compiling code. Native platforms require signing and, in some cases, provisioning. On Android, signing establishes the identity of the app and controls who can publish updates to it. On iOS, certificates and provisioning profiles are part of both development and distribution. That complexity is not specific to Codename One. It is part of mobile development itself, and understanding it early makes later build and release work much less mysterious. + +The video spends a fair amount of time on signing and provisioning, and that material is still relevant. What has changed is the workflow around the project. Today you will usually create a Maven project first and then send builds through the current Codename One tooling. But the native platform rules have not gone away. If anything, they are the reason Codename One's build tooling is valuable in the first place. + +The broader lesson is that mobile work rewards adaptability. You do not control the screen, the density, the store requirements, the operating system policies, or the amount of memory on the user's device. A good mobile framework helps you live with those constraints. A good mobile developer learns to expect them. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [Build Server](/build-server/) +- [How Do I Fetch An Image From The Resource File, Add A Multiimage](/how-do-i/how-do-i-fetch-an-image-from-the-resource-file-add-a-multiimage/) +- [How Do I Create A 9 Piece Image Border](/how-do-i/how-do-i-create-a-9-piece-image-border/) +- [How Do I Create An iOS Provisioning Profile](/how-do-i/how-do-i-create-an-ios-provisioning-profile/) diff --git a/docs/website/content/courses/course-01-java-for-mobile-devices/004-what-is-codename-one.md b/docs/website/content/courses/course-01-java-for-mobile-devices/004-what-is-codename-one.md index db88b29b96..e996117d42 100644 --- a/docs/website/content/courses/course-01-java-for-mobile-devices/004-what-is-codename-one.md +++ b/docs/website/content/courses/course-01-java-for-mobile-devices/004-what-is-codename-one.md @@ -9,9 +9,29 @@ module_order: 1 lesson_order: 4 weight: 4 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Understand Codename One as a cross-platform Java application framework and build system." --- - > Module 1: Course Lessons {{< youtube XSztvFzQAr0 >}} + +The shortest description of Codename One is that it lets you build cross-platform applications in Java. The longer and more useful answer is that Codename One is a combination of framework APIs, platform ports, build tooling, and a development workflow that turns one codebase into applications that run on multiple targets. + +That combination matters because Codename One is not just a widget library. It provides a portable UI toolkit, portable device APIs, a simulator, native build infrastructure, and the glue that keeps those parts working together. When you create a project, write Java code, run it in the simulator, and later send a native build, you are using several layers of the system at once even if you do not think about them separately. + +At the application level, you mostly work with the public Codename One API. That is the part that gives you forms, layouts, networking, storage, media, and the rest of the framework surface. Underneath that is a porting layer that maps the framework behavior onto each platform. That is one of the reasons Codename One can behave consistently across platforms while still producing native builds. + +The video goes into the history from LWUIT through the early Codename One years, and that background helps explain some design decisions that are still visible in the framework. It also spends time on the old IDE plugin flow and older toolchain assumptions. That part is out of date. Today the practical entry point is a Maven-based project created with [Initializr](/initializr/), not a legacy IDE plugin wizard. The underlying idea is the same, though: the tooling exists to assemble the framework, your application code, and the native build pipeline into one working system. + +Another important part of the Codename One model is that the framework is versioned with the application. That means the app you ship carries the framework code it was built with. In practice this gives shipped apps stability and lets you choose when to adopt framework changes. It also makes versioned builds and controlled upgrades much more practical than they would be if every running app depended on one mutable shared runtime. + +Codename One also makes a deliberate trade-off in its UI architecture. It uses a lightweight component model so the framework can keep behavior more consistent across targets and so the simulator can behave meaningfully. That design choice is a big part of why the same application logic can run across the supported targets without being rewritten for each platform. + +If you are trying to decide whether Codename One is "just Java for mobile", the answer is no. It is a full cross-platform application stack with Java at the center. The more accurate way to think about it is this: Codename One gives you one framework, one language, and one application model, then handles the platform-specific work needed to turn that into real applications. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [Getting Started](/getting-started/) +- [Build Server](/build-server/) +- [Moving To Maven](/blog/moving-to-maven/) diff --git a/docs/website/content/courses/course-01-java-for-mobile-devices/005-anatomy-of-a-codename-one-application.md b/docs/website/content/courses/course-01-java-for-mobile-devices/005-anatomy-of-a-codename-one-application.md index 29024e2dbe..864a02182d 100644 --- a/docs/website/content/courses/course-01-java-for-mobile-devices/005-anatomy-of-a-codename-one-application.md +++ b/docs/website/content/courses/course-01-java-for-mobile-devices/005-anatomy-of-a-codename-one-application.md @@ -9,9 +9,30 @@ module_order: 1 lesson_order: 5 weight: 5 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Understand the structure of a modern Codename One project and the files that matter." --- - > Module 1: Course Lessons {{< youtube 8fxZVc1hw6Q >}} + +Every framework has a moment where the generated project stops feeling friendly and starts feeling like a pile of mysterious files. This lesson is about getting past that point. A Codename One project is much easier to work with once you know which files define the application, which files are generated, and which parts are just build output that you can ignore. + +The video walks through the old IDE-plugin project layout and spends a lot of time on `build.xml`, `lib`, and other files that were central to the older Ant-based workflow. That part is out of date for new projects. Today you will usually start from a Maven project, so the file and directory structure looks different. Even so, the important ideas from the lesson still translate well: some files define the app, some define the build, some are caches or artifacts, and some exist only to support a specific toolchain. + +In a modern Codename One project, the first files worth understanding are the Maven build files and the application source itself. The Java application class still expresses the lifecycle of the app. Resource directories still contain the assets and configuration the app depends on. Build-related files still describe how the project is packaged and how platform-specific work is triggered. The main difference is that Maven now owns the overall build structure instead of the old plugin-generated Ant layout. + +One file that remains conceptually important is `codenameone_settings.properties`. It still acts as the central configuration point for many application-specific settings, including identifiers, build hints, and platform-specific options. You do not need to hand-edit it for everything, and in many cases it is better to use the supported tooling, but you do need to know it exists and what role it plays. When something about the application's identity or native configuration changes, this file is often involved. + +Build output should be treated as disposable. Whether it is a Maven `target` directory or older `build` and `dist` folders from the plugin era, generated artifacts are there to support a build or a simulator run. They are useful for debugging packaging problems, checking what assets ended up in the final jar, or understanding why an application became unexpectedly large, but they are not the place to make source-level changes. + +That distinction becomes especially important when you start troubleshooting. If the final package is too large, inspect the generated artifact and see what actually got bundled. If a build behaves differently from what you expect, check whether the configuration lives in source files, properties, or generated output. A lot of confusion disappears once you stop treating every file in the project tree as equally important. + +So the real anatomy of a Codename One application is simpler than it first appears. There is the app code you write, the project configuration that describes how it should be built, the assets it depends on, and the generated output produced along the way. Learn those boundaries early and the rest of the project becomes much easier to reason about. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [Getting Started](/getting-started/) +- [Build Server](/build-server/) +- [Hello World](/hello-world/) +- [How Do I Get Repeatable Builds, Build Against A Consistent Version Of Codename One & Use The Versioning Feature](/how-do-i/how-do-i-get-repeatable-builds-build-against-a-consistent-version-of-codename-one-use-the-versioning-feature/) diff --git a/docs/website/content/courses/course-01-java-for-mobile-devices/006-internationalization-and-localization.md b/docs/website/content/courses/course-01-java-for-mobile-devices/006-internationalization-and-localization.md index 11dc30dcab..e85d25514e 100644 --- a/docs/website/content/courses/course-01-java-for-mobile-devices/006-internationalization-and-localization.md +++ b/docs/website/content/courses/course-01-java-for-mobile-devices/006-internationalization-and-localization.md @@ -9,9 +9,29 @@ module_order: 1 lesson_order: 6 weight: 6 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Internationalization, localization, RTL support, and locale-aware formatting in modern Codename One projects." --- - > Module 1: Course Lessons {{< youtube 32mkZymqa6E >}} + +Internationalization and localization are broader than translation. Internationalization means structuring the app so it can adapt to different locales. Localization is the work of adapting the app to a specific locale. Language is part of that, but so are dates, numbers, currency, right-to-left behavior, phrasing, and the cultural meaning of visual choices. + +The first practical rule is to stop hard-coding user-facing text directly into the UI wherever possible. Codename One is designed to work with key/value bundles so that the text shown to the user can change with the current locale. The older video demonstrates this through the resource editor. For new projects, the better default is l10n property bundles, but the core idea remains the same: components should reference localizable keys, not baked-in strings that force you to revisit the code for every translation. + +This matters because translation is only the first layer. Locale-specific behavior also affects the way you display dates, numbers, and currency. If you use locale-aware formatting utilities, the app can present those values in a way that feels natural to the user instead of forcing one fixed representation on every market. In Codename One, `L10NManager` and the framework's localization utilities are the right place to start for this kind of formatting. + +Testing localization also needs to be part of the normal development loop. It is much easier to catch problems early if you force the simulator into a different language and inspect the UI there. A localized build should not only show translated strings. It should still fit properly, align correctly, and feel intentional when labels grow longer or date and number formatting changes. + +Right-to-left support is one of the most important areas to get right. Languages such as Hebrew and Arabic do not just translate the text. They change the expected flow of the interface. Text aligns differently, component order often reverses, and icons or directional affordances may need to be mirrored. Codename One helps a lot here because layouts react to RTL mode by flipping positions and alignment where appropriate. A `BorderLayout.EAST` relationship, for example, is interpreted relative to the active writing direction. + +That said, RTL is not automatic magic. Mixed-direction content still needs attention. Numbers are still read left-to-right even inside right-to-left languages, so bidirectional text can produce cursor movement and layout behavior that surprises developers who only test in English. Icons such as play arrows, chevrons, and back buttons also need review because a mirrored UI with unmirrored directional icons still feels wrong. + +The video uses the older designer workflow to define bundles and RTL markers. That part is outdated for new projects, but the localization concepts themselves are still the right ones to learn. In current Codename One development, the better default is property-bundle based localization plus locale-aware formatting in code, with the layout system doing most of the heavy lifting for RTL-aware component ordering. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [Themeing](/themeing/) +- [Layout Basics](/layout-basics/) +- [Properties Are Amazing](/blog/properties-are-amazing/) diff --git a/docs/website/content/courses/course-01-java-for-mobile-devices/007-layout-basics.md b/docs/website/content/courses/course-01-java-for-mobile-devices/007-layout-basics.md index 55685b5ad2..71da22f6cb 100644 --- a/docs/website/content/courses/course-01-java-for-mobile-devices/007-layout-basics.md +++ b/docs/website/content/courses/course-01-java-for-mobile-devices/007-layout-basics.md @@ -9,10 +9,29 @@ module_order: 1 lesson_order: 7 weight: 7 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Understand how layout managers define portable UI structure in Codename One." --- - > Module 1: Course Lessons - {{< youtube 4D_KUa2qv2o >}} + +Layouts are the reason a Codename One UI can survive different screen sizes, orientations, pixel densities, and languages. Components do not live at fixed coordinates. They live inside `Container` objects, and those containers use layout managers to decide how much space each child gets and where it should appear. + +The core problem layouts solve is portability. A button position that looks fine on one phone may be wrong on another. A label that fits in English may overflow in German. A design that works in portrait may fall apart in landscape. Layout managers let you describe intent instead of hard-coding coordinates: this component should stay on the left, this field should take the remaining width, this section should stack vertically, these buttons should all be the same size. + +That is why the classic “label on the left, field on the right” example is still such a useful starting point. In Codename One you usually express that with a `BorderLayout`, putting the label in `BorderLayout.WEST` and the field in `BorderLayout.CENTER`. The layout then handles the resizing rules for you. The label keeps the width it needs, while the center component expands into the remaining space. + +The standard layouts each have a natural role. `FlowLayout` is fine for small inline groups of components, but it is easy to outgrow. `BorderLayout` is one of the most useful outer layouts because it gives you strong structure: top, bottom, left, right, and a center area that consumes the remaining space. `BoxLayout.y()` is a great default for stacked content because it reads like the screen itself. `GridLayout` is useful when you genuinely want equal-sized components. `TableLayout` is especially helpful for forms and data entry. `LayeredLayout` is the one to reach for when components need to sit on top of each other. + +One detail that matters early is constraints. Some layouts need them and some do not. `BorderLayout` depends on explicit positions such as `CENTER` and `WEST`, while `BoxLayout` generally does not need extra constraints at all. A layout manager is not just “where children go”; it also defines what information you need to provide when you add those children. + +The most effective way to build a real screen is usually to nest a few simple containers rather than hunting for one magical layout that does everything. A form might use `BorderLayout` at the top level, a `BoxLayout.y()` in the content area, and then small `BorderLayout` or `GridLayout` sections inside it. Good layout code tends to mirror the visual structure of the screen. + +The modern adjustment is not in the layout system itself so much as in how you split responsibilities. Layout code should mostly define structure and resizing behavior. CSS should do much more of the styling work: spacing, fonts, colors, borders, and visual states. If you find yourself using extra containers only to fake visual styling, that is often a sign that the visual concern belongs in CSS instead. + +## Further Reading + +- [Layout Basics](/layout-basics/) +- [Developer Guide](/developer-guide/) +- [Themeing](/themeing/) +- [Getting Started](/getting-started/) diff --git a/docs/website/content/courses/course-01-java-for-mobile-devices/008-theme-basics.md b/docs/website/content/courses/course-01-java-for-mobile-devices/008-theme-basics.md index a5da13873c..ba407df0d0 100644 --- a/docs/website/content/courses/course-01-java-for-mobile-devices/008-theme-basics.md +++ b/docs/website/content/courses/course-01-java-for-mobile-devices/008-theme-basics.md @@ -9,12 +9,29 @@ module_order: 1 lesson_order: 8 weight: 8 is_course_lesson: true -description: "Note: I would recommend focusing on CSS for newer applications" +description: "Understand theme concepts while treating CSS as the default styling path for modern projects." --- - > Module 1: Course Lessons +{{< youtube cxllJwt10VU >}} -Note: I would recommend focusing on CSS for newer applications +Styling in Codename One starts with a simple question: are you trying to change how components look, or are you trying to change how the UI behaves structurally? If the answer is visual styling, the modern default is CSS. The video uses the older designer-centered theme workflow, and that still helps explain the underlying concepts, but for a new project you should usually start with CSS and treat the older theme editor as a lower-level tool rather than the main path. -{{< youtube cxllJwt10VU >}} +The concepts behind theming are still the same. Components get their appearance from a UIID. A UIID is effectively the name of the style a component uses. If you create a button and give it a custom UIID, that UIID becomes the hook for styling the button consistently. This is true whether the style is defined in CSS or in the older theme resource workflow. + +The simplest useful example is a custom button. Give it a background color, a foreground color, some padding, and a readable font size. The point is not to make it beautiful on the first pass. The point is to understand which properties actually shape the component. Background and foreground colors control the obvious look, but spacing is just as important. Padding affects the space inside the component and therefore the touchable area. Margin affects the space outside the component and therefore the relationship between neighboring components. + +State-specific styling is one of the first places where theming becomes real. A button usually needs at least an unselected appearance and a pressed or selected appearance. If the normal state has one background and the pressed state has no explicit styling, the result can feel inconsistent or broken. The video demonstrates this with borders and selected styles, and the underlying lesson remains important: style all of the states that matter, not just the first one you see in the simulator. + +Borders are another common source of confusion. In the older theme editor, borders often take precedence over background settings, which is why a component can look correct on one platform but unexpectedly wrong on another. The same general rule applies conceptually even when you work in CSS: understand which visual property is actually winning. + +The other important concept from the lesson is inheritance. A custom style does not need to redefine everything from scratch. It is usually better to start from a base component style and override only what you actually want to change. That keeps your styles smaller and more maintainable. + +Theme constants and other lower-level theme settings still matter, especially when you are controlling broader application behavior or integrating with older theme-based assets. But for a new project, the practical approach is simpler than the older lesson suggests: use layouts to define structure, use CSS to define appearance, and only drop to the older theme tooling when you genuinely need the lower-level control. + +## Further Reading + +- [Themeing](/themeing/) +- [Developer Guide](/developer-guide/) +- [How Do I Create A Simple Theme](/how-do-i/how-do-i-create-a-simple-theme/) +- [Work With Multi Images And Device Densities](/how-do-i/how-do-i-fetch-an-image-from-the-resource-file-add-a-multiimage/) diff --git a/docs/website/content/courses/course-01-java-for-mobile-devices/009-adapting-a-ui-design.md b/docs/website/content/courses/course-01-java-for-mobile-devices/009-adapting-a-ui-design.md index 00abf3aada..4efa68e533 100644 --- a/docs/website/content/courses/course-01-java-for-mobile-devices/009-adapting-a-ui-design.md +++ b/docs/website/content/courses/course-01-java-for-mobile-devices/009-adapting-a-ui-design.md @@ -9,12 +9,32 @@ module_order: 1 lesson_order: 9 weight: 9 is_course_lesson: true -description: "The demo source code for this section and the resource files are hosted in this github project." +description: "Turn a visual design into a practical Codename One UI using layouts, CSS, and carefully chosen assets." --- - > Module 1: Course Lessons +{{< youtube WT4cFceBWRA >}} -The demo source code for this section and the resource files are hosted in this github project. +Adapting a UI design is never just a matter of copying pixels from a mockup into code. A design file shows you the visual intention. Your job is to translate that intention into a layout that survives different screen sizes, densities, font rendering differences, and real user interaction. -{{< youtube WT4cFceBWRA >}} +The first step is to break the design into categories instead of trying to build the whole screen at once. Some parts should stay as live UI: text, buttons, lists, search fields, toolbars, and anything the user interacts with. Some parts may need image assets: photographs, illustrations, decorative textures, or highly specific shapes that are not worth recreating in code. Once you make that distinction, the design becomes much easier to implement. + +The video uses a Photoshop-based workflow and cuts image assets out of a PSD. That basic thinking is still useful, but the tooling has moved on. Today the same exercise might start from Figma, Sketch, or exported design assets rather than Photoshop, and the resulting Codename One implementation should usually be styled with CSS rather than centered around the older designer workflow. + +When you translate the design into UI, start with layout before styling. Build the screen structure using forms, containers, and layouts that express the visual hierarchy. Decide which areas belong in the toolbar, which belong in the content pane, and which elements need to float over the content. Once the structure is correct, styling becomes much easier because you are polishing a stable layout instead of trying to patch a brittle one. + +This is also where you should resist the temptation to chase pixel perfection too early. A design mockup is often drawn for one screen size with one font rasterizer and one set of exact asset dimensions. A real mobile UI has to survive much more than that. It is usually better to match the intent and rhythm of the design than to overfit to one screenshot and end up with something that breaks on the next device. + +Assets still matter, but they should be chosen carefully. If a shape can be expressed with styling, borders, padding, and standard components, that is usually preferable to shipping another image. If an icon can come from a material icon font, that is usually preferable to exporting several bitmap versions. Use raster assets where they provide something unique, not as a substitute for understanding layout and styling. + +Floating action buttons, layered elements, and custom visual accents are good examples of where the implementation needs to understand the framework rather than just the mockup. A floating button is not simply drawn in one static position. It needs to live in the right layer and respond properly as the form size changes. A highlighted row or decorative border may be better represented by a border image, a styled background, or a reusable UIID depending on how the screen behaves. + +The best way to adapt a design in Codename One today is to treat the mockup as a guide, recreate the structure with layouts, use CSS to style the resulting components, and only then introduce image assets where they genuinely improve the result. That approach scales much better than trying to paint the entire screen out of cut-up images. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [Themeing](/themeing/) +- [Layout Basics](/layout-basics/) +- [How Do I Create A 9 Piece Image Border](/how-do-i/how-do-i-create-a-9-piece-image-border/) +- [How Do I Create Gorgeous SideMenu](/how-do-i/how-do-i-create-gorgeous-sidemenu/) diff --git a/docs/website/content/courses/course-01-java-for-mobile-devices/010-storage-filesystem-and-sql.md b/docs/website/content/courses/course-01-java-for-mobile-devices/010-storage-filesystem-and-sql.md index 219f1bc69f..e68822e3cf 100644 --- a/docs/website/content/courses/course-01-java-for-mobile-devices/010-storage-filesystem-and-sql.md +++ b/docs/website/content/courses/course-01-java-for-mobile-devices/010-storage-filesystem-and-sql.md @@ -9,9 +9,28 @@ module_order: 1 lesson_order: 10 weight: 10 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Choose the right persistence layer: preferences, storage, filesystem, or SQL." --- - > Module 1: Course Lessons {{< youtube _EXEN52wQvs >}} + +Persistent data in Codename One usually starts with a choice between three levels of storage: `Preferences` for very small settings, `Storage` for simple app-private persisted objects or blobs, and SQL for data that needs real querying, sorting, or filtering. The file system sits beside those as a lower-level tool rather than the default answer to every persistence question. + +`Storage` is the place to start for many apps because it is portable and application-oriented. It is not a general shared file hierarchy. It is a higher-level persistence API tied to the app itself. That makes it a much better default than reaching immediately for raw files just because you are used to desktop development. + +The file system becomes useful when you actually need file paths, larger assets, or interoperability with APIs that naturally work in terms of files. But mobile app isolation still matters. Devices do not expose the same shared-file assumptions developers are used to on the desktop. Even when some platforms allow more shared file access than others, that behavior is not the best foundation for portable application design. + +SQL is the right tool when your data is large enough or dynamic enough that you need real query capability. If you need filtering, sorting, lookups, or structured updates over a meaningful amount of data, SQLite is usually a better fit than trying to serialize everything into a single stored object. If you just need to save app state or a few structured objects, SQL is often unnecessary complexity. + +The lesson also makes an important operational point about cleanup. Database cursors and related resources should be closed explicitly. You should not rely on the garbage collector to clean up database resources whenever it happens to run. That becomes especially important when portability differences between platforms affect database behavior and thread safety. + +Shipping an initial database is another valid pattern. If the app needs seed data, you can package a database resource and copy it into the correct writable location on first run. That is often easier than trying to generate the full initial data set programmatically every time. + +The modern recommendation is mostly about choosing the simplest layer that fits the problem. Use `Preferences` for settings, `Storage` for straightforward app-private persistence, SQL when you genuinely need query power, and filesystem APIs when the problem is really file-oriented. Starting at the highest appropriate level tends to produce the most portable and maintainable code. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [How Do I Use Properties To Speed Development](/how-do-i/how-do-i-use-properties-to-speed-development/) +- [How Do I Improve Application Performance Or Track Down Performance Issues](/how-do-i/how-do-i-improve-application-performance-or-track-down-performance-issues/) diff --git a/docs/website/content/courses/course-01-java-for-mobile-devices/011-threading-and-the-edt.md b/docs/website/content/courses/course-01-java-for-mobile-devices/011-threading-and-the-edt.md index 2fb1323fa8..2103c44954 100644 --- a/docs/website/content/courses/course-01-java-for-mobile-devices/011-threading-and-the-edt.md +++ b/docs/website/content/courses/course-01-java-for-mobile-devices/011-threading-and-the-edt.md @@ -9,9 +9,29 @@ module_order: 1 lesson_order: 11 weight: 11 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Understand the event dispatch thread and how to keep Codename One applications responsive." --- - > Module 1: Course Lessons {{< youtube p6UFNw0nGik >}} + +Threading becomes much less mysterious once you separate two different concerns: doing work, and updating the user interface. In Codename One, those two things should not be mixed casually. The UI is driven by the event dispatch thread, usually shortened to EDT, and that thread needs to stay responsive if the application is going to feel smooth. + +The simplest mental model is that the EDT is the thread that owns UI work. Painting, handling most user interaction, and changing component state should happen there. If you block it with long calculations, network waits, database work, or file processing, the app stops feeling alive. Buttons appear unresponsive, animations freeze, and the whole interface looks broken even if the code is technically still running. + +That is why background work matters. Expensive or slow operations should run off the EDT, then hand control back to the EDT when it is time to update the UI. This is one of the core habits of mobile development in general, not just Codename One. The framework gives you utilities to help with that handoff, but the underlying rule is simple: keep slow work away from the UI thread. + +The video also points out something that is still worth taking seriously: portability is one reason to keep threading simple. Codename One targets multiple platforms and runtime environments, so code that depends on subtle threading behavior or low-level JVM memory-model tricks is much more likely to become fragile. The framework is happiest when your concurrency model is boring and explicit. + +In practice, that means you should treat the EDT as a place for fast UI logic, not a place for long-running business logic. Build the screen there. React to taps there. Start background work from there when needed. Then return to the EDT to update labels, show dialogs, replace forms, or refresh components once the work is done. + +It also means you should be careful about "almost fast" work. A single blocking call may not feel dangerous while testing on a desktop simulator, but mobile hardware, slow storage, and real networks make those delays much more obvious. If an operation might pause for a noticeable amount of time, it probably does not belong on the EDT. + +Once you adopt that model, threading stops being an abstract theory lesson and becomes a practical discipline: keep the interface responsive, move slow work to the background, and bring results back to the UI thread in a controlled way. Most Codename One applications do not need elaborate concurrency. They need that one rule applied consistently. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [How Do I Access Remote Webservices, Perform Operations On The Server](/how-do-i/how-do-i-access-remote-webservices-perform-operations-on-the-server/) +- [How Do I Improve Application Performance Or Track Down Performance Issues](/how-do-i/how-do-i-improve-application-performance-or-track-down-performance-issues/) +- [How Do I Find Problems In My Application Using The Codename One Tools And The Standard IDE Tools](/how-do-i/how-do-i-find-problems-in-my-application-using-the-codename-one-tools-and-the-standard-ide-tools/) diff --git a/docs/website/content/courses/course-01-java-for-mobile-devices/012-understanding-properties.md b/docs/website/content/courses/course-01-java-for-mobile-devices/012-understanding-properties.md index c2ee1a5b62..cabb6beee4 100644 --- a/docs/website/content/courses/course-01-java-for-mobile-devices/012-understanding-properties.md +++ b/docs/website/content/courses/course-01-java-for-mobile-devices/012-understanding-properties.md @@ -9,9 +9,26 @@ module_order: 1 lesson_order: 12 weight: 12 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Understand how Codename One properties support parsing, binding, and model-driven workflows." --- - > Module 1: Course Lessons {{< youtube 77N2t2n8rbQ >}} + +Codename One properties are useful when you want one model class to do more than simply hold data. A plain old Java object can represent state just fine, but it does not automatically know how to bind to UI, serialize itself, parse structured input, or describe its own fields at runtime. The properties API exists to make those jobs easier. + +The core idea is that a property-backed object carries metadata about its fields through the property index. That means the framework can introspect the object safely even after obfuscation. Once that metadata exists, a lot of repetitive plumbing becomes easier: JSON and XML mapping, serialization, SQL helpers, UI binding, and generated forms. + +This is what makes properties more than just a different syntax for getters and setters. You still keep a clear data model, but you also gain a structured description of that model that the framework can reuse. That is why the lesson moves quickly from basic property access into parsing, storage, CRUD helpers, and binding. Those features all depend on the same underlying introspection capability. + +Two uses are especially practical. The first is data mapping. If your app receives structured data from a service and you want a cleaner route from raw JSON or XML into an object model, properties can reduce a lot of manual parsing code. The second is UI binding. If a field in the model changes and a component should reflect that change, or vice versa, the properties API gives you a much cleaner starting point than manually wiring every update yourself. + +The lesson also highlights Instant UI, which can generate forms from property objects. That is still conceptually useful, but it should be applied with judgment. Generated UI can be a strong accelerator for internal tools, simple data-entry screens, or prototypes. It is not automatically the best fit for polished product UI where custom layout and styling matter more. In modern projects, CSS and hand-authored layout code still remain the better choice for most high-touch product screens. + +So the best way to think about properties is as a productivity tool for model-driven parts of an application. If the same object needs to be bound to UI, serialized, parsed, and perhaps stored, properties can eliminate a lot of repetitive glue code. If the object is simple and none of those benefits matter, a normal POJO may still be the simpler choice. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [Properties Are Amazing](/blog/properties-are-amazing/) +- [How Do I Use Storage, File System And SQL](/how-do-i/how-do-i-use-storage-file-system-sql/) diff --git a/docs/website/content/courses/course-01-java-for-mobile-devices/013-push-notification.md b/docs/website/content/courses/course-01-java-for-mobile-devices/013-push-notification.md index ba9986cfef..c17a5a7874 100644 --- a/docs/website/content/courses/course-01-java-for-mobile-devices/013-push-notification.md +++ b/docs/website/content/courses/course-01-java-for-mobile-devices/013-push-notification.md @@ -9,9 +9,29 @@ module_order: 1 lesson_order: 13 weight: 13 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Understand push notification as a signaling channel rather than a general networking layer." --- - > Module 1: Course Lessons {{< youtube 8wzBpEp81Kc >}} + +Push notification is best understood as a user-notification channel, not as a general-purpose networking layer. It is good for telling a device that something happened and, depending on platform and app state, optionally carrying some payload with that message. It is not something you should design your core application protocol around. + +That distinction matters because push is inherently unreliable as a transport. Users can disable it. Some devices or services may not support it the same way. Different platforms treat background delivery differently. Build your app so that push signals something important, but do not assume it is always available or always delivered in the same way everywhere. + +Codename One smooths over a lot of the platform differences by providing a unified push API and server-side push entry point. You still need platform credentials for the vendor services, but you do not have to design a completely separate push implementation for every target OS. The high-level workflow is: configure the provider credentials, register the app for push, collect the push token on the device, send that token to your server, and then use your server to send push messages through the Codename One push infrastructure. + +On the client side, one of the most important details is that push callbacks belong in the main application class. The `PushCallback` implementation must live in the class that represents the app lifecycle. That is where Codename One wires push delivery into the application. The key callbacks are the message callback itself, the registration callback, and the registration error callback. + +The registration callback is especially important because that is where you usually obtain the push key and send it to your own backend. A common mistake is to treat the device identifier argument as if it were the push token. It is not. The push key is what your server needs in order to target that device later. + +Server-side push is just as important as the client setup. Sending the request is not enough; you also need to parse the response and log the outcome. A lot of push debugging time is wasted by code that fires off a request and never looks closely at the response body. If delivery fails because of a credential mismatch, certificate problem, provider rejection, or malformed request, that response is often your only useful clue. + +Push types also need to be chosen deliberately. Visible notification types are usually the safest default because they match what users and platforms expect. Hidden push and payload-heavy push are more nuanced and behave differently across operating systems, especially on iOS. The lesson is out of date in its references to older Google push naming and setup steps, but the design lesson is still current: treat push as signaling, not as the backbone of app synchronization. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [Build Hints](/build-hints/) +- [Build Server](/build-server/) +- [How Do I Use Crash Protection? Get Device Logs?](/how-do-i/how-do-i-use-crash-protection-get-device-logs/) diff --git a/docs/website/content/courses/course-01-java-for-mobile-devices/014-native-interfaces-access-native-device-features.md b/docs/website/content/courses/course-01-java-for-mobile-devices/014-native-interfaces-access-native-device-features.md index 245b43cb10..86b6638eb3 100644 --- a/docs/website/content/courses/course-01-java-for-mobile-devices/014-native-interfaces-access-native-device-features.md +++ b/docs/website/content/courses/course-01-java-for-mobile-devices/014-native-interfaces-access-native-device-features.md @@ -9,9 +9,31 @@ module_order: 1 lesson_order: 14 weight: 14 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Use native interfaces to integrate platform-specific capabilities without giving up the portability of the rest of the app." --- - > Module 1: Course Lessons {{< youtube 2xKvvv7XoVQ >}} + +Native interfaces are how you call platform-specific code from a Codename One application without giving up the portability of the rest of the project. When Codename One says "native" in this context, it does not mean Java's ordinary `native` keyword. It means "use the platform's own language and APIs" when you need something that the portable Codename One layer does not expose directly. + +That means the implementation changes by platform. On Android, a native interface implementation can use the Android SDK and third-party Android libraries. On iOS, the implementation uses the native iOS language and APIs. On JavaScript builds, you can call into JavaScript. On the desktop port, you can use ordinary JavaSE APIs. The Java code in your main app remains the same; the native interface is the bridge that dispatches to the right implementation on each platform. + +The core idea is simple: define an interface that extends `NativeInterface`, then provide platform-specific implementations of that interface. The interface becomes the contract between your portable Java code and the native side. `isSupported()` is especially important because not every platform will necessarily have an implementation. Your Java code should check whether the native feature is available before relying on it. + +This is useful both inside an application and inside a cn1lib. In fact, cn1libs are one of the best uses of native interfaces because they let you wrap messy platform-specific integration behind a clean Java API. The caller sees a normal library. The native details stay hidden inside the implementation. + +The most important habit here is to keep the native surface area small. Expose the narrowest interface that solves the problem. It is tempting to mirror a large native API directly, but that usually leads to code that is harder to maintain, harder to test, and harder to port. A small interface with a few focused methods is much easier to reason about and much easier to support across platforms. + +Native interfaces also restrict the kinds of types you can pass. That is intentional. Simple values such as primitives, strings, byte arrays, and peers are much easier to move between languages and runtimes. Once you try to pass complex Java objects directly into Objective-C, JavaScript, or other native code, translation becomes far more complicated and performance becomes harder to predict. + +`PeerComponent` is one of the most important special cases. It allows native code to return a native visual component that can be placed into a Codename One layout like an ordinary component. The classic example is a native map view. That pattern is powerful because it gives you real native UI where it matters while still letting the surrounding screen remain portable. + +The hard part of native interfaces is often not the code itself but the configuration around it. Native libraries frequently come with Gradle dependencies on Android, CocoaPods or frameworks on iOS, manifest changes, plist changes, and packaging rules for extra files. In Codename One these are usually handled through build hints and native source packaging. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [Build Hints](/build-hints/) +- [Build Server](/build-server/) +- [How Do I Access Native Device Functionality? Invoke Native Interfaces?](/how-do-i/how-do-i-access-native-device-functionality-invoke-native-interfaces/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/001-working-with-css.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/001-working-with-css.md index 3e08a41f02..6db2b0048e 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/001-working-with-css.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/001-working-with-css.md @@ -9,14 +9,34 @@ module_order: 1 lesson_order: 1 weight: 1 is_course_lesson: true -description: "The CSS in this section should work well for new versions too. In Maven CSS is the default and the whole 'setting up' portion is no longer applicable." +description: "Understand how CSS fits into modern Codename One projects." --- - > Module 1: Working with CSS +This lesson explains how CSS fits into a modern Codename One project and how it should be used in day-to-day styling work. -The CSS in this section should work well for new versions too. In Maven CSS is the default and the whole "setting up" portion is no longer applicable. +{{< youtube UxZ1HeheGwU >}} -The source code for this part is in this github repository. +For most new Codename One projects, CSS is the first place to reach when you want to style the UI. It is the most practical way to control colors, borders, fonts, spacing, and component states without pushing visual concerns into Java code. The video introduces CSS as an optional plugin, but that part is out of date now. In current Codename One development, CSS is a normal part of the workflow rather than something experimental. -{{< youtube UxZ1HeheGwU >}} +That does not mean CSS replaces the Codename One theme system entirely. It means CSS is now the most practical way to drive that system. UIIDs still matter. Theme values still matter. The difference is that instead of editing everything through the older designer workflow, you usually define and evolve those visual decisions in CSS and let the build process generate the underlying resources as needed. + +This is also why Codename One CSS feels familiar without being identical to browser CSS. Selectors, colors, borders, and states look recognizable, which is useful, but the framework has its own custom properties and behavior. You are styling Codename One components, not HTML elements. So it is better to think of CSS here as a clean styling language for Codename One rather than expecting arbitrary snippets from the web to drop in unchanged. + +The easiest way to learn this is to style one simple component first. Take a button, give it a UIID, define its appearance in CSS, and rerun the application. That small loop teaches the core idea quickly: you describe the look once, and Codename One applies it consistently across the relevant states instead of forcing you to rebuild that appearance in Java code. + +A Codename One CSS file works with the theme system rather than bypassing it. That is why theme overlays, UIIDs, and precedence still matter. If a base theme defines one thing and your CSS defines another, the later layer wins. Understanding that relationship makes it much easier to predict why a style is or is not being applied. + +Another place where the video is dated is the role of the designer and resource editor. They were much more central to styling workflows at the time. That is no longer the best default. CSS should be the main styling layer in a new project, and localization should usually live in l10n property bundles rather than the older designer-driven localization flow. + +In practice, the healthiest split is simple. Use layouts and component structure to define how the UI behaves. Use CSS to define how it looks. Use resource files only where they still make sense for assets that genuinely belong there. That separation keeps screens easier to evolve and makes style changes far less invasive than they were in older projects. + +Once you approach the topic that way, the rest falls into place. Layouts define structure. CSS defines appearance. Resource files still have a role for the assets that belong there, but they are no longer where most visual work should begin. + +## Further Reading + +- [Themeing](/themeing/) +- [Designer](/designer/) +- [Developer Guide](/developer-guide/) +- [Layout Basics](/layout-basics/) +- [Moving To Maven](/blog/moving-to-maven/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/002-introduction-to-spring-boot.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/002-introduction-to-spring-boot.md index 7bef0e4696..044d222cdb 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/002-introduction-to-spring-boot.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/002-introduction-to-spring-boot.md @@ -9,12 +9,26 @@ module_order: 2 lesson_order: 1 weight: 2 is_course_lesson: true -description: "The source code used in this part is available in this github repository." +description: "Set up a simple Spring Boot backend for a Codename One application." --- - > Module 2: Working with Custom Web Services +{{< youtube tugJ_7xMdzs >}} -The source code used in this part is available in this github repository. +Sooner or later most non-trivial mobile apps need a server. That server might store data, authenticate users, send push notifications, validate business rules, or expose an API that several client applications share. In this course module, Spring Boot is used as the server-side counterpart because it is a straightforward way to build a Java backend without drowning in boilerplate. -{{< youtube tugJ_7xMdzs >}} +Spring Boot works well for this kind of project because it removes a lot of setup friction. Instead of manually assembling an application server, wiring configuration by hand, and spending the first hour fighting infrastructure, you can create a project, add the dependencies you need, and start exposing endpoints quickly. That makes it a good fit for a mobile course where the goal is to understand the client/server relationship, not get lost in container administration. + +The video introduces Spring Boot through older IDE screenshots, but the important part is not the IDE. The important part is the shape of the backend: a normal Java project, usually built with Maven, with dependencies for web support, data access, and whatever database driver you need. Today you can create that project with Spring Initializr and open it in any mainstream IDE. + +For the persistence layer, the lesson chooses a relational database and uses JPA-style mapping. That is still a sensible default. Mobile backends often need reliable querying, reporting, and clear data relationships long before they need exotic distributed-database tricks. Starting with a conventional SQL-backed model keeps the application understandable and leaves room to grow later if the problem actually demands something more specialized. + +That does not mean every Codename One backend must be Spring Boot or even Java. The mobile client can talk to any server that exposes a usable API. But if you already work in Java, Spring Boot remains a practical choice because it keeps the server in the same language ecosystem as the client while still giving you mature tools for HTTP endpoints, data access, and deployment. + +The key outcome of this lesson is not "install Spring Boot." It is understanding that a mobile backend can be treated like a normal application project. It has its own build, its own runtime, its own persistence layer, and its own deployment concerns. Once you see it that way, the client and server stop feeling like two mysterious worlds and start feeling like two parts of the same system. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [How Do I Access Remote Webservices, Perform Operations On The Server](/how-do-i/how-do-i-access-remote-webservices-perform-operations-on-the-server/) +- [Connecting to a Web Service](/courses/course-02-deep-dive-mobile-development-with-codename-one/003-connecting-to-a-web-service/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/003-connecting-to-a-web-service.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/003-connecting-to-a-web-service.md index ccd4aa324d..9a3a6322ed 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/003-connecting-to-a-web-service.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/003-connecting-to-a-web-service.md @@ -9,12 +9,27 @@ module_order: 2 lesson_order: 2 weight: 3 is_course_lesson: true -description: "The source code used in this part is available in this github repository." +description: "Call a JSON web service from Codename One and structure the client-side code cleanly." --- - > Module 2: Working with Custom Web Services +{{< youtube 3B7C0ZbV8Bc >}} -The source code used in this part is available in this github repository. +Once a backend exists, the mobile app needs a clean way to talk to it. In practice that usually means sending HTTP requests, receiving JSON responses, and converting those responses into application objects that the UI can work with. That process is simple in concept, but it becomes messy fast if networking code leaks everywhere. -{{< youtube 3B7C0ZbV8Bc >}} +The first thing worth doing is separating transport code from UI code. A form should not be responsible for knowing how to build URLs, set headers, serialize request bodies, and parse raw JSON. Put that logic in a small service layer instead. Then the rest of the app can ask for higher-level operations such as "add item", "load items", or "log in" instead of dealing with low-level request mechanics every time. + +The video builds requests directly with `ConnectionRequest`, serializes JSON explicitly, and parses responses back into model objects. That is still a valid way to understand what is happening on the wire, and it is a good lesson because it keeps the protocol visible. Even if you later wrap the code in helper utilities or higher-level abstractions, you still need to understand the basics: HTTP method, URL, headers, body, response status, and JSON parsing. + +One of the most important design choices here is whether a call should be treated synchronously or asynchronously. Synchronous code can be easier to read because it flows top to bottom, but it must be used carefully and never in a way that freezes the user interface. Asynchronous code is more flexible and generally safer for long-running work, but it pushes you toward callback or continuation-style logic. There is no single correct answer for every case. What matters is knowing which model you are using and keeping UI responsiveness intact. + +For most applications, the practical structure is this: define a model object that matches the data you exchange with the server, define a client-side service class that knows how to talk to the backend, and keep the UI layer focused on displaying results and collecting user input. That separation pays off quickly once the project grows beyond one or two endpoints. + +This lesson also highlights a broader truth about mobile networking: the app is always dealing with a hostile environment. Networks fail, servers return unexpected responses, and payloads change over time. Good client code assumes that requests can fail and that parsing may not always go the happy-path way. Even a simple demo service is worth writing as though it were real. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [How Do I Access Remote Webservices, Perform Operations On The Server](/how-do-i/how-do-i-access-remote-webservices-perform-operations-on-the-server/) +- [How Do I Use HTTP, Sockets, Webservices & Websockets](/how-do-i/how-do-i-use-http-sockets-webservices-websockets/) +- [Threading and the EDT](/courses/course-01-java-for-mobile-devices/011-threading-and-the-edt/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/004-introduction.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/004-introduction.md index 0c47e40cdf..73e7e4a509 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/004-introduction.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/004-introduction.md @@ -9,12 +9,25 @@ module_order: 3 lesson_order: 1 weight: 4 is_course_lesson: true -description: "The source code used in this part is available in this github repository." +description: "Learn how to turn a design mockup into a practical Codename One interface." --- - > Module 3: Extracting a UI Design +{{< youtube BXwh2T7wvfc >}} + +This module is about the gap between a static design and a real application screen. A mockup can show you the look of the product, but it does not tell you which parts should become live UI, which parts should remain image assets, which elements need to stretch, and which details are safe to simplify. -The source code used in this part is available in this github repository. +The example in the video starts from a Photoshop design and turns it into a Codename One mockup. The same exercise still matters today, but the tools around it have changed. Many teams now start from Figma or similar design tools rather than PSD files, and the implementation should usually lean on CSS for styling instead of older theme-designer workflows. The core skill, though, has not changed at all: learn to read a design structurally instead of treating it like a screenshot you have to reproduce pixel by pixel. -{{< youtube BXwh2T7wvfc >}} +When you look at a design, start by asking what is truly interactive. Buttons, search fields, lists, toolbars, filters, and cards usually want to be real components. Photos, illustrations, and a few special decorative details may need to stay as assets. Once you separate those two categories, the rest of the implementation becomes far more manageable. + +The other important idea in this module is that a good adaptation does not have to be a perfect clone. Mobile UIs live on different devices, densities, and font renderers. It is often better to preserve the visual hierarchy, spacing, and behavior of the design than to overfit to one mockup and end up with something brittle. Material icons, reusable borders, and framework-native layout behavior often produce a better result than trying to freeze every pixel exactly as it appeared in the design file. + +In the lessons that follow, the design gets broken down into manageable parts: assets that need to be extracted, layout decisions, CSS styling, and the component structure that makes the screen behave like a real application. That is the right mindset for design implementation in Codename One. Start with structure, then layer styling and assets on top. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [Themeing](/themeing/) +- [Layout Basics](/layout-basics/) +- [How Do I Create A 9 Piece Image Border](/how-do-i/how-do-i-create-a-9-piece-image-border/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/005-cutting-images-in-photoshop.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/005-cutting-images-in-photoshop.md index e0df183247..184394ef1e 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/005-cutting-images-in-photoshop.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/005-cutting-images-in-photoshop.md @@ -9,10 +9,27 @@ module_order: 3 lesson_order: 2 weight: 5 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Extract only the image assets a Codename One UI actually needs from a design file." --- - > Module 3: Extracting a UI Design - {{< youtube exQ8xXAxtoU >}} + +When you receive a design file, the goal is not to export everything. The goal is to extract only the assets that should really remain images in the final application. That sounds obvious, but it is one of the easiest mistakes to make when turning a design into code. If you export too much, you end up with a UI that is heavy, rigid, and difficult to adapt. + +The video demonstrates this using Photoshop, and the basic workflow still holds up: isolate the part you need, remove anything that should remain live UI, and export only the visual element that actually belongs in an asset file. Today you might do the same work in Figma, Sketch, or another design tool, but the decision-making process is the same. + +Background photos are a good example. If the image is purely decorative and really is a photo, then exporting it as an image makes sense. A JPEG is often appropriate there because file size matters and you do not need transparency. On the other hand, if a visual element needs transparency, sharp edges, or mask-style behavior, a PNG is often the better choice. + +Rounded cards, masks, and border fragments should be handled with even more care. Sometimes a cropped image is the right answer. Sometimes a nine-piece border is the right answer. Sometimes the best answer is not an image at all because CSS, borders, padding, and standard components can express the same effect more cleanly. The lesson is useful because it forces you to ask that question for each element instead of exporting blindly. + +This is also where asset discipline starts paying off. Remove text from exported buttons if the label should remain live and localizable. Keep decorative images separate from content images. Export the smallest useful region instead of the whole screen. Those choices make the final app easier to style, localize, and maintain. + +So while the tooling in the video is older and Photoshop-specific, the principle is current: treat image extraction as selective engineering work, not as screenshot slicing. Every asset you keep should earn its place. + +## Further Reading + +- [Themeing](/themeing/) +- [How Do I Create A 9 Piece Image Border](/how-do-i/how-do-i-create-a-9-piece-image-border/) +- [How Do I Fetch An Image From The Resource File, Add A Multiimage](/how-do-i/how-do-i-fetch-an-image-from-the-resource-file-add-a-multiimage/) +- [Adapting a UI Design](/courses/course-01-java-for-mobile-devices/009-adapting-a-ui-design/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/006-css.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/006-css.md index 4400df5971..44f23c7ade 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/006-css.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/006-css.md @@ -9,10 +9,29 @@ module_order: 3 lesson_order: 3 weight: 6 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Use CSS to turn a structural Codename One layout into a polished design." --- - > Module 3: Extracting a UI Design - {{< youtube -f1yue3hDEk >}} + +Once the structure of a screen is in place, CSS is usually the fastest way to make it look intentional. That is the modern styling path for most Codename One projects. It lets you work at the right level of abstraction: UIIDs, spacing, colors, borders, backgrounds, and state-specific styling, without turning every visual adjustment into Java code. + +This lesson is really about taking a mockup and giving each part of the screen a styling identity. A form gets its base background. A title area gets transparency and alignment rules. Cards, buttons, filters, and checkout elements each get their own UIID so they can be styled consistently. Once those UIIDs exist, CSS becomes the place where the visual system lives. + +The older video mixes CSS with some manual theme-designer work, especially for a few border assets. That was understandable at the time, but the default recommendation today is simpler: stay in CSS unless you have a strong reason to drop lower. The old designer still works, but it is no longer the center of the workflow for new projects. + +Good Codename One CSS starts with a few basic habits. Containers and labels often need their transparency, margin, and padding made explicit so the design behaves consistently across themes. Toolbar-related UIIDs often need cleanup because native themes can contribute defaults you do not want. State-specific styling needs to be defined intentionally so the selected or pressed state feels like part of the same design rather than an afterthought. + +Spacing is one of the biggest reasons CSS matters. Padding defines breathing room inside a component. Margin defines its relationship to neighboring components. If a screen feels cramped or oddly balanced, the fix is often there rather than in color or typography. That is also why a visually simple design can still take real work to reproduce well: the small spacing choices are doing a lot of the design work. + +Inheritance is another important part of maintainable styling. If one button style is really a variation of another, define the common look once and override only the differences. The same idea applies to cards, list entries, and reusable decorative treatments. A style system should reduce repetition, not create it. + +So the practical workflow is straightforward. Build the screen structure in Java, give the important elements stable UIIDs, and then use CSS to express the design. Reserve lower-level theme tooling for the rare cases where CSS is not the right fit. That keeps the application easier to read, easier to evolve, and much closer to the way modern Codename One projects are styled. + +## Further Reading + +- [Themeing](/themeing/) +- [Developer Guide](/developer-guide/) +- [Working With CSS](/courses/course-02-deep-dive-mobile-development-with-codename-one/001-working-with-css/) +- [How Do I Create A Simple Theme](/how-do-i/how-do-i-create-a-simple-theme/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/007-baseform.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/007-baseform.md index 070f2b8107..d6987dc2dd 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/007-baseform.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/007-baseform.md @@ -9,10 +9,27 @@ module_order: 3 lesson_order: 4 weight: 7 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Build a reusable base form for a family of related screens." --- - > Module 3: Extracting a UI Design - {{< youtube aNFKBDeky2s >}} + +Once several screens share the same visual shell, the cleanest approach is usually to build that shell once and let individual forms supply their own content. That is what a `BaseForm` is doing in this lesson. It is not there to be clever. It is there to stop layout duplication from spreading through the app. + +In this design, the shared shell includes the top area, toolbar behavior, background treatment, and the general structure that all of the screens inherit. If every form rebuilt those pieces independently, the design would drift and small visual fixes would become repetitive. A base form keeps those common decisions in one place. + +The video uses a layered toolbar and a filler component to keep content from sliding underneath the title area. That underlying problem is still real even if your exact implementation differs. Any time you have a floating or layered top section, you need to think about how the content below it is offset. The detail may look minor, but it is exactly the sort of thing that makes a polished design feel intentional instead of improvised. + +This lesson also shows a good example of separating what is common from what is form-specific. The reusable frame belongs in the base class. The actual list of categories, the content body, and any screen-specific widgets belong in the subclasses. That split makes the UI easier to reason about because the shared layout logic stops competing with the details of one particular screen. + +One subtle but important theme here is that design implementation often depends on small framework behaviors. Empty labels, transparent containers, layered panes, and size calculations all affect the final result. Those details may feel unimportant when reading code, but they are often what turns "almost right" into "actually right." + +So the practical lesson is simple: if several screens share the same top bar, background treatment, toolbar setup, or framing logic, pull that into a reusable base form. Then let each screen add only the content that is unique to it. Reuse is not just a code quality issue here. It is also a design consistency tool. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [Layout Basics](/layout-basics/) +- [Adapting a UI Design](/courses/course-01-java-for-mobile-devices/009-adapting-a-ui-design/) +- [CSS](/courses/course-02-deep-dive-mobile-development-with-codename-one/006-css/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/008-mainmenuform.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/008-mainmenuform.md index cf7af302f6..0413b0a65c 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/008-mainmenuform.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/008-mainmenuform.md @@ -9,10 +9,27 @@ module_order: 3 lesson_order: 5 weight: 8 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Build the main menu screen by combining reusable layout structure with styled content components." --- - > Module 3: Extracting a UI Design - {{< youtube WYlM2utu4Ps >}} + +With the base form in place, the main menu screen becomes the first real test of the design system. This is where the shared shell has to support actual content: category filters, dish cards, images, buttons, and the spacing that makes the screen feel usable rather than crowded. + +The category strip at the top is a good example of choosing a component for behavior rather than for visual resemblance alone. It may look like tabs in the mockup, but its job is really closer to filtering. That distinction matters because it affects how users understand the control and how the code should model its state. + +The dish entries themselves are another good example of layered design work. Each card combines structure, styling, and assets. The title and descriptive text stay live so they can respond to layout, truncation, and localization. The image gets the decorative treatment it needs, including rounded shaping or masking where appropriate. The buttons inherit from the same visual language as the rest of the screen instead of being styled in isolation. + +This is also where reusable component-building code starts to pay off. If every dish entry is assembled in roughly the same way, that assembly should live in one helper method or one reusable component rather than being repeated inline. The design gets more consistent, and later changes become much cheaper. + +The video spends time on list behavior, selection state, and masked images. Those details are still the right things to care about because they affect how the UI feels in motion, not just how it looks in a static screenshot. A design adaptation is only successful if the interaction model feels as intentional as the visuals. + +By the end of this lesson, the main screen should no longer feel like a set of disconnected mockup fragments. It should feel like one coherent form built from reusable patterns: a shared shell, a filter control with clear behavior, and content cards that combine live data with carefully chosen visual treatment. + +## Further Reading + +- [Layout Basics](/layout-basics/) +- [CSS](/courses/course-02-deep-dive-mobile-development-with-codename-one/006-css/) +- [How Do I Create A List Of Items The Easy Way](/how-do-i/how-do-i-create-a-list-of-items-the-easy-way/) +- [How Do I Create Gorgeous SideMenu](/how-do-i/how-do-i-create-gorgeous-sidemenu/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/009-checkoutform.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/009-checkoutform.md index 4129c29b51..0ed0fbbdda 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/009-checkoutform.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/009-checkoutform.md @@ -9,10 +9,25 @@ module_order: 3 lesson_order: 6 weight: 9 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Build a checkout overlay as a real form with controlled transitions and background treatment." --- - > Module 3: Extracting a UI Design - {{< youtube ts-Q1zBGXco >}} + +One of the more useful lessons in UI implementation is learning when something that looks like a dialog should really be a form. The checkout screen in this module is a good example. Visually it behaves like an overlay, but structurally it owns more of the screen than a simple dialog and has controls that sit outside the central receipt area. Treating it as a form gives you much more control. + +That choice affects everything else. Once the checkout UI is a form, transitions, layering, dismissal, and content layout become much easier to manage deliberately. The video creates the visual overlay effect by taking the current screen, rendering it into an image, and using a blurred and tinted version as the backdrop. The exact visual treatment can vary, but the principle is still valuable: separate the visual illusion from the structural implementation. + +The receipt itself is then just another styled UI region. Its top and bottom decorations, list of items, close affordance, and checkout action all become ordinary layout problems once you stop thinking of the whole screen as a magical special case. That is often the trick with polished mobile UI work. The effect looks fancy, but the implementation becomes straightforward once the right structural choice is made. + +This lesson also reinforces an important design habit: do not let the mockup choose the component model for you. A screen can look like a popup and still be better implemented as a form. A row can look like a tab and still really be a filter. A decorative border can look custom and still be best represented by a reusable border asset or CSS treatment. The job is to choose the structure that produces reliable behavior. + +So the checkout form is valuable not only because it looks good, but because it demonstrates that advanced-looking UI often comes from ordinary building blocks combined carefully: one form, one transition, a styled content region, and a background effect that supports the illusion without dictating the architecture. + +## Further Reading + +- [Layout Basics](/layout-basics/) +- [CSS](/courses/course-02-deep-dive-mobile-development-with-codename-one/006-css/) +- [Transitions](/transitions/) +- [How Do I Create Gorgeous SideMenu](/how-do-i/how-do-i-create-gorgeous-sidemenu/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/010-introduction.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/010-introduction.md index 41ae3e39ad..0215d1a6a3 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/010-introduction.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/010-introduction.md @@ -9,10 +9,25 @@ module_order: 4 lesson_order: 1 weight: 10 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Approach UI design from first principles when the mockup is incomplete or missing." --- - > Module 4: UI Design From Scratch - {{< youtube aZ2Y74xPj_0 >}} + +Working from an existing design is one skill. Continuing when the design is incomplete is another. This module starts from that second situation: some screens already exist, but the application still needs additional forms, navigation paths, and visual decisions that were never specified in the original mockup. + +That is a very normal place to be in real projects. Designers may deliver only the primary flow. Product requirements may grow after the original handoff. Some screens, like about pages, contact screens, settings, or edge-case flows, may never have been designed in detail at all. At that point the developer has to extend the design language without turning the app into a patchwork. + +The important mindset here is that this is still not about becoming a graphic designer overnight. It is about learning to recognize the intention behind the existing design and then making new screens that feel like they belong to the same product. Consistency matters more than novelty. Reuse matters more than decoration. A good new screen often comes from carrying forward the spacing, hierarchy, motion, and interaction patterns that are already present. + +The video makes a good point about avoiding flashy effects with no purpose. That advice is still worth following. Animation and transitions should communicate meaning, not just show off. A transition can indicate entering a deeper detail screen, revealing an overlay, or moving laterally between related views. When motion has no semantic role, it usually ends up making the UI feel dated rather than polished. + +So this module is really about design judgment in a programmer's context. You already have some patterns from the previous mockup-driven work. Now the task is to extend them. Build the missing forms, keep the visual language consistent, and make choices that support usability first. That is how a design system starts to become an application instead of a one-screen demo. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [Adapting a UI Design](/courses/course-01-java-for-mobile-devices/009-adapting-a-ui-design/) +- [How Do I Create Gorgeous SideMenu](/how-do-i/how-do-i-create-gorgeous-sidemenu/) +- [Themeing](/themeing/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/011-the-new-forms.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/011-the-new-forms.md index cb08e6dd07..a468193cab 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/011-the-new-forms.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/011-the-new-forms.md @@ -9,10 +9,27 @@ module_order: 4 lesson_order: 2 weight: 11 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Extend an existing design language to new forms that were never part of the original mockup." --- - > Module 4: UI Design From Scratch - {{< youtube bH7cS5ENNlw >}} + +The real test of a design system begins when you need screens that were never designed for you. A product rarely stops at the original mockup. It needs detail screens, about pages, contact forms, side menus, and supporting flows that still have to feel like they belong to the same app. + +This lesson is about designing those missing forms without pretending you are starting from nothing. You already have a visual language: spacing, colors, image treatment, buttons, overlays, and a certain rhythm to the UI. The job is to extend that language rather than invent a different one for every new screen. + +The dish detail screen is a good example. The content is different from the menu screen, but the priorities are not mysterious. The dish itself is still the hero. The supporting text, price, and action affordances need to stay visible without competing with the image. Once you recognize that hierarchy, the new screen can grow naturally out of the original design instead of feeling pasted on. + +The same principle applies to the about and contact screens. These pages often exist outside the main product flow, but they should not look like they came from another app. If the contact form highlights the location, let the map become the dominant element. If the about page is content-heavy, use the platform's strengths to present that content cleanly. The visual language can relax a little to suit the new purpose, but it should still be recognizably part of the same product. + +The side menu is another case where familiarity matters. Users already understand the basic conventions of side navigation, so the most important job is not to reinvent the pattern. It is to make the menu feel visually consistent with the rest of the app while preserving the usability people expect. + +This lesson also reinforces something developers often underweight: taste is partly a matter of noticing why an existing design works. If you study how other apps solve similar problems, not to copy them literally but to understand their decisions, your own additions become much more grounded. That is especially useful when the original design is incomplete and you need to make judgment calls quickly. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [Themeing](/themeing/) +- [Adapting a UI Design](/courses/course-01-java-for-mobile-devices/009-adapting-a-ui-design/) +- [How Do I Create Gorgeous SideMenu](/how-do-i/how-do-i-create-gorgeous-sidemenu/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/012-fixing-the-checkout-experience.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/012-fixing-the-checkout-experience.md index d63c729f59..80bb93a510 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/012-fixing-the-checkout-experience.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/012-fixing-the-checkout-experience.md @@ -9,10 +9,26 @@ module_order: 4 lesson_order: 3 weight: 12 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Improve the checkout screen so users can actually edit the order they are about to place." --- - > Module 4: UI Design From Scratch - {{< youtube CvbcWC4hLYk >}} + +A checkout screen is not finished just because it looks good. If users cannot understand what they ordered, change quantities easily, or remove mistakes without friction, the design has failed at the point where clarity matters most. + +That is the problem this lesson addresses. The earlier checkout design was visually appealing, but it did not give the user a practical way to change the order. That kind of gap is common in mockups. A screen is designed for presentation, then later you discover that a real product still needs editing, correction, and confirmation behaviors that the design never accounted for. + +There are several possible fixes. Swipe gestures can keep the surface clean, but they are easy to miss. An edit mode can work, but it adds state and often makes the flow feel heavier than necessary. Always-visible controls take more space, but they are honest about what the user can do. + +The solution in the lesson is a good practical compromise: make the quantity directly editable through a compact control that fits the existing visual language, and use a picker-based interaction to avoid keyboard friction. That is an especially sensible choice on mobile because it reduces validation problems, keeps the interaction predictable, and makes deletion just another quantity change to zero rather than a separate destructive action. + +What matters most here is not the exact widget choice. It is the principle that checkout should support correction without making the user search for hidden functionality. At this stage of the flow, discoverability and confidence matter more than keeping the layout pristine. + +So if a checkout UI feels elegant but does not let the user edit the order naturally, fix the behavior first and let the visual solution follow that need. Real product quality comes from that willingness to adjust the design once real interaction requirements become obvious. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [How Do I Create A List Of Items The Easy Way](/how-do-i/how-do-i-create-a-list-of-items-the-easy-way/) +- [Threading and the EDT](/courses/course-01-java-for-mobile-devices/011-threading-and-the-edt/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/013-css-changes.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/013-css-changes.md index 24071e1a16..7817a7b4fb 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/013-css-changes.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/013-css-changes.md @@ -9,10 +9,27 @@ module_order: 4 lesson_order: 4 weight: 13 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Refine the CSS so the newly added screens feel like part of the same product." --- - > Module 4: UI Design From Scratch - {{< youtube MLbOdCt67Rw >}} + +Whenever new screens are added, the styling layer has to catch up. Otherwise the application ends up with correct layouts but mismatched visual language. This lesson is about extending the CSS so the new forms, overlays, and side menu feel like the same product rather than late additions. + +That work is usually less about dramatic restyling and more about tightening the small things that carry consistency: gradients, padding, transparency, text treatment, separators, and inherited button styles. These are the pieces that make a screen feel related to the rest of the app even when its structure is different. + +The newer forms in this module need that kind of refinement. The dish view, the contact screen, and the side menu all reuse ideas from earlier screens, but they each need their own UIIDs and spacing rules. Without those explicit rules, native defaults and inherited behavior start leaking in, and the app begins to look inconsistent even if the layouts are technically correct. + +The side menu is a particularly good example. Side commands often receive strong native-theme defaults, so if you want them to look intentional you usually need to override more than you first expect. Background, spacing, text decoration, and separator behavior all need deliberate choices. Otherwise the menu keeps the behavior of the framework but not the visual identity of your application. + +This lesson also reinforces a maintainability rule: if two components are visually related, their styles should be related too. Reuse through inheritance or shared style concepts is not just a convenience. It is how you keep the UI from drifting as the project grows. Modern Codename One projects should keep that work in CSS whenever possible rather than bouncing between multiple styling systems. + +So the right way to think about these CSS changes is not "decorate the new screens." It is "extend the design system so the new screens belong." That mindset produces more durable styling decisions. + +## Further Reading + +- [Themeing](/themeing/) +- [Working With CSS](/courses/course-02-deep-dive-mobile-development-with-codename-one/001-working-with-css/) +- [CSS](/courses/course-02-deep-dive-mobile-development-with-codename-one/006-css/) +- [How Do I Create A Simple Theme](/how-do-i/how-do-i-create-a-simple-theme/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/014-code-changes-and-summary.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/014-code-changes-and-summary.md index 4df307b35b..740abc4e71 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/014-code-changes-and-summary.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/014-code-changes-and-summary.md @@ -9,10 +9,27 @@ module_order: 4 lesson_order: 5 weight: 14 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Pull the new forms and interactions together into a coherent application structure." --- - > Module 4: UI Design From Scratch - {{< youtube a6q1M_wqEYY >}} + +Once the new screens have been designed, the application still needs the code structure to support them cleanly. This lesson is where the design work turns back into application engineering: model objects, reusable form logic, quantity editing, map integration, HTML content, and the small animation details that make the whole flow feel deliberate. + +One of the key ideas here is that richer UI usually demands a clearer model. A dish is no longer just something painted on screen. It now has identity, descriptive data, pricing, image variants, and behaviors associated with ordering. That is why the lesson starts pulling the underlying data representation into a more explicit form. + +The rest of the code changes follow naturally from the design decisions made earlier. The about form needs a structured way to host HTML content. The contact screen needs map integration and action buttons that connect to native capabilities. The checkout flow needs quantity editing and removal behavior that feels smooth instead of abrupt. None of these are isolated gimmicks. They are consequences of choosing to build a fuller application rather than a static mockup. + +The animation and overlay details are especially useful because they show how polish often comes from sequencing rather than from fancy APIs. Remove an element, let the layout animate, then update totals and related UI once the movement is complete. That order matters. It prevents visual interference and makes the app feel more intentional. + +The same is true of layered components, overlays, and special visual treatments. They are easiest to reason about once you understand which parts are structural and which parts are just visual support. A glass pane, a layered layout, or a positioned overlay can solve a very specific problem cleanly if the rest of the form architecture is already sound. + +So the summary of this lesson is that extending a UI from mockup to working app requires both taste and structure. The design choices create new requirements, and the code has to respond by becoming more explicit, more reusable, and a little more architectural than the early demo version. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [How Do I Access Native Device Functionality, Invoke Native Interfaces](/how-do-i/how-do-i-access-native-device-functionality-invoke-native-interfaces/) +- [How Do I Use Properties To Speed Development](/how-do-i/how-do-i-use-properties-to-speed-development/) +- [How Do I Take A Picture With The Camera](/how-do-i/how-do-i-take-a-picture-with-the-camera/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/015-overview-and-basic-model.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/015-overview-and-basic-model.md index 49a0f13d10..6549909d3b 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/015-overview-and-basic-model.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/015-overview-and-basic-model.md @@ -9,10 +9,26 @@ module_order: 5 lesson_order: 1 weight: 15 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Move from visual prototyping to a clearer application model and architecture." --- - > Module 5: Architecture - {{< youtube mFFjxs9EDW8 >}} + +At some point a project can no longer live as a collection of forms and helper methods. Once the UI starts to stabilize, the next step is to make the data model explicit and decide where application state should live. That shift is what this lesson is about. + +The important thing here is not to overreact and invent a giant architecture too early. The lesson takes a more useful approach: start from the things the UI already proved are necessary. If the app clearly revolves around dishes, categories, orders, and restaurant-level information, then those are the right places to start shaping the model. + +That is a healthier way to design architecture than beginning with abstract diagrams and hoping the product eventually fits them. A menu screen implies a menu model. A checkout flow implies an order model. Restaurant branding, address, contact details, and currency imply some application-level object that represents the current restaurant state. The UI has already told you what the model needs to be. + +The singleton-style restaurant object used in the lesson is a pragmatic choice for a demo-sized application. It centralizes the state the rest of the UI depends on and keeps the app easy to reason about while the architecture is still evolving. In a larger system you might eventually split responsibilities further, but this is a reasonable starting point because it matches the actual scope of the app. + +This lesson also hints at a broader modeling principle: not everything needs the same identity rules. Some objects naturally need stable identifiers because they represent persistent records or externally referenced entities. Others exist more as configuration or supporting data. It is fine for the model to reflect that difference instead of forcing uniformity where it is not useful. + +The result of this first architecture pass is not a grand framework. It is a cleaner separation between the UI and the data it presents. That alone makes the next set of changes easier, because once the model is explicit, the forms stop having to carry hidden application state in ad hoc ways. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [How Do I Use Properties To Speed Development](/how-do-i/how-do-i-use-properties-to-speed-development/) +- [Connecting to a Web Service](/courses/course-02-deep-dive-mobile-development-with-codename-one/003-connecting-to-a-web-service/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/016-integration-and-summary.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/016-integration-and-summary.md index bb32d9290f..1e10817500 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/016-integration-and-summary.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/016-integration-and-summary.md @@ -9,10 +9,25 @@ module_order: 5 lesson_order: 2 weight: 16 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Connect the model to the UI and let application state drive the visible behavior." --- - > Module 5: Architecture - {{< youtube Ke8bjFdD1bc >}} + +Once the model exists, the next question is whether the UI is truly using it or just sitting beside it. This lesson is the point where the restaurant, menu, order, and dish data start driving what the application shows instead of merely backing it in theory. + +That integration is where architecture starts paying off. A category list no longer needs to be hard-coded because the menu model can supply it. Totals no longer need to be manually synchronized in several places because the order model can become the source of truth. Contact screens no longer need literal strings embedded in the UI because restaurant-level data can fill them in. + +The listener-based update pattern in the lesson is especially important. When one part of the application changes shared state, the rest of the UI should respond through a clear mechanism instead of requiring scattered manual refresh logic. That keeps the application from turning into a patchwork of "remember to update this label too" style code. + +This is also the point where formatting decisions become part of the model rather than accidental view logic. Currency is a good example. If the restaurant defines the currency, then the UI should present values through that lens rather than applying a generic formatting assumption in random places. Small decisions like that are a sign that the app is beginning to think in domain terms instead of just widget terms. + +The wider lesson here is that good architecture does not need to arrive all at once. It can emerge from repeated cleanup as the UI becomes real. Start with the screens, identify the shared data they imply, move that data into an explicit model, then let the forms depend on that model instead of inventing their own local truths. That is often a much more productive path than trying to design the whole system upfront. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [Overview and Basic Model](/courses/course-02-deep-dive-mobile-development-with-codename-one/015-overview-and-basic-model/) +- [How Do I Use Properties To Speed Development](/how-do-i/how-do-i-use-properties-to-speed-development/) +- [How Do I Localize/Translate My Application, Apply i18n/l10n to My App](/how-do-i/how-do-i-localizetranslate-my-application-apply-i18nl10n-internationalizationlocalization-to-my-app/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/017-the-native-interface-callback.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/017-the-native-interface-callback.md index e18ee439a8..155dd1b12d 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/017-the-native-interface-callback.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/017-the-native-interface-callback.md @@ -9,12 +9,26 @@ module_order: 6 lesson_order: 1 weight: 17 is_course_lesson: true -description: "The version of the code matching the changes in this section is here." +description: "Understand how to design a native interface and bridge callback-based native APIs back into Java." --- - > Module 6: Native Interfaces - Billing +{{< youtube jVuNrLw4e-A >}} -The version of the code matching the changes in this section is here. +Native interfaces are most useful when the Java side stays small, deliberate, and easy to reason about. The mistake developers often make is trying to expose every detail of a native SDK directly into the portable layer. That usually creates a fragile API that mirrors platform quirks instead of hiding them. -{{< youtube jVuNrLw4e-A >}} +This lesson starts from a payment use case and shows a better approach. Look at the native SDK first, identify the smallest set of information the Java layer truly needs, and then define a narrow native interface around that. In the billing example, most of the heavy lifting is already handled by the native SDK and by the server. The portable layer mainly needs a token and a way to receive the outcome. + +The callback part is where the design gets interesting. Native SDKs often return results asynchronously, but Codename One native interfaces cannot simply accept arbitrary Java callback objects in every situation. That constraint means you need a bridging strategy. In the older lesson that strategy uses static callback methods. The exact implementation detail can vary, but the underlying lesson is still right: the Java-facing API should absorb the awkward platform boundary so the rest of the application does not have to care about it. + +That is also why the native interface itself should almost never be used directly from the UI or business layer. Wrap it in a plain Java abstraction. That wrapper can normalize differences between platforms, centralize fallback behavior, and keep the rest of the codebase isolated from native-specific edge cases. + +The video uses Braintree as the concrete example, but there is an important modern note here: when an official or maintained Codename One library already exists for an integration, prefer that over re-creating the binding yourself. Native interface work is still valuable to understand, and sometimes you do need it for unsupported features, but it should not be the first choice when a stable library already covers the problem. + +So the real takeaway from this lesson is not "copy this payment bridge." It is "design the narrowest native boundary you can, then wrap it so the application talks to a clean Java API instead of a pile of platform details." + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [How Do I Access Native Device Functionality, Invoke Native Interfaces](/how-do-i/how-do-i-access-native-device-functionality-invoke-native-interfaces/) +- [How Do I Use The Include Sources Feature To Debug The Native Code On iOS/Android](/how-do-i/how-do-i-use-the-include-sources-feature-to-debug-the-native-code-on-iosandroid-etc/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/018-dependencies-gradle-and-cocoapods.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/018-dependencies-gradle-and-cocoapods.md index 9466beabaa..f28b8cbcda 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/018-dependencies-gradle-and-cocoapods.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/018-dependencies-gradle-and-cocoapods.md @@ -9,10 +9,24 @@ module_order: 6 lesson_order: 2 weight: 18 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Understand how native SDK dependencies are expressed and resolved when building Codename One integrations." --- - > Module 6: Native Interfaces - Billing - {{< youtube d1T25sbFUKE >}} + +Most native integrations fail before the first line of native code ever runs. The usual problem is dependency setup: the Java wrapper may be correct, but the native platform build is missing the SDK, targeting the wrong platform level, or pulling in conflicting configuration. + +That is why dependency handling deserves its own lesson. On Android, third-party native SDKs usually arrive through Gradle coordinates. On iOS, they often arrive through CocoaPods. Codename One sits above those build systems, so the practical question becomes how to express those native dependencies in a way the platform builds can consume. + +The older lesson focuses heavily on Braintree-specific build hints and version mismatches. The specific versions in that video are no longer the important part. What still matters is the troubleshooting pattern. When a native dependency fails to build, look for the first meaningful error, not the giant wall of build-tool stack trace below it. Most of the time the real issue is a platform-level mismatch such as minimum SDK version, deployment target, or an incompatible transitive dependency. + +This is also one of the places where modern Codename One development is easier if you stay disciplined. Keep build hints explicit, document why they are needed, and prefer maintained integrations when available. If an old lesson suggests unpacking and editing a library artifact by hand, treat that as a temporary workaround from another era, not as a normal workflow. Today the better approach is usually to fix the library, update the dependency declaration, or isolate the workaround in a clearly documented build step. + +Native dependency management is not glamorous, but it is part of the cost of crossing the platform boundary. If you understand that Gradle and CocoaPods are simply the native package managers on the other side of your Codename One build, the problem becomes much easier to reason about. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [How Do I Access Native Device Functionality, Invoke Native Interfaces](/how-do-i/how-do-i-access-native-device-functionality-invoke-native-interfaces/) +- [How Do I Get Repeatable Builds, Build Against A Consistent Version Of Codename One & Use The Versioning Feature](/how-do-i/how-do-i-get-repeatable-builds-build-against-a-consistent-version-of-codename-one-use-the-versioning-feature/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/019-the-native-code.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/019-the-native-code.md index d9b8ac057d..078d8b2471 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/019-the-native-code.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/019-the-native-code.md @@ -9,10 +9,27 @@ module_order: 6 lesson_order: 3 weight: 19 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Implement the platform-specific side of a Codename One native interface cleanly and defensibly." --- - > Module 6: Native Interfaces - Billing - {{< youtube swgT_aDsv3U >}} + +Once the interface shape is settled and the native dependencies are in place, the remaining job is to implement the platform code without letting threading, lifecycle, and callback mechanics turn the integration into a black box. + +The most practical advice in the lesson is still the best advice today: if the integration is non-trivial, generate native sources and inspect the result in the platform IDE. That gives you real build feedback, better navigation, and a much easier debugging path than trying to reason about everything from the portable project alone. The [include sources](/how-do-i/how-do-i-use-the-include-sources-feature-to-debug-the-native-code-on-iosandroid-etc/) workflow is still the right tool for that job. + +The Android and iOS implementations shown here look different, but the conceptual structure is the same. Marshal arguments from the Java side into the native SDK, run on the thread the native platform expects, receive the native callback, and then bridge the result back into the portable layer in a controlled way. Once you view native integration through that lens, the platform syntax becomes less intimidating. + +Threading deserves special attention. The Codename One EDT is not the same thing as the native platform UI thread. That difference matters because many native SDKs assume they are being called from the platform's own event thread. If you ignore that boundary, integrations can fail in ways that look random but are really just threading mistakes. + +This lesson also helps demystify the uglier parts of native stubs, especially on iOS where generated method names and VM bridge calls can look hostile at first glance. The important point is that most of the ugliness lives at the boundary. Your application code should not have to see it. That is another reason the wrapper layer matters. + +So the modern version of this advice is: prototype in the native IDE when needed, keep the native code narrow, respect thread boundaries, and translate results back into a clean Java-facing API. That is how native integrations stay maintainable instead of becoming permanent fear zones in the project. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [How Do I Access Native Device Functionality, Invoke Native Interfaces](/how-do-i/how-do-i-access-native-device-functionality-invoke-native-interfaces/) +- [How Do I Use The Include Sources Feature To Debug The Native Code On iOS/Android](/how-do-i/how-do-i-use-the-include-sources-feature-to-debug-the-native-code-on-iosandroid-etc/) +- [Threading and the EDT](/courses/course-01-java-for-mobile-devices/011-threading-and-the-edt/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/020-introduction-and-generic-code.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/020-introduction-and-generic-code.md index 5a027d02be..8440fa7c8c 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/020-introduction-and-generic-code.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/020-introduction-and-generic-code.md @@ -9,10 +9,24 @@ module_order: 7 lesson_order: 1 weight: 20 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Design the portable layer for a camera integration before getting lost in platform details." --- - > Module 7: Native Interfaces - Camera - {{< youtube Mcw8z_uP3BA >}} + +Camera integrations are a good example of why native interfaces need design, not just implementation. If you begin by copying every method from a platform SDK into your portable API, you usually end up with an awkward abstraction that is too platform-specific to be pleasant and too incomplete to be reliable. + +This lesson takes the more useful route: study the native APIs, decide what the portable layer actually needs, and then shape a Codename One-facing API around that. In the original material, the portable surface is influenced heavily by an Android camera library. That is a reasonable way to get traction, but the deeper lesson is that the portable API should be owned by your application or library, not by whichever native SDK you happened to read first. + +The wrapper class is crucial here. The raw native interface should remain a thin bridge, while the public Java API becomes the place where you handle defaults, constants, event listener management, simulator behavior, and build-hint concerns. That separation gives you the freedom to improve the native implementation later without breaking the rest of the app. + +This is also where you can see why native interfaces are not a good place to expose callback-heavy behavior directly. Some platform APIs are naturally event-driven, but the portable layer still needs to present those events in a way that fits Codename One. Listener registration, singleton behavior, and platform capability checks all belong in the wrapper, not scattered around the app. + +One modern point worth stating explicitly: if there is already a maintained Codename One camera library that covers the use case you need, prefer that first. This lesson is still valuable because it teaches how to think about a difficult native integration, but hand-rolling a bridge should usually be reserved for unsupported features, experiments, or library work. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [How Do I Access Native Device Functionality, Invoke Native Interfaces](/how-do-i/how-do-i-access-native-device-functionality-invoke-native-interfaces/) +- [How Do I Take A Picture With The Camera](/how-do-i/how-do-i-take-a-picture-with-the-camera/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/021-implementing-the-native-camera-on-android.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/021-implementing-the-native-camera-on-android.md index 92640db36a..15f44824c2 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/021-implementing-the-native-camera-on-android.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/021-implementing-the-native-camera-on-android.md @@ -9,10 +9,26 @@ module_order: 7 lesson_order: 2 weight: 21 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Implement the Android side of a camera native interface and understand the related build requirements." --- - > Module 7: Native Interfaces - Camera - {{< youtube 16-Vkgcx2kg >}} + +Once the portable camera API exists, the Android side becomes a matter of mapping that API onto the native implementation without letting threading, lifecycle, and dependency details escape into the rest of the app. + +The lesson's most practical recommendation is still correct: if you are new to native integration, generate native sources and work in Android Studio while developing the Android side. That gives you real tooling support, native compilation feedback, and a much faster path to understanding what the platform expects. + +The Android implementation itself is conceptually straightforward. Hold onto the preview view, forward configuration calls to the native camera component, and make sure the operations that need the Android UI thread are dispatched there. The syntax may be verbose, but the pattern is simple: Codename One asks for behavior, the bridge translates that into the Android library's calls, and results are pushed back into the portable layer. + +The threading boundary matters here just as much as it did in the billing example. Some getter-like operations are harmless, but start, stop, capture, and view-related behavior often need to run on the native Android UI thread. If that distinction is blurred, camera code tends to fail in ways that are difficult to diagnose. + +The other part of this lesson is build configuration. Native Android integrations frequently depend on Gradle artifacts, ProGuard or R8 rules, SDK levels, and other platform-specific settings. The exact versions in the older video are historical now, but the lesson remains current: camera integrations are often as much about satisfying native build expectations as they are about writing bridge code. + +So the Android side is not just "translate methods one by one." It is "translate the API, honor the Android lifecycle and thread model, and make the build aware of the native library's requirements." + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [How Do I Access Native Device Functionality, Invoke Native Interfaces](/how-do-i/how-do-i-access-native-device-functionality-invoke-native-interfaces/) +- [How Do I Use The Include Sources Feature To Debug The Native Code On iOS/Android](/how-do-i/how-do-i-use-the-include-sources-feature-to-debug-the-native-code-on-iosandroid-etc/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/022-camera-ios-port-basics.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/022-camera-ios-port-basics.md index b7d6e37a3d..cef914f280 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/022-camera-ios-port-basics.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/022-camera-ios-port-basics.md @@ -9,10 +9,26 @@ module_order: 7 lesson_order: 3 weight: 22 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Understand the structure of the iOS side of a camera native interface and the lifecycle it needs to manage." --- - > Module 7: Native Interfaces - Camera - {{< youtube 17ISIksjcPM >}} + +The iOS side of a camera integration tends to look more intimidating than the Android side because the API surface is different and the generated bridge code is less familiar to most Java developers. The right way to handle that is not to memorize every Objective-C detail. It is to understand the moving parts well enough to see where the bridge is actually doing work. + +At a high level, the iOS port needs to manage three things: permission, session lifecycle, and preview presentation. If those three are under control, the rest of the integration becomes a matter of mapping options and forwarding events. + +Permission comes first. Camera access on iOS is tightly controlled, so the bridge must request it correctly and the app must declare the right usage description. Without that, nothing else matters. This is one of the places where the wrapper layer again proves its value, because it can ensure required build hints and usage strings are present instead of expecting every application author to remember them manually. + +After permission, the capture session becomes the center of the native implementation. The port needs to initialize the right camera device, create or resume the session, attach the preview layer to a view, and handle stop/start cycles cleanly. These details may feel low-level, but they are the heart of what the user experiences as "the camera preview just works." + +The preview itself is another good reminder that peer components and native views need careful treatment. The portable side wants something that can be embedded in a Codename One form. The native side wants to manage a real UIKit view and camera layer. The bridge exists to reconcile those expectations cleanly. + +So the lesson here is not about becoming an iOS media expert overnight. It is about recognizing the responsibilities of the native bridge: request permission, initialize the native camera system, expose a preview surface that the portable layer can host, and keep the lifecycle predictable. Once that model is clear, the Objective-C syntax stops being the scary part. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [How Do I Access Native Device Functionality, Invoke Native Interfaces](/how-do-i/how-do-i-access-native-device-functionality-invoke-native-interfaces/) +- [How Do I Use The Include Sources Feature To Debug The Native Code On iOS/Android](/how-do-i/how-do-i-use-the-include-sources-feature-to-debug-the-native-code-on-iosandroid-etc/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/023-arc-and-view-on-ios.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/023-arc-and-view-on-ios.md index d919109105..19525fbb39 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/023-arc-and-view-on-ios.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/023-arc-and-view-on-ios.md @@ -9,10 +9,26 @@ module_order: 7 lesson_order: 4 weight: 23 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Understand the iOS view and memory-management details behind the camera bridge." --- - > Module 7: Native Interfaces - Camera - {{< youtube 7G4OkjgTWIQ >}} + +Once the basic iOS camera bridge exists, the next challenge is not a new feature so much as learning how the native view and native memory model affect the implementation. This is where the integration stops being a thin method mapping and starts feeling like real platform work. + +The first important piece is the preview view itself. Codename One needs a peer component it can host inside a form, while iOS needs a real `UIView` whose internal preview layer stays aligned with that view's bounds. That sounds simple, but it is exactly the kind of detail that makes or breaks a native camera preview. If the view moves and the preview layer does not track it correctly, the bridge may compile fine and still behave badly on screen. + +The second important piece is object lifetime. The older lesson spends time on ARC and reference counting because iOS memory management cannot simply be ignored when bridging a Java runtime to native objects. You do not need to become an expert in every detail of Objective-C memory semantics, but you do need to understand the practical implication: when the bridge recreates or replaces native camera objects, their lifecycle has to be handled intentionally. + +That is also why operations such as changing the active camera, updating zoom, or reconfiguring focus can feel more invasive on iOS than they do at first glance. Some updates are cheap. Others effectively require tearing down and rebuilding parts of the camera session. The bridge has to hide that complexity from the portable layer without pretending it does not exist. + +The focus and zoom examples in the lesson are still useful because they show the real pattern of native camera integration: acquire the right native state, translate a portable intent into a platform-specific action, and update the native session safely. The bridge is not just a pipe. It is a compatibility layer that owns those translations. + +So the point of this lesson is not really ARC trivia for its own sake. It is understanding that on iOS, view embedding, session reconfiguration, and object lifetime are part of the contract of a usable camera bridge. Once you accept that, the implementation becomes much easier to reason about. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [How Do I Access Native Device Functionality, Invoke Native Interfaces](/how-do-i/how-do-i-access-native-device-functionality-invoke-native-interfaces/) +- [Camera iOS Port Basics](/courses/course-02-deep-dive-mobile-development-with-codename-one/022-camera-ios-port-basics/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/024-capture-and-callbacks-in-ios.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/024-capture-and-callbacks-in-ios.md index 709cfd76f5..4648d22750 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/024-capture-and-callbacks-in-ios.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/024-capture-and-callbacks-in-ios.md @@ -9,10 +9,24 @@ module_order: 7 lesson_order: 5 weight: 24 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Complete the iOS camera bridge by wiring capture operations and native callbacks back into Java." --- - > Module 7: Native Interfaces - Camera - {{< youtube MbTEz-K8iwY >}} + +The last part of the iOS camera work is where the bridge becomes truly useful: still capture, video capture, and the callback path back into the portable layer. Once those are in place, the camera integration stops being a preview widget and becomes a working feature. + +The main challenge here is not just calling the native capture APIs. It is doing so on the right thread, with the right delegate or callback setup, and then translating the result into something the Java side can consume. That is the part developers often find intimidating, but the underlying pattern is consistent: start native work, let the native API complete asynchronously, then convert the result into the portable representation you want. + +Still image capture is often more complicated than expected because platform APIs evolve over time and older iOS versions may require a different path from newer ones. The specifics in the video are tied to that era of iOS, but the general lesson is still current: camera integrations often need version-aware code paths, and a bridge must isolate that complexity so the portable API remains stable. + +Video capture tends to be easier conceptually because the lifecycle is clearer: start recording, receive the completion callback, stop recording, then hand the resulting path or media object back to the Java side. Even there, though, the important part is not the one method call. It is the callback contract between native completion and portable code. + +This lesson also reinforces why patience matters more than heroics in native work. Most of the code is not brilliant. It is procedural, repetitive, and specific. The value comes from keeping the bridge narrow enough that all of that platform-specific detail remains confined to one place instead of leaking into the application. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [How Do I Access Native Device Functionality, Invoke Native Interfaces](/how-do-i/how-do-i-access-native-device-functionality-invoke-native-interfaces/) +- [How Do I Use The Include Sources Feature To Debug The Native Code On iOS/Android](/how-do-i/how-do-i-use-the-include-sources-feature-to-debug-the-native-code-on-iosandroid-etc/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/025-communicating-with-the-server.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/025-communicating-with-the-server.md index c5109c2c77..ecdab6d59e 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/025-communicating-with-the-server.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/025-communicating-with-the-server.md @@ -9,10 +9,26 @@ module_order: 8 lesson_order: 1 weight: 25 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Build a pragmatic network layer that loads quickly, caches sensibly, and refreshes the UI safely." --- - > Module 8: Putting it All Together - {{< youtube LUHb5fuWzJE >}} + +Finishing an application is usually less about adding one big feature than about making a lot of practical decisions that keep the product moving. This lesson starts that final assembly process by focusing on the network layer and on one of the most important development skills in product work: defining a finish line and refusing to keep expanding it. + +On the technical side, the service layer in this lesson is built around a useful principle for mobile apps: load from local state first when you can, then refresh from the server. That makes the application feel responsive, reduces the cost of repeated launches, and gives the user something to look at immediately instead of forcing every cold start to wait on the network. + +The caching approach in the older lesson is intentionally simple. The app stores the server's JSON payload locally, reloads it into the model on startup, and then asks the server whether the data has changed. That is not a full offline-sync system, but it is a strong practical baseline for many applications because it keeps startup fast without hiding the fact that the server remains the source of truth. + +The other important design choice is where UI updates occur. The code uses the network layer for transport, the model layer for decoded data, and the EDT for visible updates. That separation matters. Once network completion and UI refresh logic start blurring together, responsiveness and maintainability both suffer. + +This lesson also makes a valuable product point: finishing requires discipline. There is always another improvement to make. There is always one more feature that would be nice. If you never constrain the scope, the app never reaches the stage where real users can react to it. A modest but working feature set is often far more valuable than an endlessly expanding plan. + +So the practical outcome here is a network layer with sane caching, timestamp-based refresh logic, and a UI that reacts when data arrives. Just as importantly, it is a reminder that "good enough to ship" is often a more important milestone than "still theoretically improvable." + +## Further Reading + +- [Connecting to a Web Service](/courses/course-02-deep-dive-mobile-development-with-codename-one/003-connecting-to-a-web-service/) +- [Threading and the EDT](/courses/course-01-java-for-mobile-devices/011-threading-and-the-edt/) +- [How Do I Access Remote Webservices, Perform Operations On The Server](/how-do-i/how-do-i-access-remote-webservices-perform-operations-on-the-server/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/026-address-and-validation.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/026-address-and-validation.md index 66f575bdaf..81ac3b6315 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/026-address-and-validation.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/026-address-and-validation.md @@ -9,10 +9,26 @@ module_order: 8 lesson_order: 2 weight: 26 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Collect delivery details, validate them, and connect the checkout flow to real business rules." --- - > Module 8: Putting it All Together - {{< youtube vOcKbbW9HBM >}} + +Order flows are where attractive demos become real products. As soon as delivery is involved, the app needs to collect an address, remember it sensibly, and enforce the business rules that determine whether an order can actually be placed. + +This lesson adds that missing layer. The address form itself is intentionally modest, which is fine. The important thing is not to over-design the input screen. It is to make the data collection predictable and easy to validate. + +The older implementation uses explicit property binding code, and the video notes that newer APIs such as Instant UI would now be a more natural fit for some of this work. That is still the right modern guidance. If you are building a similar form today, use the current property-binding and form-generation tools where they simplify the code instead of re-implementing boilerplate by hand. + +The validation logic is where this lesson becomes more than form wiring. Delivery range, minimum order value, and delivery fee are business rules, not presentation details. That means the UI should surface them clearly, but the application model should own them. Once those rules live in the right place, the rest of the checkout experience becomes more trustworthy because the app is enforcing real constraints instead of merely collecting text fields. + +Location-aware validation is especially useful here. If the restaurant only serves users within a certain radius, the app should check that early enough to save frustration. The exact implementation can vary, but the principle is clear: validate the order against the real-world service boundary before the user gets all the way to submission. + +So the key lesson is that checkout forms are not just a place to gather input. They are the point where UI, stored user data, and domain rules finally meet. That is why they deserve more care than a bare list of fields. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [How Do I Use Properties To Speed Development](/how-do-i/how-do-i-use-properties-to-speed-development/) +- [How Do I Access Native Device Functionality, Invoke Native Interfaces](/how-do-i/how-do-i-access-native-device-functionality-invoke-native-interfaces/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/027-categories-and-search.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/027-categories-and-search.md index 4aefe95d94..cc9db1483d 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/027-categories-and-search.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/027-categories-and-search.md @@ -9,10 +9,24 @@ module_order: 8 lesson_order: 3 weight: 27 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Make the menu easier to navigate by combining category filters with responsive search." --- - > Module 8: Putting it All Together - {{< youtube GEG_S-dyxaM >}} + +As the menu grows, navigation becomes as important as presentation. A visually nice screen is not enough if users cannot narrow the list to what they want quickly. That is where category filtering and search start to matter. + +The search approach in the older lesson is implemented manually even though Codename One has higher-level search toolbar support. That remains a useful teaching example because it exposes the moving parts: toggling the toolbar state, swapping in a text field, listening for changes, and filtering the visible content based on the underlying model. + +In a modern application, the decision between a custom search interaction and a built-in search helper should be pragmatic. If the built-in mechanism now covers the behavior you want, use it. If you need custom behavior or a design-specific interaction, implement it intentionally rather than by accident. The lesson still helps because it shows what that custom implementation really requires. + +The category filter and text search are also a good reminder that UI filtering works best when the model underneath is clean. If each dish entry carries the data the filter needs, the visible UI can be rebuilt or hidden based on that data without making the search code itself complicated. + +The important product lesson here is that finishing the app does not mean ignoring discoverability. Small features like search and category selection often do more for everyday usability than another round of visual polish. + +## Further Reading + +- [How Do I Create A List Of Items The Easy Way](/how-do-i/how-do-i-create-a-list-of-items-the-easy-way/) +- [Developer Guide](/developer-guide/) +- [Communicating with the Server](/courses/course-02-deep-dive-mobile-development-with-codename-one/025-communicating-with-the-server/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/028-security-basics-and-certificate-pinning.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/028-security-basics-and-certificate-pinning.md index 6bc4f1e029..307fb8a213 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/028-security-basics-and-certificate-pinning.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/028-security-basics-and-certificate-pinning.md @@ -9,10 +9,26 @@ module_order: 9 lesson_order: 1 weight: 28 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Understand practical mobile-security concerns and where certificate pinning fits." --- - > Module 9: Security - {{< youtube kjUFJro0yUQ >}} + +Security is one of the easiest areas to discuss in slogans and one of the hardest to handle well in real software. The first thing worth saying clearly is that security is always a trade-off problem. Stronger protections often add cost, complexity, or friction. That does not make them optional. It means they have to be applied thoughtfully. + +This lesson starts with the right framing: vulnerability, exploit, and security layers are not abstract buzzwords. They describe how real systems fail. A vulnerability is a weakness. An exploit is a concrete way to use one or more weaknesses. Most applications are not defending against movie-style attackers with full device compromise. They are trying to protect user data, application logic, and trust boundaries in realistic scenarios. + +Codename One does provide some helpful defaults, and the older lesson explains one of them well: obfuscation and the framework's structure can make reverse engineering less straightforward than in some native setups. That is useful, but it should be treated as friction for attackers, not as a complete security strategy. + +Certificate pinning is a good example of a stronger but more delicate protection. It can reduce the risk of man-in-the-middle attacks by requiring the app to trust only a specific certificate or certificate set rather than any certificate the device would otherwise accept through the normal chain of trust. That is powerful, but it also increases operational risk because certificate rotation and deployment mistakes can break legitimate traffic if you are not prepared for them. + +So the right question is not "should I pin because security is good?" It is "does this threat model justify the added operational burden, and do we have a plan for maintaining it safely?" For high-value traffic, the answer may be yes. For other applications, normal TLS validation plus good server-side hygiene may be the more responsible choice. + +The practical takeaway is that mobile security should be layered. Protect transport. Protect stored data where appropriate. Limit exposure in the UI. Avoid leaking sensitive behavior into easily modified client code. And when you adopt stronger measures like pinning, do it because the threat model is clear, not because the feature sounds impressive. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [Storage Encryption and Misc Security Features](/courses/course-02-deep-dive-mobile-development-with-codename-one/029-storage-encryption-and-misc-security-features/) +- [How Do I Access Remote Webservices, Perform Operations On The Server](/how-do-i/how-do-i-access-remote-webservices-perform-operations-on-the-server/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/029-storage-encryption-and-misc-security-features.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/029-storage-encryption-and-misc-security-features.md index cf1d2a70e1..c2b48dfbc8 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/029-storage-encryption-and-misc-security-features.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/029-storage-encryption-and-misc-security-features.md @@ -9,10 +9,24 @@ module_order: 9 lesson_order: 2 weight: 29 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Use storage encryption and other defensive features where they solve a real application problem." --- - > Module 9: Security - {{< youtube GMnWMB_JfaQ >}} + +Once transport security is in place, the next questions are usually local ones: what happens if the device is compromised, if a screenshot leaks sensitive information, or if another app can inspect copied data? These are the kinds of risks the second security lesson is trying to address. + +Storage encryption is the clearest example. If the app stores sensitive information locally, encrypting that storage can add a useful defensive layer. But encryption is never just "turn it on and forget it." Key management becomes the real issue immediately. If the decryption key is easy for an attacker to recover from the app itself, then the protection is weaker than it first appears. That is why the lesson discusses trade-offs like server-provided keys versus offline usability. + +There is also an important distinction between storage mechanisms. The older lesson is right to separate Codename One `Storage` from lower-level filesystem access and SQL persistence. The abstraction level matters because it determines where transparent protections can realistically be applied and where you need a more explicit design. + +The other features discussed here are more situational, but still useful when the threat model justifies them. Screenshot blocking can reduce accidental disclosure on some platforms. Copy/paste restrictions can help for highly sensitive fields. Jailbreak or root detection may serve as one signal among several, though it should never be treated as a perfect truth oracle because compromised devices are designed to hide that fact. + +So the practical rule for these features is simple: use them when they protect something concrete, and do not confuse them with complete security. They are layers, not guarantees. Applied carefully, they raise the cost of attack and reduce common mistakes. Applied blindly, they mostly add friction. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [Security Basics and Certificate Pinning](/courses/course-02-deep-dive-mobile-development-with-codename-one/028-security-basics-and-certificate-pinning/) +- [How Do I Use Crash Protection, Get Device Logs](/how-do-i/how-do-i-use-crash-protection-get-device-logs/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/030-introduction-and-installation.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/030-introduction-and-installation.md index 3e20d837b7..5484a4a92d 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/030-introduction-and-installation.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/030-introduction-and-installation.md @@ -9,10 +9,24 @@ module_order: 10 lesson_order: 1 weight: 30 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Choose the right map approach in Codename One and understand the setup required for native maps." --- - > Module 10: Maps - {{< youtube HRTLnLecoMA >}} + +Maps are one of those features that look simple in a product brief and become surprisingly nuanced once implementation begins. The first decision is not how to place a marker. It is which kind of map integration you actually want and how close that integration needs to be to the native experience on each platform. + +The older lesson does a useful job of separating the available map approaches. Some are native, some are browser-backed fallbacks, and some represent older technology that is no longer the preferred path. That distinction still matters. For most current projects, the native map implementation plus a reasonable fallback is the right mental model. Older tiled-map approaches belong to legacy compatibility, not to the default recommendation for new work. + +This lesson also highlights a practical truth about map integrations: they are not self-contained. You usually need provider keys, platform-specific configuration, and some awareness of quota or billing implications. That means map support is partly a UI feature and partly an operational setup task. + +One place where the older material is still directionally right is key handling. Hard-coding keys into a demo may be acceptable for a tutorial, but production applications should treat provider keys as operational assets and protect them appropriately. The exact best practice depends on the provider and the architecture, but the principle is the same: keep the production path more careful than the tutorial path. + +So the right starting point for maps in Codename One is not "drop in a map widget." It is "pick the right implementation strategy, understand the native and fallback story, and configure the provider side correctly before building map-heavy features on top." + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [How Do I Access Native Device Functionality, Invoke Native Interfaces](/how-do-i/how-do-i-access-native-device-functionality-invoke-native-interfaces/) +- [Communicating with the Server](/courses/course-02-deep-dive-mobile-development-with-codename-one/025-communicating-with-the-server/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/031-hello-world-and-devices.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/031-hello-world-and-devices.md index 387482fe9a..f27341a797 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/031-hello-world-and-devices.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/031-hello-world-and-devices.md @@ -9,10 +9,24 @@ module_order: 10 lesson_order: 2 weight: 31 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Build the simplest useful map example and understand how native and fallback behavior differ across targets." --- - > Module 10: Maps - {{< youtube m43A68sNcvg >}} + +The first useful map example is not complicated. Create a map, make it render on the supported targets, and verify that the fallback behavior is acceptable where native maps are not available. That sounds simple, but it teaches an important lesson: a map feature is only as good as its weakest platform path. + +The older lesson compares several rendering outcomes and shows why some map implementations feel much more modern than others. That is still the right instinct. If one path gives you an outdated or visually inconsistent experience while another produces a much better fallback, choose the path that keeps the feature coherent across devices. + +This lesson also exposes a subtle but practical issue: not every runtime environment behaves like a real user device. Build-time screenshot generation, simulators, and special platform modes may not support every embedded component. When a feature depends on browser-backed or heavyweight behavior, the application should have a graceful fallback instead of assuming the full environment is always available. + +That is why even a "hello world" maps lesson matters. It is where you verify native keys, fallback configuration, and platform-specific behavior before you build markers, overlays, routing, or location-driven flows on top. If the base map setup is fragile, every map feature after that becomes harder to debug. + +So the real goal here is not just to get a map on screen. It is to make sure the basic map experience is acceptable on the targets you care about and that your application degrades cleanly where full support is not present. + +## Further Reading + +- [Introduction and Installation](/courses/course-02-deep-dive-mobile-development-with-codename-one/030-introduction-and-installation/) +- [Developer Guide](/developer-guide/) +- [Markers, Lightweight Overlays and Map Layout](/courses/course-02-deep-dive-mobile-development-with-codename-one/032-markers-lightweight-overlays-and-map-layout/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/032-markers-lightweight-overlays-and-map-layout.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/032-markers-lightweight-overlays-and-map-layout.md index 70ac602341..fbbe7f6031 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/032-markers-lightweight-overlays-and-map-layout.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/032-markers-lightweight-overlays-and-map-layout.md @@ -9,10 +9,24 @@ module_order: 10 lesson_order: 3 weight: 32 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Go beyond built-in markers by placing real lightweight components over the map." --- - > Module 10: Maps - {{< youtube sed5OPQSfe0 >}} + +Markers are the first useful map feature because they let the user connect geographic coordinates to something meaningful. But the default marker API is only the beginning. As soon as you want richer visuals or interactions, you start wanting more control than a simple pin can provide. + +That is where overlays become interesting. Instead of treating the map as a sealed native surface that can only show provider-defined markers, this lesson shows how to place lightweight components above the map and position them according to latitude and longitude. That opens up much richer UI possibilities: custom buttons, status badges, composite visuals, or other interactive elements that feel like part of the app instead of part of the map provider. + +The layout-manager approach used here is especially instructive because it keeps the problem honest. A map overlay is still a layout problem. You need to convert coordinates into screen points, place components using their preferred size, and update their positions when the map moves or zooms. Once you describe it that way, the solution becomes much less magical. + +This is also a good example of how Codename One's lightweight UI model enables features that would once have been awkward or impossible. If heavyweight native components always won the z-order battle, this pattern would be much harder to support. The ability to mix map content with lightweight overlays is a real design advantage. + +So the lesson here is that built-in markers are fine when they are enough, but do not stop there if your map UI needs richer interaction. A carefully designed overlay layer can turn the map from a passive display into a real part of the application interface. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [Introduction and Installation](/courses/course-02-deep-dive-mobile-development-with-codename-one/030-introduction-and-installation/) +- [Hello World and Devices](/courses/course-02-deep-dive-mobile-development-with-codename-one/031-hello-world-and-devices/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/033-introduction-and-setup.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/033-introduction-and-setup.md index ec17583606..aa834b2a04 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/033-introduction-and-setup.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/033-introduction-and-setup.md @@ -9,10 +9,24 @@ module_order: 11 lesson_order: 1 weight: 33 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Understand when building from source is useful and what pieces of the Codename One toolchain are involved." --- - > Module 11: Building from the Open Source Project Offline Without Build Servers or Plugin - {{< youtube wqcM8pSOGTY >}} + +Working directly from the Codename One source tree is not the normal path for most application developers. That is worth saying upfront because this module makes more sense when treated as advanced infrastructure knowledge rather than as the default workflow. + +If your real goal is just to build and ship apps, the hosted build flow and modern project tooling are usually the right choice. Even the older lesson says as much. Building everything locally from source is valuable for a different set of reasons: understanding how the platform is assembled, being able to debug the lower layers more confidently, or contributing to the framework itself. + +The conceptual model in the lesson is still the useful part. Codename One is not one monolith. It is a combination of APIs, ports, runtimes or VMs on some targets, tooling, and supporting binaries or stubs that make compilation practical across platforms. Once you understand those pieces, the source layout stops feeling arbitrary. + +The exact version guidance in the video is obviously dated now, and the recommended setup for ordinary application development has moved on. But the deeper lesson is still current: if you build from source, you are taking responsibility for more of the platform stack. That means understanding what each repository or module contributes and which parts exist as implementation support rather than public API. + +So this lesson is best read as an anatomy-of-the-platform walkthrough for advanced users. It is not the new default way to start a project. It is the way to get closer to the internals when you deliberately need that level of control. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [How Do I Get Repeatable Builds, Build Against A Consistent Version Of Codename One & Use The Versioning Feature](/how-do-i/how-do-i-get-repeatable-builds-build-against-a-consistent-version-of-codename-one-use-the-versioning-feature/) +- [How Do I Use Offline Build](/how-do-i/how-do-i-use-offline-build/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/034-running-the-kitchen-sink-in-the-simulator.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/034-running-the-kitchen-sink-in-the-simulator.md index 4b6167cbe8..4e7e5de1f9 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/034-running-the-kitchen-sink-in-the-simulator.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/034-running-the-kitchen-sink-in-the-simulator.md @@ -9,10 +9,22 @@ module_order: 11 lesson_order: 2 weight: 34 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Use the simulator as the first proof that a direct source build is assembled correctly." --- - > Module 11: Building from the Open Source Project Offline Without Build Servers or Plugin - {{< youtube l1TPKuHYr9c >}} + +If you are building Codename One directly from source, the simulator is the first place to prove that your environment is actually coherent. Before you worry about native packaging, you want one unambiguous success signal: the framework builds, the demo project links against it correctly, and the application runs in the Java SE simulator path. + +That is why the Kitchen Sink demo is a good target. It exercises a wide range of framework behavior, so if it launches successfully you have more confidence than you would from a toy example. It is not a guarantee that every platform path is correct, but it is the right first checkpoint. + +The older lesson necessarily spends time on the mechanics of assembling jars, copying the right pieces into place, and working around the absence of the usual build client tooling. The exact commands are historical, but the practical lesson remains: once you operate outside the normal project-generation path, you have to understand which artifacts the simulator actually expects and which pieces of the usual workflow you are now responsible for yourself. + +This is another reason direct-source work should be treated as an advanced mode. The simulator run is no longer "click run." It becomes "verify that the platform pieces, demo project, and launch path all line up." That can be valuable knowledge, but it is different from normal application development. + +## Further Reading + +- [Introduction and Setup](/courses/course-02-deep-dive-mobile-development-with-codename-one/033-introduction-and-setup/) +- [Developer Guide](/developer-guide/) +- [Hello World](/hello-world/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/035-building-a-desktop-version-of-the-kitchen-sink.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/035-building-a-desktop-version-of-the-kitchen-sink.md index 33b65cf155..a0badf9060 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/035-building-a-desktop-version-of-the-kitchen-sink.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/035-building-a-desktop-version-of-the-kitchen-sink.md @@ -9,10 +9,24 @@ module_order: 11 lesson_order: 3 weight: 35 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Wrap a Codename One app for desktop execution and understand what the desktop bootstrap is responsible for." --- - > Module 11: Building from the Open Source Project Offline Without Build Servers or Plugin - {{< youtube PbRbzCGQCQE >}} + +Running a Codename One app as a desktop application is a useful way to understand what the simulator and Java SE port are really doing for you. The central idea is straightforward: Codename One still needs a bootstrap environment, and on desktop that environment is responsible for creating the host window, initializing the port, and handing lifecycle control to the application. + +That is why the lesson introduces a small desktop stub. The stub is not application logic in the normal sense. It is the code that embeds the Codename One runtime into a standard Java desktop container and gives the app somewhere to live. + +The specific Swing-based bootstrap in the older lesson is less important than the pattern. Desktop support in this context means: create a native host window, initialize the Codename One environment against it, forward lifecycle events properly, and package the runtime pieces the app needs. Once you see it that way, the "desktop app" stops looking like a mystery and starts looking like an ordinary host-shell problem. + +This lesson is also useful because it shows how different the concerns are from normal portable application code. The portable app should not care about Swing setup, storage path heuristics, or simulator-only flags. Those belong in the desktop-specific bootstrap layer, and keeping them there preserves the clean separation between the app and the host platform. + +So the main value here is architectural. A desktop wrapper is just one more example of Codename One's layering: portable app logic inside, host-specific bootstrap outside. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [Running the Kitchen Sink in the Simulator](/courses/course-02-deep-dive-mobile-development-with-codename-one/034-running-the-kitchen-sink-in-the-simulator/) +- [How Do I Use Desktop Javascript Ports](/how-do-i/how-do-i-use-desktop-javascript-ports/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/036-building-an-android-native-version-of-the-kitchen-sink.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/036-building-an-android-native-version-of-the-kitchen-sink.md index 4179ef3c6d..4f11efc950 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/036-building-an-android-native-version-of-the-kitchen-sink.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/036-building-an-android-native-version-of-the-kitchen-sink.md @@ -9,10 +9,22 @@ module_order: 11 lesson_order: 4 weight: 36 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Understand what is involved in assembling a native Android app directly from the Codename One sources." --- - > Module 11: Building from the Open Source Project Offline Without Build Servers or Plugin - {{< youtube 5eMvwRcDcug >}} + +Building a native Android application directly from the Codename One sources is a useful exercise in understanding how the Android port is assembled, but it is not a lightweight workflow. The amount of setup in the older lesson makes that point on its own. + +The conceptual structure is still worth learning. You need the portable sources, the Android-specific implementation sources, the right resources and bootstrap files in the places Android expects, and an activity layer that maps Android lifecycle behavior onto the Codename One application model. Once all of those are aligned, the app can run as a normal Android application. + +The old lesson also spends time on Java 8 support, Gradle configuration, copied implementation files, and Android activity wiring. The specific historical version details are not the important thing now. The important thing is understanding why these steps exist at all: Android needs an actual native project structure, and Codename One's Android port includes implementation classes that intentionally override or extend the portable layer. + +This is one more reason the normal hosted or standard build flow remains the right default for most application work. When you bypass that flow, you take on the responsibility of reproducing all the Android-specific packaging and lifecycle glue yourself. That can be educational and sometimes necessary for framework-level work, but it is not a simpler way to build apps. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [Introduction and Setup](/courses/course-02-deep-dive-mobile-development-with-codename-one/033-introduction-and-setup/) +- [How Do I Use Offline Build](/how-do-i/how-do-i-use-offline-build/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/037-build-an-ios-native-version-of-the-kitchen-sink.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/037-build-an-ios-native-version-of-the-kitchen-sink.md index 91f5ed428f..c33c451a6a 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/037-build-an-ios-native-version-of-the-kitchen-sink.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/037-build-an-ios-native-version-of-the-kitchen-sink.md @@ -9,10 +9,22 @@ module_order: 11 lesson_order: 5 weight: 37 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Understand the source-built iOS path and the translation/bootstrap steps it relies on." --- - > Module 11: Building from the Open Source Project Offline Without Build Servers or Plugin - {{< youtube RsVOanGYjqo >}} + +The source-built iOS path makes the underlying Codename One architecture especially visible because it forces you to confront the translation step directly. Unlike the simulator or Java SE path, this is not just "compile the app." It is "prepare the classes, translate them into the form the iOS runtime expects, and then wrap the result in an Xcode project that can actually run on Apple tooling." + +That is why the older lesson spends so much time on translation, stubs, generated output, and Xcode setup. The value is not in memorizing those exact commands. The value is in understanding that the iOS path depends on an explicit bootstrap process and a generated native project, not on some hidden magic. + +The font-registration and project-setting details in the older video are historical, but the broader message is still current: once you are operating at this layer, you are responsible for the native platform's packaging requirements. iOS expects declared resources, native project metadata, and the right build settings. The standard Codename One build flow handles that for ordinary projects. A source-built path makes you do it yourself. + +So the lesson here is the same as the Android one, but even more visible. Working from source is useful when you want to understand or modify the lower layers of the platform. It is not the recommended day-to-day workflow for building applications. Its value is educational and infrastructural, not ergonomic. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [Introduction and Setup](/courses/course-02-deep-dive-mobile-development-with-codename-one/033-introduction-and-setup/) +- [How Do I Use The Include Sources Feature To Debug The Native Code On iOS/Android](/how-do-i/how-do-i-use-the-include-sources-feature-to-debug-the-native-code-on-iosandroid-etc/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/038-what-is-performance-breaking-down-the-problem.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/038-what-is-performance-breaking-down-the-problem.md index f875fcf076..c7a2504193 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/038-what-is-performance-breaking-down-the-problem.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/038-what-is-performance-breaking-down-the-problem.md @@ -9,10 +9,26 @@ module_order: 12 lesson_order: 1 weight: 38 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Separate rendering, logic, memory, and perception problems before trying to optimize anything." --- - > Module 12: Performance and Memory Tuning - {{< youtube x-mUTz23Cd4 >}} + +Performance problems become much easier to solve once you stop treating them as one category. "The app feels slow" can mean several very different things, and each one points to a different fix. + +This lesson makes that distinction well. Rendering problems are not the same as business-logic delays. Memory pressure is not the same as a blocking network call. A UI bug caused by nested scrollables or conflicting focus behavior is not really a performance issue at all, even if the user experiences it as sluggishness. + +That separation matters because performance work is one of the places where developers burn time most easily. If you start optimizing without identifying which kind of problem you actually have, you usually end up making the code harder to maintain without meaningfully improving the user experience. + +The lesson also makes an important point about perception. Users do not experience performance as a profiler chart. They experience it as feedback, responsiveness, and confidence that the app is doing something sensible. Cached content, progressive loading, and visible movement toward a result often matter as much as raw timing numbers. + +Another idea here that deserves repeating is the warning against premature optimization. That advice is not an excuse to ignore performance. It is a reminder to optimize based on evidence. In real applications, a small fraction of the code usually drives most of the measurable pain. Find that fraction first. + +So the best way to start performance work is to classify the problem. Is it rendering? Is it logic? Is it memory churn? Is it perceived slowness caused by feedback and loading design? Once you answer that, the next steps become much more rational. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [Threading and the EDT](/courses/course-01-java-for-mobile-devices/011-threading-and-the-edt/) +- [How Do I Improve Application Performance Or Track Down Performance Issues](/how-do-i/how-do-i-improve-application-performance-or-track-down-performance-issues/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/039-edt-threading-caching-and-soft-references.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/039-edt-threading-caching-and-soft-references.md index f954768281..933cc1e936 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/039-edt-threading-caching-and-soft-references.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/039-edt-threading-caching-and-soft-references.md @@ -9,10 +9,28 @@ module_order: 12 lesson_order: 2 weight: 39 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Avoid the most common performance mistakes by respecting the EDT and caching deliberately." --- - > Module 12: Performance and Memory Tuning - {{< youtube 85MyytvMS9I >}} + +If there is one performance rule that deserves to be repeated until it becomes reflex, it is this: do not block the EDT. Most performance advice in Codename One eventually comes back to that point, because the EDT owns interaction and painting. When you make it wait, the whole application feels worse no matter how efficient the rest of the code may be. + +That does not mean "move everything to another thread and hope for the best." Background work still competes for CPU time. On mobile hardware especially, a badly behaved background task can starve the UI just as effectively as work on the EDT if it burns enough processor time. So good threading is not just about moving work away from the UI. It is about moving it away responsibly. + +The lesson's discussion of `invokeAndBlock`, `callSerially`, and idle-time work points to the real trade-off: convenience versus control. Sometimes a blocking abstraction makes code easier to write. Sometimes a cleaner callback or explicit background thread is the better choice. The right answer depends on how much work is being done and how sensitive the interaction needs to feel. + +Caching is the other half of the story. In practice, many of the biggest performance wins come from caching the right thing in the right place. But caching also creates memory pressure, and memory pressure causes its own performance problems. That is why there is no universal answer to questions like "should I cache this image or this component?" The cost depends on what gets duplicated, how often it is reused, and how aggressively memory needs to be reclaimed on target devices. + +This is where soft references and other reclaimable caches become useful. They let the application benefit from reuse when memory is available without insisting on holding everything forever. The point is not to be clever for its own sake. It is to keep the application responsive while avoiding wasteful re-creation of expensive objects. + +The lesson's advice on form reuse is also still relevant. Reusing a form is often simpler and smoother than rebuilding it constantly, but that stops being true if the form drags along a huge memory footprint full of heavyweight images or other costly state. Performance work is always about choosing where to pay. + +So the practical takeaway is this: keep the EDT lean, use threads deliberately, and treat caching as a measured optimization rather than a reflex. Most performance tuning becomes easier once those three habits are in place. + +## Further Reading + +- [Threading and the EDT](/courses/course-01-java-for-mobile-devices/011-threading-and-the-edt/) +- [What is Performance? Breaking Down the Problem](/courses/course-02-deep-dive-mobile-development-with-codename-one/038-what-is-performance-breaking-down-the-problem/) +- [How Do I Improve Application Performance Or Track Down Performance Issues](/how-do-i/how-do-i-improve-application-performance-or-track-down-performance-issues/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/040-the-different-image-types-and-their-impact-on-performance-ram.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/040-the-different-image-types-and-their-impact-on-performance-ram.md index ba28959cee..8537ebc80c 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/040-the-different-image-types-and-their-impact-on-performance-ram.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/040-the-different-image-types-and-their-impact-on-performance-ram.md @@ -9,10 +9,26 @@ module_order: 12 lesson_order: 3 weight: 40 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Choose image representations with a clear understanding of their memory and rendering trade-offs." --- - > Module 12: Performance and Memory Tuning - {{< youtube DWifLQZyPDE >}} + +Images are often the biggest single contributor to both application size and runtime memory pressure. That means image choices are not just aesthetic or API-level decisions. They are core performance decisions. + +The key lesson here is that not all image representations cost the same thing. Some image types are fast to draw but expensive in memory. Others are compact until decoded, but pay a cost when they need to expand into drawable form. Still others exist mainly to support specific low-level use cases and should not be your default tool. + +Encoded images are especially important in Codename One because they are the workhorse of practical memory management. The image can stay compact while idle and expand when actually needed. That makes them a strong fit for many UI situations, especially when the alternative is keeping a large decoded image resident all the time. But that benefit comes with a new responsibility: understand when an image should stay locked in memory and when it should be allowed to fall out of cache. + +This is where many performance issues become subtle. A decoded image may render smoothly because it stays warm in memory, but too many of those images can cause memory pressure and cache churn elsewhere. An encoded image may be smaller overall, but if it is constantly being re-decoded during active use, the app can feel sluggish. The right answer depends on how often the image is used and whether it is central to the current screen. + +The broader point is that image optimization is not just about file size. Runtime footprint matters just as much, and the simple width-times-height-times-four mental model is still a useful way to estimate the cost of keeping decoded images alive. + +For modern projects, this lesson pairs well with the general advice to keep styling in CSS and use icon fonts or vector-friendly approaches where possible. Every place where an image can be replaced by a cheaper or more flexible representation is one less place where you have to pay this memory/performance trade-off. + +## Further Reading + +- [Themeing](/themeing/) +- [How Do I Fetch An Image From The Resource File, Add A Multiimage](/how-do-i/how-do-i-fetch-an-image-from-the-resource-file-add-a-multiimage/) +- [What is Performance? Breaking Down the Problem](/courses/course-02-deep-dive-mobile-development-with-codename-one/038-what-is-performance-breaking-down-the-problem/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/041-list-network-parsing-and-resource-file-size.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/041-list-network-parsing-and-resource-file-size.md index 9edeccb77f..989d00647e 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/041-list-network-parsing-and-resource-file-size.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/041-list-network-parsing-and-resource-file-size.md @@ -9,10 +9,26 @@ module_order: 12 lesson_order: 4 weight: 41 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Recognize the common structural performance traps in lists, parsing, and oversized resources." --- - > Module 12: Performance and Memory Tuning - {{< youtube ZEe_hb1Lz6Y >}} + +Performance problems do not always come from one obviously slow algorithm. Just as often they come from structural choices that look harmless until the app scales a little. Lists, parsing location, overdraw, network threading, and resource-file bloat all fall into that category. + +The first useful point in this lesson is that `List` is not automatically the fast answer just because it has a reusable-cell architecture. On mobile, huge endlessly scrollable datasets are often the wrong interaction model in the first place, and smaller data sets can frequently be handled more cleanly with other UI structures. Performance is one reason, but usability is another. + +Overdraw is another example of a problem that is easy to ignore until it starts hurting. Drawing the same region several times is normal to a point, but too much layered opacity and background painting can create unnecessary rendering cost. That is why inspecting component hierarchy and reducing unnecessary opaque layers can matter so much. + +The lesson is also right to push parsing and similar work off the EDT when possible. If a network response arrives quickly but the JSON parsing happens on the EDT, the user still experiences a slow app. Getting the work off the UI thread does not just improve raw performance. It protects the user's sense that the app is alive. + +Resource-file size deserves equal attention because large resources hurt more than one stage of the product. They make downloads heavier, builds larger, memory use worse, and startup or asset loading less predictable. In practice, most of that size is usually images, especially multiple density variants. So the most effective optimization is often to identify the biggest offenders rather than trying to shave a few bytes from everything. + +This lesson is really a collection of reminders that performance is often about architecture and asset discipline, not just low-level code tricks. If the lists are appropriate, the parsing happens in the right place, and the resources stay under control, many other problems become much easier to manage. + +## Further Reading + +- [What is Performance? Breaking Down the Problem](/courses/course-02-deep-dive-mobile-development-with-codename-one/038-what-is-performance-breaking-down-the-problem/) +- [How Do I Create A List Of Items The Easy Way](/how-do-i/how-do-i-create-a-list-of-items-the-easy-way/) +- [How Do I Fetch An Image From The Resource File, Add A Multiimage](/how-do-i/how-do-i-fetch-an-image-from-the-resource-file-add-a-multiimage/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/042-profiling-on-the-desktop-using-the-performance-monitor-tool.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/042-profiling-on-the-desktop-using-the-performance-monitor-tool.md index e17384b41e..2d74517744 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/042-profiling-on-the-desktop-using-the-performance-monitor-tool.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/042-profiling-on-the-desktop-using-the-performance-monitor-tool.md @@ -9,10 +9,26 @@ module_order: 12 lesson_order: 5 weight: 42 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Use Codename One and desktop profiling tools to find rendering and resource problems before guessing." --- - > Module 12: Performance and Memory Tuning - {{< youtube BY1lMQz873g >}} + +The most important rule of performance debugging is simple: stop guessing. Profiling exists because intuition is unreliable, especially in systems with several interacting layers. + +This lesson makes that point with exactly the right kind of story. Teams can spend hours arguing about where a slowdown must be coming from, only to discover through profiling that the real problem is somewhere much less glamorous. That is not an exception. That is how performance work usually goes. + +The built-in performance monitor in the simulator is useful because it gives you visibility into rendering behavior without requiring external tooling. It will not solve every performance question, but it can show you which components are drawing slowly, whether unlocked images are being rendered in suspicious places, and where overdraw or repeated painting is happening in the hierarchy. + +That tool is especially helpful because it teaches you to ask "why was this painted?" rather than merely "why does this feel slow?" Once you inspect the drawing tree and the associated stack traces, rendering behavior becomes something you can reason about instead of something you vaguely experience. + +Desktop profilers add another layer of insight. CPU and memory profilers on the simulator side can help you spot suspicious hotspots, excessive invocation counts, or unexpected memory growth. They are not a perfect proxy for device behavior, but they are good enough to invalidate bad assumptions and point you toward real candidates for investigation. + +The most practical habit from this lesson is the back-of-the-envelope check. If the numbers a profiler shows you do not roughly match the size of the data or the amount of work you think you are doing, that mismatch is itself a clue. Simple sanity checks are often what turn profiler output into understanding. + +## Further Reading + +- [What is Performance? Breaking Down the Problem](/courses/course-02-deep-dive-mobile-development-with-codename-one/038-what-is-performance-breaking-down-the-problem/) +- [How Do I Improve Application Performance Or Track Down Performance Issues](/how-do-i/how-do-i-improve-application-performance-or-track-down-performance-issues/) +- [Profiling on Devices iOS and Android](/courses/course-02-deep-dive-mobile-development-with-codename-one/043-profiling-on-devices-ios-and-android/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/043-profiling-on-devices-ios-and-android.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/043-profiling-on-devices-ios-and-android.md index b65664ab61..6d4f5f10cf 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/043-profiling-on-devices-ios-and-android.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/043-profiling-on-devices-ios-and-android.md @@ -9,10 +9,24 @@ module_order: 12 lesson_order: 6 weight: 43 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Move from simulator measurements to on-device profiling when the real bottleneck only appears on hardware." --- - > Module 12: Performance and Memory Tuning - {{< youtube YwkwqXkHGwg >}} + +The simulator gets you far, but not all the way. Some performance problems only become visible on the real device, where GPU behavior, memory pressure, input latency, and platform-specific implementation details finally line up the way your users will experience them. + +That is why device profiling matters. Android Studio and Xcode provide the next level of evidence once the simulator has stopped being enough. The goal is not to abandon the desktop tools. It is to move to hardware when you need to confirm what is really happening under mobile conditions. + +The Android guidance in the older lesson is still useful because it combines two kinds of observation: profiler data and developer-option visual debugging. GPU overdraw visualization, in particular, is one of those rare tools that can make a rendering problem immediately visible even before you have fully quantified it. If a screen is awash in red, you already know where to start asking questions. + +On iOS, the tooling differs, but the principle is identical. Use the platform profiler to isolate expensive methods, understand where time is being spent on device, and confirm whether the suspected bottleneck is real. The purpose is not just to collect screenshots of charts. It is to narrow the problem until you can act on it. + +The broader lesson is that performance work should move from general to specific. Start with conceptual classification, then use simulator tools, then escalate to device profiling when the evidence demands it. That progression keeps you from over-investing in low-level profiling too early while still giving you a path to real answers when desktop measurements are no longer enough. + +## Further Reading + +- [Profiling on the Desktop, Using the Performance Monitor Tool](/courses/course-02-deep-dive-mobile-development-with-codename-one/042-profiling-on-the-desktop-using-the-performance-monitor-tool/) +- [What is Performance? Breaking Down the Problem](/courses/course-02-deep-dive-mobile-development-with-codename-one/038-what-is-performance-breaking-down-the-problem/) +- [How Do I Improve Application Performance Or Track Down Performance Issues](/how-do-i/how-do-i-improve-application-performance-or-track-down-performance-issues/) diff --git a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/044-case-study-performance-problems-in-the-kitchen-sink.md b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/044-case-study-performance-problems-in-the-kitchen-sink.md index 83669eb8e2..0cd7f6073b 100644 --- a/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/044-case-study-performance-problems-in-the-kitchen-sink.md +++ b/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/044-case-study-performance-problems-in-the-kitchen-sink.md @@ -9,10 +9,24 @@ module_order: 12 lesson_order: 7 weight: 44 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "See how small architectural mistakes turn into real performance problems and how to fix them methodically." --- - > Module 12: Performance and Memory Tuning - {{< youtube qGJaVaQ_MNY >}} + +Performance advice is easiest to remember when it is attached to a real mistake. That is why this case study is valuable. Instead of repeating general rules, it shows how ordinary-looking decisions in a real application created noticeable problems and how those problems were fixed. + +The first example is a perfect reminder not to trust first impressions. The contacts demo seemed slow for an obvious reason, but profiling and inspection uncovered a different issue entirely: the same resource file was being loaded repeatedly in places where the code looked superficially innocent. That kind of bug is common because the expensive part is often hidden behind constructors, helpers, or convenience APIs. + +The second example is equally important because it shows the limits of the "just move it to a background thread" reflex. Loading contact images off the EDT sounded like the right fix, and in one sense it was. But the background work still competed with the user experience during scrolling. The real improvement came from coordinating the work with user activity so the app deferred expensive updates until interaction settled down. + +That is a strong pattern to remember. Performance is often not about doing less work overall. It is about doing work at a better time. If the UI is busy, defer the optional work. If the user is idle, use that moment to fill in detail. This is one of the cleanest ways to improve perceived and actual responsiveness at the same time. + +So the real lesson from the case study is methodological. Do not assume. Measure. Look for hidden repeated work. And when a background task still harms the experience, ask whether the issue is not the work itself but its timing relative to user interaction. + +## Further Reading + +- [What is Performance? Breaking Down the Problem](/courses/course-02-deep-dive-mobile-development-with-codename-one/038-what-is-performance-breaking-down-the-problem/) +- [EDT, Threading, Caching and Soft References](/courses/course-02-deep-dive-mobile-development-with-codename-one/039-edt-threading-caching-and-soft-references/) +- [Profiling on the Desktop, Using the Performance Monitor Tool](/courses/course-02-deep-dive-mobile-development-with-codename-one/042-profiling-on-the-desktop-using-the-performance-monitor-tool/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/001-server.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/001-server.md index 1d1c72ce4b..ed03cc72e1 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/001-server.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/001-server.md @@ -9,10 +9,26 @@ module_order: 1 lesson_order: 1 weight: 1 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Understand the server-side shape of the restaurant application and the responsibilities that belong there." --- - > Module 1: Restaurant Server - {{< youtube exL7bS0StP4 >}} + +This course starts by bringing the server back into the picture. Earlier material focused heavily on the mobile side, but a real full-stack application only makes sense once the server responsibilities are clear as well. + +The good news is that most of the server code in this module is deliberately boring, and that is exactly what you want. A good backend is not usually interesting because of flashy implementation tricks. It is useful because it behaves predictably, enforces the rules the client should not be trusted to enforce, and keeps the application state coherent. + +The lesson uses a Spring Boot backend again, which is still a sensible choice for this kind of Java-based full-stack application. The exact IDE screenshots are older, but the architectural point remains current: the mobile app and the server are two parts of one system, and the server is where pricing, authorization, versioning, and multi-tenant concerns need to be treated seriously. + +One of the most important examples in the lesson is the pricing discussion around Braintree. If the client submits prices and the server trusts them, the system is already too weak. Even if no one is attacking it actively, stale client data and altered requests can still produce wrong results. The server should recalculate authoritative values from trusted identifiers and server-side state. That single design habit prevents an entire class of problems. + +The same principle shows up in entity design. IDs, DAO objects, and transport representations may look repetitive, but they serve different purposes. The entity models storage. The DAO or DTO models communication. Keeping those roles distinct gives the backend room to evolve without exposing internal persistence choices directly to the client. + +So the first server lesson is really about responsibility boundaries. The mobile app owns experience and interaction. The server owns authority, validation, and cross-client consistency. Once that boundary is clear, the rest of the full-stack application becomes much easier to reason about. + +## Further Reading + +- [Introduction to Spring Boot](/courses/course-02-deep-dive-mobile-development-with-codename-one/002-introduction-to-spring-boot/) +- [Connecting to a Web Service](/courses/course-02-deep-dive-mobile-development-with-codename-one/003-connecting-to-a-web-service/) +- [Security Basics and Certificate Pinning](/courses/course-02-deep-dive-mobile-development-with-codename-one/028-security-basics-and-certificate-pinning/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/002-scope-and-basic-ui-design.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/002-scope-and-basic-ui-design.md index 019b568770..fc9177fa42 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/002-scope-and-basic-ui-design.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/002-scope-and-basic-ui-design.md @@ -9,10 +9,26 @@ module_order: 2 lesson_order: 1 weight: 2 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Define the scope of the app-maker product and shape a first usable UI direction without waiting for a PSD." --- - > Module 2: The App Maker - {{< youtube tiY5ofnJop8 >}} + +The app maker in this course is a different kind of product from the restaurant app that came before it. The restaurant app had plenty of familiar reference points. The app maker does not. That means the design process has to start from structure and purpose rather than from an existing polished mockup. + +This lesson handles that correctly by starting with scope. Before trying to make the app beautiful, it asks what information the product must collect, what parts should be editable immediately, and what kind of first impression the user should get. That is the right order. A screen can be refined later. A confused product scope is harder to recover from. + +The first design attempt in the video is intentionally useful because it fails in an informative way. A tabbed, detail-heavy form may make sense to a developer looking at the data model, but it is not a good first impression for a user who wants to understand the product quickly. That is an important design instinct to build: the right interface is not necessarily the one that mirrors the underlying data most directly. + +The improved direction shifts the focus to preview and momentum. The user sees something that already resembles a customizable app, not just a pile of settings. That is a stronger product choice because it lets people feel progress before they have finished entering every detail. A preview-first workflow is often the right answer in builder-style products for exactly that reason. + +This lesson is also a reminder that defaults are part of design. If the app opens in a half-empty, awkward state, it does not matter that the user could eventually make it look good. Good defaults reduce the amount of work required before the product feels alive. + +So the real takeaway here is not just one UI sketch over another. It is the idea that scope, first impression, and editability should drive the product shape from the beginning, especially when you do not have a designer handing you a finished visual system. + +## Further Reading + +- [Fleshing Out the UI Design](/courses/course-03-build-real-world-full-stack-mobile-apps-java/003-fleshing-out-the-ui-design/) +- [Adapting a UI Design](/courses/course-01-java-for-mobile-devices/009-adapting-a-ui-design/) +- [Themeing](/themeing/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/003-fleshing-out-the-ui-design.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/003-fleshing-out-the-ui-design.md index 21fd602617..734805b532 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/003-fleshing-out-the-ui-design.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/003-fleshing-out-the-ui-design.md @@ -9,10 +9,26 @@ module_order: 2 lesson_order: 2 weight: 3 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Refine the main app-maker flows and decide which supporting forms are worth building now." --- - > Module 2: The App Maker - {{< youtube IxVUX0s2Nms >}} + +Once the high-level direction is clear, the next step is to turn that direction into actual screens and decide which ones belong in the first version. This is where many projects start drifting, because every missing form suddenly feels important. + +The strongest part of this lesson is its willingness to separate essential flows from tempting extras. Billing, details, styling, address entry, and media selection all matter, but they do not all need to be built at once in full generality. The act of choosing what to postpone is part of the design work, not an admission of failure. + +The lesson's styling and customization ideas are also useful because they think in terms of targeted editing rather than giant monolithic settings screens. If a user can tap an area and customize the relevant background, color, icon, or font in context, the builder becomes much easier to understand. That is generally more approachable than making the user translate abstract configuration names into visual outcomes. + +At the same time, this lesson keeps the product grounded by acknowledging that some features should stay simpler for now. That restraint is important. A builder product can easily become unusable if every form tries to expose every possible option immediately. + +The implementation-plan discussion at the end is also worth carrying forward. A depth-first approach makes sense here because it validates the product loop early. Instead of building a thin layer of every feature, it gets one narrow slice working end to end so the design, data model, and code-generation assumptions can be tested against reality. + +So the broader lesson is this: fleshing out the UI is not only about drawing more screens. It is about deciding which screens matter now, which editing interactions are intuitive, and how to sequence the work so the product can be validated before the whole plan gets too large. + +## Further Reading + +- [Scope and Basic UI Design](/courses/course-03-build-real-world-full-stack-mobile-apps-java/002-scope-and-basic-ui-design/) +- [Architecture of Mockup](/courses/course-03-build-real-world-full-stack-mobile-apps-java/004-architecture-of-mockup/) +- [How Do I Use Properties To Speed Development](/how-do-i/how-do-i-use-properties-to-speed-development/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/004-architecture-of-mockup.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/004-architecture-of-mockup.md index 8e16d1ba62..76499315df 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/004-architecture-of-mockup.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/004-architecture-of-mockup.md @@ -9,10 +9,24 @@ module_order: 3 lesson_order: 1 weight: 4 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Define the shared form structure that the app-maker mockup will build on." --- - > Module 3: Initial UI Mockup - {{< youtube vxsUf3gw1zg >}} + +Once the product direction is stable enough, the next question is architectural: what parts of the UI are common, and what parts belong to individual screens? This lesson starts answering that by defining the form hierarchy for the initial app-maker mockup. + +That hierarchy matters because the app maker already has a recognizable shell: a navigation structure, shared editing affordances, and a preview-oriented top-level experience. If every screen were built independently, that shell would quickly drift. A shared base form is the right tool for keeping navigation and common editing patterns consistent. + +The lesson also makes a pragmatic point about reuse that is easy to miss. Copying an existing model or structure is often the right first move if it gets you to a working baseline quickly. Generalization should come from repeated need, not from the fear of duplication on day one. That is especially true in builder-style products where the real shape of the domain is still being discovered. + +The result is a simple but useful structure: one shared navigation-oriented base, a small number of top-level forms that inherit from it, and one-off forms like dish editing that are allowed to stand on their own because their responsibilities are different. That split keeps the code honest about which UI concerns are shared and which are truly local. + +So the architectural lesson here is not complicated, but it is important. Establish a common shell for the parts of the product that behave alike, and do not force unrelated screens into that same abstraction just to look tidy. + +## Further Reading + +- [Base Navigation Form and Shape Effects](/courses/course-03-build-real-world-full-stack-mobile-apps-java/005-base-navigation-form-and-shape-effects/) +- [Developer Guide](/developer-guide/) +- [Layout Basics](/layout-basics/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/005-base-navigation-form-and-shape-effects.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/005-base-navigation-form-and-shape-effects.md index 150cae17cd..7b2049a113 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/005-base-navigation-form-and-shape-effects.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/005-base-navigation-form-and-shape-effects.md @@ -9,10 +9,24 @@ module_order: 3 lesson_order: 2 weight: 5 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Build the reusable navigation shell and the image treatments that define the app-maker’s visual identity." --- - > Module 3: Initial UI Mockup - {{< youtube xzwq4P9ogoU >}} + +This lesson is where the app-maker mockup starts becoming visually specific. The shared navigation shell is not just a place to hang menus. It is also where the product gets much of its identity: editable title areas, branded imagery, and the small visual treatments that make the preview feel like an actual product instead of a form editor. + +One of the strongest ideas here is the use of editable text fields styled to feel like normal title elements. That approach matches the product well. The app maker is about live customization, so turning parts of the preview itself into editable elements makes the interface more direct and more understandable. + +The rounded-logo discussion is also useful because it shows how shape work should be approached in Codename One. The important point is not the specific masking trick alone. It is the broader principle that visual effects need to be implemented in a way that stays portable and predictable across platforms. A trick that looks elegant but behaves differently from target to target is not actually helping the design. + +This lesson predates the stronger CSS-first emphasis that now makes sense for most styling work, but the underlying visual decisions still hold up. Use CSS and current theming practices for the bulk of the style system, and reserve lower-level image or mask work for the cases where the design truly needs it, such as logo shaping or image treatment that cannot be expressed cleanly through normal styling. + +So the navigation form in this lesson is important for two reasons. It gives the app maker a consistent structural shell, and it establishes the visual language that later editable screens will build on top of. + +## Further Reading + +- [Themeing](/themeing/) +- [Working With CSS](/courses/course-02-deep-dive-mobile-development-with-codename-one/001-working-with-css/) +- [Architecture of Mockup](/courses/course-03-build-real-world-full-stack-mobile-apps-java/004-architecture-of-mockup/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/006-dish-list-and-edit.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/006-dish-list-and-edit.md index 8e57400e5e..dad783ab8e 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/006-dish-list-and-edit.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/006-dish-list-and-edit.md @@ -9,10 +9,26 @@ module_order: 3 lesson_order: 3 weight: 6 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Build the core dish-management flow of the app maker and make editing feel immediate." --- - > Module 3: Initial UI Mockup - {{< youtube yv3WXt8o88k >}} + +The dish list is where the app maker stops feeling like a shell and starts feeling like a real editor. The user is no longer just changing a title or background. They are managing a collection of product content that will define the generated app. + +The most important decision in this lesson is the editing flow. Instead of presenting a separate “create dish” ceremony and then making the user confirm every step, the app immediately creates an entry and opens it for editing. That is a very mobile-friendly choice. It keeps momentum high and avoids forcing a cumbersome OK/cancel workflow where deletion is often the simpler and clearer form of undo. + +The grid-based presentation is also a good product choice because dishes are fundamentally visual. People think about menu items through images and headlines, not just through rows of text. The list screen should therefore behave more like a content board than a data table. + +On the editing side, the lesson continues the preview-first design language established earlier. The title is editable in place, the image remains a dominant part of the form, and the editing controls are arranged to support that rather than overwhelm it. The floating action button placement, background treatment, and bottom-positioned delete action all serve that same goal: keep the main content visible while still making the editing affordances clear. + +This is also a good example of where layout choices matter more than decorative polish. Wrapping controls correctly, avoiding awkward line breaks in editable title areas, and positioning actions relative to the title region are what make the form feel intentional. Once the structure is right, the styling has something solid to work with. + +So this lesson is really the first end-to-end content-management interaction in the app maker. It shows how to let users create, inspect, and refine one of the core artifacts of the generated app without turning the editing experience into a wall of forms. + +## Further Reading + +- [Base Navigation Form and Shape Effects](/courses/course-03-build-real-world-full-stack-mobile-apps-java/005-base-navigation-form-and-shape-effects/) +- [How Do I Create A List Of Items The Easy Way](/how-do-i/how-do-i-create-a-list-of-items-the-easy-way/) +- [Layout Basics](/layout-basics/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/007-introduction-architecture-and-authorization.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/007-introduction-architecture-and-authorization.md index 56e64dddb3..4d3cca40cc 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/007-introduction-architecture-and-authorization.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/007-introduction-architecture-and-authorization.md @@ -9,10 +9,26 @@ module_order: 4 lesson_order: 1 weight: 7 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Connect the app maker to the backend and define the authorization model for editing restaurant data." --- - > Module 4: App Maker Server - {{< youtube FcHVK9ObONg >}} + +Up to this point the app maker has mostly been a client-side mockup and workflow exercise. This module turns it into a real system by deciding how it talks to the server and how the server decides who is allowed to change what. + +The first architectural choice is pragmatic and sensible: reuse the restaurant server instead of inventing a second independent backend immediately. That may not be the forever architecture, but it is the right depth-first move because it lets the product validate its end-to-end flow before spending time on system separation that may not yet be justified. + +Authorization is the central problem here. Public restaurant data can be fetched by ordinary identifiers, but editable restaurant state needs a different trust boundary. The lesson solves that by introducing a secret key used only for write operations. That is a strong fit for this kind of product because it avoids the ceremony of a full user-password system while still creating a private capability that the public app should never expose. + +This is a good example of designing security around the actual product, not around abstract purity. The restaurant app and the app maker do not need identical access models. One is a public consumer-facing app. The other is an editing tool. They should not be using the same credential path just because they operate on the same data. + +The lesson also hints at an important full-stack pattern: some operations are naturally asynchronous. A build request, for example, may take long enough that the server should queue the work and report the result later rather than pretending it is an ordinary fast request. That is the right instinct, because builder-style products often have a split between quick CRUD operations and longer-running generation tasks. + +So the broader takeaway is that once the app maker becomes real, the server is no longer just a storage endpoint. It becomes the place where editing authority, asynchronous work, and product boundaries are defined. + +## Further Reading + +- [Server](/courses/course-03-build-real-world-full-stack-mobile-apps-java/001-server/) +- [REST API Design](/courses/course-03-build-real-world-full-stack-mobile-apps-java/008-rest-api-design/) +- [Security Basics and Certificate Pinning](/courses/course-02-deep-dive-mobile-development-with-codename-one/028-security-basics-and-certificate-pinning/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/008-rest-api-design.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/008-rest-api-design.md index eb780ea2e6..93d6af58f8 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/008-rest-api-design.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/008-rest-api-design.md @@ -9,10 +9,24 @@ module_order: 4 lesson_order: 2 weight: 8 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Design the app-maker API around the actual workflow instead of forcing everything through one giant request." --- - > Module 4: App Maker Server - {{< youtube L7ulPCUxIsw >}} + +API design becomes much easier once you stop asking what would be theoretically elegant and start asking what the client actually needs to do. That is the real value of this lesson. + +The tempting design for an app maker would be one giant “submit everything and build” request. It sounds simple on paper, but it does not fit the product well. The user is editing incrementally, uploading assets separately, previewing changes, and eventually asking for a build. Those are different operations with different reliability and security concerns, so they should not be collapsed into one oversized endpoint. + +The lesson's incremental API design is stronger because it matches the workflow. Restaurant updates are separate from dish updates. File upload is separate from structured JSON changes. Build triggering is separate from both of those because it starts a distinct asynchronous process. That separation makes the system easier to reason about, easier to retry, and easier to secure. + +The file-download discussion is especially valuable because it highlights a type of mistake that often sneaks into “internal” APIs. The moment you expose file retrieval, you are no longer just returning an object from a database. You are handling a path into part of the server's filesystem boundary. That demands validation even if the API feels private or product-specific. + +This lesson also reinforces a broader design rule: REST is not about worshipping nouns or HTTP verbs in the abstract. It is about shaping the server surface so that the client can perform its real tasks safely and cleanly. When the operations are different, the endpoints should reflect that. + +## Further Reading + +- [Introduction, Architecture and Authorization](/courses/course-03-build-real-world-full-stack-mobile-apps-java/007-introduction-architecture-and-authorization/) +- [Connecting to a Web Service](/courses/course-02-deep-dive-mobile-development-with-codename-one/003-connecting-to-a-web-service/) +- [Communicating from the Client](/courses/course-03-build-real-world-full-stack-mobile-apps-java/009-communicating-from-the-client/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/009-communicating-from-the-client.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/009-communicating-from-the-client.md index fd4478477e..ae1ab8f8a2 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/009-communicating-from-the-client.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/009-communicating-from-the-client.md @@ -9,10 +9,24 @@ module_order: 4 lesson_order: 3 weight: 9 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Make the app maker feel responsive while it quietly establishes identity and synchronizes with the server." --- - > Module 4: App Maker Server - {{< youtube VnYaxvVn6OA >}} + +Once the server API exists, the client has to use it without making the product feel like a network admin console. That is the theme of this lesson. + +The first important client-side job is establishing or retrieving the secret associated with the current editing device or session. That is an infrastructure step, but the user should not experience it as a setup ritual. The app should just do the work and continue. + +That is why the asynchronous design here matters. The client asks for what it needs, keeps the UI moving, and treats network coordination as background product infrastructure rather than as a constant source of prompts and ceremony. This is especially important in builder-style tools, where too much visible networking friction quickly makes the product feel fragile. + +The lesson's philosophy is worth keeping: do not overexpose network reliability concerns to the user if the application can reasonably recover, retry, or defer those concerns internally. That does not mean ignoring errors. It means designing the flow so the product behaves like a cohesive tool instead of narrating every transport detail back to the user. + +So the key takeaway here is not a specific request helper. It is the product stance: client/server coordination should support the editing flow quietly, not dominate it. + +## Further Reading + +- [Connecting to a Web Service](/courses/course-02-deep-dive-mobile-development-with-codename-one/003-connecting-to-a-web-service/) +- [REST API Design](/courses/course-03-build-real-world-full-stack-mobile-apps-java/008-rest-api-design/) +- [Communicating with the Server](/courses/course-02-deep-dive-mobile-development-with-codename-one/025-communicating-with-the-server/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/010-sqlite-abstraction-with-object-relational-mapping.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/010-sqlite-abstraction-with-object-relational-mapping.md index 7627106a5a..aab2b21d99 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/010-sqlite-abstraction-with-object-relational-mapping.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/010-sqlite-abstraction-with-object-relational-mapping.md @@ -9,10 +9,24 @@ module_order: 5 lesson_order: 1 weight: 10 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Add local persistence through a small application-specific abstraction instead of scattering SQL through the UI." --- - > Module 5: SQLite and ORM Binding - {{< youtube Qw7moLv-dE4 >}} + +As soon as the app maker starts feeling real, local persistence becomes valuable. The point is not that SQLite is always the perfect storage choice. The point is that instant startup, local continuity, and reduced server dependency make the editing experience much better. + +The lesson handles this well by refusing to let SQL details leak through the whole application. Instead of scattering persistence code across forms, it introduces an application-specific storage abstraction. That is the right move regardless of the underlying technology because it keeps the rest of the app working in domain terms rather than persistence terms. + +The ORM-style mapping used here is also pragmatic. It reduces boilerplate enough that local persistence becomes approachable instead of feeling like a second product hidden inside the first. The exact API details have evolved since the original lesson, but the architectural point remains sound: let a higher-level mapping layer handle repetitive SQLite concerns where it genuinely reduces friction. + +The lesson is also refreshingly honest about why this local database exists. It is not there because local state is the ultimate source of truth. It is there because performance and responsiveness matter. That is a healthy way to reason about client-side persistence in a full-stack app. If the server is authoritative but the UI needs to feel fast and resilient, some duplication is often worth it. + +So the main lesson here is to persist locally on purpose, behind a small abstraction, and for a reason the product can justify. + +## Further Reading + +- [Communicating with the Server](/courses/course-02-deep-dive-mobile-development-with-codename-one/025-communicating-with-the-server/) +- [Integrating SQLite into the Code](/courses/course-03-build-real-world-full-stack-mobile-apps-java/011-integrating-sqlite-into-the-code/) +- [How Do I Use Storage, File System, SQL](/how-do-i/how-do-i-use-storage-file-system-sql/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/011-integrating-sqlite-into-the-code.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/011-integrating-sqlite-into-the-code.md index 2e28cf824b..b3d1d4e259 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/011-integrating-sqlite-into-the-code.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/011-integrating-sqlite-into-the-code.md @@ -9,10 +9,24 @@ module_order: 5 lesson_order: 2 weight: 11 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Wire local persistence into the editor flow and keep the UI loosely coupled to the underlying data model." --- - > Module 5: SQLite and ORM Binding - {{< youtube vAiTW4Y8QuA >}} + +Adding local persistence is only half the job. The bigger question is how the rest of the application reacts to that persistence without turning every screen into a tightly coupled database client. + +This lesson moves in the right direction by letting the model and the storage abstraction drive updates while the UI stays focused on presentation. Property listeners and deletion events are doing the real work here. They allow the forms to respond to meaningful changes in the data model instead of polling or manually synchronizing every visible element. + +The delete flow is also a good product lesson. Mobile interfaces often work better when the app performs the obvious action and offers undo than when it stops to ask for permission on every destructive step. That pattern only works, though, if the underlying model and UI are structured well enough to make undo or re-addition straightforward. This lesson gets that balance right. + +The broader idea is that persistence should not make the app feel heavier. Users should not experience "now we are saving to SQLite" as a separate phase. The storage integration should simply make edits survive, lists update, and property-driven UI stay in sync. + +So the integration lesson is really about coupling and user experience at the same time: connect local persistence deeply enough that the app benefits from it, but not so deeply that every screen becomes persistence-aware in all the wrong ways. + +## Further Reading + +- [SQLite Abstraction with Object Relational Mapping](/courses/course-03-build-real-world-full-stack-mobile-apps-java/010-sqlite-abstraction-with-object-relational-mapping/) +- [How Do I Use Properties To Speed Development](/how-do-i/how-do-i-use-properties-to-speed-development/) +- [How Do I Use Storage, File System, SQL](/how-do-i/how-do-i-use-storage-file-system-sql/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/012-details-categories-and-validation.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/012-details-categories-and-validation.md index 29f7315bdf..8f235d6b4d 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/012-details-categories-and-validation.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/012-details-categories-and-validation.md @@ -9,10 +9,24 @@ module_order: 6 lesson_order: 1 weight: 12 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Fill in key product gaps by simplifying category management and validating details at the point of edit." --- - > Module 6: Miscellaneous Features - {{< youtube VCNFKMeud-w >}} + +Many products reach a point where the remaining work is not one dramatic feature but a collection of missing pieces that quietly determine whether the app feels complete. This lesson is about that stage. + +The category flow is a strong example of solving a product problem by reducing UI rather than adding more of it. Instead of introducing a whole separate category-management experience, the app lets categories emerge through dish editing with autocomplete support and cleanup logic behind the scenes. That is a simpler model for both the code and the user. + +Validation is the other major theme here. Category presence, numeric pricing, and basic detail integrity are not glamorous features, but they are part of making the builder trustworthy. The lesson is right to put validation near the editing experience instead of treating it as a distant backend-only concern. + +The details form also shows a good instinct about complexity management. Whenever a setting leads into a more focused editing experience, navigating to a dedicated screen is often clearer than cramming everything into a single overloaded form. That is especially true in a builder product where users can easily get lost in a wall of unrelated controls. + +So the common thread in this lesson is simplification through structure. Remove unnecessary management screens where the workflow can be inferred, validate where the user is already working, and split details into focused editors instead of building one giant control panel. + +## Further Reading + +- [Fleshing Out the UI Design](/courses/course-03-build-real-world-full-stack-mobile-apps-java/003-fleshing-out-the-ui-design/) +- [Billing and Global Server](/courses/course-03-build-real-world-full-stack-mobile-apps-java/013-billing-and-global-server/) +- [How Do I Use Properties To Speed Development](/how-do-i/how-do-i-use-properties-to-speed-development/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/013-billing-and-global-server.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/013-billing-and-global-server.md index 1844a42d4e..a1ff848911 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/013-billing-and-global-server.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/013-billing-and-global-server.md @@ -9,10 +9,24 @@ module_order: 6 lesson_order: 2 weight: 13 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Finish the billing flow and decide how app settings should be represented locally and on the server." --- - > Module 6: Miscellaneous Features - {{< youtube Q0fkApAGMZA >}} + +Billing screens are often deceptively simple. The form may look like a set of ordinary fields, but the layout, data mapping, and server contract beneath it are doing more work than the visuals suggest. + +This lesson handles that well by separating the visible editing experience from the way the data is ultimately packaged and sent. The billing form itself is mostly straightforward UI. The more interesting design choice is how the builder's local application state is combined into the server-facing representation. + +That merging step is a useful reminder that client-side models and server-side payloads do not need to map one-to-one. Sometimes the client organizes data for editing convenience, while the server expects a flatter or differently structured contract. That is normal. The important thing is to keep the translation explicit instead of letting it happen accidentally in random places. + +The lesson also points to a broader truth about rapidly evolving builder products: persistence strategies may start pragmatically. Preferences, lightweight local state, and partial mappings can all be acceptable as long as they serve the workflow and are honest about their limits. Perfection in local storage architecture is not the first milestone. A working end-to-end billing and settings flow is. + +So this section is less about billing forms in isolation and more about how builder-state becomes server-state cleanly enough that the product can keep moving. + +## Further Reading + +- [Introduction, Architecture and Authorization](/courses/course-03-build-real-world-full-stack-mobile-apps-java/007-introduction-architecture-and-authorization/) +- [REST API Design](/courses/course-03-build-real-world-full-stack-mobile-apps-java/008-rest-api-design/) +- [How Do I Use Properties To Speed Development](/how-do-i/how-do-i-use-properties-to-speed-development/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/014-sidemenu-and-preview.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/014-sidemenu-and-preview.md index 7fa5dc8a30..439f819a67 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/014-sidemenu-and-preview.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/014-sidemenu-and-preview.md @@ -9,10 +9,24 @@ module_order: 6 lesson_order: 3 weight: 14 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Polish the app-maker shell and make preview a first-class part of the editing experience." --- - > Module 6: Miscellaneous Features - {{< youtube jhPep9vHv_U >}} + +Preview is one of the most important features in a builder product because it turns editing from abstract configuration into immediate feedback. This lesson finally gives that idea the implementation attention it deserves. + +The side menu matters here because it helps the builder feel like a complete tool rather than a pile of forms. Small visual decisions such as padding, selection styling, and header content do more than improve aesthetics. They make the product feel stable and intentional while users move between editing contexts. + +The preview path is even more important. The clever part is not that a preview exists. It is that the system reuses the actual restaurant app code instead of inventing a fake representation of what the generated app might look like. That is a strong product choice because the preview is more trustworthy when it is built from the same implementation path as the real result. + +The lesson openly takes the pragmatic route by copying the restaurant app sources instead of prematurely generalizing everything into a shared framework. That is consistent with the rest of the course and still the right instinct here. If the preview proves valuable and the duplication becomes painful, that is the right moment to refactor. Not before. + +So the key lesson is that preview should be treated as a product feature, not as a nice extra. The more directly it reflects the actual generated app, the more useful it becomes to the user and to the developer. + +## Further Reading + +- [Scope and Basic UI Design](/courses/course-03-build-real-world-full-stack-mobile-apps-java/002-scope-and-basic-ui-design/) +- [Themeing](/themeing/) +- [How Do I Create Gorgeous SideMenu](/how-do-i/how-do-i-create-gorgeous-sidemenu/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/015-about-forms.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/015-about-forms.md index c0e39bf075..9cf5e46b6c 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/015-about-forms.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/015-about-forms.md @@ -9,10 +9,26 @@ module_order: 6 lesson_order: 4 weight: 15 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Handle about-page content in both the builder and the generated app without overcomplicating the workflow." --- - > Module 6: Miscellaneous Features - {{< youtube a7MF3oSCASE >}} + +About pages are easy to underestimate because they are rarely the headline feature of an app. In practice they matter a lot. They give the product a place for identity, contact information, explanatory content, and other material that should exist without cluttering the main workflow. + +This lesson is dealing with two related but different problems. The builder itself needs an about experience, and the generated restaurant app needs a configurable about destination of its own. Those are not the same concern, and treating them separately is the right design move. + +For the builder's own about page, HTML is a reasonable tool because the content is mostly informational and static. That is one of the few areas where a browser-based presentation can be more convenient than building everything out of native-looking components. The older lesson says this directly, and that judgment still makes sense. + +For the generated app, the choice to rely on a URL instead of embedding arbitrary HTML is also pragmatic. It keeps the app-maker workflow simpler and assumes something that is usually true in the real world: a restaurant likely already has a site or a page that can serve as the deeper “about” destination. That is often better than turning the builder into a full CMS just to support one screen. + +The validation portion of the lesson is also important. If the builder is going to save and preview an about destination, it should not accept obviously broken values and hope the user notices later. Lightweight validation at the point of edit is part of making the builder feel trustworthy. + +So the real lesson here is not “how to show HTML.” It is how to give both the editor and the generated app an about experience that fits their purpose without dragging the product into unnecessary complexity. + +## Further Reading + +- [Sidemenu and Preview](/Users/shai/dev/cn1/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/014-sidemenu-and-preview.md) +- [Details, Categories and Validation](/Users/shai/dev/cn1/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/012-details-categories-and-validation.md) +- [Developer Guide](/Users/shai/dev/cn1/docs/website/content/developer-guide.md) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/016-style-customization-1-introduction-and-basics.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/016-style-customization-1-introduction-and-basics.md index b766cd9214..decccfeec9 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/016-style-customization-1-introduction-and-basics.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/016-style-customization-1-introduction-and-basics.md @@ -9,10 +9,26 @@ module_order: 7 lesson_order: 1 weight: 16 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Introduce the style editor and define the limits that keep customization powerful without becoming chaotic." --- - > Module 7: Style Form - {{< youtube p05yMCleSLo >}} + +Style customization is where the app maker stops being a form editor and starts behaving like a product-design tool. That shift is important, because once users can see and alter the look of the generated app directly, the builder becomes much more compelling. + +The first thing this lesson gets right is restraint. Full visual freedom sounds attractive, but in practice it can produce a confusing editor and a generated app that looks broken. The builder deliberately limits what can be customized, where it can be customized, and how deep the options go. That is a strength, not a weakness. + +The core interaction is simple and still strong: show the actual app UI, let the user tap meaningful parts of it, and make those parts customizable in context. That beats a detached theme-control panel because users do not need to translate abstract style names into visible outcomes. + +The lesson also makes an important architectural distinction between temporary editing state and committed styling changes. Users need to be able to explore, preview, and cancel without immediately persisting every choice. That is fundamental for builder UX. A customization workflow that commits too early feels risky and brittle. + +From a modern Codename One perspective, the main idea still holds even though newer projects generally center styling around CSS rather than older theme workflows. In a builder product, you still need an application-level representation of style choices and a consistent way to layer those choices onto the generated app. The mechanics may evolve, but that product need does not. + +So the purpose of the style form is not just to expose colors and fonts. It is to define a safe, understandable boundary around customization so users feel they are shaping the app, not breaking it. + +## Further Reading + +- [Themeing](/Users/shai/dev/cn1/docs/website/content/themeing.md) +- [Working With CSS](/Users/shai/dev/cn1/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/001-working-with-css.md) +- [Style Customization 2 - The Customization Popup](/Users/shai/dev/cn1/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/017-style-customization-2-the-customization-popup.md) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/017-style-customization-2-the-customization-popup.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/017-style-customization-2-the-customization-popup.md index f78112a318..ccc34f7582 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/017-style-customization-2-the-customization-popup.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/017-style-customization-2-the-customization-popup.md @@ -9,10 +9,24 @@ module_order: 7 lesson_order: 2 weight: 17 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Use contextual popups to expose only the style options that make sense for the selected UI element." --- - > Module 7: Style Form - {{< youtube uYCCbE70kE0 >}} + +Once the style form exists, the next design problem is deciding how customization should be presented. The popup approach in this lesson is a good answer because it keeps the editing UI close to the thing being edited and limits the choices to what actually makes sense for that element. + +That contextual behavior matters. A builder should not offer font controls for elements that have no meaningful text. It should not pretend background customization is useful where borders or other constraints make it irrelevant. Good customization tools are as much about saying “not here” as they are about exposing more controls. + +The lesson’s focus on pointer release instead of pointer press is also a subtle but good reminder that editing tools still need to respect normal UI behavior. A customization feature that feels glitchy or race-prone because it fires too early undermines the trust users need in the editor. + +The popup itself is a strong interaction pattern because it keeps the mental model local: tap a thing, get the relevant choices for that thing, apply a change, and see the result right there. That is far easier to understand than jumping out to a distant control panel and hoping the user remembers what they were editing. + +So this lesson is really about matching the editing surface to the structure of the UI. Contextual style editing works well when the options are filtered intelligently and the feedback is immediate. + +## Further Reading + +- [Style Customization 1 - Introduction and Basics](/Users/shai/dev/cn1/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/016-style-customization-1-introduction-and-basics.md) +- [Style Customization 3 - Font and Color Pickers](/Users/shai/dev/cn1/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/018-style-customization-3-font-and-color-pickers.md) +- [Developer Guide](/Users/shai/dev/cn1/docs/website/content/developer-guide.md) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/018-style-customization-3-font-and-color-pickers.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/018-style-customization-3-font-and-color-pickers.md index 0d78379293..dd18a4fab0 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/018-style-customization-3-font-and-color-pickers.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/018-style-customization-3-font-and-color-pickers.md @@ -9,10 +9,24 @@ module_order: 7 lesson_order: 3 weight: 18 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Build practical pickers for color and font choices without turning customization into an advanced design tool." --- - > Module 7: Style Form - {{< youtube ZzSCPcnFkxs >}} + +Color and font controls are where a style editor can become either empowering or exhausting. This lesson stays on the right side of that line by keeping the tools practical rather than pretending to be a full professional design suite. + +The color picker works because it balances directness with precision. Sliders give users a tactile way to explore, while the hex value keeps the result grounded in something explicit and reusable. That combination is usually enough for a product builder. Most users do not need a huge color science interface. They need a reliable way to land on a good color and see it applied immediately. + +The font picker is harder because typography is harder. Fonts carry meaning, hierarchy, tone, and readability concerns all at once, and they are not equally well supported across every target environment. The lesson’s constrained approach is therefore the right one. Offer a smaller, portable subset of choices and make sure the preview reflects the real result as closely as possible. + +One of the most useful implementation ideas in this lesson is the care taken to avoid accidental feedback loops between controls. Any time you have multiple inputs representing the same value, the UI needs to update cohesively without endlessly retriggering itself. That sounds mechanical, but it is part of making customizer tools feel solid. + +So the real product lesson here is that builder controls should be expressive enough to be useful and narrow enough to stay trustworthy. A constrained but dependable color or font picker is better than a theoretically unlimited one that behaves inconsistently across the generated apps. + +## Further Reading + +- [Style Customization 2 - The Customization Popup](/Users/shai/dev/cn1/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/017-style-customization-2-the-customization-popup.md) +- [Themeing](/Users/shai/dev/cn1/docs/website/content/themeing.md) +- [Working With CSS](/Users/shai/dev/cn1/docs/website/content/courses/course-02-deep-dive-mobile-development-with-codename-one/001-working-with-css.md) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/019-style-customization-4-saving-style-settings.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/019-style-customization-4-saving-style-settings.md index 9f798d538d..fd0ecdbd19 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/019-style-customization-4-saving-style-settings.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/019-style-customization-4-saving-style-settings.md @@ -9,10 +9,24 @@ module_order: 7 lesson_order: 4 weight: 19 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Persist style choices as structured data so customization survives beyond the current editing session." --- - > Module 7: Style Form - {{< youtube Wiy2goFwfQA >}} + +Customization is only useful if it survives. Once users start shaping the look of a generated app, those choices need to exist as real data, not just as temporary UI state. + +That is what this lesson is about. Style settings become first-class business data: something that can be stored locally, synchronized with the server, and reapplied later. That is the right abstraction for a builder product because appearance is part of the product definition, not just a visual side effect of the current screen. + +The important architectural move here is turning style changes into structured objects rather than scattered theme tweaks. Once styles are represented as data, the rest of the system can reason about them the same way it reasons about titles, dishes, prices, or other editable entities. + +This also connects back to the earlier product decisions about temporary versus committed edits. A user can experiment locally, but once they confirm the changes, those changes need to be durable. That durability is what makes the builder trustworthy across sessions and eventually across devices or server sync. + +So the final lesson in this style-customization block is straightforward but important: styling in a builder is not just presentation logic. It is part of the editable domain model. + +## Further Reading + +- [Style Customization 1 - Introduction and Basics](/Users/shai/dev/cn1/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/016-style-customization-1-introduction-and-basics.md) +- [SQLite Abstraction with Object Relational Mapping](/Users/shai/dev/cn1/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/010-sqlite-abstraction-with-object-relational-mapping.md) +- [Billing and Global Server](/Users/shai/dev/cn1/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/013-billing-and-global-server.md) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/020-push-1-initial-registration-process.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/020-push-1-initial-registration-process.md index 627cbdba5a..2e47f58541 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/020-push-1-initial-registration-process.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/020-push-1-initial-registration-process.md @@ -9,10 +9,31 @@ module_order: 8 lesson_order: 1 weight: 20 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Set up the Android and iOS pieces required before a Codename One app can receive push notifications." --- - > Module 8: Push and In-App Purchase - {{< youtube SBqYZSdIdec >}} + +Push usually feels harder than it really is because the setup is split across several systems. You need Android-side configuration, iOS-side configuration, and project settings that tell Codename One how those pieces fit together. Once that groundwork is done, the rest of the push flow is much easier to understand. + +On Android, the core job is to register the app with Google's push infrastructure and copy the sender information back into the project. The video uses the old Google Cloud Messaging terminology. Today the equivalent work is done through Firebase Cloud Messaging, but the role of the value is the same: Android needs to know which sender the application belongs to so it can receive notifications correctly. + +In Codename One, that sender information ends up in the project configuration as a build hint. The video shows the legacy settings and plugin-based workflow, which is now out of date. In a modern Maven-based project you still provide the same information, but you manage the project through Maven and the current Codename One configuration flow instead of relying on the old IDE plugin setup. + +The Android step is the easier half. iOS is where push becomes certificate-driven. Push on iOS does not just reuse your normal signing setup. It requires push-enabled certificates and provisioning profiles that match the app. That is why the original lesson spends so much time on the certificate wizard. + +One warning from the video is still very important: if you already have working certificates, do not revoke them casually just because a wizard offers that option. Replacing or revoking an existing certificate can break other apps that depend on it. If this is your first push-enabled setup, generate what you need. If you already have a valid certificate chain, reference and reuse the existing material instead of resetting it blindly. + +iOS also separates development and production push environments. A debug build installed directly on a device is not the same thing as a production build distributed through the App Store, and the push credentials need to match that distinction. If push works in one environment but not the other, this mismatch is one of the first things to check. + +Once the iOS setup is complete, Codename One gives you the cloud certificate URLs and related values that the server side will later need when it sends notifications. Keep those values somewhere safe. They are not part of client registration, but they are essential later when the app server needs to deliver a push to a device. + +At this stage, the goal is not to send a notification yet. The goal is to leave the project correctly identified on Android, correctly provisioned on iOS, and ready for the client and server code in the next lessons. + +## Further Reading + +- [Push 2 - Client Side Code](/courses/course-03-build-real-world-full-stack-mobile-apps-java/021-push-2-client-side-code/) +- [Push 3 - The Server Side and Build Logic](/courses/course-03-build-real-world-full-stack-mobile-apps-java/022-push-3-the-server-side-and-build-logic/) +- [Push Notifications](/push-notifications/) +- [Build Hints](/build-hints/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/021-push-2-client-side-code.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/021-push-2-client-side-code.md index 2d92c865ab..47241a0c73 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/021-push-2-client-side-code.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/021-push-2-client-side-code.md @@ -9,10 +9,33 @@ module_order: 8 lesson_order: 2 weight: 21 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Register the app for push, store the push key, and route incoming notifications into normal application logic." --- - > Module 8: Push and In-App Purchase - {{< youtube BwWhFcHc6LM >}} + +Once the platform setup is done, the client side of push becomes a lifecycle problem. The app needs to register when it starts, store the push key when registration succeeds, and react sensibly when a message arrives. + +The first thing to get right is where the callback lives. In Codename One, push callbacks belong in the main application class, the one that owns `init()` and `start()`. That is not just convention. Push is tied to the application lifecycle, so the framework expects the callback implementation to live in the object that represents that lifecycle. + +Registration should happen every time the app runs. The video mentions that `registerPush()` had changed and no longer needed the old extra arguments, and that is still the modern direction. The registration call itself is simple. The useful information arrives later through the callback flow. + +When registration succeeds, the app can obtain the actual push key and send it to the server. That is the value the server needs if it wants to target this device later. One of the easiest mistakes here is confusing the native device identifier with the push token. They are not interchangeable. If the server is going to send a push notification, it needs the push key. + +Receiving a push is a little subtler than many developers expect. Push is not a guaranteed background RPC channel. If the app is running, the callback can handle the message directly. If the app is not running, behavior depends on the platform and on the push type. That is why this lesson routes the incoming notification into a shared `onBuildResult()` path instead of putting all the business logic inside the push callback itself. + +That is still the right design. Push should act as a signal that new work is available. The actual business logic, such as downloading a built artifact or refreshing state, should live in ordinary application code that can also be triggered by fallback transports. + +The different push types matter mostly because they influence how much information reaches the app and when. A visible push is useful when the user needs to see a notification. An invisible push can sometimes be used as a signal, but it is not a good foundation for general networking logic. The mixed approach shown in the lesson is the pragmatic one: send a human-readable notification and include a machine-readable payload the app can act on. + +In the original code that payload is a URL. That is perfectly reasonable. It could just as well be JSON or another structured message format. What matters is that the app can distinguish between text that is just meant for the user and data that should trigger behavior. + +The lesson also touches on registration failures, and that part is worth updating. A failed registration should usually be logged and reflected in app state, not shown as an alarming early-launch toast. Some environments will never produce a perfectly clean failure signal, which is exactly why the rest of the module introduces HTTP and WebSocket fallbacks. A production app should keep working even when push is unavailable. + +## Further Reading + +- [Push 1 - Initial Registration Process](/courses/course-03-build-real-world-full-stack-mobile-apps-java/020-push-1-initial-registration-process/) +- [Push 3 - The Server Side and Build Logic](/courses/course-03-build-real-world-full-stack-mobile-apps-java/022-push-3-the-server-side-and-build-logic/) +- [Push WebSockets Fallback](/courses/course-03-build-real-world-full-stack-mobile-apps-java/024-push-websockets-fallback/) +- [Push Notifications](/push-notifications/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/022-push-3-the-server-side-and-build-logic.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/022-push-3-the-server-side-and-build-logic.md index d329687edd..0543f6dc0d 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/022-push-3-the-server-side-and-build-logic.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/022-push-3-the-server-side-and-build-logic.md @@ -9,10 +9,31 @@ module_order: 8 lesson_order: 3 weight: 22 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Send push notifications from the server when the build pipeline finishes and return a usable payload to the client." --- - > Module 8: Push and In-App Purchase - {{< youtube qaYExTzcCVY >}} + +On the server side, push is part of the result-delivery pipeline. The build finishes, the server decides what happened, and then it sends a short notification to the device so the app can react. + +That framing matters because this lesson is not really about building a generic push utility. It is about closing the loop in a product where the server builds an app on behalf of the user and needs a clean way to report the result back to the device. + +The first practical concern is credential management. The server needs Codename One push credentials, Android server-side credentials, and iOS push certificate information. The original lesson stores these values in a local properties file instead of hard-coding them into source. That is still the right approach. Secrets and environment-specific configuration should live outside application code so they can be rotated and deployed safely. + +The second concern is the build mode itself. The lesson uses a synchronous automated build flow so the server can wait for the result, inspect the generated artifacts, and decide what message to send. That works well for this kind of product because a build request is not fire-and-forget. The user expects a concrete answer: success, failure, and where the result can be downloaded. + +Once the build is complete, the push itself is conceptually simple. The server posts to the Codename One push service with the device key and the credentials needed for the target platforms. In this app the message includes both a visible notification for the user and a machine-readable payload for the client code. That makes the notification useful immediately while still giving the app enough information to continue the flow automatically. + +That mixed-message approach is one of the better ideas in this module. Push is used for what it is good at: signaling a state change and drawing attention to something that is ready. The real work, such as downloading or opening the generated result, still happens in normal application logic where it can be validated and retried. + +The iOS warning from the video also deserves to survive in updated form. Development and production push environments are different, and they do not share credentials. A debug build installed directly on a device is not the same thing as a production build distributed through the App Store. If push works in one environment and fails in the other, mismatched certificates or environment configuration are a likely cause. + +Finally, log the push response on the server. Even in a small project, delivery failures are much easier to diagnose when the server records what it attempted to send, which credentials were used, and what response came back from the push service. + +## Further Reading + +- [Push 1 - Initial Registration Process](/courses/course-03-build-real-world-full-stack-mobile-apps-java/020-push-1-initial-registration-process/) +- [Push 2 - Client Side Code](/courses/course-03-build-real-world-full-stack-mobile-apps-java/021-push-2-client-side-code/) +- [Push Notifications](/push-notifications/) +- [In-App Purchase](/courses/course-03-build-real-world-full-stack-mobile-apps-java/025-in-app-purchase/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/023-push-http-fallback.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/023-push-http-fallback.md index 6b933ff590..00da4646f2 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/023-push-http-fallback.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/023-push-http-fallback.md @@ -9,10 +9,29 @@ module_order: 8 lesson_order: 4 weight: 23 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Keep the app working when push is unavailable by falling back to a simple HTTP polling path." --- - > Module 8: Push and In-App Purchase - {{< youtube VDyltmk_Hu8 >}} + +Push is useful, but it should never be the only transport a feature depends on. Users can disable it, devices can fail to register, and platform delivery is not reliable enough to be the single mechanism that keeps an app functional. + +That is why this lesson adds an HTTP fallback. It is not elegant, but it is dependable and easy to understand. The server stores the latest build result somewhere the client can fetch it later, and the client polls only when push is unavailable. + +In this app the existing restaurant entity is used as a convenient place to store the most recent build result. That is a pragmatic choice. The app already has a server-side record for the current builder state, so attaching the latest build status to that record avoids introducing extra infrastructure just to support a fallback notification path. + +On the client side, the logic is simple and that simplicity is a strength. If push registration succeeded and the app has a usable push key, push remains the preferred notification mechanism. If not, the client periodically sends a normal HTTP request until the server reports a changed result. + +That is the right priority order. Push gives a better user experience, but HTTP polling is easier to reason about and easier to debug. The important thing is that both paths eventually call into the same result-handling code. The transport changes, but the meaning of the result does not. + +This is also a good example of a broader rule in mobile development: if an event-driven channel is optional, always keep a plain request-response path available. It may be slower and less efficient, but it prevents an entire feature from failing just because one delivery mechanism is missing. + +The video correctly presents polling as a fallback rather than the ideal design. Polling creates unnecessary traffic and introduces delay because the client only learns about changes on the next timer tick. It is acceptable as a safety net, but not as the best long-term solution. The next lesson shows how WebSockets provide a cleaner event-driven fallback when you want real-time updates without relying on push. + +## Further Reading + +- [Push 2 - Client Side Code](/courses/course-03-build-real-world-full-stack-mobile-apps-java/021-push-2-client-side-code/) +- [Push WebSockets Fallback](/courses/course-03-build-real-world-full-stack-mobile-apps-java/024-push-websockets-fallback/) +- [Connecting to a Web Service](/courses/course-02-deep-dive-mobile-development-with-codename-one/003-connecting-to-a-web-service/) +- [How Do I Use HTTP, Sockets, Webservices & WebSockets](/how-do-i/how-do-i-use-http-sockets-webservices-websockets/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/024-push-websockets-fallback.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/024-push-websockets-fallback.md index f03934436f..b6168aaad8 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/024-push-websockets-fallback.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/024-push-websockets-fallback.md @@ -9,10 +9,33 @@ module_order: 8 lesson_order: 5 weight: 24 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Use WebSockets as a more responsive fallback channel when push is unavailable." --- - > Module 8: Push and In-App Purchase - {{< youtube FSek5IOQ0IE >}} + +If HTTP polling is the safety net, WebSockets are the more polished fallback. They preserve the event-driven feel of push without depending on the mobile push infrastructure, which makes them a good fit for workflows like build completion, job status changes, and other lightweight live updates. + +The reason WebSockets work well here is simple. After the connection is established, the server can push short messages back to the client immediately. That avoids the repeated request loop of polling and gives the app a much more direct way to learn that something changed. + +The comparison in the video between HTTP, raw sockets, and WebSockets is still useful. HTTP is excellent for normal request-response operations and large payloads, but it is wasteful when the client keeps asking whether anything has changed. Raw sockets are powerful but awkward to operate through common web infrastructure. WebSockets sit in the middle: they begin as ordinary web traffic, then remain open so client and server can exchange small messages efficiently. + +On the client side, the structure is straightforward. Open the WebSocket connection, wait until the connection is actually open, send whatever identifying information the server needs, and then react to incoming messages. In this app that identifying information is the restaurant secret, which allows the server to bind the socket to the correct builder session. + +That sequencing matters. One easy mistake with asynchronous communication is assuming the socket is ready immediately after construction. It is not. The handshake has to complete first, which is why the first outgoing message belongs in the `onOpen` callback instead of immediately after creating the object. + +Incoming messages can then be routed into the same result-processing code used by push and HTTP fallback. That reuse is the strongest part of the design. WebSockets change how the notification arrives, but they do not change what the notification means. + +The server side is where WebSockets become architectural rather than just mechanical. The app needs a handler for the WebSocket endpoint, a way to associate each open connection with the right logical user or restaurant, and a way for the rest of the server code to find that connection when an asynchronous event finishes. The lesson uses a shared map keyed by the restaurant secret to do exactly that. + +The implementation shown in the video is intentionally pragmatic and not especially scalable. That is fine for understanding the pattern. The idea to keep is not the exact static-field implementation. The important part is the binding step: when the client opens the socket, the server records which logical entity that connection belongs to so later server-side work can publish a message to the right place. + +The video also shows WebSocket support being installed through the old settings UI as a CN1Lib. That workflow is outdated now. In a modern Maven-based Codename One project, dependency management should live in Maven rather than in the legacy extension flow. + +## Further Reading + +- [Push Http Fallback](/courses/course-03-build-real-world-full-stack-mobile-apps-java/023-push-http-fallback/) +- [How Do I Use HTTP, Sockets, Webservices & WebSockets](/how-do-i/how-do-i-use-http-sockets-webservices-websockets/) +- [Connecting to a Web Service](/courses/course-02-deep-dive-mobile-development-with-codename-one/003-connecting-to-a-web-service/) +- [Push Notifications](/push-notifications/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/025-in-app-purchase.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/025-in-app-purchase.md index 8c9af28bf7..d6f9e9d0db 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/025-in-app-purchase.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/025-in-app-purchase.md @@ -9,10 +9,31 @@ module_order: 8 lesson_order: 6 weight: 25 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Use the store billing flow to sell in-app items and continue the build workflow once payment succeeds." --- - > Module 8: Push and In-App Purchase - {{< youtube uvQKs_PdB64 >}} + +In-app purchase is one of those features where the code is usually easier than the store setup. Apple and Google own the payment flow, product definitions, pricing, refunds, and approval rules. Your app mostly needs to define what is being sold and decide what to unlock when a purchase succeeds. + +That store ownership is not just convenient. For digital goods and in-app features, it is often a platform requirement. If the user is buying something that exists inside the app experience, the stores generally expect that transaction to go through their billing system rather than through a custom payment form. + +In this project, the purchase flow is tied directly to the build workflow. The user chooses a build target, buys that build if necessary, and then the normal build process continues. That is a sensible product design because the purchase does not create a separate mini-application inside the app. It simply unlocks the next step the user was already trying to perform. + +Like push, purchase callbacks belong in the main application class. The app starts a purchase, waits for the store result, and then handles success, failure, cancellation, or refund through the appropriate callback methods. The most important callback is the successful purchase event, which includes the SKU of the item that was bought. + +The SKU is the bridge between store configuration and application logic. You define it in App Store Connect or Google Play Console, and the app uses it to decide what the purchase means. Once you have more than one purchasable item, that mapping becomes the heart of your purchase logic. + +The lesson keeps the client-side code intentionally small: present a purchase choice, start the purchase, and continue into the pending build flow once the store confirms success. That is the right scope. Most of the complexity in in-app purchase is not in the callback methods. It is in product configuration, testing, and making sure the entitlement model is correct. + +The warnings about store policy from the video are still worth taking seriously. Apple in particular is strict about in-app purchase rules, and review feedback can be broader than many developers expect. If a reviewer believes something inside the app experience should be sold through store billing, you should assume they may challenge any alternative payment path. + +The exact screens shown in the video are dated now, but the overall store workflow has not really changed. You create the product, assign its SKU, give it display text and pricing, and make sure the application uses the same SKU constants when purchase callbacks arrive. + +## Further Reading + +- [Push 3 - The Server Side and Build Logic](/courses/course-03-build-real-world-full-stack-mobile-apps-java/022-push-3-the-server-side-and-build-logic/) +- [Billing and Global Server](/courses/course-03-build-real-world-full-stack-mobile-apps-java/013-billing-and-global-server/) +- [In-App Purchase](/in-app-purchase/) +- [What Is Codename One](/courses/course-01-java-for-mobile-devices/004-what-is-codename-one/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/026-setting-up-the-vps-server.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/026-setting-up-the-vps-server.md index cf9bca2455..df956bc628 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/026-setting-up-the-vps-server.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/026-setting-up-the-vps-server.md @@ -9,10 +9,29 @@ module_order: 9 lesson_order: 1 weight: 26 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Provision a small Linux server, install Java, and prepare a low-cost deployment target for the backend." --- - > Module 9: Setting Up a Cloud Server - {{< youtube PRk7EhXQRqs >}} + +At some point the app has to leave the laptop and run somewhere public. This module takes the deliberately pragmatic route: rent a small VPS, install what the backend needs, and get to a working deployment without pretending that this is a full course on cloud architecture. + +That framing is still useful. A small VPS is often the right starting point for a real project because it keeps cost under control and forces you to understand the basic moving parts of your deployment. The lesson is not trying to design a globally distributed platform. It is trying to get a Java backend onto the internet in a way you can operate and afford. + +The Spring Boot detail at the start is one of the best parts of the original lesson. Packaging the application so it behaves like an executable service on Linux makes the rest of the deployment story much simpler. Instead of treating the server as a special one-off machine where you run `java -jar` manually forever, you can integrate the app into the operating system like a real service. + +The provider shown in the video is just an example. The important decision is the class of machine, not the brand. A modest Linux VPS is enough for many early deployments, and it avoids the trap of overbuilding infrastructure before the product has earned it. + +The old lesson uses CentOS and walks through a root-first setup with a dedicated non-root `builder` user for normal work. The exact Linux distribution you choose today may differ, because the hosting and Linux ecosystem has moved on since the video was recorded, but the operational advice is still right. Use root sparingly, create a regular deployment user, and keep day-to-day work under that account so mistakes are less dangerous. + +The JDK download discussion in the video is mostly historical now. The specific Oracle download friction it describes is not the interesting part anymore. The enduring lesson is that the server needs a predictable Java runtime and that you should install it in a way that is maintainable for your distribution and deployment process. + +This setup step is intentionally unglamorous, but it matters. A backend deployment is not just code. It is an operating system, a runtime, an account model, a filesystem layout, and a plan for how the application will actually be started and updated. + +## Further Reading + +- [Yum, MariaDB, Security and iptables](/courses/course-03-build-real-world-full-stack-mobile-apps-java/027-yum-mariadb-security-and-iptables/) +- [Starting the Server on Boot](/courses/course-03-build-real-world-full-stack-mobile-apps-java/028-starting-the-server-on-boot/) +- [Introduction to Spring Boot](/courses/course-02-deep-dive-mobile-development-with-codename-one/002-introduction-to-spring-boot/) +- [Corporate Server](/corporate-server/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/027-yum-mariadb-security-and-iptables.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/027-yum-mariadb-security-and-iptables.md index 079de19719..4a4372e6fd 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/027-yum-mariadb-security-and-iptables.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/027-yum-mariadb-security-and-iptables.md @@ -9,10 +9,27 @@ module_order: 9 lesson_order: 2 weight: 27 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Install the backend dependencies, harden the database, and expose the application through standard web ports." --- - > Module 9: Setting Up a Cloud Server - {{< youtube 4jIqplr19HA >}} + +Once the server exists, the next job is to make it useful. That means installing the packages the backend depends on, getting the database into a safe enough state for internet exposure, and making sure the application can be reached through the usual HTTP and HTTPS ports. + +The video uses the package tools and service layout of its Linux distribution, and those exact commands are now more dated than the underlying ideas. What still matters is the sequence: install only what the app genuinely needs, understand why each dependency is there, and enable the pieces that must survive a reboot. + +One of the more interesting details in this lesson is the use of a virtual framebuffer. That solves a real deployment problem for Java applications that occasionally need graphical capabilities on a headless server. If part of your build or asset pipeline relies on Java2D or other GUI-linked code, a headless Linux box can fail in surprising ways unless you provide an off-screen display environment. + +The database step is less subtle but more important. Installing MariaDB or another compatible database engine is only the beginning. The real work is hardening it: removing unsafe defaults, setting credentials intentionally, and deciding whether remote database access should exist at all. In many small deployments, the safest default is to keep the database private to the server and administer it through SSH rather than by opening it broadly to the internet. + +The lesson also uses port redirection so the backend can listen on higher application ports while the machine still exposes standard web ports such as 80 and 443. The exact firewall or proxy mechanism may differ in a modern deployment, but the reason for it is unchanged. Users and clients expect web services on standard ports, while the application process itself often runs more safely on a non-privileged internal port. + +That combination of service hardening and port mapping is the difference between “the backend runs on the machine” and “the backend is actually deployable.” Getting it right early keeps the rest of the deployment process much simpler. + +## Further Reading + +- [Setting up the VPS Server](/courses/course-03-build-real-world-full-stack-mobile-apps-java/026-setting-up-the-vps-server/) +- [Starting the Server on Boot](/courses/course-03-build-real-world-full-stack-mobile-apps-java/028-starting-the-server-on-boot/) +- [Introduction to Spring Boot](/courses/course-02-deep-dive-mobile-development-with-codename-one/002-introduction-to-spring-boot/) +- [Security Basics and Certificate Pinning](/courses/course-02-deep-dive-mobile-development-with-codename-one/024-security-basics-and-certificate-pinning/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/028-starting-the-server-on-boot.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/028-starting-the-server-on-boot.md index 47ff9525f7..547a6ed809 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/028-starting-the-server-on-boot.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/028-starting-the-server-on-boot.md @@ -9,10 +9,25 @@ module_order: 9 lesson_order: 3 weight: 28 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Copy the deployed artifacts to the server and wire the backend into the operating system so it starts like a real service." --- - > Module 9: Setting Up a Cloud Server - {{< youtube mqNtfN2C3fM >}} + +Manual deployment is tolerable exactly once. After that, you want a repeatable way to copy the backend onto the server, keep configuration files in the right place, and make sure the application comes back automatically after a reboot. + +The lesson starts with secure copy, and that is still a perfectly reasonable first deployment tool. You copy the configuration file, copy the built artifact, and keep the transfer explicit enough that you can see what changed. It is not fancy, but it is understandable, and that matters when you are still assembling the deployment story. + +The next step is the one that turns the backend from a jar file into a service. The original lesson leans on a Spring Boot feature that allows the packaged artifact to behave like an executable Linux service. That is a strong fit for a small VPS because it lets the operating system manage startup and logs instead of forcing you to keep the app alive manually in a shell. + +The video uses symbolic links and classic init-style service integration. The exact boot integration mechanism you would choose today may differ depending on distribution and tooling, but the principle is the same: the server should know how to start the backend on boot, where its logs live, and which configuration belongs to that deployed instance. + +The lesson also includes supporting pieces such as copying environment-specific properties, creating the database, and placing build tools where the backend expects them. Those details are not glamorous, but they are what make deployment dependable. A server is not really deployed until the configuration, runtime, filesystem layout, and startup behavior all line up. + +## Further Reading + +- [Setting up the VPS Server](/courses/course-03-build-real-world-full-stack-mobile-apps-java/026-setting-up-the-vps-server/) +- [Let’s Encrypt, HTTPS Certificate Support](/courses/course-03-build-real-world-full-stack-mobile-apps-java/029-let-s-encrypt-https-certificate-support/) +- [Introduction to Spring Boot](/courses/course-02-deep-dive-mobile-development-with-codename-one/002-introduction-to-spring-boot/) +- [Connecting to a Web Service](/courses/course-02-deep-dive-mobile-development-with-codename-one/003-connecting-to-a-web-service/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/029-let-s-encrypt-https-certificate-support.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/029-let-s-encrypt-https-certificate-support.md index ee9505217c..7a2a148331 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/029-let-s-encrypt-https-certificate-support.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/029-let-s-encrypt-https-certificate-support.md @@ -9,10 +9,27 @@ module_order: 9 lesson_order: 4 weight: 29 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Move the backend to HTTPS, connect a real domain name, and prepare the certificates Java needs." --- - > Module 9: Setting Up a Cloud Server - {{< youtube sH5GY816sRc >}} + +Once the backend is public, HTTPS stops being optional. Modern mobile platforms expect secure traffic, and production apps should assume that all client-server communication needs transport security from the start. + +The lesson begins with the application-side change: configure the server to listen on an HTTPS port and tell the Java runtime where to find the keystore, alias, and password it should use. That is still the correct mental model. HTTPS in Java is not just a hosting concern. The application itself needs access to the certificate material it will present to clients. + +Before any certificate can work, the server also needs a real domain name. The video is right to pause on this. Certificates are tied to names, not to bare IP addresses, so DNS is part of the HTTPS setup whether you like it or not. The domain has to resolve to the server before certificate issuance makes sense. + +The Let's Encrypt workflow shown in the video is from an earlier moment in the ecosystem, and some of the pain it describes is very specific to that period and stack. The durable lesson is that certificate issuance, renewal, and Java keystore handling need to be understood as one pipeline. Getting a certificate from a certificate authority is only half the job. The Java application also needs the certificate material in a format it can actually use. + +That conversion step is why this lesson spends time on PEM files, keystores, and ownership changes. Even if the exact commands evolve, the deployment concern remains the same: obtain the certificate, convert or package it into the format your runtime expects, and place it somewhere the application can read without turning the whole server into a permissions mess. + +The warning about Java trust stores is also still relevant in spirit. When certificate authority support changes over time, older runtimes can fail in surprising ways. Secure deployment is easier when your Java runtime is current enough to understand the certificate ecosystem you are using. + +## Further Reading + +- [Automating Lets Encrypt Renewal Process](/courses/course-03-build-real-world-full-stack-mobile-apps-java/030-automating-lets-encrypt-renewal-process/) +- [Security Basics and Certificate Pinning](/courses/course-02-deep-dive-mobile-development-with-codename-one/024-security-basics-and-certificate-pinning/) +- [Introduction to Spring Boot](/courses/course-02-deep-dive-mobile-development-with-codename-one/002-introduction-to-spring-boot/) +- [Communicating from the Client](/courses/course-03-build-real-world-full-stack-mobile-apps-java/009-communicating-from-the-client/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/030-automating-lets-encrypt-renewal-process.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/030-automating-lets-encrypt-renewal-process.md index 69daf1f638..f78efed67e 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/030-automating-lets-encrypt-renewal-process.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/030-automating-lets-encrypt-renewal-process.md @@ -9,10 +9,25 @@ module_order: 9 lesson_order: 5 weight: 30 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Automate certificate renewal so the HTTPS setup survives beyond the first 90 days." --- - > Module 9: Setting Up a Cloud Server - {{< youtube 0l4R049tSOY >}} + +Getting HTTPS working once is not enough. Let's Encrypt certificates expire quickly by design, so the real deployment milestone is automated renewal, not manual issuance. + +That short lifetime is not a flaw. It is the reason the ecosystem can lean so heavily on automation. Instead of treating certificate replacement as an infrequent ceremony people dread, the system pushes you toward a scriptable renewal path that keeps the server current with minimal manual work. + +The lesson takes the direct Linux approach: write a renewal script, make it executable, and schedule it with cron. The details in the video are old-school, but the operational idea is still good. Renewal should be repeatable, unattended, and explicit enough that you can audit what it is doing. + +The awkward part in the original setup is downtime. Because the certificate tooling and the server stack do not integrate perfectly, the backend may have to stop briefly while the renewal process runs. That is not ideal, but it is better than letting the certificate expire. In a more mature deployment you would try to reduce or eliminate that interruption, yet the basic rule remains: an imperfect automated renewal process is still far better than a perfect manual process nobody remembers to run. + +The main thing to preserve from this lesson is the deployment mindset. Security-related operational steps should not live only in your memory. They should live in scripts, scheduled tasks, and configuration that the server can execute predictably. + +## Further Reading + +- [Let’s Encrypt, HTTPS Certificate Support](/courses/course-03-build-real-world-full-stack-mobile-apps-java/029-let-s-encrypt-https-certificate-support/) +- [Setting up the VPS Server](/courses/course-03-build-real-world-full-stack-mobile-apps-java/026-setting-up-the-vps-server/) +- [Security Basics and Certificate Pinning](/courses/course-02-deep-dive-mobile-development-with-codename-one/024-security-basics-and-certificate-pinning/) +- [Corporate Server](/corporate-server/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/031-abstraction-and-architecture.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/031-abstraction-and-architecture.md index e8f5cb7297..07b3de0e44 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/031-abstraction-and-architecture.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/031-abstraction-and-architecture.md @@ -9,10 +9,27 @@ module_order: 10 lesson_order: 1 weight: 31 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Refactor the app so tablet and desktop layouts feel intentional instead of like oversized phone screens." --- - > Module 10: Adapting to Tablets and Desktops - {{< youtube Dh1bYqiKBQo >}} + +Running a phone UI on a tablet is easy. Making it feel like it belongs there is not. That is the problem this module solves. + +The lesson begins with the right observation: a phone-oriented app often looks merely enlarged on a tablet. Images feel oversized, empty space is wasted, navigation patterns become awkward, and the whole application looks like it was stretched rather than designed. + +The fix is not a pile of tablet-specific special cases. The fix is to step back and change the abstraction. On a phone, it is natural for each major screen to be its own form. On a tablet or desktop, that often becomes clumsy. A better pattern is to keep one stable outer shell and replace the content inside it as the user moves through the app. + +That architectural decision is the heart of the module. Instead of deriving everything directly from `Form`, the lesson introduces a higher-level UI abstraction that can behave like a normal form on phones while participating in a single-shell layout on larger displays. That lets the same application logic support very different presentation patterns without duplicating the whole app. + +The OK/cancel discussion in the video is a good example of why this matters. On a phone, toolbar commands may make sense. On a tablet, large action buttons placed naturally in the layout can feel much better. Once that choice is expressed in the abstraction layer, individual screens stop caring about which form factor they are on and can focus on their own job. + +This is the real benefit of the module. It is not just about making one screen wider. It is about moving form-factor differences into an architectural seam where they can be controlled deliberately. + +## Further Reading + +- [The UIAbstraction Class](/courses/course-03-build-real-world-full-stack-mobile-apps-java/032-the-uiabstraction-class/) +- [The TabletUI Class](/courses/course-03-build-real-world-full-stack-mobile-apps-java/033-the-tabletui-class/) +- [Putting it all Together](/courses/course-03-build-real-world-full-stack-mobile-apps-java/034-putting-it-all-together/) +- [Adapting a UI Design](/courses/course-01-java-for-mobile-devices/009-adapting-a-ui-design/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/032-the-uiabstraction-class.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/032-the-uiabstraction-class.md index 0c34b6927e..9356036717 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/032-the-uiabstraction-class.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/032-the-uiabstraction-class.md @@ -9,10 +9,29 @@ module_order: 10 lesson_order: 2 weight: 32 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Use a shared UI abstraction so screens can behave like forms on phones and like content panels on tablets." --- - > Module 10: Adapting to Tablets and Desktops - {{< youtube bU6pZPs3uto >}} + +Once the architectural decision is made, the next question is how to encode it in code without making every screen harder to write. The answer in this course is `UIAbstraction`, a layer that hides whether the current screen is really a form or just content living inside a larger tablet shell. + +That is a strong design choice because most screens do not actually care. They want to add components, bind actions, show the next screen, validate an OK button, and go back when needed. They should not have to know whether they are running inside a phone-style navigation stack or a tablet-style container swap. + +The class therefore acts as a small compatibility layer. In phone mode it delegates to a real form. In tablet mode it works with a container that can be embedded into the persistent tablet shell. The application code above it gets a stable API either way. + +The OK/cancel handling is a good example. Instead of forcing every screen to decide how its action buttons should be rendered on each form factor, the abstraction turns that into a capability. If a screen says it needs OK/cancel behavior, the abstraction can implement that in the form-appropriate way for the current device. + +The same idea appears in navigation, validation, and floating action button handling. The app code asks for behavior, and the abstraction adapts that behavior to the current presentation model. That keeps the screen classes readable while still allowing the larger-device UI to diverge meaningfully from the phone layout. + +The video goes fairly deep into wrapper containers and decorated containers, and that is worth understanding at a conceptual level even if the exact implementation later changes. Once a screen can live in more than one kind of outer structure, it becomes useful to distinguish between the core content and the final wrapped container that includes shared controls or extra layout layers. + +This kind of abstraction can become overengineered very quickly, so the best thing about the version in the course is that it stays tightly tied to a concrete product need. It is not trying to become a universal UI framework. It is solving one application's phone/tablet split in a way that keeps the rest of the code mostly unchanged. + +## Further Reading + +- [Abstraction and Architecture](/courses/course-03-build-real-world-full-stack-mobile-apps-java/031-abstraction-and-architecture/) +- [The TabletUI Class](/courses/course-03-build-real-world-full-stack-mobile-apps-java/033-the-tabletui-class/) +- [Putting it all Together](/courses/course-03-build-real-world-full-stack-mobile-apps-java/034-putting-it-all-together/) +- [Base Navigation Form and Shape Effects](/courses/course-03-build-real-world-full-stack-mobile-apps-java/005-base-navigation-form-and-shape-effects/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/033-the-tabletui-class.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/033-the-tabletui-class.md index 8923b4f303..d9d98d46d4 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/033-the-tabletui-class.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/033-the-tabletui-class.md @@ -9,10 +9,27 @@ module_order: 10 lesson_order: 3 weight: 33 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Build the persistent tablet shell that keeps navigation stable while the center content changes." --- - > Module 10: Adapting to Tablets and Desktops - {{< youtube DyzEgAGyRcA >}} + +If `UIAbstraction` is the contract, `TabletUI` is the concrete shell that makes the larger-screen experience real. + +The key idea is that a tablet version of the app does not need to recreate the entire screen on every navigation step. Instead, it can keep one stable outer form, leave navigation permanently available, and replace only the main content area. That makes the product feel much more natural on tablets and desktops because the app stops behaving like a sequence of full-screen phone cards. + +This lesson takes that pattern seriously. The tablet shell owns the outer layout, the persistent side menu, the title treatment, and the central content area. Navigation then becomes a matter of replacing the content in that center region instead of constructing and showing a brand-new form for every move. + +That is why the side menu design changes so much here. On a larger screen the menu does not need to hide behind a command. It can stay present and act like a real part of the application structure. The code also uses grouped selectable components so the current section remains visually obvious, which is exactly the kind of small polish that makes a desktop or tablet UI feel intentional. + +The `showContainer()` behavior is the heart of the lesson. The first screen is added into the shell. Later screens replace the current content in the center. That is a small implementation detail with a large user-experience effect because it changes the mental model from “move to another form” to “work within one application frame.” + +The video also shows some duplication between phone-specific and tablet-specific navigation code. That is acceptable here. Once the tablet experience diverges enough from the phone experience, a little explicit duplication can be cleaner than forcing both models through one overcomplicated generic API. + +## Further Reading + +- [Abstraction and Architecture](/courses/course-03-build-real-world-full-stack-mobile-apps-java/031-abstraction-and-architecture/) +- [The UIAbstraction Class](/courses/course-03-build-real-world-full-stack-mobile-apps-java/032-the-uiabstraction-class/) +- [Putting it all Together](/courses/course-03-build-real-world-full-stack-mobile-apps-java/034-putting-it-all-together/) +- [How Do I Create Gorgeous SideMenu](/how-do-i/how-do-i-create-gorgeous-sidemenu/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/034-putting-it-all-together.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/034-putting-it-all-together.md index cacaf97fb4..79143dcedb 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/034-putting-it-all-together.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/034-putting-it-all-together.md @@ -9,10 +9,27 @@ module_order: 10 lesson_order: 4 weight: 34 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Integrate the abstraction layer into the real screens so the app adapts cleanly across form factors." --- - > Module 10: Adapting to Tablets and Desktops - {{< youtube AMlnslUn1bA >}} + +The value of an abstraction is not that it looks clever in isolation. It is that real screens become easier to adapt. This final lesson in the module is where that claim gets tested. + +The encouraging thing in the original walkthrough is how little many screens actually need to change once the abstraction layer exists. Base navigation screens can move from `Form` to `UIAbstraction` with relatively small edits, and more specialized screens can opt into the specific behaviors they need such as OK/cancel handling or form-specific theming. + +That is exactly what good application architecture should do. It should move the difficult decision to one place and reduce the amount of code that needs to know about it everywhere else. + +The address form example in the video shows this clearly. Validation still happens. The submission flow still happens. Back and OK actions still happen. What changes is where those controls come from and how they are rendered on different form factors. The business logic is not rewritten just because the app now has a larger-screen presentation. + +The lesson also makes a fair point about framework design. It would be tempting to say that this whole abstraction should just be part of Codename One itself. The reason not to rush that is that framework APIs are expensive to commit to. A product-specific abstraction can be tuned aggressively for one app. A general-purpose framework abstraction has to survive many different apps and many years of backwards compatibility. + +So the right takeaway here is not that every app needs exactly this abstraction layer. It is that tablet and desktop adaptation becomes much more manageable when you first identify which parts of your UI are truly form-factor-specific and then isolate them behind a stable application-level API. + +## Further Reading + +- [Abstraction and Architecture](/courses/course-03-build-real-world-full-stack-mobile-apps-java/031-abstraction-and-architecture/) +- [The UIAbstraction Class](/courses/course-03-build-real-world-full-stack-mobile-apps-java/032-the-uiabstraction-class/) +- [The TabletUI Class](/courses/course-03-build-real-world-full-stack-mobile-apps-java/033-the-tabletui-class/) +- [Adapting a UI Design](/courses/course-01-java-for-mobile-devices/009-adapting-a-ui-design/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/035-transitions.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/035-transitions.md index 0cb522b941..b334d80d6f 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/035-transitions.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/035-transitions.md @@ -9,10 +9,27 @@ module_order: 11 lesson_order: 1 weight: 35 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Use transitions to communicate navigation, emphasis, and context changes without turning the UI into a distraction." --- - > Module 11: Animations - {{< youtube aVkeOIx-Is8 >}} + +Animation helps when it explains something. It hurts when it becomes decoration for its own sake. That warning at the start of the video is still the right way to approach transitions in Codename One. + +The two most useful jobs for transitions are emphasis and confirmation. Sometimes you want to draw the user's eye to something important. More often, you want motion to confirm what just happened: a new screen was opened, a dialog came from outside the normal navigation stack, or a piece of content expanded into a detail view. + +That is why the lesson spends more time on meaning than on visual variety. Codename One has many transition types, but the important decision is not which effect looks coolest. It is which effect best matches the user’s mental model of the action that just occurred. + +The examples in the app are good ones. A cover-style transition makes sense when opening something that behaves like a layer over the current flow. A morph transition makes sense when a thumbnail turns into a full-size detail view because the user can visually track the relationship between the two states. Even the reverse direction matters. If the return motion tells a different story, a different transition may be the better choice. + +The video also explains one detail that often confuses people: forms and dialogs each have transition-in and transition-out behavior, but in practice form navigation often focuses on the outgoing transition. That is less about API trivia than about keeping navigation consistent. What the user perceives is the movement between states, and consistency matters more than theoretical symmetry. + +Another good pattern from the lesson is restoring the previous transition after a special-case navigation. That prevents one locally chosen effect from leaking into unrelated parts of the app. Transitions should describe the current interaction, not accidentally redefine the whole application. + +## Further Reading + +- [Layout Animations](/courses/course-03-build-real-world-full-stack-mobile-apps-java/036-layout-animations/) +- [Animation Manager, Style Animations and Low Level Animations](/courses/course-03-build-real-world-full-stack-mobile-apps-java/037-animation-manager-style-animations-and-low-level-animations/) +- [Morph Transition: Animating Elements Between Forms](/courses/course-03-build-real-world-full-stack-mobile-apps-java/038-circular-floating-action-button-animation/) +- [Bubble Border](/courses/course-03-build-real-world-full-stack-mobile-apps-java/007-bubble-border/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/036-layout-animations.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/036-layout-animations.md index f613294556..2b4d6b6aa5 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/036-layout-animations.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/036-layout-animations.md @@ -9,10 +9,29 @@ module_order: 11 lesson_order: 2 weight: 36 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Use reflow-driven layout animation to make structural UI changes feel responsive and intentional." --- - > Module 11: Animations - {{< youtube anfVMvBXXX0 >}} + +Layout animation is one of the most useful animation tools in Codename One because it works with the framework instead of fighting it. The layout manager already decides where components belong. Layout animation simply turns that change of layout into motion the user can understand. + +The underlying concept is explicit reflow. When you add, remove, or rearrange components after a form is already visible, the UI does not magically update itself in a meaningful animated way. You need to trigger layout so the new positions are calculated. Once you do that, Codename One can animate the move from the old layout state to the new one. + +That is why these APIs feel so practical. They are not asking you to compute every pixel of an animation by hand. They let you describe the structural change and then animate the framework’s reflow process. + +The lesson distinguishes between layout and unlayout, and that distinction is worth understanding. One direction animates components into their valid final positions. The other direction is useful when you want to visually move something out before it is removed. In the dish-delete example, that creates a two-step effect: the item slides away, and then the rest of the layout closes the gap. + +That pattern works well because it mirrors the user’s understanding of what happened. First the chosen item leaves. Then the remaining list reorganizes itself. Motion is carrying meaning here, not just adding polish. + +The fade variants and blocking variants of the APIs are useful for the same reason. They let you compose changes in a controlled sequence instead of firing several visual updates at once and hoping the result feels natural. + +The lesson is also right to be cautious about the more recursive hierarchy-wide variants. The deeper the framework has to infer for you, the more edge cases appear. Layout animation is most powerful when it is used intentionally around a specific visible change. + +## Further Reading + +- [Transitions](/courses/course-03-build-real-world-full-stack-mobile-apps-java/035-transitions/) +- [Animation Manager, Style Animations and Low Level Animations](/courses/course-03-build-real-world-full-stack-mobile-apps-java/037-animation-manager-style-animations-and-low-level-animations/) +- [Dish List and Edit](/courses/course-03-build-real-world-full-stack-mobile-apps-java/006-dish-list-and-edit/) +- [Base Navigation Form and Shape Effects](/courses/course-03-build-real-world-full-stack-mobile-apps-java/005-base-navigation-form-and-shape-effects/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/037-animation-manager-style-animations-and-low-level-animations.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/037-animation-manager-style-animations-and-low-level-animations.md index b17d2daaf9..0bdf2646dc 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/037-animation-manager-style-animations-and-low-level-animations.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/037-animation-manager-style-animations-and-low-level-animations.md @@ -9,10 +9,29 @@ module_order: 11 lesson_order: 3 weight: 37 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Coordinate style animation safely and understand the lower-level animation hooks that power custom effects." --- - > Module 11: Animations - {{< youtube qUJ4FwZMEQY >}} + +By the time you reach this lesson, the easy animation APIs are no longer enough. You start needing orchestration: style changes that happen in sequence, animations that should not collide with each other, and low-level control for custom graphical effects. + +That is where the animation manager becomes useful. Style animation does not fit as neatly into the older one-method convenience APIs because it has to coordinate state changes on components that may already be participating in other updates. The animation manager gives the form one place to serialize and supervise those operations. + +This is more important than it sounds. Without coordination, the UI can become fragile when users trigger structural changes while an animation is still running. The manager exists partly to make these interactions safe by postponing conflicting operations until the current animation finishes. + +The style animation example in the video is a good one because it shows both the power and the limit of this approach. Some style properties animate beautifully, especially values such as colors. Others are too discrete to produce satisfying motion. The point is not to animate every style attribute you can find. It is to choose the ones that meaningfully communicate change. + +The lesson then moves all the way down to the low-level animation hooks, and that material is worth keeping because it explains how Codename One animation really works. The EDT advances in ticks, animated components opt into receiving animation callbacks, and repainting happens only when the framework is told something visual actually changed. + +That design is powerful, but it comes with responsibility. A component that stays registered for animation unnecessarily can waste battery and CPU. An `animate()` method that does too much work or returns true too often can quietly become a performance problem. This is one of those APIs where understanding the cost model matters as much as understanding the signatures. + +So the real lesson here is not “here is one more animation trick.” It is that Codename One gives you both high-level convenience APIs and low-level rendering hooks, and the farther down you go, the more disciplined you need to be about performance, registration, and repaint frequency. + +## Further Reading + +- [Transitions](/courses/course-03-build-real-world-full-stack-mobile-apps-java/035-transitions/) +- [Layout Animations](/courses/course-03-build-real-world-full-stack-mobile-apps-java/036-layout-animations/) +- [Threading and the EDT](/courses/course-01-java-for-mobile-devices/011-threading-and-the-edt/) +- [What Is Performance? Breaking Down the Problem](/courses/course-02-deep-dive-mobile-development-with-codename-one/037-what-is-performance-breaking-down-the-problem/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/038-1-introduction.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/038-1-introduction.md index 7b03e4df49..8c58edcebc 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/038-1-introduction.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/038-1-introduction.md @@ -11,8 +11,234 @@ weight: 38 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube xCEOuV43Uqw >}} + +## Transcript + +Uber is a big influence on the mobile +market +and a lot of us try to replicate their +functionality and Design +in this module I'll try to create an +Uber application clone +but let's discuss a bit what that means +let's start by setting expectations in +place +I'm not going to build everything in the +app as there are so many nuances and +details within the final app that it +would be impossible to go through +everything +however +I will build most of the big ticket UI +elements and I'll actually try and focus +on the hard stuff rather than doing +things that are mostly simple +I chose to clone the existing app rather +than building my own since I want to +show a professional grade application +and Uber is pretty much that +the goal of this module is to teach the +theory of building a professional app +it's not there for the purpose of +rebuilding Uber +are you string boot as usual for the +server but I'll try to keep it bare +it's actually better to do stuff in the +server in real world scenarios but in +this case I want to focus on client +development +I'm trying to create a close clone but +not an identical clone +during that last bit between identical +and pretty close is a huge amount of +effort that doesn't provide a benefit +in fact it makes things worse because it +forces the code and designed to be more +convoluted +I will spend a lot of time in the map UI +and try to explain how to build a decent +GIS application +this isn't a GIS tutorial though as I'm +not an expert in that field +I took some shortcuts in building this +app so hopefully they don't show much +before we start we need to essentially +understand the functionality of uber +even if you use the app in the past a +lot of the functionality is pretty +subtle and you might have run through it +without noticing +we need to grab screenshots of uber +features that we can review and compare +to what we are trying to implement +once we have those images we can use +them to create a mock-up of the Uber UI +once the mock-up is in place we can +create a server and then connect the +whole thing together +and fill in the details as we move along +this is pretty similar to the process I +used to build all the apps and the +course as you will notice I prefer +building the UI first and think it's +always the best approach +when we finish the app it should be +fully working at but you will probably +need some work in order to bring it to +production grade I'll try to highlight +the bits that are necessary as we move +through the UI +there are some great transition +animations in the Uber app which you can +see here +I'll go into some details of how we can +achieve some of them near the end of the +module +we'll start by focusing on the basic +skeleton UI +notice that these animations differ +between Android and iOS +now let's go into the screenshots I +captured of the Uber application +this shows that even a major native app +can have ux blunders +we have the Uber logo splash screen +followed by a permission prompt for +location +but let's move on +this is the basic login flow to Uber +one of the first things I checked is the +look and landscape mode turns out that +Uber doesn't support that +the app is fixed to Portrait on mobile +phones it doesn't seem to have much +support for tablets either which makes +some sense as you would probably not +hail a cab with a tablet +this allows them to simplify some of +their user experience logic and we can +probably use a similar approach +there are two options for login the +first uses social again through Google +Facebook +and that falls to the native login +option +the second one uses SMS style activation +by collecting the phone number +we have support for that in our SMS +activation cn1 lib +the UI is shown here are all very simple +clean and minimalistic which should make +them very easy to replicate in codename +one +on the right you can see a simple form +that allows us to select a country +if the one we detected isn't correct +it's pretty simple list with flags and +search +notice that when you scroll down in the +form the title collapses in the material +design style to provide a more compact +View +the SMS activation process works with +four digits it doesn't seem to +automatically offer to grab the SMS +which is pretty lame +we can do better than this I don't +understand why Uber wouldn't do +something better on Android where it's +possible +one important thing to notice is the way +the digit input looks +these are four separate digits but they +have one error message below +when a number is already active in the +server +you can use your password and get a +password reset form which I didn't +include in the screenshots +I'm not sure I'll go into that level of +detail with the implementation +one thing that is missing from the +screenshots is the next button progress +effect which is pretty cool when you +press the arrow button the screen tints +and the arrow is surrounded by a +circular blue progress bar that should +be pretty easy to accomplish in codename +one so it's something I'll try to do as +well +the UI itself is mostly the map which is +great +there are the cause and landmarks +highlighted in the map the where to text +field isn't really a text field it's a +button +when you click on it you see the search +form to find directions to order an Uber +that you can see in the screenshot next +to the map +notice notices can be swiped from the +bottom +this is a doable element but it's +non-trivial so I won't go into it with +this app and ignore that specific +element +we can see two floating buttons of +recent searches trips that you can +repeat +that should be pretty easy to replicate +as well +notice that the side menu icon that just +floats with no title to disturb the UI +of the application +one of the small details is the fact +that the menu back button is black +surrounded by a white outline +that means it will be reasonably visible +both on a dark and light map that's a +great attention to detail +once we pick a location we can get a UI +prompt with an order option it also +shows the direction on the map +highlighting my location and destination +if we open the sign menu we can see the +design is very simple +I'll skip the Uber for business stuff in +the app we make but I'll try to +reproduce this exact UI design +notice how the minimalistic design that +even skims on colors is able to +broadcast Elegance +payment is a relatively simple you user +experience +I won't go into it at all because we can +just integrate brain free for billing +and Skip on some of these complexities +credit card billing is problematic not +just due to technical difficulties but +due to liability I wouldn't go into that +unless I had to +I won't go into the details of each of +these forms notice I blocked out in red +some private information about my trip +that isn't really important +one thing you should notice is how +simple these uis are I won't really get +into them but they should be pretty +trivial +the iOS version is remarkably similar to +the Android version both in design and +transitions notice that the login form +has some pretty cool background rotation +animation +but other than that only the transitions +differ in the app everything else looks +identical +even the text input and The Floating +Action button this will make our lives +much easier diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/039-2-basic-setup.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/039-2-basic-setup.md index 69099351b1..2132ccb83d 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/039-2-basic-setup.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/039-2-basic-setup.md @@ -11,8 +11,204 @@ weight: 39 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube -brmZYVWb0Y >}} + +## Transcript + +let's jump into the code and styling +with a mock-up of the final application +but before we begin i'd like to start by +setting up some styles and basic utility +methods +i've created a new uber cn1 project and +placed it in the package +com.codname1.apps.uberclone +i gave the main class the name uberclone +as well +in the main form i'd like to highlight +three different lines +first we have the default gap between +the label text and icon +which is relatively large in the uber +wrap +so i've set it to two millimeters by +default +next +we lock the phone into portrait mode +this isn't the only thing we need to use +for this +finally we show the login form which +will get to in the next part +we need to install and configure some +cn1 lib extensions +we'll install more later but for the +first part we'll need sms activation +for the ui of the country picker +we also need google native maps for the +map ui support +don't forget to set up google maps in +the project as mentioned in the maps +module +as i mentioned before locking your in +orientation and code isn't enough for +ios +in ios we need to define orientation +lock in the project level +which we can do in the codename one +settings ios section +some styles are essential to begin with +so we need to add the following styles +into the theme +notice that a lot of these styles are a +result of trial and error to get the ui +to look like the designs +the process of choosing the values +boiled down to trying grabbing device +screenshots adjusting runes repeat +it sounds like a lot of work but it's +not too hard as you quickly get a sense +of what needs fixing +i define form as white +which is really the main thing here +on android by default they are a bit +off-white +i defined label as heavily padded with a +light font +black on white +this is consistent with the common use +of labels within the app +so i've set foreground to black and +background to white +i've set padding to a generous four +millimeters as padding is very heavy in +the uber +the margin is set to zero for almost all +of the components here +i use standard native light fonts for +almost everything as they look great +everywhere in this case i chose a 3.2 +millimeter font +which seems to closely match the +dimensions of uber font choices +one important thing to mention is that i +used derive all on pretty much every +style in the theme +this works by right clicking a style and +selecting derive all +once you do that it creates styles for +selected pressed disabled +that derive from this style +that's a very useful starting point it's +important even for labels as they can be +used in lead components and we'd like +them to have a common base setting +i defined toolbar as transparent without +the border that exists on some platforms +notice that this doesn't handle them +consistent title issue +where some forms have a black title area +where others have a transparent white +title area +i will discuss those later +i've set the background to opaque white +just to be sure +i've also disabled the border of the +base toolbar by explicitly defining it +as empty +i defined title command +as black on white +this is a bit problematic with the black +toolbar which requires +a bit of a hack and code to work +the padding numbers are there to make +the collapsible toolbar possible +this collapse effect featured +in several forms such as the country's +form +margin is zero as usual +and the font is relatively large +four millimeters this is mostly used to +size +the back arrow icon +and the search icon +the side navigation panel is mostly +black on white and relatively clean so +i'll just define the background as +opaque white and ignored the black since +that's part of the command +we have an underline at the bottom to +separate the panel from the south +component below +so we need to reserve 2 pixels for it in +the margin +the padding is zero as usual +as spacing will come from the commands +not from the panel +the underline separator from the south +component is just an underlying gray +border with a thickness of two pixels +side command pretty much continues what +we started in side navigation panel +here we set the foreground to black on +transparent color +this will be useful with the black +toolbar where we will only change the +color to leave +to white but leave the transparency in +place +the padding of the style command +prevents duplicate padding when commands +are one on top of the other which is why +the bottom padding is so small +margin is zero as usual +and the font is again a standard size +light font +the text field in the uber app is based +on the material design simple underlined +text field even when running on ios +so we need the text field to have an +underlying border and work with black on +white +we define the ui as transparent with a +black foreground +the padding below is relatively low +too so the line won't be too far from +the text input +the left and right paddings are zero so +the text starts +will align with the line start +the margin serves the role we usually +use for padding +it spaces out the component +the underlying border is pretty simple a +black +two pixel border +however in the selected version of the +text field we have a four pixel version +of the same border to indicate selection +font is a standard three millimeter +light font +the text hint needs to align with the +text field +so it's important to override it when we +manipulate the text field +we use the same padding as text fields i +could have derived text field which +might have been better a better approach +but i didn't want to get into that +the margin is again identical to the one +in the text field +the font size is smaller and regular +instead of common light font +that looked closer to the choices uber +made +finally the floating action button which +is just white on black nothing else +with this we can move forward to +creating the mockup +although there are some additional +styles we'll define during the creation +itself diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/040-3-login-and-country-code.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/040-3-login-and-country-code.md index e677a9f986..25b164cbee 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/040-3-login-and-country-code.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/040-3-login-and-country-code.md @@ -11,8 +11,182 @@ weight: 40 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube Cc8YG-_M02M >}} + +## Transcript + +in this part we'll create a mock-up for +some of the forms starting with the +login process +we'll start with the login form +since the uber app is portrait locked it +should be pretty easy to produce this ui +however +we'll first start with one element and +that's the country button +the reason it's logistically separate +is that it has some non-trivial logic +and resides in two separate forms +so it makes sense to define it +in a single generic class +the country code picker +is a button subclass +but it doesn't really look like it +because we set the uid +to the country code picker ui id +in this block of code we try to guess +the current country based on the +localization settings +the flags.res file is included in the +sms activation cn1 lib 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 +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 show picker form +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 +the styling for country code picker +are pretty simple +it's black on transparent +background the padding is big on the +left but small on the right so the text +element stays near the button +margin is again zero +and the font is pretty standard +light three millimeter font +so without further ado let's go to the +login form code +this is the code of the first form you +will see when the uber app launches +it's a relatively simple class without +too many frills +let's 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 +i want the logo to be square +so +height and width should be identical +we place the entire tile 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 +i override the behavior of the country +picker button +for consistency with the native uber app +this looks like a text field but acts +like a button +in the native app so i implemented it as +such +considering the rows and size +is important for proper layout +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 portray clocked +this shouldn't be a problem +we need +these two images to complete the form ui +the tile png +and uberlogo.png +there are a few styles we need to define +in order to finish this form +the square logo ui id +is used for the logo +i still have the foreground defined as +this is +this used to be text and not an image +the main thing here +are the white opaque background +we use some white padding on the logo +but we don't need margin +the logo background style represents the +pattern tile +in behind the logo +this is pretty easy to accomplish +once we have the tile.png file +we can style it to tile on both asus +we set the transparency to 255 +as the image is opaque and we want to +make sure +this is totally opaque +we define the margin to zero as usual +notice we ignore padding as it just +doesn't matter for this component +the get moving with uber ui id just sets +the padding to a right size so it's +spaced enough from the sides but close +enough to the element below +we don't need margin color or anything +else because we derive from label +the main reason for this ui id +is the large dominating 4.8 millimeter +font size +the phone number hint represents the +text that looks like a hint next to the +flag +when we move to the next form it +actually becomes the real hint text +it's gray with no background +it has zero padding on the left to keep +it close to the country picker button +the font is 3.7 millimeters which looked +right after some trial and error +the separator ui id is a container +which has an underline below it +as a container we define it as a as +completely transparent +it has a two pixel bottom padding to +leave space for the underline +the margin is zero as usual +the border is an underlying two pixel +border in a gray color +the connect with social button is the +button at the bottom of the ui it looks +like a label but has a bluish color +i need to define the padding even though +it should be derived from label as +deriving from built-in types isn't +always 100 reliable +i could have worked around it by +defining a my label uiid and deriving +from that +i still chose to derive from label +which mostly works +but i define the font +just to be on the safe side +this is the ui we've made next to the +native uber ui you will notice some +minor differences mostly with fonts not +being pixel perfect +i didn't aim for perfection with fonts +as that can be endless +there are some other nuances i'll go +into diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/041-4-login-shadow-and-rotation.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/041-4-login-shadow-and-rotation.md index 9275ea17ca..555abf2c7a 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/041-4-login-shadow-and-rotation.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/041-4-login-shadow-and-rotation.md @@ -11,8 +11,194 @@ weight: 41 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube owhInk5YAtg >}} + +## Transcript + +in this part we'll refine the login form +and go into low-level graphics for +animation and background +this is how i finished the previous part +i discussed the fonts but there are +other differences +obviously the flag is slightly different +since i used our own resource file +but there are two other noticeable +differences +the first is the drop shadow behind the +logo which is missing +the second is the background rotation of +the pattern which is ios specific in the +native app but i don't see a reason for +that +i'd like to have it on android too +the simplest thing to do is generate a +square image of the logo +that already has a translucent shadow +within +this would be pretty trivial to anyone +versed in photoshop and would look great +on the device +however my goal is to teach programming +not photoshop +so i'm picking the hard way of solving +this +the effects class in codename one allows +us to create a shadow image for the +given dimensions or image +since the logo is square we can just use +the dimensions approach +the method accepts the size of the +shadow +the blur radius which means how far it +should go out of the size limits +and the opacity +as a value between +0 and 1. +so now we have an image +of the shadow +but the logo image and the background +are already fixed so we need something +new +instead of using the logo as it is +we place the shadow in a layer below +using the layered layout +and this will produce the desired effect +with one huge caveat +it's really slow +shadows are computationally slow +we use +gaussian blur to generate shadows and +that's a very slow algorithm +the solution is to move that code +offline the ui will appear and the +shadow will appear a second later +when it's ready the placeholder is there +so we can put the shadow into place +when it's ready +when the shadow image is ready +we replace it on the edt with the new +shadow label +the label uses the container ui id +which is always transparent with zero +padding +and zero margin +the android version of uber doesn't +include the rotation animation for +reasons that are just unclear to me +i think it might collide with some of +the material design transitions or some +other problem +it works nicely on all os's with the way +i implemented it +i could just rotate the tiles like +the one pictured above and call it a day +the effect would look decent and perform +well +however i wanted better control +and in order to get that i need shapes +shapes allow us to draw arbitrary +vectors curves +in a performant way +since this is effectively a vector api +rotation and scaling don't distort the +result +in order to use this api i need to use +the low level graphics api +and the background painter +we can set the painter for the logo +object using this code +notice that normally we don't need a +reference to the parent component logo +but in this case we need it for the +animation +i'll go into that soon +but first i'd like to say a few words +about painters +styling can only go so far +if you want to customize the background +of a component in a completely custom +way +you can use the painter api to define +the actual rendering of the background +this overrides all style rendering and +provides you with a graphic object +you can use for drawing +notice that the graphics api is a low +level api and might have platform +specific behaviors that aren't as +refined as the component style apis +it's harder to optimize low-level +graphics code so use it with caution +now that we got this out of the way +let's look at the painter code itself +this is the rotation angle in degrees +we increment this as part of the +animation logic +this is the shape object representing +the background pattern +we draw it or stroke it like a rubber +stamp +the constructor and the draw shape +method +create the pattern shape that we stroke +later +this code happens once to generate the +lines +and we then color them later on +the register animated method of form is +needed for low-level animations +it triggers invocations of the animate +method with every +edt tick +so we can update the animation state +in this case we change the rotation +angle with every tick +the draw shape method +adds logical lines and quads to the +given path +a quad means quadratic curve to the +given position you can see three methods +used on the path element +move two +moves the virtual pen in the air without +drawing anything to a starting point +line two draws a line from the last +position of the pen to the given +position +quad 2 +draws a quadratic curve +bezier curve to the given position +through the given curve position +the paint method +is the callback from the painter +we fill the background rotate the +graphics context and draw the shapes +notice we just invoke draw shape and it +draws with the current alpha and color +in place +the low level animation code invokes +animate at fixed intervals based on edt +heartbeats +normally you would return true to +trigger a repaint but here i only want +to repaint a specific component +notice that i only change the angle and +move every +other frame to conserve cpu +also notice i rotate by 0.1 degrees +which creates a very smooth slow and +subtle +rotation +this paint method belongs to the +animation interface +we don't need it as we always return +force +once all of this is done the login ui +rotates in the background slowly and +smoothly a shadow appears after a second +and the ui looks in my opinion as good +as the native ui diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/042-5-social-login-and-country-picker.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/042-5-social-login-and-country-picker.md index 5d8d6587ed..043649cc85 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/042-5-social-login-and-country-picker.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/042-5-social-login-and-country-picker.md @@ -11,8 +11,187 @@ weight: 42 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube gM-InTtdhVE >}} + +## Transcript + +in this part we'll finally leave the +confines of the login form +and move to the simple forms of social +login and the country picker ui +i'll start with the social login markup +since it's so small and simple +this is the form you see when you choose +the social login option +i crop the bottom as it's just white +this is a pretty trivial ui +there isn't that much to say about this +form it's trivial +we use two icons for facebook and google +and define the back arrow +thanks to all the theming work we did up +to this point everything just works and +looks like the uber app +we do need to define the flag button +though +as it's a ui id that i reused in several +forms +it got the name from the country's +picker form which we'll go into next +the flag button is just a label in terms +of padding etc +it has a sub +subtly larger font at 3.2 millimeters +not much larger +the country picker form +lists the countries next to their flags +the first letter of the country name is +highlighted between the countries and +when you scroll down the title area +collapses to make more room +it does that with a smooth animation +effect +speaking of the title area it's white on +black instead of black on white +we reach this form when we click the +country picker button but that only +happens in the phone number entry form +which we will discuss shortly +let's jump right into the code +predictably the form is a box layout +container on the y axis +the init black title form method is a +static method in a common utility class +we'll cover it soon +we don't have flags for all the +countries so we need a blank space icon +so the elements align +here we loop over all the country codes +and create a button with the flag +letter ui id for every entry we also +need to implement the alphabet letter +headers +every time the first character of a +country changes we add a label +representing the entry +when an entry is selected +we update the text and icon of the +country code pickup button that launched +this form +we need to override the toolbar +initialization so we can set the proper +black toolbar ui +id speaking of the black toolbar +it is predictably styled as black and +opaque +we have a one millimeter padding which +doesn't exist in the default toolbar +it's helpful for the collapse animation +effect so +padding still remains +the margin is zero as usual +flag's letter is the letter that appears +on top of every +letter change between country names +the colors +and the opacity are things are picked +from the screenshot image +the other aspects of this ui are derived +from label +the form with the black title requires +some work which should be more generic +as a black title area is used in several +places within the uber application +for this purpose we have the common code +class which stores common static code in +the application +this is a non-trivial task as the logic +needs to support animated collapse of +the title area as the user scrolls down +the method accepts a callback for the +case of a search operation +this isn't implemented yet +but +if it's null +a search icon isn't added +we add the +back command +as a button +which allows us to place it above the +title in a custom way and animate the +position +we can't use the title command uiid +as is +since it uses a black on white scheme +in other forms +i could have used a different ui id here +if we have a search callback +i build the layout that includes the +search +button otherwise i create a layout +without it +i place the title on top of the back +button container using a layered layout +it doesn't seem to be on top because +i've set the top margin so it resides +below the black +arrow icon +i did this so i can animate the position +of the label fluidly by changing the +margin value +the this one line allows the title to +collapse into place next to the arrow it +translates the style of the title which +currently has a large top margin +to one without top margin and with side +margin +this means that the change in the style +causes the title to move next to the +back arrow +cover transition is used in the back +title form on ios +notice that cover transitions expect in +and out values for cover and uncover +the white on black title is a white +title style +and as the name suggests it has white +foreground and a transparent background +as the black portion comes from the +black toolbar ui id +the padding is pretty standard these +numbers were picked to align properly +with the commands both in expanded and +collapsed states +the margin is actually zero +as we change this manually and code it +might make sense to do the margin here +for some cases +the font is +standard light font +but four millimeters in size which +should appear bigger but not huge +subtle +the left margin version of the style +does define the side margin to leave +room for the arrow this means that the +collapse animation will mutate +into this ui id which has no top margin +so it will effectively align with the +back arrow +however the left margin will keep it +from going on +on top of the arrow +it also evens out the padding so things +look more aligned +now that they are on the same row +derive the white on black title ui id so +the settings +we don't override are identical +the font is the same but a smaller three +millimeter size +this will also animate as the title +slides into place it's subtle but +noticeable diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/043-6-sms-activation-flow.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/043-6-sms-activation-flow.md index 17bd157d90..506a5ac09d 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/043-6-sms-activation-flow.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/043-6-sms-activation-flow.md @@ -11,8 +11,216 @@ weight: 43 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube cRqvkIpJlkg >}} + +## Transcript + +in this part we'll go into the sms +activation flow +the first form in the sms activation +flow is the enter mobile number form +it's a simple form even though there are +some interesting subtle features here +the cool thing is that we did almost all +of the work for this element already +Text Fields +let's jump right into the code that +makes that form +we'll use standard back navigation since +the toolbar is pretty standard here +the phone number text field is right +next to the country code button +we place it in the center of the border +layout so it will take up all available +space +i want the padding on the text field and +button to match so they align properly +once paddings are set they are always in +pixels so we need to change the style to +use pixels +i don't want to impact the left right +padding values so i extract them first +and save them so i can restore them into +the ui +i could technically create a separate ui +id to align both but i wanted to do this +in the code so future changes to the +theme don't break alignment +just so you'll get a sense of why this +exists +this screenshot shows +side by side how this looks with and +without the alignment code +guess +which is the right one +you can start editing a text field by +invoking start editing +however this is a bit more challenging +to do +with a form that isn't showing yet so we +have a special case for that +set edit on show +and this is pretty much it for this form +SMS Verification +once the number is entered +we move to the sms verification stage or +password entry stage +in this case +i've hard coded the sms verification +stage i didn't do the sms resend +countdown +but i did do the number input +notice that the text fields look like +android text fields but have a sort of +center alignment structure +also notice that the error mode spans +the four text elements +let's jump into the code and look at how +this was done +Digits Form +the enter sms verification digits form +is a bit of a mouthful but it describes +the function of the form rather well +let's go over this form line by line +we use a border layout and place a box +in the center +which we make scrollable on the y axis +the reason for the border layout is so +we can stick the countdown label in the +south +otherwise i would have used +box layout for the entire form +notice i set the container to be +scrollable on the y-axis this is +important for containers where we have +text input it allows our keyboard code +to resize the container properly +when the keyboard shows +i'd like to also point out that i used +the standard back command +in the toolbar +we create an array of text fields to +loop over this allows us to easily +change the code to accept six digits +i'll discuss the create digits method +soon +yes +this works it adds all the components +and the array so it will add the four +digit text fields +the error label +is always there +we just hide it +for now i don't animate the recent text +again notice that i use board layout to +position the recent label at the bottom +and place the rest of the stuff in a box +layout in the center +when the floating action button is +pressed we validate the input so we can +decide whether to show an error or +proceed +the generic creation code creates the +array of numeric text fields and aligns +the hints to the center +this logic makes sure that once we type +a character the input will automatically +move to the next text field +in case of an error +we just change the underline style +we could have also done this by invoking +set ui id which might have been more +elegant +we bind a listener to each text field +and if the length of the text is 1 +we stop editing and move to the next +text field +and this is pretty much it with the +exception of the styles we had to add to +make this happen +Digits Style +the digit style is a special case of +text field specifically designed for +this form +the main reason for a special style is +the problematic center alignment and +text field +because of the way this works i +preferred using a one millimeter padding +on the sides to give the feel of center +alignment in this case +center alignment works in text area +label etc however it's flaky in text +fields because it's really hard to get +the position right when moving from +lightweight to native editing +another important bit +is the smaller margin that makes the +fields stand closer to one another +Selected Style +as i mentioned before since this is a +specialization of text field we derive +from text field the text field class +one thing to notice +is that the selected style +we need to override the border as well +to implement a +4 pixel underline border +it's because we derived from the +unselected digit and not from the +selected version of text field so we +need to redefine how selected digit +entry looks +however we also override the font size +to make it slightly smaller and thus +more consistent with the native uber app +the error label is just a red on white +label +it has a bit of a smaller padding +so it can use up space +it still has zero margin like most +components +but it has a smaller light font at 2.8 +millimeters +which is more consistent with the +material design aesthetic +the recent code style +just pads the text so it will align +properly with the floating action button +it leaves margin as 0 by default +but it has smaller text size than a +typical label +Password Entry +the last form in the sms activation flow +is the password entry form +it's a trivial form after the others +we've been through +here i'll gloss over it relatively +quickly +this is the entire form code literally +in one page +after the activation +form there is literally nothing new or +interesting here the only aspect that's +here and wasn't there is the forget +password uiid +which we align with the floating action +button +in this case we have +two elements that we enclose in a box +layout y in the south +most of the work here is in the ui id +itself +the forget password buttons +have a bluish color and are transparent +the padding is carefully measured to +align properly with the floating action +button +margin is zero as usual +the font is a relatively small 2.5 +millimeter size +and that concludes the sms activation ui +flow mockup diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/044-7-map-form.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/044-7-map-form.md index e1c435b52c..94e0c6f41d 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/044-7-map-form.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/044-7-map-form.md @@ -11,8 +11,163 @@ weight: 44 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube UU6HCbenVAA >}} + +## Transcript + +in this section we finally get to the +map ui +i hope you followed the instructions +before for configuring the map +if not +please follow through with that if +things don't work and if you don't see +the map or it acts funny check out with +us in the online support forums +you can also check out the map section +in the deep dive into mobile development +course which comes bundled +before we begin +we need a class we discussed before in +the maps module +the map layout +i didn't change much as +left it as is +since it's already available elsewhere i +won't go into the full discussion here +and go right into the form itself +before we proceed i'd like to highlight +some subtle things in this screenshot +and discuss what we will and will not do +i won't do the side menu right now +but i will do it soon +the way to field is really a button that +leads to a different ui +this ui is overlaid on top of the map so +it is a part of this form +i'll position +one taxi in a hard-coded location as +part of the mock-up +the icons at the bottom are historic +rides i'll add two hard-coded historic +rides for now +i won't go into the notice at the bottom +it's it's possible +but it would be non-trivial +let's jump right into the code +you need the js key from google maps as +explained in the map extension page +this +must have a filled up value +we usually use border layout which +implicitly disables scrollability +layout layout doesn't do that +and the form's content pane is +scrollable on the y-axis by default +notice we didn't use a thread here +and instead used call serially on idle +method +on the login form i didn't use that +because the animation might have +prevented idle from occurring +this shadow is used later on in the show +navigation toolbar method +the transition and the main application +are based on cover and the transition +out will only be a problem +the map is on the lowest layer and +everything is placed on top of it +the layer is on top of the map and uses +the map layout +here we will place the car and other +landmarks we need +i place a car on top of the map in tel +aviv +notice that the car is just a label +i've set the opacity to 140 to match the +translucent cars in the native app +notice that the map layout takes a quart +as constraint +so it can properly position the car +this is the small square we place next +to the where to button +i could have used a unicode value too +but it wasn't available in all the fonts +notice the where to element is just a +button as it moves us to a separate ui +and isn't really a text field +the history buttons are floating action +button instances +that are customized in terms of styling +i used text area instead of span label +because i wanted the history element to +act as a single component with lead +component +lead components can take over a +hierarchy of several components and +handle the events for everyone so in +this case click on the text area below +the history will trigger an event in the +floating action button +the bottom of the map has a gradient +overlay that darkens the bottom +this is probably in place to make the +history labels readable +i just generated a gradient image in +photoshop and placed it here +we do +two important things here +we use the overlay toolbar which floats +on top of the ui +we initialize the side menu +which we will discuss soon +that was a lot to cover +but there is a lot more +we did mention three new styles above +which isn't that much all things +considered +the wear 2 style has some subtle nuances +such as +dark gray text +the padding is large and obvious i +played with it a bit to get it right +the margin is special we want some +margin from the sides so it won't touch +them +we need a lot of margin from the top to +leave room for the title area +the corners are rounded on the native +widget this is very subtle so i left it +at 0.3 millimeters which should be very +easy +it also has a shadow which is more +obvious +and 80 opacity +the font isn't big just slightly bigger +than normal +but the typical light font +the history button is the round button +on the bottom of the map leading to +historic rides it's black on white but +is implemented as a floating action +button +so it derives from that +and uses the border settings from the +floating action button +the +the history label is the dark text +element below which is technically a +text area but acts as a label +the text color for this is black +while the padding is two millimeters on +all sides except for the top where we +want to be as close as possible to the +floating action button which is already +well padded +margin is zero as usual +and the font is a relatively small 2.2 +millimeters so we can fit multiple rides +in one form diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/045-8-where-to-ui.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/045-8-where-to-ui.md index e96d1f8ee0..ce8542bc61 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/045-8-where-to-ui.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/045-8-where-to-ui.md @@ -11,8 +11,130 @@ weight: 45 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube -7XHkBMK4NY >}} + +## Transcript + +when you tap the where to button on the +map form you see something that might +look like a new form but it isn't really +what you see initially is this and it +seems like a new form +but notice that the focus is on the +where to text field +if we switch focus to the where from +text field on top you will see something +else +you will see the map and the ability to +type in a new location +so what we really have here are two +separate overlays on top of the map +one above and one below +there is another septal behavior that i +only noticed when i started playing with +this ui +notice the line and the shapes next to +the text field +when you move the focus between the +fields the shapes +flip to highlight the focused field +we could build something like this with +a dialog or interaction dialog but i +chose to go with simpler container +instances on top of the map +to do this i first had to add a listener +to the where to +button then i add the show navigation +button method +let's dive into this method +we create a new layer on top of the +current layers +in the form +layers are associated with a component +class +which allows us to keep it unique and +prevents different code from messing +with our layer +also notice that we replicate the look +of the title area without actually +creating a title area +the square image already exists from +before +we created it for the where to button +we add a new circle image that we can +place next to the from two fields +we place the text fields in a border +layout next to the labels representing +the circle and square +we place that in a box layout y +container and that's effectively the +entire ui of the top portion +the background painter allows us to +control the shadow from the top area and +draw the line between the circle +square images +the fact that we have a background +painter makes some of the aspects of the +ui id less significant +for instance background color +but we still need it for padding margin +etc +the shadow image +is created asynchronously +by the call serially on idle code and +the constructor +so it might might not be ready when this +is drawn +we fill the rectangle on top of the drop +shadow +covering half of it +this makes it feel like a directional +shadow +i used fill rect instead of draw line to +make a 2 pixel wide line +i could have used draw line with stroke +but this is simpler and probably faster +the entire layer uses border layout +north makes sense for this as we wanted +to span the width but remain at +preferred height in the north +we'll use the center for the rest of the +ui soon +the component animates down from the top +with animate layout we pre position the +component location above +the from so animate layout will slide +everything +from the right point +this ui requires three new styles +first is +the wear toolbar +which is an opaque white container +we have five millimeter padding on the +bottom for the shadow of the container +and as usual the +zero margin +the from two text field is opaque with a +grayish color background and black +foreground +it has +two millimeters of padding +and two millimeters of margin to keep it +spaced +it uses a standard light font +the component also has a selected +version +which has +slightly darker grayish color +it derives from the unselected version +of the +uid +we also have a custom ui id which for +the most part just uses a darker gray +color for the hint text +the margin is zero again +most everything else is derived from the +from to text field diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/046-9-where-to-ui-part-ii.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/046-9-where-to-ui-part-ii.md index 2c2462e847..b89211ef7f 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/046-9-where-to-ui-part-ii.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/046-9-where-to-ui-part-ii.md @@ -11,8 +11,158 @@ weight: 46 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube Lai--eYYJTw >}} + +## Transcript + +the where2 ui is a pretty big piece of +the puzzle and it also includes the +navigation ui +which is the bottom portion +up until now we focused on this part of +the toolbar area +now we need to do this portion the +destination ui toggle +is a huge part of the navigation ui +it's the bottom section of the form that +contains the list of destinations +we can build it on top of the code we +wrote for the navigation ui and place it +in the center +of the layer +so it will play nicely with the rest of +the ui +for now i'll ignore this portion +as it's mostly a specialization +of other things and can be done +relatively easily if you understand the +rest of the things i did +let's jump right into the show +navigation bar method +the top elements are relatively +simple multi buttons we use container as +their ui id so they will be transparent +with zero padding and margin +the separator +is just a label with a specific style +notice that blank label +buttons etc are hidden by default in +codename one +and you should invoke set show even if +blank +if you want such a label to still render +we can reuse the form ui id here +because that's effectively what we want +we want this ui to appear as if it's a +form +we need this to be scrollable but we +don't want the scroll bar on the side as +it might cause +aesthetic issues +the showing of this element +is animated from the bottom +of the form +while this ui is very simple +it did define a few ui ids +let's start with where to button line 1 +which represents the entries in the list +of elements and also adds the underline +the color is just black over transparent +which in this case leads +to white +the padding on the left side is +relatively low since the icon will take +the extra padding on the other sides +we have typical four millimeter padding +margin is zero +as usual +we put the underline here because the +design placed the underline only under +the text and it will place it under the +icon which has a different ui id +the underline is a gray two pixel high +line +the font is the standard three +millimeter light font +the where to button icon style +applies to the icon which has +less horizontal padding so it won't +drift too far from the text +but identical vertical padding +so it will align properly with the text +it derives from where to button line one +so they will fit together well +we need the no border version of the +style +so it will remove the underline border +on the last entry +otherwise we can see +an out of place underline in that one +last entry in the list +we derive from the same style so +everything else is identical +the wear separator +is just a gray padded line so it has the +right gray color and is completely +opaque so +with no background transparency +it's exactly +two millimeters tall so it will stand +out but won't take out an entire line +margin is zero so it can reach the edge +of the parent container +now that we added this we need to show +this ui and hide it when the user +toggles the focus in the text fields +we can do this by binding focus +listeners +to the to and from text fields +when focus is lost or gained we toggle +between the square and circle modes by +setting the icon +to the appropriate labels +we always have one container in the +layer except for the case where the +second component is the where2 container +it's always the second component because +it's always added last +we set the position of this container +below the forms +animate and layout moves the component +outside of the screen to the position we +asked for +using a smooth animation +this callback is invoked when the +unlayout completes +at this point we have an invalid ui that +needs a layout but before we do that we +remove the component that we animated +out of the form +now +that the ui appears we also need to +remove it when going back +so i'll update the back action listener +from above to handle the where to ui as +well +this is the exact same unlayout +operation +we did before +and finally +we need to make a subtle but important +change to the background painter code +from before +because of the drop shadow a gap is +formed between the top and bottom pieces +so a special case here paints a white +rectangle under the shadow to hide the +gap +without that the shadow would appear +on top of the map and not on top of the +white background +once this is done +opening the where to ui +and toggling the fields should work +as expected diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/047-10-side-menu.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/047-10-side-menu.md index 92c30b1fed..489608ee44 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/047-10-side-menu.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/047-10-side-menu.md @@ -11,8 +11,76 @@ weight: 47 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube AFvuY7Ev-XA >}} + +## Transcript + +we are nearing the end of the mock-up +code +the next thing on the agenda is the side +menu ui +i've moved that code into a separate +hard-coded class for reuse in other +forms frankly i'm not sure if this ui is +used in other forms but i think it's a +good habit to separate the menu +from the component code +the code in the common code class which +i used for the black toolbar as well +it's a sort of util class that's really +convenient when you have repeating ui +elements +one thing you will notice is that there +just isn't all that much code here +the main reason is that +most of the ui is +within the theme and we already defined +most of these ui elements such as side +command uid +let's dive into the code +we'll discuss the get avatar method soon +this code generates the avatar image at +the top +with the name next to it +the gap between the text and the icon in +the avatar is larger than average +the legal button +is a south component +it's a special case in the on top side +menu that allows you to place an element +below the menu itself +its styling is separate and it slides in +out +so +we need to give it the psi navigation +panel styling to +let's move to the get avatar method +which generates the round image of the +user +we create an opaque 10 millimeter black +image +to use as a mask +masks allow us to crop out unwanted +pieces of an image in this case we want +to make the image round +we fill the shape we want in white in +this case as an arc +notice we activate anti-aliasing +otherwise the resulting image will look +jagged +which is also why we avoided +shape clipping here +the font image class can use the given +color and opacity settings +we use the version of the class that +accepts a style object and size so we'll +have fine grained control from code +we can't apply a mask to an image of a +size that's different from the mask size +masking doesn't work well with complex +images such as font images +so we convert it to a regular image +first diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/048-11-the-spring-boot-server.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/048-11-the-spring-boot-server.md index dc9123d69f..881ffb3b1c 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/048-11-the-spring-boot-server.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/048-11-the-spring-boot-server.md @@ -11,8 +11,398 @@ weight: 48 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube EkiTDQn9Cpg >}} + +## Transcript + +now that we got the mock-up running +let's jump to the other side of the +fence and set up the server +if you aren't familiar with spring boot +or mysql or don't understand why i +picked both of them i suggest +checking the previous modules where i +discussed the reasons for this +extensively and gave a long overview +over both +you can create a new database by logging +into mysql and issuing a create database +command +i created a new spring boot project +which includes maven dependencies for +jpa which is the java persistence +architecture or hibernate +jersey which is the json and xml +serialization framework +web which is useful for web service +development +websocket for connecting over the newer +websocket protocol +security is mostly used for password +hashing which we will discuss +and mysql support is needed for the jdbc +connectivity +and finally braintree for the payment +processing we'll need later on +before we get into the code let's take a +minute or so to think about the things +we need from the server +the first thing the server needs to +offer +is an ability to add a new user +we also need +authentication and authorization for the +user such as password validation and +authentication +we need a way to update the user +information +we need to track car positions so we can +show them on the map +we need the ability to hail a car and +pick up +a driver +we +need a +we need to pay the hailed car so the +system knows the car is paired to us +we need to log every trip taken +including distance path etc +and finally we need to provide rating +facility so we can rate the drivers +i'm +glossing over some things here such as +billing push etc +now that we have a spring boot project +let's skip ahead to the server code +we'll start with storage which is a good +place to start a common design strategy +is deciding on the data structure and +then filling in the blanks +a good way to decide on the elements we +need in the user object is through the +ui +the account setting class contains +a lot of the data we need +the user jpa entity uses an auto +increment id value for simplicity +let's go over the various pieces here +these are +part of the user settings such as +just general configuration +the password stores +the hashed value of password and not the +plain text version we'll discuss hashing +passwords soon +we will use these when we need to enable +login with a social network account +these are the internal network ids +we'll +use for verification +a driver is also a user in the system +if this user is a driver this is marked +as true +by referring to both the end user and +the driver with the same class we can +simplify the code +if this is a driver +this is the description of his car +we'll need for the app +this field is set to true +if we are currently in the process of +hailing a taxi +if the taxi is taken by user this field +maps to the user id it is set to null if +the taxi is available +we will have a separate object dealing +with rating +but we can sum it for every query so the +rating value will be cached here +this is the position and direction of +the current user +whether it's a taxi or an end user +notice i chose to just store the +location values instead of using one of +the custom location based apis +supported by hibernate and mysql +i looked into those apis and they are +very powerful if you need complex +location based apis +but for most simple purposes like we +have here +they are an overkill and would have made +the project more complex than it needs +to be +if you are building a complex gis +application i would suggest delving into +some of those custom apis +this is a picture of the user stored in +the database blob +one last column is the auth token which +we initialize with a unique random id +we will use this token to update the +user and perform operations only the +user is authorized for think of +authorization as a key to the server we +want to block a different user from +sending a request that pretends to be +our user +this is possible to do if a hacker +sniffs out our network traffic and tries +to pretend he's our app +one approach would be sending the +password to the server every time +but that means storing and sending a +password which holds risk in this case +we generate a random and long key that's +hard to brute force +we send the key to the client +and it stores that key +from that point on we have proof that +this user is valid +i've discussed this before in the +restaurant app if you want to check that +out +the repository class +for the user starts off +pretty standard entries +we first +have the option to find a user based on +common features +you would expect such as the auth token +phone etc +however +we also need some more elaborate +location-based queries +in this case +i verify that the entry +is a driver by always passing true to +the driver value +i also use the between keyword to make +sure that the entries i find +fall between the given latitude +longitude values +the find by driver method finds all the +drivers in a region +it's useful to draw the driver on the +map +even if they are currently busy +the second method returns only the +available drivers +and is used when hailing +like before we need a data access object +or dao to abstract the underlying user +object and make client server +communication easier +notice several things about this dao +first notice that we don't provide the +of avatar in the dial +it doesn't really fit here +as we'll apply it directly to the image +also notice that the auth token and +password are never returned from the +server they are there for the client +requests only +in this case the password +would be the actual password and not the +hash as the client doesn't know the hash +and the server doesn't store the +password +i'll skip the rest of the code as it's +pretty obvious +including constructors +setters and getters +we create the user dao instances +in the server by asking the user object +notice we have two versions of the +method +one of which includes some private +information and is useful internally +the other is the one we need to ask for +when dealing with client requests +the user service class is a business +object that abstracts the user access +code +if we think of the user object as the +database abstraction and the dao as a +communication abstraction the service is +the actual api +of the server we will later wrap it with +a web service call to make that api +accessible to the end user +this might seem like an overkill with +too many classes which is a common +problem for java developers +however in this case it's justified +by using the service class i can build +unit tests that test the server logic +only without going through the +complexities of the web tier +i can also connect some of the common +apis to the websocket layer moving +forward +so having most of my business logic in +this class makes a lot of sense +i have two other wired values here +first is the crude interface we +discussed earlier the tarkan used to +work with users and drivers +the second one is this spring boot +interface +used to hash and salt the passwords just +using this interface means that even in +a case of a hack your user's passwords +would still be safe +i'll discuss this further soon +adding a user consists of creating a new +user object +with the dao and invoking the built-in +crude save method +here we encode the password this is +pretty seamless in spring but remarkably +secure as it uses a salted hash +passwords aren't encrypted they are +hashed and salted +encryption is a two-way algorithm you +can encode data and then decode it back +hashing codes the data in such a way +that can't be reversed to verify the +password we need to rehash it and check +the hashed strings +hashing alone isn't enough +as it can be assaulted with various +attacks +one of the tricks against hash attacks +is salt +the salt is random data that's injected +into the hash +an attacker can distinguish between the +salt and hash data which makes potential +attacks much harder +the password hashing algorithm of spring +boot always produces a 60 character +string which would be pretty hard to +crack +i'll soon discuss the process of +checking a password for validity as it's +a pretty big subject +notice that +get avatar uses the id value that leaves +a small security weakness +where a user can scan the ids for images +of the drivers users +i'm not too concerned about that issue +so i'm leaving it in place however +letting a user update the avatar is +something that needs a secure token +continuing with the security aspect +notice that things such as password and +token are special cases that we don't +want to update using the same flow as +they are pretty sensitive +we have +three login methods +and they are all technically very +similar so they all delegate to a single +login api call +they all throw the user authentication +exception which is a simple subclass of +exception +if a user wasn't found in the list +we failed +this should never +ever happen but it's important to test +against such conditions as during a hack +these should never happen conditions +might occur +since the passwords are hashed and +sorted +we can't just compare +regenerating the hash and comparing that +wouldn't work either as salt is random +the only way to test is to use the +matches method +we need to manually set the auth value +as it's not there by default to prevent +a credential leak +the one place where the auth value +should exist is in the login process +when we log in +at first we need to check if the user +with the given phone or social network +exists +the ui flow for users that exist and +don't exist is slightly different +a small piece of the puzzle i skipped +before is the security configuration +class +in spring boot we can use configuration +classes like this instead of xml +which i prefer by far +first we need to disable some oauth and +csrf attack protection +both of these make a lot of sense for +web-based javascript applications which +are vulnerable to attacks +and can use the built-in authentication +but in a native app they just add +complexity and overhead so they aren't +really necessary and can cause problems +if you recall the password encoded from +before +this is the location where we include +the actual implementation of this +encoder +you can place it in any configuration +class but i thought it's fitting to put +it into the security configuration class +so far so good +but the user service is +a server-only class +we'd like to expose this functionality +to the client code to do that we can add +a json-based web service by using user +web the user web service class +notice that this is just a thin layer on +top of the injected user service class +the user service class we throw a +user authentication exception when login +failed +this code automatically translates an +exception of that type to an error dao +object which returns a different error +json effectively +this means that when this exception type +is thrown a +user will receive a forbidden http +response +with the json body containing an error +message of invalid password +maps the +user exists with phone number url so it +will return true or +false strings based on whether the user +actually exists +images just map to our url for the given +image id so the url user slash avatar +slash user id will return the image for +the given user with the mime type image +jpeg if the image isn't there we'll +return an http not found +error 404 +which we can handle in the client code +the user update avatar auth token api is +a mime multipart upload request +which we can use to upload an image +a multi-part upload is encoded using +base64 and is the http standard for file +upload +we check whether an id is set +to determine +if this is +an add or an update operation however we +don't use the id value for editing then +internally as the underlying api uses +the token diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/049-12-server-websocket-handler.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/049-12-server-websocket-handler.md index 11e84049eb..a762286e13 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/049-12-server-websocket-handler.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/049-12-server-websocket-handler.md @@ -11,8 +11,197 @@ weight: 49 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube 3-ZH2IFIIMY >}} + +## Transcript + +continuing the server side code will now +delve into the location logic +which maps to the websocket support in +spring boot +WebSockets +websocket is a special type of socket +that is created through http or https +request +a web server that supports web sockets +opens a regular http connection and then +uses +the socket open there to continue +working as a regular socket +as a result the websocket setup is +slower than a regular tcp socket +but they provide the same level of +flexibility +after creation +the advantage over tcp sockets is +compatibility and the ability to patch +pass through potential problematic +firewalls +as those would see a websocket as +another http +connection +the websocket api includes two types of +packets +text and binary +in this case i'll use the binary +protocol because it's pretty easy to do +this in java +up until now all our communications +went through web services +which is convenient and scalable +the fact we can use tools like curl and +the network monitor to see what is going +on under the hood +is very helpful +however web services suffer +from the performance overhead and fixed +structure issues of http +for more interactive data we would +prefer something like websockets +some people use websockets for all their +communications and it might work for +your use cases +a lot of developers use the text-based +websocket as a substitute to web +services altogether +and in some cases that makes sense +however as i mentioned before we have +decades of experience with http it works +well and has a huge infrastructure of +tools behind it +websockets are a low level api there are +some higher level abstractions on top of +them +but these often go back to the problems +of http without giving much in return +WebSocketConfig +spring boot has +decent support for websockets but you +need to activate it first +we need to define a configuration class +that sets up the websocket environment +this class serves as a configuration +tool for the websocket api defining +limits quotas and handlers +here i set common configuration +arguments for websocket messages +setting buffer sizes +for the different types +here i find the handler class to the ws +msg url which will receive all of the +websocket callbacks +before we go into the handler class +let's create a special service class to +handle location-based callbacks +similarly to the user service +LocationService +most of the location apis +map to the user class +but it's logically separate from the +user service +we will periodically update the user's +location +notice that location can only be updated +by the user himself +as the token is required +for that operation +it's more intuitive to work with radius +from the client but the jpa query +language +makes it easier to work in absolute +coordinates so i convert the kilometer +radius unit +to latitude longitude values +we have two versions of the query +one finds all of the drivers in the area +so we can draw them on the map +the second searches for available +drivers +only for hailing purposes +i use a version of the method that only +returns a part of the user data as we +normally don't need +all of the data +once this is in place we can implement +the handler class which is the actual +websocket implementation +but first let's review the communication +Handler - Packet structure for location update +protocol this is the binary structure we +will use when receiving request +on the server for a location update +so when a user changes his current +location we will send this data +the message type should be 1 for a +location update from the user +the length of the user token string +followed by a byte array of the token +length representing the string +notice that i used bytes instead of cars +since the token is 100 ascii +i can rely on that fact and reduce the +packet size further +the location data and the radius +slash direction of the user +a byte which is set to one when we are +hailing a taxi in which case it will +seek +only the available drivers +once this packet is processed the server +would return the cars within the search +radius by sending a packet back +Handler - Packet structure for response +in this case we don't need the token as +this is a message from the server +the response type can be 2 for driver +position update and 3 for available +driver position update +the entry +indicates the number of drivers +in the returned data +the rest of the lines repeat for every +driver +response size times +and include the position data for every +driver +now that we understand the protocol +let's dig into the code that implements +it +the handler class is a binary websocket +handler that receives callbacks on +incoming packets +let's go over the code +these are constants used in the binary +protocol to communicate the type of +request or response +this is a callback for a binary message +from the client +the api works with nios +bytebuffer which allows us to run +through a request +efficiently +we get the length of the user token +string and the battery again i used +bytes instead of cars since the token is +100 ascii +we can rely on that +assuming this is a location update +we pull out the data and update the user +object +we prepare to return a response based on +the seeking flag +we also need to mark the response type +correctly +i used a bytearray output stream to +construct the response +i use try with resources to close the +streams automatically when i'm done +i just write out the response data to +the stream +and finally we convert the battery data +from the stream to a battery +then send to the client +this is it for the basic server code diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/050-13-client-side-userservice.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/050-13-client-side-userservice.md index 156eb13203..7facbab2da 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/050-13-client-side-userservice.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/050-13-client-side-userservice.md @@ -11,8 +11,153 @@ weight: 50 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube l97sDefhHMM >}} + +## Transcript + +now that we have a server and a mock +client we need to connect them together +so we have a working prototype +we also need to implement some core +functionality such as sms activation +before we get started we need to add +some things to the client project +first we need to add the websockets cn1 +lib from the extension manager this is +pretty simple to do +if you already added a cn1 lib before +next we need to sign up to twillow.com +as developers +they have a free trial account notice we +don't need to install support for the +twillow lib since we already installed +the sms activation cn1 lib before +notice that we are sending the sms +activation code from the client side +this is a bad practice +you should use the server twillo api and +send the sms activation code from there +i chose this approach because it's +seamless and shows the usage of the apis +on the client which is what i'm trying +to teach +however +keeping authentication code in the +server is far more secure +you will need the following values from +the twillow developer account +account id +sid account sid +auth token +and phone number +make sure to pick a us phone number for +the free account otherwise payment would +be required +once you have those values you can +create a new globals class which we will +use for the global application data +notice you might want to replace +localhost with your ip during +development so you can test the device +against a server running on your machine +the device would obviously need to be +connected to the same wifi +the following values +are the values we have from twillow +for convenience i used static import for +these constants within the code +the user class on the client side +mirrors the user dao +but uses the properties syntax so we can +leverage observability json persistence +and other core capabilities +if you are familiar with properties +already you won't notice anything +special about this class it's just a +standard property object +if you aren't familiar with properties +please check out the video covering them +as it would be helpful moving forward +we need to define a connection layer +that will abstract the server access +code this will allow us flexibility as +we modify the server implementation and +the client implementation it will also +make testing far easier by separating +the different pieces into tiers +the abstraction is +similar to the one we have in the server +i chose to go with a mostly static class +implementation for the user service +as it's inherently a static web service +it makes no sense to have more than one +user service +once logged in +we will cache the current user object +here +so we have all the data locally +and don't need server communication for +every query +we bind the user object to preferences +so changes to the user object implicitly +connect to the preferences storage api +and vice versa +preferences allow us to store keys and +values in storage which maps every entry +to a similar key value pay +whether we are logged in or not or out +is determined by the token value +we need that to send updates to the +server side +i'm creating the four digit verification +code +and sending it via the twillow sms web +service api +i'm also storing the value and +preferences so i can check against it +when it's received even if the app dies +for some reason +this method +is invoked to validate the received sms +code +notice i don't just use equals +instead the validation string might +include the four sms text +this can happen on android where we can +automatically validate +notice i still limit the length of the +string to prevent an attack where a user +can inject all possible four code +combinations +into this method +maps to the user +exists method in the server which we use +to determine +add slash login flows +i use the rest api to make a single +connection with the get method +in this case the response is the string +true or the string force so i can just +check against the letter t +when adding a user i use the rest api's +post method +here i can set the body to the json +content content +of the user object +the response is a string token +representing the user which we can now +store into preferences +the login method accepts a phone and +password and is invoked +after we've validated the phone +it can succeed +or fail +if we get back a token that means the +user authentication exception +wasn't thrown in the server and we can +set it into the preferences +otherwise we need to send a failure +callback diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/051-14-sms-activation-and-interception.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/051-14-sms-activation-and-interception.md index bf055fafd4..aea9fb6e67 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/051-14-sms-activation-and-interception.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/051-14-sms-activation-and-interception.md @@ -11,8 +11,110 @@ weight: 51 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube TOWhbEhiRe4 >}} + +## Transcript + +the next step is binding this to the ui +for a fully working sms activation +process +the sms activation process is +practically done +the first step is in the enter mobile +number form where we need to change the +event handling on the floating action +button +this code might produce a dialog +if we show it in the current form it +might go back to this form instead of +enter sms verification digits form +by using this callback we can make sure +the next form is shown +only android supports intercepting sms's +so this code will only run on that +platform +in that case we automatically validate +against the string we get from the next +sms +if that works we automatically skip +ahead to the password form +regardless of the above +we send an sms message +to the given phone number +next we need to validate the input +in a case where sms isn't validated +automatically +or if the user rejected the permission +on android +we can do this in the enter sms +verification digits form class +by editing the is valid method +this pretty much does the sms activation +but we'd also want the countdown +functionality to work +if you recall the ui there is a +countdown label for resending the sms +to implement that we need to first +define two new member variables and +define two helper methods +the vari +the variables represent the countdown +value in seconds +for resending the sms +and the timer object +which we need to cancel +once it elapses +the format method +formats time in seconds as two digits +four minutes and two digits for seconds +next we need to make the following +changes to the constructor code +we schedule the timer to elapse every +second and repeat on the current form +we update the text which we draws +automatically notice that it's also a +good practice to revalidate normally +but since the string size would be +roughly the same +this shouldn't be necessary +we cancel the timer so we don't keep +sending the sms's over again +notice we don't cancel the timer in case +of success we don't need to +since it's a ui timer it's bound to the +form and once we leave the current form +it will no longer elapse +this sends us to the password entry form +where we now have two versions of the ui +we connect to the server to check if the +user exists and shown infinite progress +ui +over the previous form +if the user exists we show a welcome +back prompt +otherwise we enter a new password +we also need to change the code that +handles the floating action button event +to actually add or load the user +if the user exists +we call the login method and show the +map +on success +if the server returned an error on the +login +we dispose the progress dialog and show +the error message label +we prepared before +we use revalidate as the error label +size changed +and will occupy more space +if the user didn't exist before we +create a new user object and add that +user to the server +then show the map +if the operation is successful the error +handling code is pretty similar to the +previous code diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/052-15-location-service-client-side.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/052-15-location-service-client-side.md index 1738812cd0..af468dafa7 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/052-15-location-service-client-side.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/052-15-location-service-client-side.md @@ -11,8 +11,115 @@ weight: 52 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube TRF76P1Dwwc >}} + +## Transcript + +next we'll bind the websocket logic and +map UI to bring this all together +we can get started with a location +service class similarly to the user +service class that would abstract the +local location +unlock the user service class the +location service class should also deal +with the physical location of the device +these are the same constants we have on +the server +when sending a location update to the +server I don't want to exceed a fixed +amount of updates so we won't burden the +server or our Network +the Clause has a private Constructor so +the only way to create the location +service is via the bind method which in +turn invokes the instance method bind +impul +we provide two callbacks one is for a +car being added which we will use to +bind a new car to the UI and the other +is for location updates so we can +position the map +location update notifies the server +about changes to the location and also +invokes the Callback once we can +position the map +we invoke the callback with the location +so the map can be shifted to our current +position +once the server socket is connected we +start sending location updates there +we open the websocket connection using +the connect call +we cache the last set of values so we +don't send data to the server unless +something changed +easy threads lets us Post jobs onto a +dedicated thread so we don't have to +block the main EDT +it also means we don't need to deal with +synchronization or any other complexity +related to that as all operations happen +on that thread +until on open is invoked the connection +isn't ready +that's why the server member field is +only initialized here +when it's actually ready +if we already have a location we should +send a user location update +one we have once we have the socket +Connection in place +the connection is single threaded as I +mentioned before +there is this it method is similar to is +EDT and indicates if the current thread +is the one managed by the easy thread if +not we use the run runnable which +invokes the Target runnable on the easy +thread similarly to call serially +if the values didn't change since last +update so we do nothing +we don't update too much there is a +chance we'll miss an update here but +it's probably not a deal breaker if a +user didn't move much +we create a byte array output stream +into which we construct the message that +we received on the server with the +header location +Etc +I currently hard coded a one kilometer +search radius and find explicitly that +we aren't in taxi hailing mode +one line to actually send the binary +data it would be similar with text Data +with the exception of passing overhead +the IR exception isn't likely as this is +a ram-based stream +the +here we received the messages sent from +the server specifically driver search +result +we store user instances in a map where +the user ID is the key +this saves us from sending duplicate +core added events and allows us to just +mutate the user properties which other +code can observe using the built-in +listeners and properties +notice that this code is running on the +websocket thread so events need to go +back into the EDT to prevent potential +issues +this is really important we need to +handle errors properly in a websocket +application otherwise a failure can +leave us without a connection +the Callback interface is Trivial it's +mostly used as a Lambda expression in +the code +and that's it for the location service diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/053-16-connecting-the-location-service-to-the-map-form.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/053-16-connecting-the-location-service-to-the-map-form.md index 8ddc16fcab..eae873b7bc 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/053-16-connecting-the-location-service-to-the-map-form.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/053-16-connecting-the-location-service-to-the-map-form.md @@ -11,8 +11,71 @@ weight: 53 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube ymocxBIQn0o >}} + +## Transcript + +we discussed the location service class +now let's see how this maps into the ui +back in the map form we can change the +map player code which during markup just +featured a fixed position car image +we bind to the location service where we +get a callback every time a driver comes +into play +we create a label for every car and set +the icon with the right angle we keep +the angle in a client property +so when there is a change event +we can check if the angle actually +changed +one important piece of information that +might not be clear from the code is that +the core image must be square +we use the rotate method on the car +image which assumes a square image +otherwise it will appear cropped +we place the new car where the user is +located +thanks to the map layout +the change listener on the angle +property +automatically rotates the icon image in +the right direction +but it does that only if the angle +changed +to avoid performance +penalty we update latitude and longitude +separately +but we need to guard against duplicate +changes so we first test the existing +value +we can't replace a constraint in the +layout so we remove the component and +add it back +animate layout should still work in this +case +and it will move the car gracefully to +its new position +once all of this is done +we should be able to see everything +working right now we don't have drivers +in our database but we can add a fake +driver by pushing an entry to the mysql +database +this will create a fake driver entry and +allow you to see him when you log in +assuming you configured the values in +globals.java you should be able to run +the server and client then activate the +device using sms +and see the driver +i used the password from my account so i +would be able to log in as the driver +later +you can just copy the same password +value from your account +as you already know the password diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/054-17-reverse-gecoding-google-webservice.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/054-17-reverse-gecoding-google-webservice.md index 4791f57ce6..7fcc37a69b 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/054-17-reverse-gecoding-google-webservice.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/054-17-reverse-gecoding-google-webservice.md @@ -11,8 +11,225 @@ weight: 54 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube A72rY4rU7E0 >}} + +## Transcript + +we finally have a client server +application that works +but we don't have any real functionality +to get to that point we need some web +services +WebServices from the Device? +i chose to invoke the google web +services from the device code instead of +using our server to proxy the calls +i did this because this is a mobile +programming course not a server +development course +this makes sense for a simple project +and is arguably faster as i connect +directly to google +however if i was building a real world +application i would have directed all +requests to our server and invoked +google from there +this might have a performance penalty +but it's worth it +here are some of the advantages +api keys would be stored on the server +and would thus be more secure from theft +we can cache queries and reduce api +costs between users +currently i can only cache calls per +user but if lots of people are searching +for the same things +this can add up +i can use analytics and statistics in +the server code +and notice patterns about user behavior +and finally i can switch to a different +company for the web service +implementation to get a better deal +if google discontinues its service i +don't have to ship an app update either +Googles GIS Services +we'll use +four google location-based web services +to implement the functionality of this +app +with geocoding we provide an address or +location and get back a location on the +map +this is useful when a driver receives a +set of locations and need to know +the actual map coordinates +reverse geocoding +is the exact opposite +it provides the name of a given location +this is useful for pointing a pin on the +map and naming the location +directions provides +directions trip time etc we can get the +points of the path and plot them out on +the map +the places api allows searching for a +place similar to the geocoding api +the auto complete version +lets us type into a text field and see +suggestions appear +we'll use it for the search +functionality +New Fields in Globals +all of these apis require developer keys +which you can obtain from their +respective websites +i've edited the globals class to include +these new keys required by the three +apis +make sure to replace the dashes with the +relevant keys +you can get the keys by going to the +directions geocoding and places websites +and follow the process +there +we use the maps api geocode json url for +reverse geocoding +google provides this example for usage +of the api +it's just a latitude longitude pair +and your api key +Reverse Geocoding - Result +the result of that url +look like this response json +let's go over two important pieces +we need to get this result array from +the response +we only care about the first element and +will discard the rest +this is the only attribute we need at +this time from this api +now that we know what we are looking for +let's look at the code that accomplishes +this +SearchService +i'll use the search service class to +encapsulate this functionality +for each of these services +there is an edge case where location +isn't ready yet when this method is +invoked +in this case i found it best to just do +nothing +usually it's best to fail by throwing an +exception +but that is a valid situation +to which i have a decent fallback option +so i prefer doing nothing +if we send two such calls in rapid +succession i only need the last one +so i'm canceling the previous request +the reverse geocode api +latitude long argument +determines the location +for which we are +looking +we get the past result as a map +containing a hierarchy of objects the +callback is invoked asynchronously +when the response arrives +this gets the result list from the json +and extracts the first element from +there +we extract the one attribute we care +about +the formatted address entry and invoke +the callback method with this result +the places autocomplete api is a bit +more challenging since this api is +invoked as a user types +we'll need the ability to cancel a +request just as we would with the +geocoding calls +caching is also crucial in this case +so we must cache as much as possible to +avoid overuse of the api +and performance issues +let's start by reviewing the api url and +responses +the default sample from google wasn't +very helpful so i had to read the docs a +bit and came up with this url +the search is relevant to a specific +location and radius +otherwise it would suggest places from +all over the world which probably +doesn't make sense for an uber style +application notice the radius is +specified in meters +the input value is a the string for +which we would like +autocomplete suggestions +Places Autocomplete - Result +this request produces this json result +all predictions +are again within an array but this time +we'll need all of them +the ui would require the text broken +down so we need the main text +and we'll need the secondary text to +we'll also need the place id and the +reason for this is a huge omission in +this api +notice it has no location information +we will need the place id value to query +again for the location +SuggestionResult +before we move on to the code +will need a way to send the results back +we can do that with a list of suggestion +result entries +this is a pretty trivial class and +doesn't require any explaining +the class solves the issue of getting +the location for a specific entry +with the method get location +i won't go too much into the details of +that code above since it's +very similar to the code we saw before +we just get additional details about a +place and parse the results +notice that this is a part of the +suggestion result class +so we don't invoke this unless we +actually need the location of a place +there is one last thing we need before +we go into the suggestion method itself +we need variables to cache the data and +current request +otherwise multiple incoming requests +might collide and block the network +we need the last +suggestion request so we can cancel it +the last suggestion value +lets us distinguish duplicate values +this can sometimes happen as an edit +event might repeat a request +that was already sent +for example if a user types and deletes +a character +this can happen since we will wait two +500 milliseconds before sending +characters +the location cache +reduces duplicate requests notice that +this can grow to a level of a +huge memory leak +but realistically that would require a +huge number of searches +if this still bothers you we can have +the cash map class +that serializes extra data to storage diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/055-18-directions-and-places-google-web-services.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/055-18-directions-and-places-google-web-services.md index b313b7a783..772e50bd66 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/055-18-directions-and-places-google-web-services.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/055-18-directions-and-places-google-web-services.md @@ -11,8 +11,105 @@ weight: 55 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube KhSWyE6rAN8 >}} + +## Transcript + +we can now move forward to the last two +pieces of the search service class +if the last request is this request +which can happen as a user types and +deletes etc +then we don't want to do anything +let the last request finish +however +if it isn't then we want to kill the +last request which might still be queued +and blocking us from going forward +we check if an entry is already in the +cache +as users might type and revise a lot +thus triggering significant web service +cost overhead +we clean the variable values +and then invoke the response +notice i use call serially +in this case to defer the response to +the next cycle +if we call back immediately we might +delay the input code which is currently +in place +by shifting the callback to the next edt +cycle we guarantee that suggest +locations will behave in a similar way +whether the data is cached locally or +not +the request extracts the predictions +array so we can construct the result +list +we iterate over the entries +notice i discard the generic context +which is legal in java but might produce +a warning +i could have used a more elaborate +syntax that would have removed the +warning but that would have created more +verbus code with no actual benefit +i extract the elements from the map +and create the suggestion result entries +then store the whole thing in cash +followed by the on success call +notice that this in this case i didn't +need the call serially since the +response is already asynchronous +the final web service api we will cover +is the directions api which will allow +us to set the path taken by the car on +the map +the directions api is challenging it +returns encoded data in a problematic +format +this is the sample query from google +notice we can give the origin and +destination values as longitude latitude +pair +which is +what we'll actually do +the response is a bit large so i trimmed +a lot of it to give you a sense of what +we are looking for +the one thing that matters to us from +the response +is the overview polyline entry which +seems like a bunch of gibberish but it +isn't +this is a special notation from google +that encodes the latitude longitude +values of the entire trip in a single +string +this encoding is described by google +in their map documentation +being lazy i found someone +who already implemented the algorithm in +java +and his code worked as is +i won't go into the code since it's +mostly just bitwise shifting to satisfy +requirements from google +the method signature is the only thing +that matters +it takes an encoded string and returns +the path matching that string +as a list of coordinates that we will be +able to add into the map +shortly +now that this is all out of the way the +directions method is relatively simple +this method is just another rest call +that doesn't include anything out of the +ordinary we extract the overview +polyline value and pass it to the +callback response diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/056-19-auto-complete-location-search-ui.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/056-19-auto-complete-location-search-ui.md index bc107ac57d..eb099b0bce 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/056-19-auto-complete-location-search-ui.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/056-19-auto-complete-location-search-ui.md @@ -11,8 +11,172 @@ weight: 56 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube Jk3tTyZroP0 >}} + +## Transcript + +now that the basic infrastructure is out +of the way +we'll start wiring it into the ui +starting with search +the initial search ui i did in the +mock-up was very naive +it just toggled the completion on and +off +the real uber app allows us to swipe the +search ui down and then pick a location +in a map using a pin +this then updates the text field with +the selected location +alternatively if you type into the text +field locations are suggested to you as +you type +there is a great deal of nuanced +behavior in this ui so i only focused on +the big ticket features +the first big feature is the swipe down +feature which allows us to swipe down +the completion ui as we type +the second is the actual completion ui +where elements update as we type +and finally the ability to point at a +location on the map +with a pin +and select that location +i didn't implement a lot of the +relatively easy features such as +bookmarked locations or history in the +search ui +those should be trivial to fill in +in order to implement these i moved most +of the ui logic into separate classes +specifically auto complete address input +and completion container both of which +i'll discuss shortly +a major feature of auto complete address +input is its ability to fold slash +unfold the completion container +it accomplishes this by binding pointer +listeners to the parent form and using +them to implement the drag and drop +behavior +on the left you can see the search ui +with completion suggestions appearing +below +when the suggestions are dragged down +we +can pick the location from the map as +you can see on the right +as the map is dragged the location name +is updated +into the text field +i refactored some of the code from the +map form class into the autocomplete +address input class +it made it easier to implement some of +the related logic +i chose to derive text field rather than +encapsulated +mostly due to convenience +encapsulation would have worked just as +well +for this case +with the exception of these last two +variables every other variable here is +in +the service of the drag and drop logic +we use the data change listener to send +events to the completion logic +however +this callback can be very verbose and +it's sometimes invoked by set text +the solution is a special version of set +text that blocks this callback and +reduces the noise +in the completion code +with the block change event variable +the last focused text field is the one +that now handles the completion +so if the user was in the to text field +everything typed will now impact the +completion for two +and vice versa +pointer listeners on the form allow us +to detect pointer events everywhere +we bind them in the init component +method and remove them in the +de-initialize method +this prevents a memory leak and a +situation where pointer processing code +keeps running and taking up cpu +the initialize is invoked when a +component is removed from the ui or its +hierarchy is removed +it's also invoked when a different form +is shown instead of the current form +init component is invoked when a +component is there +it will be invoked if a component is +added to an already showing form or if a +parent form is shown +you can rely on init component and +de-initialize working in tandem +they might be invoked multiple times in +a valid situations for instance a +dialogue shown on top of the form +triggers a de-initialize on the +components of the form +followed by an init component when it's +disposed +despite using the shorthand lambda +syntax for event handling i need to keep +a reference to the drag and release +event objects so i can remove them later +the dragged element is always the second +element 0 is the first +it can be dragged between the center +location and the south location +if this is indeed a drag operation we'd +like to block the event from propagating +onwards +when a component is in the south we set +its preferred size to 1 8 of the display +height +so it won't peak up too much +when it's dragged up we just increase +that size during drag +components in the center ignore their +preferred size and take up available +space so we use margin to provide the +drag effect +this prevents a drag event on a +different region of the form from +triggering this event +for instance if a user drags the map +dragging just displayed a motion +we now need to remove the component and +place it where it should be we also +reset the ui id +so styling changes for instance margin +unit type etc +will reset to the default +when we place the container in the south +we set the preferred size and margin to +match +when we place it in the center we set +the preferred size to null which will +which is a special case +that resets previous manual settings and +restores the default +the location of a text field +uses strings +but what we really care for care about +is +coordinates on the map +which is why i store them here +this is used both by the map pen logic +and by the search logic we will use +later diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/057-20-search-completion-container.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/057-20-search-completion-container.md index aa2329bb2b..2351c555e8 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/057-20-search-completion-container.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/057-20-search-completion-container.md @@ -11,8 +11,85 @@ weight: 57 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube T8l7k2OeGpo >}} + +## Transcript + +we'll continue the search ui +implementation as we step into the +completion container class +the completion container +tries to coordinate the two instances of +the autocomplete address input class +by providing a single class that handles +the completion ui +most of the code in this class should be +very familiar +and some of it is refactored code from +the map form while other code relates to +the suggest suggest location api +we implemented earlier +the name of the class is misleading +completion container is not a container +here instead of deriving i encapsulated +the ui logic and tried to expose only +the business logic +event dispatches allow us to broadcast +events +using the add remove listener observer +style api +we use this dispatcher to broadcast an +event when a user presses a completion +button +this method is invoked when completion +is in progress +it invokes the web service call to +request completion suggestions for a +given string +we fill up the container with buttons if +one of the buttons is pressed we fetch +the location from the web service and +fill it into the autocomplete +address input +we then fire the event dispatcher to +process the actual selection in the ui +we have two types of entries here +one with only one line of text and one +with two lines of text +this is mostly in place to fit the ui +ids correctly with the right underline +behavior +this method is invoked externally +to clear up the content of the +completion ui and show the clean set of +initial options +history is positioned here +so we could later fill this with actual +search history etc +this method constructs and animates the +completion ui into place +notice that we place the content in a +container which we wrap up in a border +layout +this allows us to manipulate the +preferred size without breaking the +scrolling behavior +of the child container +in order to accomplish the design for +the buttons i had to add the where to +button line to uiid +it uses grey text on a transparent +background +padding aligns with the text above +we keep the top padding to zero so it +won't drift away from the first line in +the where to button +margin is zero +as usual +we have an underline here instead of the +underline of the first line of text +we use a slightly smaller font but the +standard main light font nevertheless diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/058-21-plotting-the-route-on-the-map-setup.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/058-21-plotting-the-route-on-the-map-setup.md index 4ad44bedb3..288ac67fab 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/058-21-plotting-the-route-on-the-map-setup.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/058-21-plotting-the-route-on-the-map-setup.md @@ -11,8 +11,108 @@ weight: 58 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube FKTM8jAepJs >}} + +## Transcript + +next we'll bring these changes into the +map form class which handles the actual +heavy lifting of search +first we need to add a couple of members +to the class +some of these variables really exist in +the method body +i just move them into the class level +i'm skipping that code since it's pretty +trivial +we use map container to place a pin and +position it +we can track map position events using +this object +we need the last focused entry +so we can set the text in the text field +when the user drags the map to point at +a location +the listener is important for cleanup to +prevent multiple listener instances +that way we always have at most one +the timer instance allows us to cancel +it +we use it to delay web service requests +so we don't send them too frequently +the where to button +we need to hide it when the search ui is +showing and show it again when it's done +we place a lot of elements in that layer +on top of the map +it's pretty useful +indicates whether we are in the +navigation mode or in another mode such +as map or browse mode +this is important as we can't enter +navigation mode twice +Code +now that we have these variables in +place +let's look at the code +we created and placed a new layer for +the pin image placement +this allows us to drag the completion +container down and see the pin image on +the map +that also means we can remove it easily +once we exit the search ui +i refactored the text fields to use this +new api +and set the location to the current +location +be to the default +normally a user wouldn't enter the +origin address +only the destination +so using the current location makes +sense +Map +i'm using +name my current location method +to fetch the name of the location of +origin +the map listener is used for the point +location on the map functionality +if i drag the map it will fetch the +location from there and set it to the +last focused text field +however we wait 500 millisecond seconds +before doing that +so we don't send too many web service +requests +we cancel the timer if there is one +that's already in the waiting stage +this used to be if layer dot get +component count is greater than one +but that doesn't make sense anymore as +the completion container is always there +only folded or expanded +so +i check if the completion container is +in the center +or the south +when a button is pressed in the search +completion we get an event to begin +navigation at which point we ask for +directions and enter navigation mode +i'll discuss the whole navigation mode +in the route section +i use animation completion event to show +the completion bar which also has an +animation +in place +one thing i neglected to mention in the +map is the map listener +which we bind using this new method +this prevents duplicate map listeners +and allows us to easily clear the +selection diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/059-22-plotting-the-route-on-the-map-to-from-tags.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/059-22-plotting-the-route-on-the-map-to-from-tags.md index 4a97ff6fba..b0e0611c67 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/059-22-plotting-the-route-on-the-map-to-from-tags.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/059-22-plotting-the-route-on-the-map-to-from-tags.md @@ -11,8 +11,137 @@ weight: 59 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube vjTexRDCihA >}} + +## Transcript + +with that out of the way search should +work +but how does it display the result +for that we need to add additional +features to the map form that address +these capabilities +before we go into that +let's check out what that means +Ride Booking UI +the ride booking ui plots the path of +the ride on the map +and includes two tags one pointing at +your current location and one on the +destination the first tag is divided to +a time indication and name +the latter only contains the name +notice that the elements have rounded +rectangle shape +with a pointy end on one side to point +at the position within the path +Tags +let's start with creating these tags +the tag code itself is trivial it's just +a label or a container with some details +nothing special +except for one important detail +the black and white border +in order to implement the unique shape +of the tag i created a new border class +notice i used the preferred width of the +west component to determine the black +section +this is done in the black line position +method +before we go any further i'd like to +make one point clear +i would have used a nine piece border if +this were a real application i would +have just cut a nine piece border and +moved on +however +since the point is teaching i chose to +do this the hard way +UI IDs +before we get into the black and white +border +there are a few ui ids we need to define +the navigation label uid +is the black on white label that appears +in the tag +the only unique thing about it +is the relatively small amount of +padding +so the tag doesn't take up too much +space +the margin is predictably zero +and the font is slightly smaller than +usual +the navigation minute label ui id +is the white on black minute value +it's center aligned +it has zero padding below to keep the +text and number close together +but has similar padding on the other +side either sides to match the +navigation label +it has zero margin +and it has a smaller font size than +usual although not a tiny font +the navigation minute disk label uid +is used for the text below that the text +with the word min +it derives from navigation minute label +and has an even smaller font size than +that +Border +that these are out of the way +let's take a look at the border +notice we can just subclass the border +class just like we can implement +painters etc +this provides a similar path for +customization but is sometimes more +flexible +most of this code is based on the +built-in +round wrecked border class +drawing this type of border is pretty +expensive +so we draw onto an image and place that +image in cache within the component +using put client property +we use this value +as the key +the black line position is used in the +version of this border that's partially +black +here we create the border image that we +will cache for the given component +since a shadow is set on the border and +that can take up some processing power +to speed this up we have two versions of +the method +fast and slow we call the fast one and +invoke the slow one asynchronously to +update the border +we do a shadow effect by drawing a +gradient with varying alpha degrees then +blurring that out +if we have a cached version of the +border image we will just use that as +the background of the component +assuming the size of the component +didn't change +otherwise we create that image and +update it later +with the slower version that includes +the gradient shadow effect +we create the shape of the component +if it's the one with the black line we +place the corner in the top right +otherwise we place it in the bottom left +we can now fill out the shape as part of +the image creation code +if we have a black line we do the fill +operation twice with different clip +sizes to create that effect diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/060-23-plotting-the-route-on-the-map-completion.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/060-23-plotting-the-route-on-the-map-completion.md index dae2f3621c..e226444361 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/060-23-plotting-the-route-on-the-map-completion.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/060-23-plotting-the-route-on-the-map-completion.md @@ -11,8 +11,126 @@ weight: 60 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube aW3OlGTmasw >}} + +## Transcript + +finally after all the buildup let's draw +the path on the map form +we've had +quite a bit of code and it brought us to +the point where the navigation ui should +function as expected +this is implemented in the enter +navigation mode method +which i mentioned before +as you might recall it it's invoked when +clicking an entry in the search results +this method is invoked from a callback +on navigation +we have the coordinates of the path to +display on the map +as the arguments to the method as you +may recall these coordinates are +returned by the directions +method the first thing we do is remove +the existing search ui from the form +and animate it out +due to the way events are chained this +method can be invoked more than once in +some unique cases +this works around that behavior +we convert the path to an array and add +it to the map +this uses native path +plotting for these coordinates +i could have used a painter or something +similar +and might still use it later on +i move the camera to show the entire +path within the form +i create the two tags and add them to +the ui +notice that the from tag has a right +alignment +also notice i used the trimmed string +method to limit the string length +i'll cover that method soon +the back behavior is just a button style +to look like a command it invokes the +exit navigation mode method which we +will get to shortly +this is the ui to approve the taxi we +are ordering +i used a white container with the form +uiid on the south portion of the form +as a layer +Trimming +before i continue +i also use the trimmed string method in +the code before to trim the tag +components +there isn't much to say about this +method we rely on the fact that +addresses usually have a comma after +them +if the string is missing that or is too +long we have special cases for those +this guarantees a string of decent +length +for the tag elements +Exit Navigation +the one last missing piece of the code +is the exit navigation mode call +which just removes all the elements and +sets the invisible pieces back to +visible +it's pretty trivial +UI +we also have a few ui ids of note +mentioned in the code +the first is ride title which is the +title area for the ride ui it's pretty +much a label so it has black over +transparent colors +but +it's centered +it has the same padding as a label +zero margin +and same font +margin separator is a separator that has +marginal margins on the side +which we use in the ride dialog +other separators in the app +reach the edge of their parent container +it has a couple of pixels of padding in +the bottom +to leave room for the separator +it has the margin to keep it away from +the edges of the parent container +and it features a standard underline +border +the black button +is just a standard white over black +button +with center text alignment as is common +with buttons +it's got some padding but not too much +so it won't look huge +it's got some margin so it won't +literally touch +the things next to it +and to compensate over smaller padding +it uses a subtool round wrecked effect +that just +is just barely noticeable at 0.2 +millimeters +and it's got a slightly +larger regular font instead of the +typical smaller light font +once all of this is done we can just see +navigation work and appear on the map as +expected diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/061-24-hailing-in-the-client-showing-a-beacon.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/061-24-hailing-in-the-client-showing-a-beacon.md index 780833dbcf..f709fb6693 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/061-24-hailing-in-the-client-showing-a-beacon.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/061-24-hailing-in-the-client-showing-a-beacon.md @@ -11,8 +11,79 @@ weight: 61 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube qEgDZHZJEYo >}} + +## Transcript + +now that we have a path on the map +we can move forward to the hailing +process +the hailing process is relatively simple +we tint the ui show a beacon and during +that time we ask the server for a call +we can start by adding an event handler +to the black button from the enter +navigation mode method +we are effectively coloring the pin +layer to create the tint effect +we added a new blink dot class +to implement the pausing blue dot effect +another new api +the hail ride method in location service +allows us to hail a ride +notice i don't show anything when the +ride is hailed i'll add that workflow +with the driver app later +there is one uid we need to cover here +and it's the searching dialog ui id +technically this ui element isn't a +dialog it is a label +but it looks like a dialog +padding is pretty standard for label +we have some margin on the sides to +space it out from the edges +most uii of this ui id is pretty +standard except for the use of the +special mode of the round wrecked border +the top only mode allows only the top +portion to be rounded and the bottom +appears square +usually we use it to combine two borders +together +with different colours or ui ids +in this case +we give the component a feel of peaking +from the bottom of the form +the font is the standard font just like +any other label +the blinker.class is pretty trivial +i could have used an animated gif but +instead i just did this +this is mostly for transparency we don't +really use +the uid here +i use +low level animations here +so the best practice is to register +remove +with init component +slash the initialize +the motion class represents a timed +motion +between values which allows us to +animate a value from point x to point y +in this case i'm just growing the circle +using the value +notice +only the animate method mutates values +as the paint method can be invoked more +than once per cycle +in theory +the drawing logic is mostly hard-coded +i would have used the shape api to get a +more refined effect +but it would have made things more +complicated diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/062-25-hailing-in-the-client-networking-and-sending-push-messages.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/062-25-hailing-in-the-client-networking-and-sending-push-messages.md index b29da1b134..914346f845 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/062-25-hailing-in-the-client-networking-and-sending-push-messages.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/062-25-hailing-in-the-client-networking-and-sending-push-messages.md @@ -11,8 +11,136 @@ weight: 62 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube 54POZ4PFFBw >}} + +## Transcript + +next we'll go into the underlying +business logic portion +of the hailing process on the client +up until now i kept hailing as a vague +process +i think uber probably has a far more +elaborate system than the one i +developed in an hour +but this should work fine for most cases +healing includes the following phases +we mark what we are interested +that we are interested in hailing in the +location service websocket code +the server checks which drivers are +available in the area and returns to us +their push keys +we send push notifications to the +available drivers +as time moves on we expand the circle of +search for drivers +in order to do this i had to make some +changes to the websocket protocol +in the location service class +but first we need some additional +variables in the globals class +i'll go into more details on those +values in the next chapter +but for now we need the variables only +all of these apis require developer keys +which you can obtain from their +respective websites +i've edited the globals class to include +these new keys required by the three +apis +right now we can leave these all blank +and get to them +later +let's move to the location service class +and the variables i had to add +when a driver accepts our hail +the server sends a special message +indicating that the hail was accepted +previously we had two modes for polling +the server for searching and not +searching +i added a third mode that allows us to +disable hailing +i've made the location service into a +singleton +this these represent whether we are +hailing +and if so to what radius +we use a motion object to get a growing +radius that will stretch over time to +encapsulate a wider region for hailing +our source and destination values which +we need to broadcast a hail +when we send a push notification to a +car we need to make sure we didn't +already notify it +the unique id of the driver we've found +this is the callback we invoke when a +driver is found +now that these are out of the way let's +look at the other things that need doing +we changed the way we handle +communication protocol by and we added +some additional details +first we need to ignore location changes +when doing hailing +which we can do by adding that condition +next we need to change the protocol a +little bit +this was previously limited to 0 only +and now we check if we are in hailing +mode +during hailing mode the radius of search +grows over time +we send the from two values as utf-8 +encoded strings +which allows us to communicate locale +specific locations +when we turn off healing in the server +it's a one-time thing after it's off we +can go back to the regular mode +this isn't likely as this is a ram based +stream +we also need to handle +message reception code +this is a new special case that provides +us with details on the driver that +picked up the right +we are provided with the driver id card +and name +notice we need the final user variable +since car might change and the value +that can change +can't be passed to an inner class or +lambda in java +this is a list of push keys +who we should not notify +i added a push token to the driver +details so we can send a push message to +a specific driver +if the car wasn't notified yet +add it to the list of cars +that we should notify +we send the push message +in a batch to speed this up +we send push type 3 which includes a +data payload the first section +and a visual payload which you can see +after the semicolon +before we can compile that code we need +to add a push token attribute to the +user class +finally we have the hail ride method +which is relatively simple +there isn't much we need to cover here +it just initializes the variables and +starts the motion object for the +expanding radius +this should conclude the client side of +the hailing process +and now we need to address the server +side diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/063-26-driver-app-server.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/063-26-driver-app-server.md index 3d464f3730..a10cba6350 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/063-26-driver-app-server.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/063-26-driver-app-server.md @@ -11,8 +11,192 @@ weight: 63 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube KckMUmmSzd4 >}} + +## Transcript + +before we proceed into the actual driver +app work and push notification +we need to implement all the +infrastructure in the server side +i also need to implement some changes we +need in the protocol +start by looking at the uber class +i had to add three new fields and modify +slash add some methods +hailing from and healing 2 allows us to +communicate our trip details with the +driver community +i need the push token of drivers so we +can hail them directly from the app +i'll discuss the ride dao class soon it +allows us to send the details about the +trip to drivers +not much of a change +but i added the push token to the user +dao factory method +ride isn't as simple as rydao despite +their common name +it contains far more information +currently we don't use all of that but +the fact that it's logged will let you +provide all of that information within +the app or a management app easily +the right class is a jpa entity similar +to the user class +i used an auto increment value for the +id instead of a random string +i wanted to keep things simple +but notice this can expose a security +vulnerability +of scanning for rides +the passenger and driver are relational +database references to the respective +database object representing each one of +them +the route itself is a set of waypoints +sorted by the time associated with the +given waypoint +we'll discuss waypoints soon enough but +technically +it's just a set of coordinates +i really oversimplified the cost field +it should work for some and currency +but it's usually not as simple as that +it's important to use something like big +decimal and not double when dealing with +financial numbers +as double is built for scientific usage +and has rounding errors +we have two balloon flags a ride is +started once a passenger is picked up +it's finished once he is dropped off or +if the ride was cancelled +the companion crude ride repository +is pretty standard with one big +exception +i added a special case finder that lets +us locate the user that is currently +hailing a call +notice the syntax b dot driver dot id +equals +question mark 1 +which points through the relation to the +driver object +the waypoint entity referenced from the +right entity is pretty trivial +notice we still need a unique id for a +waypoint even if we don't actually use +it in code +the interesting part here +is the time value +which is the value of system current +time melees +this allows us to build a path based on +the time sequence +it will also allow us to reconstruct a +trip and generate additional details +such as speed cost +if we wish to do that in the future +notice that there is also a waypoint +repository interface i'm skipping it as +it contains no actual code +the right service class serves the same +purpose as the user service class +focusing on rides and driver related +features +i could have just stuck all of this +logic into one huge class +but separating functionality to +different service classes based on logic +makes sense +we manipulate both the rides and users +crude objects from this class +healing is a transactional method +this means that all operations within +the method will either succeed or fail +depending on the outcome this is +important to prevent an inconsistent +state in the database +this method +can be invoked to start and stop hailing +in this case we use the assigned user +property to detect if a driver accepted +the ride +if so we return the driver data to the +client +when a driver gets a notification of a +ride he invokes this method +to get back the data about the ride +if the driver wishes to accept the ride +he invokes this transactional method +the method accepts the token from the +driver and the id of the user hailing +the right +it creates a new write entity and +returns its id +from this point on we need to refer to +the right id and not the user id or +token +start ride and finish ride are invoked +by the driver when he picks up the +passenger and when he drops him off +normally +finish ride should also handle elements +like billing etc +but i won't go into that now +the next step is bringing this to the +user through a web service +the ride web service class +exposes the ride service call almost +verb team to the client +the get call +fetches the right dial +for the given user +id start and finish rides are again very +simple with only one argument which is +the ride id +we also have to add some minor changes +to the uber service and location service +classes +let's start with the user service class +drivers need a push token so we can hail +them +this is always set outside of the user +creation code for two reasons +the first time around the user is +created but the push key isn't there yet +it arrives asynchronously +push is re-registered in every launch +and refreshed so there is no reason to +update the entire object for that +the user web service class needs to +mirror these changes obviously +there isn't much here we just added a +new set push token url +and we accept this update +the location service +needs a bit more work +every time we update a user's location +we check if he's a driver on a ride +assuming we have a ride object we check +if this is currently an ongoing ride +that wasn't finished +if so +we add a waypoint to the ride and update +it +so we can later on inspect the path of +the ride +this pretty much tracks rides seamlessly +if we wanted to be really smart we could +detect the driver and use a position to +detect them traveling together and +automatically handle the ride +there are obvious problems with this as +it means a user can't order a cab for +someone else +but it might be an interesting feature +since we have two close data points diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/064-27-driver-app-server-websocket-portion.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/064-27-driver-app-server-websocket-portion.md index aa5a159cf6..31f3fce1bd 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/064-27-driver-app-server-websocket-portion.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/064-27-driver-app-server-websocket-portion.md @@ -11,8 +11,54 @@ weight: 64 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube IaalGP1UDeU >}} + +## Transcript + +the code for morph transitions +broke another thing +it broke the facebook or google login +form +which looks awful going in now because +morph is generally designed for a +specific form +i want to use the vertical cover effect +which is common on ios and looks pretty +decent on android 2. +cover slides the form on top of the +existing form from the bottom +it's usually combined with uncover +which slides the form out in the reverse +way +because of this unique semantic the +cover transition uses both the in and +out transition flags +however this can pose a problem +with the default out transition of the +form that we are leaving +in this case you would see the out +animation of the login form +which in this case is morph +followed by the incoming cover animation +the solution is to remove the out +animation from the outgoing form +and restore it to the original value +when we get back +we do that within the remove transition +temporarily method +which we call here from the facebook or +google login form +we need to remove both the in and out +transitions +as we might show a cover transition +on top of another cover transition form +when we return to the original form we +restore its transitions to their +original values +we remove the show listener to prevent a +memory leak +and multiple restore calls when going +back and forth diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/065-28-the-driver-app-2-apps-in-one-project.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/065-28-the-driver-app-2-apps-in-one-project.md index 1df81e68f4..39f0c2bc2b 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/065-28-the-driver-app-2-apps-in-one-project.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/065-28-the-driver-app-2-apps-in-one-project.md @@ -11,8 +11,171 @@ weight: 65 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube 91BrBoia4nM >}} + +## Transcript + +when i started on this road i didn't +want to create a driver app +and wanted to focus only on the client +app +unfortunately a driver app is +unavoidable as the main app is useless +without it +unlike the main uber app i don't want to +clone the driver app as this would throw +the whole course out of focus +i decided to hack the existing app to +implement driver specific features there +and as a result we use a whole lot of +code +this means the driver app can reuse sign +up map networking code etc +this is important +as code reuse breeds stability and +maturity +unfortunately it also means the app +isn't as well made +as the main app +since it was designed for end users with +driver mode tacked on top +Use Cases +we often build one app and sell it to +multiple customers +after all +most customers ask for similar things +with minor changes +for example if i build a restaurant app +and then sell it to one establishment i +can then resell it to another one with +almost no change at +all another common use case is the demo +or free version of a paid app +you want to use as much of the work as +possible without maintaining two code +bases +another case which is relevant what i'm +building today +is two target audiences of roughly the +same functionality +for instance in this case the driver app +has many common elements with the +passenger app +so why not build the same app with minor +modifications +the first thing we need to understand is +how the app store identifies your +application +pretty much +all apps use a unique identifier +a +string that is similar to package names +which we map to which we map the main +application package name so the trick is +to add a new package for the new app +Creating a New Package +the basic hello driver +would be something like this +notice i chose to override the methods +but i didn't have to +i could have just derived the main class +and that would have been enough +but i prefer overriding the lifecycle +methods for clarity in this case +this isn't enough though +the codename one settings properties +file contains all of the internal +configuration details about project +i copied it aside and renamed it to +codename one settings user properties +i then edited the file to use the driver +details and copied that into codename +one settings driver properties +notice i snipped most of the text here +and there are several other locations +where the values +are mentioned +Updating the App +it's important to update the app id +to use your apple developer account +prefix and package name +this is automatically generated when you +run the signing wizard to create ios +developer certificates +these certificates should be the same +between the driver and the user app +however provisioning profiles should be +generated +for both when you build an ios version +of your app +update codename1 dot main name to match +the class name +update codename 1 package name to match +the package name +other than certificates there are many +nuances you would probably want to +customize in this file such as +the app name and the icon +once this is all done +you would have a separate app +that does the exact same thing +Driver Detection +it's really easy for us to detect the +driver app and write custom code for it +first let's change the uber clone class +to add driver detection +this introduces a public is driver mode +method into the uber clone class +it would always return false unless we +made one tiny change +to driver app +this +is a block within driver app class +we can now invoke his driver on the uber +clone class and get the right result +Push Notification +i've discussed push notification before +in the +beginner course +please make sure to follow the +registration instructions there and +update the values in the globals class +once those are in place we can integrate +push into the driver app +we need to register for push every time +the app loads +the logic is that the push key might +change so we need to keep it up to date +we send a type 3 push which will invoke +this code twice +the first time around will receive a +hash symbol followed by a numeric id +the second time will receive a the +display value +when we receive the display value we can +show a notification to the driver +and if he clicks this notification +we can show him the details of the ride +ideally we would also have a rides menu +item in the side menu but for now i'm +compromising on the driver app to move +forward +when register succeeds this method is +invoked +the main use case is sending the push +key to the server +so it can trigger +push messages +notice that the device id +is not equal to the push dot get key +value +device id is the historic native device +key +and shouldn't be used +also notice i send this every time even +if the value didn't change +it's not a big deal in terms of overhead +so i ignored that small bit of overhead diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/066-29-separating-the-driver-app-and-push-notification.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/066-29-separating-the-driver-app-and-push-notification.md index 95b8f8bc74..662fb639db 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/066-29-separating-the-driver-app-and-push-notification.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/066-29-separating-the-driver-app-and-push-notification.md @@ -11,8 +11,119 @@ weight: 66 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube gmqFd2bU_fM >}} + +## Transcript + +before we go into the big ui changes +let's go over some of the networking +level level changes in the code +in order to encapsulate the new ride +json object from the server i added an +equivalent properties object locally +the class is practically identical to +the server side ride dao class +but uses the properties syntax instead +of the standard pojo syntax +the driver service class is a static +representation of the driver specific +server api +this field represents the id of the +current ride from this driver +here we have the standard get method +i mentioned earlier to retrieve the ride +details and return them via a callback +we use accept to indicate a driver is +accepting a user's healing +if he doesn't we don't care +once accepted he gets a reference to the +id of the newly created ride object and +the server +notice that +failure +is indeed a possibility for example if +the user canceled the ride or a +different driver accepted first +when we invoke start ride and finish +ride we use current ride id +unlike the user id which we used to +create the ride +in search service we had to add support +for geocoding +before this we only had the reverse +geocoding which we used to locate the +from slash 2 points on the map +we need this api since the driver only +gets the two slash from location names +and we want to plot them on the map +there isn't all that much to say about +this method it just searches the google +geocode api for a location with the +given name and returns the coordinates +of that location +there were many small changes +in the user service class +most of them relate to the way identity +is managed in the app +one of the big problems in having two +applications with one project +is that both projects share the same +data in the simulator +so if i want to launch the project twice +once to run the user version and once +for the driver version i will have a +problem +both will inspect the same storage +information and use the same user +identity +they might collide +notice that this is purely a simulator +problem +the simulator doesn't currently isolate +separate applications +ideally this is something to improve in +the simulator +and might not be an issue in the future +the solution is simple though +we can just save the data to different +locations or keys +if we are in the driver app +let's review the changes +this is illustrated perfectly in the +first change in this class +we use a different token to determine if +the user is logged in +for the case of a driver +notice we replaced the invocations of +preferences dot get token +that were all over the code with this +method call +the preferences bind api +lets us set a different prefix for the +driver object that will be prepended to +the properties in the preferences +this is a cool little trick that allows +me to debug with a fake number +i used the is simulator method to log +the verification code on the simulator +and can just type it in +even if the twilio code failed to send a +message +sends the push notificati token to the +server +right now we don't need to do anything +in the event callback +this is invoked on registration success +and allows the server to send driver +push keys to the client +after the first activation of the driver +app +we need to register for push +notice i'm using the version of this +method from the cn class +with static import +but the callback will go as expected +into the driver app class diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/067-30-driver-and-user-hailing-process.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/067-30-driver-and-user-hailing-process.md index 7632790741..f7fc1bee0b 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/067-30-driver-and-user-hailing-process.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/067-30-driver-and-user-hailing-process.md @@ -11,8 +11,200 @@ weight: 67 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube sBAiPOnjX6o >}} + +## Transcript + +the last two big ticket items are +billing and social login +i won't implement them with adherence to +the way they are were implemented with +by uber +i want to keep both of these features +simple as they are both very volatile +features and requirements within both of +these apis can change +literally overnight +i will implement billing as a request +before the write starts +i'll use braintree to do this mostly +because it's already implemented in +codename one +the original implementation in uber +checks whether a billing method already +exists this is possible to do in +braintree but it would require some +extra work to keep billing simple i'll +just charge one dollar per minute and do +the charge in the server side +in-app purchase is one of the big ticket +features in mobile apps +we support this rather well in codename +one +but we can't use enough purchase for +this case +in-app purchase was devised as a tool to +buy virtual goods inside an application +this reduces friction as no credit card +is needed google slash apple already +have it +and makes purchases more fluid +the definition of virtual goods has some +gray areas but generally the idea is +that a good or service sold +would be something that has no immediate +physical cost +good examples of virtual goods would be +in-game item +upgrade of software functionality app +subscription etc +however +physical items and services are +explicitly prohibited +from using in-app purchase +this isn't a bad thing in-app purchase +takes a hefty commission of 30 percent +which isn't viable for most physical +goods sold +braintree is a part of paypal and +provides an easy to integrate mobile +payment sdk for selling physical goods +and services +in theory we could just collect a credit +card and call it a day +but that's naive +securing online transactions is a +nuanced task +by using a trusted third party a great +deal of the risk and liability is +transferred to them +one of the core concepts when working +with the braintree is opacity +the developer doesn't get access to the +credit card +or +billing +information instead a nonce and token +are passed between the client and the +server +even if a security flaw exists in the +app a hacker wouldn't gain access to any +valuable information +as the values expire +this diagram +covers the process of purchasing +via braintree +let's dig into the pieces within it +the client the client code our mobile +app +asks our server for a token +the server generates a token with the +braintree server code and returns it +a client token is a signed data blob +that includes configuration and +authorization information needed by +braintree to associate the transaction +correctly +it can't be reused and should be hard to +spoof in an attack +the mobile app invokes the braintree ui +with the token +that ui lets the user +pick a credit card or other payment +option +for instance paypal android pay apple +pay etc then communicates with +braintree's server +the result of all this is a nonce +which is a unique key that allows you to +charge this payment method +our app +now sends our nonce +to our spring boot server +the server uses the server side +braintree api +and the nonce to charge an amount amount +to the payment method +notice that the amount charged is +completely up to the server +and isn't part of the client-side ui +the braintree sdk for java is pretty +easy to use we already have it in maven +but just in case you skipped those lines +this needs to be in the pom file +next we add a braintree service class +which is remarkably simple +these values should be updated from +braintree and sandbox should be updated +to production once everything is working +this is the client token that we use to +identify the transaction +notice we generate a new one for every +request +we save the nonce into the ride object +this assumes payment authorization +happens before the ride is completed +once the rod is finished the nonce is +instantly available +to do +to perform the charge +before we proceed further the obvious +next step +is the web service to match +it's mostly trivial but i'd like to +point out a small +nuance +pay isn't mapped +we invoke pay in the server so we don't +need to expose it to the client side +that code requires some unexpected +changes which i will get to shortly the +first change was pretty predictable +though we just had to add a non-field to +the right +class +here's the part i didn't expect i needed +to add the right id to the user object +a driver has a reference to the right +object which is why we didn't need this +up until now +however when the user tries to pay he +can't set this anywhere else +unfortunately there is no other place +where the nonce would fit +since it's transient we can't add it to +the user as we'd want some logging +the ride object is the right place for +the nonce +to get this to work i had to make a few +changes to the accept ride method +i added the right reference to both the +driver and passenger for future +reference +i moved these lines downward because the +rider id will only be available after +the ride's save +call since payment is handled on the +server side we can go directly to it +even before we do the client side i've +decided to do this in the finish ride +method +a ride that was finished before it was +started is effectively cancelled a ride +without a nonce can't be charged +at all +i use the route which is +ordered based on time to find the start +time of the ride +i then go to the last element and find +the end time of the ride +assuming the ride has more than one +waypoint otherwise end time would be -1 +we can just charge one usd per 60 +seconds and payment is effectively done +on the server again i oversimplified a +lot and ignored basic complexities like +the driver forgetting to press finish diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/068-31-billing-with-braintree-flow-explained-and-server-side.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/068-31-billing-with-braintree-flow-explained-and-server-side.md index b18b1ea302..8f91c137e0 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/068-31-billing-with-braintree-flow-explained-and-server-side.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/068-31-billing-with-braintree-flow-explained-and-server-side.md @@ -11,8 +11,200 @@ weight: 68 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube sBAiPOnjX6o >}} + +## Transcript + +the last two big ticket items are +billing and social login +i won't implement them with adherence to +the way they are were implemented with +by uber +i want to keep both of these features +simple as they are both very volatile +features and requirements within both of +these apis can change +literally overnight +i will implement billing as a request +before the write starts +i'll use braintree to do this mostly +because it's already implemented in +codename one +the original implementation in uber +checks whether a billing method already +exists this is possible to do in +braintree but it would require some +extra work to keep billing simple i'll +just charge one dollar per minute and do +the charge in the server side +in-app purchase is one of the big ticket +features in mobile apps +we support this rather well in codename +one +but we can't use enough purchase for +this case +in-app purchase was devised as a tool to +buy virtual goods inside an application +this reduces friction as no credit card +is needed google slash apple already +have it +and makes purchases more fluid +the definition of virtual goods has some +gray areas but generally the idea is +that a good or service sold +would be something that has no immediate +physical cost +good examples of virtual goods would be +in-game item +upgrade of software functionality app +subscription etc +however +physical items and services are +explicitly prohibited +from using in-app purchase +this isn't a bad thing in-app purchase +takes a hefty commission of 30 percent +which isn't viable for most physical +goods sold +braintree is a part of paypal and +provides an easy to integrate mobile +payment sdk for selling physical goods +and services +in theory we could just collect a credit +card and call it a day +but that's naive +securing online transactions is a +nuanced task +by using a trusted third party a great +deal of the risk and liability is +transferred to them +one of the core concepts when working +with the braintree is opacity +the developer doesn't get access to the +credit card +or +billing +information instead a nonce and token +are passed between the client and the +server +even if a security flaw exists in the +app a hacker wouldn't gain access to any +valuable information +as the values expire +this diagram +covers the process of purchasing +via braintree +let's dig into the pieces within it +the client the client code our mobile +app +asks our server for a token +the server generates a token with the +braintree server code and returns it +a client token is a signed data blob +that includes configuration and +authorization information needed by +braintree to associate the transaction +correctly +it can't be reused and should be hard to +spoof in an attack +the mobile app invokes the braintree ui +with the token +that ui lets the user +pick a credit card or other payment +option +for instance paypal android pay apple +pay etc then communicates with +braintree's server +the result of all this is a nonce +which is a unique key that allows you to +charge this payment method +our app +now sends our nonce +to our spring boot server +the server uses the server side +braintree api +and the nonce to charge an amount amount +to the payment method +notice that the amount charged is +completely up to the server +and isn't part of the client-side ui +the braintree sdk for java is pretty +easy to use we already have it in maven +but just in case you skipped those lines +this needs to be in the pom file +next we add a braintree service class +which is remarkably simple +these values should be updated from +braintree and sandbox should be updated +to production once everything is working +this is the client token that we use to +identify the transaction +notice we generate a new one for every +request +we save the nonce into the ride object +this assumes payment authorization +happens before the ride is completed +once the rod is finished the nonce is +instantly available +to do +to perform the charge +before we proceed further the obvious +next step +is the web service to match +it's mostly trivial but i'd like to +point out a small +nuance +pay isn't mapped +we invoke pay in the server so we don't +need to expose it to the client side +that code requires some unexpected +changes which i will get to shortly the +first change was pretty predictable +though we just had to add a non-field to +the right +class +here's the part i didn't expect i needed +to add the right id to the user object +a driver has a reference to the right +object which is why we didn't need this +up until now +however when the user tries to pay he +can't set this anywhere else +unfortunately there is no other place +where the nonce would fit +since it's transient we can't add it to +the user as we'd want some logging +the ride object is the right place for +the nonce +to get this to work i had to make a few +changes to the accept ride method +i added the right reference to both the +driver and passenger for future +reference +i moved these lines downward because the +rider id will only be available after +the ride's save +call since payment is handled on the +server side we can go directly to it +even before we do the client side i've +decided to do this in the finish ride +method +a ride that was finished before it was +started is effectively cancelled a ride +without a nonce can't be charged +at all +i use the route which is +ordered based on time to find the start +time of the ride +i then go to the last element and find +the end time of the ride +assuming the ride has more than one +waypoint otherwise end time would be -1 +we can just charge one usd per 60 +seconds and payment is effectively done +on the server again i oversimplified a +lot and ignored basic complexities like +the driver forgetting to press finish diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/069-32-braintree-client-side-integration.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/069-32-braintree-client-side-integration.md index 232ec38e15..966378a449 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/069-32-braintree-client-side-integration.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/069-32-braintree-client-side-integration.md @@ -11,8 +11,71 @@ weight: 69 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube RBYGdCllnww >}} + +## Transcript + +next we'll address the client side of +the payment process +before we begin we need to download the +braintree cn1 lib from the extension +manager right click the project and +select refresh libs +we'll start the client side with the +payment service class which encapsulates +the web service aspects +payment service has a private +constructor so it can't be instantiated +by other classes +we use the instance of +this class to get callback events from +the client side purchase api +using the purchase callback interface +notice that we need +a ride id in the object instance so we +can communicate purchase results to the +server correctly +this is literally the entire purchase +api process +we just invoke the native purchase ui +and provide the callback instance for +the native code +on purchase success is the first +callback from the callback interface +it occurs when a purchase succeeded +and produced a nonce +we can then send the nonce to the server +with the ride id +on purchase fail or console +aren't very interesting in this use case +i chose to ignore them but you might +need them to know whether that +the charge ui should be shown again +notice the only way to verify purchase +success is on the server +fetchtoken is a callback method +in the callback interface +it's invoked internally by the on +purchase process to fetch the server +token value +that initializes the purchase process +this is pretty much everything +the only remaining piece is binding this +into the ui i've changed the ok button +to pay with cash +and added an option to pay with credit +which essentially maps to the braintree +api +this implements the four payment process +integration +including credit card verification and +everything involved +once this is done payments should now +work both in the client and the server +the user is presented with an option to +pay or use cash +which +just dismisses the dialogue diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/070-33-social-login-basics-and-facebook-app.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/070-33-social-login-basics-and-facebook-app.md index dc6a3e1f3f..c4ea2ecaf5 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/070-33-social-login-basics-and-facebook-app.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/070-33-social-login-basics-and-facebook-app.md @@ -11,8 +11,169 @@ weight: 70 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube qKc1hZyw360 >}} + +## Transcript + +we already prepared a lot of the +groundwork for social login on the +server +but didn't finish all the pieces so +before i step into the client side +changes needed for social login let's +discuss some of the required server side +work +i had to include support for the exists +functionality that works based on a +social token +i also had to include a similar call in +the user web service class +i also have a similar subtitle change +to the regular exists method +the argument name was phone and is now v +which means i can invoke all three web +services with very similar code on the +client side +that's it pretty much everything else +was already done +social login lets us authenticate a user +without getting into the +username password complexity +this is usually almost seamless on +devices where we +the pre-installed social app is invoked +explicitly and the user just needs to +approve the permission +this is defined as a low friction +approach to authenticate the user +and is often superior to phone number +activation +in codename one this is pretty trivial +to accomplish especially for +google and facebook login both of which +are built into codename one and to +android slash ios respectively +connection to social networks and +codename one has several common concepts +if the device has native support or +social app installed this native +integration will perform a login +if it doesn't but we are on the device +the native sdk will show a web based +login +if we are on the simulator we will fall +back to an oauth based login this leads +to a situation where login might work on +one device but fail on a simulator or +fail on a different device type +it also makes the configuration process +a bit more tedious +to be fair the native configuration is +much harder and involves more code +since the driver app is physically a +separate app +we'll need to redo some of the steps and +effectively go through everything +twice +a core concept of the login process +in facebook is the app +which is a facebook internal term +unrelated to your actual app +facebook's view of an app +is anything that uses the facebook graph +api and authentication +in this case we need to create a new app +and should name it like we do our actual +app so the user will be able to identify +it +the steps are pretty easy we navigate to +developer.facebook.com +apps +and press the add a new app button +next we need to select the product we +are trying to use and we need to select +facebook login +once there we are presented with a +wizard containing multiple steps +to set up your app +you need to run through the wizard twice +once for ios and once for android +the content of the wizard +changes but the gist is the same +we don't really need much information +and can skip almost everything +the first step is download and install +the facebook sdk for ios +this is obviously unnecessary for us so +we can just press +next +the second step is add login kit to your +xcode project again there is no need to +do anything and we can press next +the third step is add your bundle +identifier +it's more interesting we need to enter +the project package name here and +press save +then we are effectively done with ios as +everything else is more of the same +the android wizard has one task that is +a bit challenging but other than that it +should be trivial +before we begin we need to generate key +hashes for facebook which need to be +done on your development machine +to do that you will need a command line +with the jdks bin directory in your path +you will also need the path to the +android key store you use for signing +you can find this file in the android +signing section in codename one settings +if there is no certificate file there +make sure to generate it +once all this +is in place you can use this command +line for linux mac +this will provide the sha1 key you will +need in the android wizard +similarly on windows the command +follows a similar structure but uses +windows command line conventions +now that this is out of the way +let's go over the android wizard steps +the first step is download the facebook +sdk for android this is obviously +unnecessary for us so we can just press +next +the next step is import the facebook sdk +again there is no need to do anything +and we can press next +the next step is tell us about your +android project we need to specify the +package name for the application +which in our case is com +codename1.app.uberclone +we also need to specify the main class +which is ubercn1 stub +the main class is effectively the main +class name with the word stub with an +uppercase s appended at the end we can +then press next +after i press next i got this warning +because the app isn't in the store yet +facebook thinks i might have typed the +package incorrectly and provides this +warning which we can ignore +and finally +we are at the add your development and +release key hashes here we need to add +the hash we got before and press next +the rest of the wizard isn't important +before we proceed we need to enter the +facebook dashboard and copy two values +the app id +and app secret which we will need when +we set up the code diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/071-34-facebook-and-google-login-code.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/071-34-facebook-and-google-login-code.md index e62aba23d0..6f1a86f1b9 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/071-34-facebook-and-google-login-code.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/071-34-facebook-and-google-login-code.md @@ -11,8 +11,98 @@ weight: 71 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube UwdI_tMqYgU >}} + +## Transcript + +next we'll integrate the facebook login +process into the code +to get the native login working we only +need one step +add the build hint +facebook.app id +equals +the app id +app id is the id from the dashboard on +the facebook app page this will make +facebook work on the devices almost +seamlessly +notice that since we have effectively +two apps we'll need to add an app id to +the user app and the driver app +to get login working on the simulator +we'll need a bit more we'll also need to +write code that supports the login +process within the facebook or google +login form class +facebook connect is a subclass of the +login class that lets us login into +facebook and request publish permissions +if necessary +the client id and secret aren't used on +devices +these are hair strictly for the benefit +of the simulator +if you don't need to debug on the +simulator the lines until set callback +are redundant +notice that we have two versions of +these values for the uber app and the +driver app +the callback is invoked upon login +success failure +if the login is successful we get the +token from facebook which is an +authorization token this token allows us +to access information within the +facebook graph api to query facts about +the user +notice that we have a new constructor +for enter password form which i will +discuss soon +this triggers the actual login +but the method is asynchronous and login +will only actually succeed or fail +when the callback is reached +before we go to the google login support +let's look at the additional changes we +need to get +both facebook and google working +i already discussed the changes to enter +password form +so let's start there +the constructor accepts +one of the three options the other two +should be null in this case +i also updated the user service method +accordingly i'll get into that shortly +notice i snapped some code below here +to keep the entire block in one page +but it's still there +the login method now accepts the google +facebook credentials as an optional +argument +two of the three values for +identification will be null so we can +set all of them +and only one will have a value +next let's see the changes to the user +service class +this is the main method we use +which we broke up for the other types +the generic implementation demonstrates +why i chose to change +the argument names from phone to v +so it can now suit all permutations of +this method +login is almost identical to the +original code +i added the new values to the mix if +they are null the arguments won't be +sent +and everything will work as expected +once this is done facebook login should +work on the device and simulator diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/072-35-google-login-process.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/072-35-google-login-process.md index 58f551a893..dc11876fb6 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/072-35-google-login-process.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/072-35-google-login-process.md @@ -11,8 +11,93 @@ weight: 72 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube JWLsSbbo4N4 >}} + +## Transcript + +the last piece is the google login +support which we almost finished as the +code we did for facebook is nearly +identical +google again went through some back and +forth originally it was mapped to google +plus functionality +as google phased out google plus we +switched to use the new google login +authentication +the process for google is pretty similar +to the one we had with facebook +first we need to go to the google +developer portal at +developers.google.com +mobile slash ad +and follow the steps to create an app +for google sign-in +notice we need to run through the +process four times once for android and +once for ios and again for the driver +app +the ios version requires the app name +and bundle id which map to the app name +and +package name in codename one +next select the google sign-in option +and click the generate button +once we enable google sign-in +we can generate the configuration files +then we can download and the +configuration file which should be named +googleserviceinfo.plist +place this file in your project root +under the native ios directory +the process for android is nearly +identical with one big difference we +need to provide an sha1 key for this to +work +since i discussed the process of +generating an sha1 key for your +certificate earlier i won't repeat it +again +check out the facebook section for the +details on that process +once we finish this step +we will receive a file named +googleservices.json +we should place this file under the +native slash android directory +in order to work in the simulator we'll +need some additional credentials from +console.cloud.google.com +apis +in the top portion of the browser make +sure the correct app name is selected +select the credentials menu +find the web client entry and click it +you should see the client id and client +secret values there +we will need them in the code soon +in the authorize redirect uri section +you will need to enter the url of the +page that the user will be sent to after +a successful login +this page will only appear in the +simulator for a split second as codename +one's browser component will intercept +this request to obtain the access token +upon successful login +you can use any url you like here +but it must match the value you give to +google connect dot set redirect url +in the code +once all of this is in place we can add +the code to handle google login process +you'll notice that the code is almost +identical to the facebook login code in +fact both google connect and facebook +connect +derive the login class which means we +can write very generic login code at +least in theory diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/073-36-morph-transition-animating-elements-between-forms.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/073-36-morph-transition-animating-elements-between-forms.md index ebd5a109cf..6d5683030f 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/073-36-morph-transition-animating-elements-between-forms.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/073-36-morph-transition-animating-elements-between-forms.md @@ -11,8 +11,120 @@ weight: 73 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube 9T8MBBuWBDs >}} + +## Transcript + +i kept most of the default transitions +and did a few animations along the way +but i didn't spend too much time on +either one of those +by default codename one uses the slide +or slide fade transitions +these should look decent for the most +part but i wanted to demonstrate and +discuss +some of the nuanced transitions in the +uber app +[Music] +in the native uber app transitions look +a bit different between ios and android +i didn't go there because i don't think +this was done on purpose +in android's material design a common +transition pattern +is one where we move an element from one +view to the next and indeed +this is what we have between the login +form and the enter mobile number form +as you can see the enter your mobile +number and flag elements animate to the +place in the next form while other +elements fade in out respectively +this transition repeats itself in +reverse +when we press back +there are a couple of things that might +not be immediately obvious when you look +at this +the background pattern +instantly disappears +instead of fading +this might be on purpose but it doesn't +look good +this is a bit hard to see as it happens +relatively quickly but the back arrow +slides in from the left +codename one has a morph transition +which doesn't include the slide in out +option +for some elements +only the fade in out of these elements +so we'll pass on that aspect i chose to +fade the background pattern in out as it +looks much better +i'm not sure why uber chose not to do +that +notice that this works for us despite +the fact that the background is +constantly rotating +when we get back to the main form +and this isn't supported in the native +android app +transitions are decoupled from the forms +or components that they transition +this allows us to define a transition +regardless of the contents of a form in +order to use the morph transition +we need to communicate to it the +components we would like to animate +but they might not be instantiated at +this time yet +so we need to use component names +if the components on both forms have the +same name +we can make the code even shorter +we can perform the transition using this +code in login form +we will obviously need the corresponding +code in enter mobile number form +notice we set the names to the identical +values +we could have used different names and +then just specified those different +names in the morph method +we would also want morph to run in +reverse when going back +so the obvious thing to do +is define a morph transition in the back +command +but there are a couple of nuances +notice i used strings instead of get +name as the back command is defined +before the components in the full code +listing +this is one of those things that you +only see on the device the virtual +keyboard opens when we enter the mobile +number form +so when we go back it looks a bit weird +on android +i stop editing to fold it first +and then use the callback to detect when +the keyboard actually finished closing +otherwise the transition will run before +the form had time to adjust +the problem is that if you run this code +it will fail badly +the background animation tries to +repaint while the transition is in +progress +the solution for that is a small simple +change to login form painter +i'm effectively blocking animation of +the background during transition which +would also make the transition smoother +as a result diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/074-37-cover-transition-conditionally-showing-a-form-transition.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/074-37-cover-transition-conditionally-showing-a-form-transition.md index c9265a98d8..8666f73e6d 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/074-37-cover-transition-conditionally-showing-a-form-transition.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/074-37-cover-transition-conditionally-showing-a-form-transition.md @@ -11,8 +11,54 @@ weight: 74 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube IaalGP1UDeU >}} + +## Transcript + +the code for morph transitions +broke another thing +it broke the facebook or google login +form +which looks awful going in now because +morph is generally designed for a +specific form +i want to use the vertical cover effect +which is common on ios and looks pretty +decent on android 2. +cover slides the form on top of the +existing form from the bottom +it's usually combined with uncover +which slides the form out in the reverse +way +because of this unique semantic the +cover transition uses both the in and +out transition flags +however this can pose a problem +with the default out transition of the +form that we are leaving +in this case you would see the out +animation of the login form +which in this case is morph +followed by the incoming cover animation +the solution is to remove the out +animation from the outgoing form +and restore it to the original value +when we get back +we do that within the remove transition +temporarily method +which we call here from the facebook or +google login form +we need to remove both the in and out +transitions +as we might show a cover transition +on top of another cover transition form +when we return to the original form we +restore its transitions to their +original values +we remove the show listener to prevent a +memory leak +and multiple restore calls when going +back and forth diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/075-38-circular-floating-action-button-animation.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/075-38-circular-floating-action-button-animation.md index 4a04df776e..f12bb1c737 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/075-38-circular-floating-action-button-animation.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/075-38-circular-floating-action-button-animation.md @@ -9,10 +9,27 @@ module_order: 12 lesson_order: 38 weight: 75 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Build a lightweight progress animation around a floating action button without turning it into a generic loading spinner." --- - > Module 12: Creating an Uber Clone - {{< youtube YE7OQFQE0yA >}} + +Some animations are worth keeping product-specific. This lesson is a good example. The circular highlight around the floating action button is not a general-purpose UI pattern you would add to every app. It is a small piece of branding and feedback that fits the ride-hailing experience the course is cloning. + +The useful idea here is not really the Uber imitation itself. It is the decision to build a focused animation for one concrete interaction instead of trying to solve “all progress indication” with one giant generic component. + +The implementation works by styling the floating action button directly and animating the stroke of its round border. That keeps the effect local. The button keeps its identity, but it gains a sense of motion and ongoing work while the app is waiting for the next step. + +The lesson also uses a UI timer rather than a plain background timer. That is the right choice for a visual effect that updates component styles because the callbacks need to run on the EDT. A progress effect that quietly hops across threads is exactly the kind of thing that becomes glitchy for no good reason. + +Another good detail is the cleanup step. The animation stores its instance in a client property and restores the original UIID when it stops. That matters because a temporary visual effect should leave the component exactly as it found it. If an animation modifies style state but never restores it properly, the UI gradually becomes harder to reason about. + +This lesson sits in a good middle ground between high-level animation APIs and fully custom rendering. It is custom enough to feel specific to the product, but still simple enough that the code stays understandable. + +## Further Reading + +- [Transitions](/courses/course-03-build-real-world-full-stack-mobile-apps-java/035-transitions/) +- [Layout Animations](/courses/course-03-build-real-world-full-stack-mobile-apps-java/036-layout-animations/) +- [Animation Manager, Style Animations and Low Level Animations](/courses/course-03-build-real-world-full-stack-mobile-apps-java/037-animation-manager-style-animations-and-low-level-animations/) +- [Enter Password Form](/courses/course-03-build-real-world-full-stack-mobile-apps-java/072-35-google-login-process/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/076-39-settings-form-and-fetching-the-avatar-image.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/076-39-settings-form-and-fetching-the-avatar-image.md index e78abe8d94..37181c6e92 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/076-39-settings-form-and-fetching-the-avatar-image.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/076-39-settings-form-and-fetching-the-avatar-image.md @@ -9,10 +9,27 @@ module_order: 12 lesson_order: 39 weight: 76 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Turn the side menu into real account navigation and load the user's avatar with proper local caching." --- - > Module 12: Creating an Uber Clone - {{< youtube tBBtpdz-JJg >}} + +Up to this point the Uber clone mostly proves that the core ride flow works. This lesson starts turning the surrounding app chrome into something more believable by making the side menu lead to real account-oriented screens. + +That shift matters because a convincing application is not just its flagship interaction. Once a user can open settings or tap their avatar, the app starts feeling like a complete product instead of a narrow demo. + +The settings form itself is mostly straightforward UI work, but the more interesting part of the lesson is avatar loading. Fetching a user image sounds simple until you care about round masking, correct sizing, local caching, and the fact that the client is the only place that really knows how large the final image needs to be. + +That last point is still the right way to think about it. The server should not hard-code every display size the client might need. The client decides how the image will appear, and the client can then cache the processed result so future loads are fast. + +The lesson updates the avatar API so the image can be delivered asynchronously through a callback. That is a natural fit for remote image loading because the UI can continue rendering while the image is fetched from storage or downloaded from the server. If the image is already cached locally, the callback can complete quickly without making the app feel network-bound. + +This is one of those small service-layer improvements that pays off all over the UI. Once avatar loading is centralized and cache-aware, the app can reuse it for settings, side-menu identity, profile editing, and eventually any place where another user’s image should appear. + +## Further Reading + +- [40. Edit User - UI Binding and Multipart Image Upload](/courses/course-03-build-real-world-full-stack-mobile-apps-java/077-40-edit-user-ui-binding-and-multipart-image-upload/) +- [Client Side UserService](/courses/course-03-build-real-world-full-stack-mobile-apps-java/050-13-client-side-userservice/) +- [Storage, Filesystem and SQL](/courses/course-01-java-for-mobile-devices/010-storage-filesystem-and-sql/) +- [How Do I Fetch an Image from the Resource File / Add a MultiImage](/how-do-i/how-do-i-fetch-an-image-from-the-resource-file-add-a-multiimage/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/077-40-edit-user-ui-binding-and-multipart-image-upload.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/077-40-edit-user-ui-binding-and-multipart-image-upload.md index 514436e10b..4311ac0e81 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/077-40-edit-user-ui-binding-and-multipart-image-upload.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/077-40-edit-user-ui-binding-and-multipart-image-upload.md @@ -9,10 +9,27 @@ module_order: 12 lesson_order: 40 weight: 77 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Bind profile fields directly to the user model and upload avatar images as multipart requests when the account changes." --- - > Module 12: Creating an Uber Clone - {{< youtube S1SHZLXQxSI >}} + +Editing account details is one of those places where a lot of apps make the user work harder than necessary. The lesson starts by calling out Uber’s more segmented editing flow and then chooses a flatter, more direct alternative: bind text fields straight to the user object and save only when the user leaves the form. + +That is a good tradeoff for this app. The screen stays simple, the code stays short, and the user can edit several values naturally instead of drilling through one tiny form at a time. + +The binding API does most of the heavy lifting here. Once the user object is bound to the text fields, the form can populate itself from the model and keep the model updated as the user types. That is exactly the kind of repetitive plumbing worth automating. + +The lesson is also careful about when data is actually sent to the server. Binding the UI to the model does not mean every keystroke should become a network request. Saving on exit keeps the experience responsive and avoids unnecessary traffic. The comparison against the original object state is a simple but effective way to decide whether anything changed. + +The avatar upload flow extends the same idea. The app lets the user capture a new image, resizes it on the client so the upload stays within sensible limits, updates the local UI immediately, and then sends the file to the server using a multipart request. That sequencing feels good because the user sees the result right away even while the server-side update is still in flight. + +The warning about unbinding is also important. Once bindings are attached to a global model object, forgetting to release them can keep entire form hierarchies alive longer than intended. This is exactly the kind of subtle memory problem that is easy to create and hard to notice unless you think about object lifetimes deliberately. + +## Further Reading + +- [39. Settings Form and Fetching the Avatar Image](/courses/course-03-build-real-world-full-stack-mobile-apps-java/076-39-settings-form-and-fetching-the-avatar-image/) +- [Properties Are Amazing](/blog/properties-are-amazing/) +- [Storage, Filesystem and SQL](/courses/course-01-java-for-mobile-devices/010-storage-filesystem-and-sql/) +- [How Do I Take a Picture with the Camera](/how-do-i/how-do-i-take-a-picture-with-the-camera/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/078-1-introduction.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/078-1-introduction.md index 314eb7f0ba..fcfe2a5684 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/078-1-introduction.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/078-1-introduction.md @@ -9,10 +9,27 @@ module_order: 13 lesson_order: 1 weight: 78 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Study the Facebook app as a reference, decide what to imitate, and define the architectural goals for the clone." --- - > Module 13: Creating a Facebook Clone - {{< youtube S5pAGkV4h4w >}} + +Before writing code, this module does something the course does well when it is at its best: it studies the product being cloned instead of charging straight into implementation. + +That matters because the point is not to reproduce Facebook pixel for pixel. The point is to understand which product decisions are worth copying, which ones are inconsistent or awkward, and which ones can teach useful lessons about building a large data-heavy mobile app. + +The login and signup flow is a good place to start because it immediately exposes several design tensions. Deep wizard-style flows are easier to understand and adapt well to different screen sizes, but they are also more tedious when users only need to edit one thing. Flatter forms are more efficient, but they can become harder to scan and harder to adapt cleanly across phones and tablets. + +That tradeoff is more valuable than the specific Facebook screens shown in the video. The lesson is really about learning how to think through UI depth, progressive disclosure, platform conventions, and the relationship between data complexity and screen structure. + +The module also makes a smart scope decision. Facebook is enormous. A useful clone needs to choose which capabilities are interesting enough to implement and which ones would mostly add volume without teaching much. That kind of discipline matters in real projects too. A clone is only educational if it stays focused. + +The architectural hint near the end is also important: a social app carries a huge amount of structured data, and Codename One properties are a natural fit for that. Once the app starts dealing with posts, users, friends, comments, privacy flags, and media, the benefit of declarative structured data becomes very obvious. + +## Further Reading + +- [2. Creating the Project and CSS](/courses/course-03-build-real-world-full-stack-mobile-apps-java/079-2-creating-the-project-and-css/) +- [Overview and Basic Model](/courses/course-02-deep-dive-mobile-development-with-codename-one/015-overview-and-basic-model/) +- [Properties Are Amazing](/blog/properties-are-amazing/) +- [Adapting a UI Design](/courses/course-01-java-for-mobile-devices/009-adapting-a-ui-design/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/079-2-creating-the-project-and-css.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/079-2-creating-the-project-and-css.md index 4ad7bbbb7a..e57d9db36f 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/079-2-creating-the-project-and-css.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/079-2-creating-the-project-and-css.md @@ -9,10 +9,25 @@ module_order: 13 lesson_order: 2 weight: 79 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Create the project, define the visual direction, and set up a CSS-driven styling workflow for a large data-rich app." --- - > Module 13: Creating a Facebook Clone - {{< youtube 3rLf9A9RYPY >}} + +With the product direction defined, the next step is to create a project that can actually support it. This lesson is less about raw project generation and more about choosing the right styling and structural foundation for an app that will grow quickly. + +The video contrasts Facebook and Uber in a useful way. One app leans into a more native platform look, the other uses a more unified cross-platform visual style. The goal of this clone is not blind accuracy. It is to take inspiration from Facebook’s flow while still making architectural choices that age well. + +That is why CSS is such an important decision here. The video talks about CSS as something new and in transition, which was true at the time. Today that part is out of date. CSS is the recommended styling path for new Codename One work, and this is exactly the kind of project where it pays off. A large app with many screens, repeated patterns, and evolving visual rules is much easier to maintain when styling lives in CSS instead of in older designer-driven workflows. + +The icon-font setup is also a practical choice. A social app needs many small symbolic elements, and icon fonts remain a simple way to keep those assets scalable, lightweight, and easy to recolor. The lesson’s Fontello-based workflow is one way to build that bundle of icons, but the broader idea is the important one: gather the symbols the app will need early and make them part of the styling system instead of scattering image files everywhere. + +The CSS details in the lesson are the beginning of a theme system, not just a few cosmetic tweaks. Constants, default gaps, transparent backgrounds, icon fonts, and base styling decisions all become easier to manage once they are centralized. That gives the rest of the clone a cleaner starting point as the UI expands. + +## Further Reading + +- [1. Introduction](/courses/course-03-build-real-world-full-stack-mobile-apps-java/078-1-introduction/) +- [Working with CSS](/courses/course-02-deep-dive-mobile-development-with-codename-one/001-working-with-css/) +- [CSS](/courses/course-02-deep-dive-mobile-development-with-codename-one/006-css/) +- [CSS Changes](/courses/course-02-deep-dive-mobile-development-with-codename-one/013-css-changes/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/080-3-splash-screen.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/080-3-splash-screen.md index 79741951e0..4c0d2093a6 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/080-3-splash-screen.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/080-3-splash-screen.md @@ -9,10 +9,25 @@ module_order: 13 lesson_order: 3 weight: 80 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Introduce a splash screen and a simple UI controller so the app has a clean first-launch experience." --- - > Module 13: Creating a Facebook Clone - {{< youtube Rat-FDqDCTU >}} + +Splash screens are easy to overthink. The point is not to hide all startup work behind branding. The point is to make the first moment of the app feel intentional instead of abrupt. + +This lesson uses a splash screen for a practical reason. The Facebook-style login experience looks very different from the rest of the app, so dropping the user immediately into an unrelated partially loaded screen would feel messy. A simple transition screen gives the app a more coherent starting point. + +The old video spends time on iOS screenshot generation and older native splash mechanics. That part is historical now and should not be treated as the main lesson. The lasting idea is that startup needs a dedicated entry point. Instead of letting the default generated form dictate the experience, the app introduces a UI controller that owns the initial navigation flow. + +That controller is the more important architectural move. Once the app has a single place that decides whether to show the splash screen, login flow, or some later authenticated screen, startup logic stops being scattered across individual forms. + +The morph from the splash logo into the next screen’s logo is also a good example of animation serving a real purpose. It visually connects the temporary startup state to the application state that follows it, which makes the change feel continuous rather than arbitrary. + +## Further Reading + +- [2. Creating the Project and CSS](/courses/course-03-build-real-world-full-stack-mobile-apps-java/079-2-creating-the-project-and-css/) +- [Transitions](/courses/course-03-build-real-world-full-stack-mobile-apps-java/035-transitions/) +- [Animation Manager, Style Animations and Low Level Animations](/courses/course-03-build-real-world-full-stack-mobile-apps-java/037-animation-manager-style-animations-and-low-level-animations/) +- [Creating a Hello World App](/courses/course-01-java-for-mobile-devices/002-creating-a-hello-world-app/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/081-4-login-form.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/081-4-login-form.md index 30e7e698d2..597a364bd6 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/081-4-login-form.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/081-4-login-form.md @@ -9,10 +9,27 @@ module_order: 13 lesson_order: 4 weight: 81 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Build a login form that adapts across platforms and orientations without pretending every device should look identical." --- - > Module 13: Creating a Facebook Clone - {{< youtube vdFr815Dhpg >}} + +The login screen is the first place where the Facebook clone stops being an abstract styling exercise and starts making real product decisions. Facebook’s native login UI is inconsistent across platforms, and the video handles that honestly by taking inspiration rather than trying to reproduce every awkward choice exactly. + +That is the right instinct. A good clone should learn from the source product, not inherit every mistake. This lesson keeps the broad shape of the Facebook login flow while making clearer decisions about tablets, landscape mode, and how much the interface should diverge between iOS and Android. + +One useful Codename One detail here is `ComponentGroup`. On iOS, grouped fields are a familiar pattern and the native theme already knows how to style first, middle, and last grouped entries. On Android, that same container can degrade gracefully into a more ordinary stacked layout without forcing the whole screen into an iOS look. That makes it a good example of using native theme behavior as an asset instead of trying to flatten every platform into one visual language. + +The landscape treatment is also worth noticing. The lesson is not just making the same login form wider. It is changing the balance of the layout so the screen still feels intentional when the device rotates. That is much better than letting the form expand passively and hoping it still looks good. + +The logo transition from the splash screen matters for the same reason. Naming the logo component so the morph transition can connect startup to login gives the first seconds of the app a sense of continuity, which is exactly the kind of polish users notice without consciously naming it. + +The CSS work in this lesson is more than ornament. It defines which buttons should follow Android’s uppercase convention, which containers should carry platform-specific spacing, and how the login screen should hold together visually across form factors. This is where the earlier decision to use CSS starts paying off. + +## Further Reading + +- [3. Splash Screen](/courses/course-03-build-real-world-full-stack-mobile-apps-java/080-3-splash-screen/) +- [5. Rich Text View and Signup Form](/courses/course-03-build-real-world-full-stack-mobile-apps-java/082-5-rich-text-view-and-signup-form/) +- [Working with CSS](/courses/course-02-deep-dive-mobile-development-with-codename-one/001-working-with-css/) +- [Adapting a UI Design](/courses/course-01-java-for-mobile-devices/009-adapting-a-ui-design/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/082-5-rich-text-view-and-signup-form.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/082-5-rich-text-view-and-signup-form.md index 0797ac0a04..4311d09291 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/082-5-rich-text-view-and-signup-form.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/082-5-rich-text-view-and-signup-form.md @@ -9,10 +9,25 @@ module_order: 13 lesson_order: 5 weight: 82 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Create the shared signup form infrastructure and add a lightweight rich-text component for short formatted messages and links." --- - > Module 13: Creating a Facebook Clone - {{< youtube UhLFHJj8qnM >}} + +Once the login screen exists, the next challenge is the signup flow. This lesson makes two important architectural moves before building more screens: it creates a reusable form shell for the signup wizard, and it introduces a lightweight rich-text component for short formatted text with links. + +That second piece is especially useful. Full HTML rendering is often more power than you actually want for tightly controlled UI copy. For this case the goal is modest: allow short blocks of text with line breaks, emphasis, and clickable links while keeping the component visually integrated with the rest of the app. + +The custom rich-text view is therefore a good example of choosing the right level of abstraction. Instead of embedding a browser component just because HTML is involved, the lesson builds a small renderer for a constrained subset of markup. That keeps styling, layout, and event handling under application control. + +The signup form abstraction is the other big win. Multiple signup stages share the same general structure: a title area, content in the middle, fixed controls near the bottom, and slightly different back behavior depending on platform conventions. Capturing that once makes the later signup screens much easier to build and much easier to keep visually consistent. + +This is the kind of reusable UI infrastructure that is worth writing. It is close to the needs of the app, clearly justified by repetition, and still small enough that it does not turn into a second framework inside the project. + +## Further Reading + +- [6. Signup Form - Terms and Conditions](/courses/course-03-build-real-world-full-stack-mobile-apps-java/083-6-signup-form-terms-and-conditions/) +- [Working with CSS](/courses/course-02-deep-dive-mobile-development-with-codename-one/001-working-with-css/) +- [Layout Basics](/courses/course-01-java-for-mobile-devices/007-layout-basics/) +- [How Do I Handle Events/Navigation in the GUI Builder & Populate the Form from Code](/how-do-i/how-do-i-handle-eventsnavigation-in-the-gui-builder-populate-the-form-from-code/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/083-6-signup-form-terms-and-conditions.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/083-6-signup-form-terms-and-conditions.md index 7f92f9fe34..5973e4cb27 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/083-6-signup-form-terms-and-conditions.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/083-6-signup-form-terms-and-conditions.md @@ -9,10 +9,25 @@ module_order: 13 lesson_order: 6 weight: 83 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Use the shared signup form shell to build the first step of the wizard and establish the visual rules for the rest of the flow." --- - > Module 13: Creating a Facebook Clone - {{< youtube xLa5yeeVsE8 >}} + +The first real signup screen is intentionally simple, and that is exactly why it matters. This is where the reusable signup shell proves itself and where the rest of the wizard’s visual language starts becoming concrete. + +The form itself is mostly content and framing: a clear title, explanatory text, a prominent next action, and supporting links below. Because the structure was abstracted in the previous lesson, the screen-specific code can stay focused on meaning rather than layout plumbing. + +The rich-text component earns its place here by rendering highlighted legal and explanatory copy without dragging in a heavier HTML solution. That makes this lesson a good checkpoint for the earlier architectural decision. The app now has a custom text component that feels native to the design rather than bolted on from outside. + +The CSS additions are also important because they define the vocabulary for the rest of the signup wizard: toolbar spacing, title sizing, back-command behavior, off-white backgrounds, and the visual identity of the “next” action. Once those rules exist, later stages can reuse them instead of negotiating their visual hierarchy from scratch. + +This is one of those lessons where the code is not flashy, but the payoff is large. A clean first step in a wizard sets expectations for every step that follows. + +## Further Reading + +- [5. Rich Text View and Signup Form](/courses/course-03-build-real-world-full-stack-mobile-apps-java/082-5-rich-text-view-and-signup-form/) +- [7. Signup Form - Name, Birthday and Gender](/courses/course-03-build-real-world-full-stack-mobile-apps-java/084-7-signup-form-name-birthday-and-gender/) +- [Working with CSS](/courses/course-02-deep-dive-mobile-development-with-codename-one/001-working-with-css/) +- [CSS Changes](/courses/course-02-deep-dive-mobile-development-with-codename-one/013-css-changes/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/084-7-signup-form-name-birthday-and-gender.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/084-7-signup-form-name-birthday-and-gender.md index 9c0be716ef..7cbe57fc3b 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/084-7-signup-form-name-birthday-and-gender.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/084-7-signup-form-name-birthday-and-gender.md @@ -9,10 +9,25 @@ module_order: 13 lesson_order: 7 weight: 84 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Build the middle signup stages and use Codename One components that adapt naturally to platform-specific input conventions." --- - > Module 13: Creating a Facebook Clone - {{< youtube Nv_0NVgCbSk >}} + +These middle signup stages are where the wizard starts feeling like a real product instead of a static mockup. The screens are still simple, but they exercise a wider range of input patterns: paired text entry, date selection, and mutually exclusive choices with visual emphasis. + +The name form is a good showcase for `TextComponent` and `TextModeLayout`. Together they give the app a way to describe input fields at a higher level while still letting the underlying platform shape how those fields feel. On Android, that means the material-style animated labels. On iOS, it means a more grouped presentation. The lesson uses that flexibility well instead of hard-coding one platform’s style everywhere. + +The birthday form is deliberately plain, and that honesty is useful. Not every part of a product needs to become a design showcase. If the platform’s date-picking UI already solves the interaction well, it is often better to use it than to invent a more fragile custom control. + +The gender step then adds a different kind of UI problem: choosing between visually equivalent options where the selection itself needs to be obvious. Radio-button semantics with stronger visual styling are a clean fit here. The lesson’s use of grouped toggle-style buttons backed by one-button selection rules is straightforward and easy to reason about. + +Taken together, these screens show a good principle for signup flows: keep the structure stable, but let each input type use the most appropriate interaction model available. + +## Further Reading + +- [8. Signup Form - Phone, Email, Password and Confirmation](/courses/course-03-build-real-world-full-stack-mobile-apps-java/085-8-signup-form-phone-email-password-and-confirmation/) +- [Properties Are Amazing](/blog/properties-are-amazing/) +- [Layout Basics](/courses/course-01-java-for-mobile-devices/007-layout-basics/) +- [How Do I Position Components Using Layout Managers](/how-do-i/how-do-i-positioning-components-using-layout-managers/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/085-8-signup-form-phone-email-password-and-confirmation.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/085-8-signup-form-phone-email-password-and-confirmation.md index 175c6d51cf..4c8cda15f7 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/085-8-signup-form-phone-email-password-and-confirmation.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/085-8-signup-form-phone-email-password-and-confirmation.md @@ -9,10 +9,25 @@ module_order: 13 lesson_order: 8 weight: 85 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Finish the signup wizard by handling alternate contact routes, password entry, and final confirmation." --- - > Module 13: Creating a Facebook Clone - {{< youtube v-kJ_nIYq3I >}} + +The final signup stages are less about new UI ideas and more about keeping the flow coherent as the user approaches completion. That makes this lesson mostly a test of whether the earlier abstractions were worth building. + +The answer is yes. Phone-number and email entry are almost the same screen, and the code treats them that way without forcing them into a confusing generic abstraction. This is a good example of restraint. Similar screens can share a helper without erasing the fact that they represent slightly different decisions in the flow. + +The key detail here is navigation between alternatives. The user can move from phone to email or back again without the wizard feeling like it split into two unrelated branches. That continuity matters more than the individual input fields themselves. + +The password and confirmation stages are then intentionally minimal. By this point the wizard has already done most of its real work, so the final screens should feel like progress, not like the app suddenly discovered a new set of demands. The lesson handles that correctly by keeping the screens short, contextual, and clearly connected to what the user just entered. + +The final wiring into the UI controller is also important. A wizard is not complete until the rest of the app can actually route into it and out of it cleanly. This lesson closes that loop so the signup flow stops being a pile of isolated forms and becomes a real branch of application navigation. + +## Further Reading + +- [7. Signup Form - Name, Birthday and Gender](/courses/course-03-build-real-world-full-stack-mobile-apps-java/084-7-signup-form-name-birthday-and-gender/) +- [9. The Main Form](/courses/course-03-build-real-world-full-stack-mobile-apps-java/086-9-the-main-form/) +- [Threading and the EDT](/courses/course-01-java-for-mobile-devices/011-threading-and-the-edt/) +- [Creating a Hello World App](/courses/course-01-java-for-mobile-devices/002-creating-a-hello-world-app/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/086-9-the-main-form.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/086-9-the-main-form.md index 6dd7f516d4..c0eaf785f1 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/086-9-the-main-form.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/086-9-the-main-form.md @@ -9,10 +9,25 @@ module_order: 13 lesson_order: 9 weight: 86 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Create the tabbed shell of the Facebook clone and define the four major areas the rest of the app will build on." --- - > Module 13: Creating a Facebook Clone - {{< youtube q4YjUClRHBk >}} + +Once the signup flow is in place, the app finally needs a home. This lesson builds that shell: the tabbed main form that holds the news feed, friends, notifications, and “more” sections together. + +That shell is more important than it first appears. A social app is mostly movement between adjacent sections of one large experience. If the outer navigation structure feels wrong, the whole app feels fragmented no matter how good the individual screens are. + +The tab design in the lesson takes platform differences seriously. Tabs belong in different places on different platforms, and Codename One makes it possible to respect that expectation without duplicating the whole app structure. That is exactly the kind of platform-aware decision that improves a clone without making the code unmanageable. + +The lesson also makes a useful styling decision by turning the search affordance into something that looks like a field while still behaving like a navigation trigger. That matches the product’s intent: the main form is not trying to host a full search experience yet, but it still needs to make search feel present and ready. + +At this stage, the individual containers behind the tabs are still mostly placeholders, and that is fine. The job of this lesson is to define the application frame and make it possible for the later feature work to land in the right places. + +## Further Reading + +- [10. Client Data Model - User, Post and Comment](/courses/course-03-build-real-world-full-stack-mobile-apps-java/087-10-client-data-model-user-post-and-comment/) +- [12. The Newsfeed Container](/courses/course-03-build-real-world-full-stack-mobile-apps-java/089-12-the-newsfeed-container/) +- [Friends Container](/courses/course-03-build-real-world-full-stack-mobile-apps-java/090-13-friends-container/) +- [How Do I Create Gorgeous SideMenu](/how-do-i/how-do-i-create-gorgeous-sidemenu/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/087-10-client-data-model-user-post-and-comment.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/087-10-client-data-model-user-post-and-comment.md index 99c95e7fba..ea5a373a7e 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/087-10-client-data-model-user-post-and-comment.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/087-10-client-data-model-user-post-and-comment.md @@ -9,10 +9,27 @@ module_order: 13 lesson_order: 10 weight: 87 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Define the client-side business objects that the feed, comments, profiles, and later server code will all rely on." --- - > Module 13: Creating a Facebook Clone - {{< youtube PHb5YO77OQk >}} + +There is a moment in every UI-heavy project when the mockup stops being “just screens” and starts forcing you to define the underlying data. This is that moment for the Facebook clone. + +The lesson introduces `User`, `Post`, and `Comment` as property-based business objects, and that is the right direction for this app. A social product is full of structured data that needs to move between storage, server communication, caching, and UI. Codename One properties fit that problem well because they reduce a lot of repetitive plumbing. + +The `User` model comes first because so much of the app depends on it: signup, identity, profile images, social connections, and display names. The avatar handling is especially useful because it treats the image not as a dumb URL but as something the client can fetch, cache, resize, and shape into the exact visual form the UI needs. + +That is a recurring pattern throughout this course. The raw data is not always the same thing as the UI-ready data. Sometimes the business object should help bridge that gap directly when the transformation is common and predictable. + +The `Post` and `Comment` models then complete the minimum vocabulary needed for a social feed. Visibility, likes, comments, author identity, timestamps, and content styling all show up here because the feed UI is about to demand them. This is a good example of building the model close to the product rather than designing a grand universal schema first. + +The nested-comment support hinted at in the `Comment` model is also a good sign of foresight. Even before the threaded UI exists, the model is being shaped so the app will not need to undo basic decisions later. + +## Further Reading + +- [11. ServerAPI Abstraction Mockup](/courses/course-03-build-real-world-full-stack-mobile-apps-java/088-11-serverapi-abstraction-mockup/) +- [12. The Newsfeed Container](/courses/course-03-build-real-world-full-stack-mobile-apps-java/089-12-the-newsfeed-container/) +- [Properties Are Amazing](/blog/properties-are-amazing/) +- [Overview and Basic Model](/courses/course-02-deep-dive-mobile-development-with-codename-one/015-overview-and-basic-model/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/088-11-serverapi-abstraction-mockup.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/088-11-serverapi-abstraction-mockup.md index fb5a431cca..6103990680 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/088-11-serverapi-abstraction-mockup.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/088-11-serverapi-abstraction-mockup.md @@ -9,10 +9,27 @@ module_order: 13 lesson_order: 11 weight: 88 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Create a temporary server API layer so the feed can be built against stable application-level calls before the real backend exists." --- - > Module 13: Creating a Facebook Clone - {{< youtube PERdJq3I-As >}} + +Once the client data model exists, the next architectural step is to stop letting the UI imagine that data comes from nowhere. Even if the real backend is not ready yet, the app should already talk to an abstraction that looks like a server boundary. + +That is what this lesson gets right. The mock `ServerAPI` is not just fake data for convenience. It is a way to force the UI to depend on application-level operations instead of hard-coded inline sample objects. + +That decision pays off later because the UI can be written once against methods such as “fetch timeline posts” or “get current user,” and the implementation behind those methods can later switch from mock data to real network calls without rewriting every screen. + +The mock data itself is also more intentional than a typical placeholder. It includes realistic users, timestamps, avatars, and simple relationships such as friends and suggestions. That makes the upcoming feed and friends UI feel like they are attached to a real product even before the server work is done. + +The utility methods added alongside this abstraction are part of the same story. Formatting “just now” style timestamps and creating reusable separators may feel small, but they keep repeated UI logic out of the container classes where it would otherwise start to pile up. + +This is exactly the right stage to introduce a server boundary. The app is complex enough to need one, but still small enough that putting it in place now will simplify everything that follows. + +## Further Reading + +- [10. Client Data Model - User, Post and Comment](/courses/course-03-build-real-world-full-stack-mobile-apps-java/087-10-client-data-model-user-post-and-comment/) +- [12. The Newsfeed Container](/courses/course-03-build-real-world-full-stack-mobile-apps-java/089-12-the-newsfeed-container/) +- [17. Spring Boot Server Architecture and the User Entity](/courses/course-03-build-real-world-full-stack-mobile-apps-java/094-17-spring-boot-server-architecture-and-the-user-entity/) +- [Connecting to a Web Service](/courses/course-02-deep-dive-mobile-development-with-codename-one/003-connecting-to-a-web-service/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/089-12-the-newsfeed-container.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/089-12-the-newsfeed-container.md index 6f8a2ff13a..a52b4b5707 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/089-12-the-newsfeed-container.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/089-12-the-newsfeed-container.md @@ -9,10 +9,25 @@ module_order: 13 lesson_order: 12 weight: 89 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Assemble the first real social feed by combining infinite scrolling, a post composer stub, and reusable post-rendering helpers." --- - > Module 13: Creating a Facebook Clone - {{< youtube B4QCJRFpG-k >}} + +This is the lesson where the Facebook clone starts feeling alive. The news feed is the first screen in the app that combines real scrolling content, structured models, reusable view fragments, and a data source that looks like a real server. + +Using `InfiniteContainer` is a natural fit here. A feed is exactly the kind of UI that should not pretend it knows all of its content up front. The container can ask for more entries as the user scrolls and can also support pull-to-refresh without requiring a completely different architecture for reloading. + +The lesson handles that well by separating the feed into small pieces. There is a top “what’s on your mind” style post bar, a title/header area for each post, a stats area, and then a final method that assembles those parts into one feed item. That keeps the code readable and makes it much easier to evolve the rendering later. + +The time-formatting helper from the previous lesson also pays off here. Social feeds always need human-readable time, and handling that in a utility layer instead of repeating it in every view method is exactly the kind of small discipline that keeps UI code healthy as the app grows. + +The feed item assembly code also shows good layout judgment. Buttons that should align are placed in a grid. Repeated structural spacing is centralized in reusable UIIDs. Rich text is used where post content needs it. The lesson is not just rendering data. It is turning several reusable visual rules into one coherent feed entry. + +## Further Reading + +- [10. Client Data Model - User, Post and Comment](/courses/course-03-build-real-world-full-stack-mobile-apps-java/087-10-client-data-model-user-post-and-comment/) +- [11. ServerAPI Abstraction Mockup](/courses/course-03-build-real-world-full-stack-mobile-apps-java/088-11-serverapi-abstraction-mockup/) +- [13. Friends Container](/courses/course-03-build-real-world-full-stack-mobile-apps-java/090-13-friends-container/) +- [How Do I Create a List of Items the Easy Way](/how-do-i/how-do-i-create-a-list-of-items-the-easy-way/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/090-13-friends-container.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/090-13-friends-container.md index 53a216976f..3702c09edb 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/090-13-friends-container.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/090-13-friends-container.md @@ -9,10 +9,25 @@ module_order: 13 lesson_order: 13 weight: 90 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Render friend requests and suggestions with a simpler container-based approach that still supports refresh and dynamic avatars." --- - > Module 13: Creating a Facebook Clone - {{< youtube LZ_ydua__gY >}} + +After the feed, the friends screen feels almost relaxing. It is a simpler UI, but it still has enough moving parts to show how the same data and styling ideas can support a different kind of social surface. + +The lesson makes a sensible choice by using a regular scrollable container instead of `InfiniteContainer`. Not every list in an app needs infinite loading just because one important screen does. Friend requests and suggestions are finite enough here that a simpler structure keeps the code easier to understand. + +The screen itself is built from two kinds of sections: requests that need action and suggestions that invite exploration. That split is useful because it gives the UI a little product logic instead of flattening all relationships into one undifferentiated list. + +The avatar handling is also a good example of being flexible about presentation. The feed used circular identity images because that matched the product language there. This screen uses square images because the visual reference calls for them. The underlying user identity is the same, but the rendering can still adapt to the needs of the specific screen. + +The confirm/delete controls then finish the pattern. The screen is not just decorative. It expresses clear actions, and the CSS gives those actions distinct visual weight without needing a large amount of custom code. + +## Further Reading + +- [12. The Newsfeed Container](/courses/course-03-build-real-world-full-stack-mobile-apps-java/089-12-the-newsfeed-container/) +- [39. Settings Form and Fetching the Avatar Image](/courses/course-03-build-real-world-full-stack-mobile-apps-java/076-39-settings-form-and-fetching-the-avatar-image/) +- [Client Side UserService](/courses/course-03-build-real-world-full-stack-mobile-apps-java/050-13-client-side-userservice/) +- [How Do I Create a List of Items the Easy Way](/how-do-i/how-do-i-create-a-list-of-items-the-easy-way/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/091-14-notifications-container.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/091-14-notifications-container.md index 815e4dd1a1..fcb044e4bc 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/091-14-notifications-container.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/091-14-notifications-container.md @@ -9,10 +9,25 @@ module_order: 13 lesson_order: 14 weight: 91 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Render a notification feed with lightweight reaction metadata and the same incremental loading approach used by the main timeline." --- - > Module 13: Creating a Facebook Clone - {{< youtube FczBAgUIb1c >}} + +Notifications are one of the places where a social app starts feeling reactive instead of static. This lesson adds that layer without overcomplicating it. The notification screen is basically a specialized feed: timestamped items, user identity, a short action summary, and a visual reaction marker that hints at why the notification happened. + +That makes the design choice in this lesson straightforward and correct. Use the same incremental-loading model as the news feed, but simplify the rendering because notifications do not need the full structure of a post. + +The new notification business object is also a good example of the client/server contract staying focused. The server decides the text, icon semantics, and background accent for the reaction. The client’s job is to render those values consistently, not to recalculate their meaning locally. + +The layered avatar-plus-reaction treatment is the nicest UI detail here. It uses familiar identity imagery as the base and then overlays the event type in a way users can scan quickly. That is exactly the sort of small visual system that helps a dense app stay readable. + +This lesson also demonstrates that not every feed-style screen needs a long chain of helper methods. The news feed needed more decomposition because posts are complex. Notifications are simpler, so the screen can stay lighter without becoming messy. + +## Further Reading + +- [12. The Newsfeed Container](/courses/course-03-build-real-world-full-stack-mobile-apps-java/089-12-the-newsfeed-container/) +- [23. NotificationService and MediaService](/courses/course-03-build-real-world-full-stack-mobile-apps-java/100-23-notificationservice-and-mediaservice/) +- [39. Settings Form and Fetching the Avatar Image](/courses/course-03-build-real-world-full-stack-mobile-apps-java/076-39-settings-form-and-fetching-the-avatar-image/) +- [Push Notifications](/push-notifications/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/092-15-the-more-container.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/092-15-the-more-container.md index 47b6b55507..0a857a58fc 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/092-15-the-more-container.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/092-15-the-more-container.md @@ -9,10 +9,25 @@ module_order: 13 lesson_order: 15 weight: 92 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Build the overflow area of the app and turn miscellaneous functionality into a coherent menu rather than a dumping ground." --- - > Module 13: Creating a Facebook Clone - {{< youtube kfKsVAx659s >}} + +The “more” tab is where large apps often reveal whether they still have any structural discipline. It can either become a dumping ground for leftovers or a clean overflow area for features that do not deserve top-level navigation. + +This lesson takes the second path. The screen is intentionally simple, but it still treats the section as a real part of the app by giving entries consistent icon treatment, spacing, and text hierarchy. That is enough to make the area feel deliberate even before most of its destinations are implemented. + +The use of `MultiButton` is a good fit here. These entries are really structured menu rows: primary label, optional secondary text, icon treatment, and clear tap behavior. A higher-level component makes more sense than rebuilding that row structure manually for every entry. + +The lesson also shows a healthy willingness to handle a small styling gap in code when the CSS support of the time was not sufficient. That part is historical, but the underlying principle still holds: use CSS as the default, and fill the rare gaps pragmatically instead of distorting the whole design to avoid one line of code. + +What matters most is that the app now has a place for user-adjacent functions such as settings without forcing them into the main feed tabs. That keeps the primary navigation focused while still leaving room for the app to grow. + +## Further Reading + +- [39. Settings Form and Fetching the Avatar Image](/courses/course-03-build-real-world-full-stack-mobile-apps-java/076-39-settings-form-and-fetching-the-avatar-image/) +- [9. The Main Form](/courses/course-03-build-real-world-full-stack-mobile-apps-java/086-9-the-main-form/) +- [How Do I Create Gorgeous SideMenu](/how-do-i/how-do-i-create-gorgeous-sidemenu/) +- [Working with CSS](/courses/course-02-deep-dive-mobile-development-with-codename-one/001-working-with-css/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/093-16-the-new-post-form.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/093-16-the-new-post-form.md index 578b686b3b..3119563a25 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/093-16-the-new-post-form.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/093-16-the-new-post-form.md @@ -9,10 +9,25 @@ module_order: 13 lesson_order: 16 weight: 93 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Create the first version of the post-composer UI and let users switch between several visual post styles." --- - > Module 13: Creating a Facebook Clone - {{< youtube 0a6mPI412C4 >}} + +The post composer is where the Facebook clone stops being a read-mostly app and becomes participatory. Even in this first simplified version, that changes the feel of the whole product. + +The form is intentionally scoped to text-first posting, and that is the right call. Images, video, and richer media behavior can come later. The important step here is establishing the composer as its own screen with clear identity, visibility controls, and a way to preview different post styles. + +The style picker at the bottom is the most interesting part of the lesson. Instead of treating post styling as a bag of unrelated options, the UI presents a small set of named visual modes. That makes the experience fast and tactile. The user is not editing raw styling properties. They are choosing among recognizable post treatments. + +The use of radio-style selection for those styles is a good fit because only one visual mode should be active at a time. The composer can then translate that choice into a combination of UIIDs and content treatment without exposing that machinery to the user. + +This lesson also shows a useful boundary between content and presentation. The post text stays the same, but the surrounding style changes. That separation matters later when the app needs to store style choices alongside the post rather than flattening them into one opaque blob of rendered content. + +## Further Reading + +- [12. The Newsfeed Container](/courses/course-03-build-real-world-full-stack-mobile-apps-java/089-12-the-newsfeed-container/) +- [10. Client Data Model - User, Post and Comment](/courses/course-03-build-real-world-full-stack-mobile-apps-java/087-10-client-data-model-user-post-and-comment/) +- [Working with CSS](/courses/course-02-deep-dive-mobile-development-with-codename-one/001-working-with-css/) +- [Style Customization 1 - Introduction and Basics](/courses/course-03-build-real-world-full-stack-mobile-apps-java/016-style-customization-1-introduction-and-basics/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/094-17-spring-boot-server-architecture-and-the-user-entity.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/094-17-spring-boot-server-architecture-and-the-user-entity.md index 56a5e214ce..95801165fe 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/094-17-spring-boot-server-architecture-and-the-user-entity.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/094-17-spring-boot-server-architecture-and-the-user-entity.md @@ -9,10 +9,27 @@ module_order: 13 lesson_order: 17 weight: 94 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Set up the backend architecture and define the first real server-side entity around the user model." --- - > Module 13: Creating a Facebook Clone - {{< youtube iCY64ThCleI >}} + +Up to this point the Facebook clone has been living on a mock server boundary. This lesson starts building the real backend behind that boundary, and it does so in the right order: architecture first, then the first entity. + +The four-layer split in the lesson is still a strong way to explain the backend: web-service endpoints at the edge, service classes for business logic, DAO objects for transport, and JPA entities plus repositories for persistence. Even if the exact Spring stack evolves, that separation of concerns remains valuable because it keeps storage, transport, and business rules from collapsing into one class. + +The best part of this approach is that it leaves room for change. A service method can later be exposed through a different transport without rewriting the business logic. A storage mechanism can later be swapped without forcing the client contract to change at the same rate. That is the real payoff of the structure, not just aesthetic cleanliness. + +The `User` entity is the right place to begin because almost every feature built so far depends on it. Signup, login, profile rendering, friendships, avatars, and notifications all become easier to reason about once the server has a proper user model. + +The discussion of IDs is also one of the more useful backend design points in the course. Exposing sequential numeric primary keys to the client is convenient at first, but it creates obvious problems. UUID-style external IDs make much more sense for a public-facing system where client-visible identifiers should not invite trivial enumeration. + +The client and server user models are deliberately similar, and that is good. They represent the same domain concept. The server version can still carry extra concerns such as password hashing and security token handling, but the basic overlap is a strength, not a problem. + +## Further Reading + +- [21. Service Layer and UserService](/courses/course-03-build-real-world-full-stack-mobile-apps-java/098-21-service-layer-and-userservice/) +- [10. Client Data Model - User, Post and Comment](/courses/course-03-build-real-world-full-stack-mobile-apps-java/087-10-client-data-model-user-post-and-comment/) +- [Introduction to Spring Boot](/courses/course-02-deep-dive-mobile-development-with-codename-one/002-introduction-to-spring-boot/) +- [Connecting to a Web Service](/courses/course-02-deep-dive-mobile-development-with-codename-one/003-connecting-to-a-web-service/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/095-18-media-entity.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/095-18-media-entity.md index 18479e2fd6..1f2b0c8e82 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/095-18-media-entity.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/095-18-media-entity.md @@ -9,10 +9,25 @@ module_order: 13 lesson_order: 18 weight: 95 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Define a reusable media model that can hold uploaded files now and still leave room for more scalable storage later." --- - > Module 13: Creating a Facebook Clone - {{< youtube W_1S2Rzgff8 >}} + +A social app quickly runs into the question of where media should live. This lesson answers that by introducing a dedicated media entity instead of burying file data inside unrelated objects. + +That is the right first step regardless of whether the actual bytes stay in the database forever. Separating media into its own entity means the application can reason about uploads, ownership, visibility, and purpose independently from the records that reference them. + +The lesson is also honest about the tradeoff. Storing blobs in a database can be convenient, especially in a simple deployment or clustered environment, but it is not always the final answer. The more durable architectural lesson is that the app should already have a media abstraction, so moving later to object storage or another file service does not require redesigning the whole domain model. + +Fields such as filename, timestamp, role, visibility, and owner are doing real work here. They turn the media record from “just bytes” into something the rest of the application can reason about. An avatar image is not the same thing as a post attachment, and visibility rules matter long before the app has every UI feature built. + +The DAO stays intentionally simple, which is also a good sign. Media transport should be explicit and predictable rather than full of hidden side effects. + +## Further Reading + +- [19. Post and Comment Entities](/courses/course-03-build-real-world-full-stack-mobile-apps-java/096-19-post-and-comment-entities/) +- [23. NotificationService and MediaService](/courses/course-03-build-real-world-full-stack-mobile-apps-java/100-23-notificationservice-and-mediaservice/) +- [39. Settings Form and Fetching the Avatar Image](/courses/course-03-build-real-world-full-stack-mobile-apps-java/076-39-settings-form-and-fetching-the-avatar-image/) +- [40. Edit User - UI Binding and Multipart Image Upload](/courses/course-03-build-real-world-full-stack-mobile-apps-java/077-40-edit-user-ui-binding-and-multipart-image-upload/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/096-19-post-and-comment-entities.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/096-19-post-and-comment-entities.md index db6db64b80..0f45d5642d 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/096-19-post-and-comment-entities.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/096-19-post-and-comment-entities.md @@ -9,10 +9,27 @@ module_order: 13 lesson_order: 19 weight: 96 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Define the server-side post and comment entities so the feed can move from mock data toward persistent social content." --- - > Module 13: Creating a Facebook Clone - {{< youtube tjgyigzxo5U >}} + +Once users and media exist on the server, posts and comments are the next unavoidable step. These are the entities that turn the app from a profile system into a real social product. + +The lesson follows a sound pattern by keeping the server-side models close to the client-side ones while still allowing the server versions to carry extra persistence and relationship detail. That overlap is a feature, not a flaw. Both sides are describing the same social concepts. + +The `Post` entity brings together authorship, timing, content, visibility, styling, comments, and likes. That is a lot of responsibility, but it is the right bundle because those features are what the feed needs to display and what the backend needs to query. + +The choice to store individual likes rather than only a count is especially important. Counts are convenient for rendering, but a social app usually needs to know who liked something, not just how many people did. Once that requirement exists, the model has to reflect it. + +The `Comment` entity then rounds out the conversation layer. Supporting a parent comment reference early is a good move because it leaves room for nested threads later even if the first client UI stays simpler. + +The repository discussion is also useful because this is where paging starts to matter. Social content is exactly the kind of data that should not be loaded all at once. Building pageable queries into the persistence layer early keeps later service code cleaner and keeps the app aligned with the feed-style UX it already has on the client. + +## Further Reading + +- [20. Notification, Newsfeed and ShadowUser Entities](/courses/course-03-build-real-world-full-stack-mobile-apps-java/097-20-notification-newsfeed-and-shadowuser-entities/) +- [21. Service Layer and UserService](/courses/course-03-build-real-world-full-stack-mobile-apps-java/098-21-service-layer-and-userservice/) +- [10. Client Data Model - User, Post and Comment](/courses/course-03-build-real-world-full-stack-mobile-apps-java/087-10-client-data-model-user-post-and-comment/) +- [12. The Newsfeed Container](/courses/course-03-build-real-world-full-stack-mobile-apps-java/089-12-the-newsfeed-container/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/097-20-notification-newsfeed-and-shadowuser-entities.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/097-20-notification-newsfeed-and-shadowuser-entities.md index f418fad7f2..5f26a822e5 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/097-20-notification-newsfeed-and-shadowuser-entities.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/097-20-notification-newsfeed-and-shadowuser-entities.md @@ -9,10 +9,27 @@ module_order: 13 lesson_order: 20 weight: 97 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Add the remaining social entities, including notifications, a ranked newsfeed table, and the shadow-user model behind friend suggestions." --- - > Module 13: Creating a Facebook Clone - {{< youtube Otujb_KyofA >}} + +This lesson is where the server model becomes recognizably social. Notifications are the obvious missing piece, but the more interesting work is the introduction of two server-only concepts: a materialized newsfeed entity and the shadow-user model behind “people you may know.” + +The notification entity is straightforward and intentionally mirrors the client model closely. That makes sense because notifications are already being rendered on the device and mostly need a clean transport path plus some server-side persistence. + +The `Newsfeed` entity is the more ambitious idea. Instead of treating the feed as a fresh query over posts every time, the lesson frames it as something that can be assembled, ranked, stored, and served as its own dataset. That is a stronger design for a real social product because feed ordering is not just raw chronology. It is the result of ranking rules, personalization, and the need for some consistency over time. + +Even in this simplified implementation, that architectural move is important. It gives the backend a place to evolve ranking logic without forcing every client request to recompute the whole feed from scratch. + +The `ShadowUser` entity is the most revealing social-graph concept in the module. It models information the system knows about people who may not yet be full users, typically because someone uploaded contacts or other identifying data. That is what allows the app to make friend suggestions with more context than the visible user table alone would provide. + +Even though the implementation here is intentionally simplified, the lesson is useful because it makes an often invisible product mechanism explicit. Suggestions in social apps do not come from magic. They come from graph-building work and stored relationship hints, and that requires its own model. + +## Further Reading + +- [14. Notifications Container](/courses/course-03-build-real-world-full-stack-mobile-apps-java/091-14-notifications-container/) +- [21. Service Layer and UserService](/courses/course-03-build-real-world-full-stack-mobile-apps-java/098-21-service-layer-and-userservice/) +- [22. UserService Part II](/courses/course-03-build-real-world-full-stack-mobile-apps-java/099-22-userservice-part-ii/) +- [13. Friends Container](/courses/course-03-build-real-world-full-stack-mobile-apps-java/090-13-friends-container/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/098-21-service-layer-and-userservice.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/098-21-service-layer-and-userservice.md index f28acb7d5e..b25facba4b 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/098-21-service-layer-and-userservice.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/098-21-service-layer-and-userservice.md @@ -9,10 +9,27 @@ module_order: 13 lesson_order: 21 weight: 98 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Move from storage to business logic by implementing the first major service class behind signup, login, and activation." --- - > Module 13: Creating a Facebook Clone - {{< youtube QY7josfVbdg >}} + +The entity layer stores information. The service layer is where the application starts making decisions. That distinction matters, and this lesson is the first place in the Facebook backend where the system begins to feel like more than a database with endpoints attached to it. + +`UserService` is the natural place to start because signup, login, activation, and identity all flow through it. Those are not just persistence operations. They involve validation, password handling, token creation, activation workflows, and relationship bootstrapping. + +The login path is a good example of service-layer value. The repositories can find candidate users by email or phone, but the service is the right place to enforce expectations such as uniqueness and password verification. The service is also the right place to convert failure into a meaningful application-level error instead of leaking raw persistence concerns outward. + +Signup is more interesting because it touches almost every part of the surrounding architecture. It needs to reject duplicate accounts, absorb information from shadow-user records, hash the password, mint an auth token, generate an activation code, and then send that code through an external delivery mechanism. That is exactly the kind of workflow that belongs in a service class and nowhere else. + +The lesson’s `setProps` helper is also a useful reminder that not every field should be updated just because it exists on the entity. A service method should decide explicitly which properties are safe to copy from client input and which ones require special handling. + +The sections on Twilio and Mailgun are partly about tooling, but the more durable architectural lesson is that delivery integrations should be driven from the service layer and configured from outside source control. API keys, sender identities, and environment-specific credentials belong in configuration, not hard-coded into business logic. + +## Further Reading + +- [22. UserService Part II](/courses/course-03-build-real-world-full-stack-mobile-apps-java/099-22-userservice-part-ii/) +- [17. Spring Boot Server Architecture and the User Entity](/courses/course-03-build-real-world-full-stack-mobile-apps-java/094-17-spring-boot-server-architecture-and-the-user-entity/) +- [20. Notification, Newsfeed and ShadowUser Entities](/courses/course-03-build-real-world-full-stack-mobile-apps-java/097-20-notification-newsfeed-and-shadowuser-entities/) +- [Push 3 - The Server Side and Build Logic](/courses/course-03-build-real-world-full-stack-mobile-apps-java/022-push-3-the-server-side-and-build-logic/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/099-22-userservice-part-ii.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/099-22-userservice-part-ii.md index 1de4360ce2..12658d1da9 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/099-22-userservice-part-ii.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/099-22-userservice-part-ii.md @@ -9,10 +9,27 @@ module_order: 13 lesson_order: 22 weight: 99 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Extend UserService with avatar updates, friend requests, and contact-upload logic that grows the social graph." --- - > Module 13: Creating a Facebook Clone - {{< youtube JZvrsZzdays >}} + +If the first half of `UserService` is about identity, the second half is about relationships. This is where the service starts earning its place as the social core of the app rather than just the entry point for account creation. + +Avatar retrieval and upload are a good warm-up because they clearly separate public and authenticated behavior. Anyone who knows a user’s public identity can fetch the avatar. Only the authenticated owner should be able to replace it. That is exactly the sort of permission distinction the service layer should make explicit. + +Friend requests raise the stakes a little more because they mutate two users’ relationship state and also trigger notifications. That is a strong reminder that many social operations are not local edits to one record. They are workflows that span multiple entities and often require side effects in other parts of the system. + +The acceptance path is especially important because it has to verify that the underlying request actually exists. Social operations are full of these asymmetry problems: one user can request, the other can accept, and the system has to make sure those actions line up instead of trusting the client blindly. + +The contact-upload logic then moves even deeper into social-graph construction. Once users can upload contact information, the service can start reconciling that data against known users and shadow-user records to improve the “people you may know” suggestions. The implementation here is intentionally simpler than a production social graph would be, but the architectural idea is sound: relationship-building often starts from imported hints rather than explicit in-app actions. + +This lesson also quietly reinforces a pattern that matters across the whole backend: batch operations such as `saveAll()` are often worth using when the service is processing a lot of related data. Social features can create large cascades of small updates, and the service layer is where you get to decide whether those updates happen efficiently. + +## Further Reading + +- [21. Service Layer and UserService](/courses/course-03-build-real-world-full-stack-mobile-apps-java/098-21-service-layer-and-userservice/) +- [20. Notification, Newsfeed and ShadowUser Entities](/courses/course-03-build-real-world-full-stack-mobile-apps-java/097-20-notification-newsfeed-and-shadowuser-entities/) +- [23. NotificationService and MediaService](/courses/course-03-build-real-world-full-stack-mobile-apps-java/100-23-notificationservice-and-mediaservice/) +- [13. Friends Container](/courses/course-03-build-real-world-full-stack-mobile-apps-java/090-13-friends-container/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/100-23-notificationservice-and-mediaservice.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/100-23-notificationservice-and-mediaservice.md index f7d447910f..c0e5c2a453 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/100-23-notificationservice-and-mediaservice.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/100-23-notificationservice-and-mediaservice.md @@ -9,10 +9,27 @@ module_order: 13 lesson_order: 23 weight: 100 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Finish the first service layer with dedicated notification and media services that can later grow into push and richer attachment workflows." --- - > Module 13: Creating a Facebook Clone - {{< youtube zJ7L5fi60H8 >}} + +These two services are simpler than `UserService`, but they matter because they isolate two concerns that would otherwise spread across the rest of the backend: event delivery and file handling. + +`NotificationService` is small on purpose. Right now it persists notifications and serves paged results back to the client. That may not look dramatic, but the separation is valuable because notifications are exactly the kind of feature that tends to accumulate extra delivery mechanisms over time. By giving them their own service now, the app already has the right place to add push, WebSocket fanout, or other delivery channels later. + +That is why the send-notification method is more important than it first appears. Even if it currently just writes a record, it defines the one place where “something happened and the user should hear about it” enters the backend. + +`MediaService` plays a similar role for uploads and access control. It owns the rule that media creation is authenticated, that timestamps come from the server rather than the client, and that visibility checks determine who can retrieve a given file. + +The visibility logic is especially important in a social app. Public media is easy. Friend-only media is where the service layer has to translate social relationships into authorization decisions. That should not be left to the client, and it should not be scattered across controller code. + +The little permission and visibility helpers in this lesson may feel unremarkable, but they are the sort of infrastructure that prevents the backend from drifting into a pile of ad hoc access checks. + +## Further Reading + +- [14. Notifications Container](/courses/course-03-build-real-world-full-stack-mobile-apps-java/091-14-notifications-container/) +- [18. Media Entity](/courses/course-03-build-real-world-full-stack-mobile-apps-java/095-18-media-entity/) +- [19. Post and Comment Entities](/courses/course-03-build-real-world-full-stack-mobile-apps-java/096-19-post-and-comment-entities/) +- [Push 3 - The Server Side and Build Logic](/courses/course-03-build-real-world-full-stack-mobile-apps-java/022-push-3-the-server-side-and-build-logic/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/101-24-postservice.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/101-24-postservice.md index 3dee61f94b..0ee9f02280 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/101-24-postservice.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/101-24-postservice.md @@ -9,10 +9,27 @@ module_order: 13 lesson_order: 24 weight: 101 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Implement the service-layer logic behind posting, commenting, likes, and the ranked user newsfeed." --- - > Module 13: Creating a Facebook Clone - {{< youtube UxATbrfWveU >}} + +`PostService` is where the social app starts acting like a social app instead of a collection of account features. This service owns the operations that make the feed move: creating posts, inserting them into newsfeeds, adding comments, and tracking likes. + +The first useful distinction in the lesson is between a user’s own posts and the newsfeed they consume. Those are not the same thing. A profile post list can often be queried directly from posts. A feed is more contextual. It blends content from multiple sources and needs its own ordering rules. + +That is why the separate `Newsfeed` entity introduced earlier matters so much here. `PostService` can write newly created posts into the right feed records when the post is created instead of trying to reconstruct the whole ranking story every time the client asks for page one again. + +The posting path is also a good example of service-layer responsibility. A new post is not just one database save. It creates the post record, decides who should see it, inserts it into the author’s feed, inserts it into friends’ feeds where appropriate, and returns the information the client needs to keep going. + +Comments and likes follow the same pattern. These are not just property changes on one row. They are social events with permission checks, relationship checks, and notification side effects. The lesson keeps them in one service, which is the right place because this is where the rules about who can interact with what actually belong. + +The simplifications are also sensible. There is no full reactions model yet, and unlike is omitted even though it would be easy to add later. That restraint keeps the service focused on the core feed interactions instead of widening the scope unnecessarily. + +## Further Reading + +- [20. Notification, Newsfeed and ShadowUser Entities](/courses/course-03-build-real-world-full-stack-mobile-apps-java/097-20-notification-newsfeed-and-shadowuser-entities/) +- [23. NotificationService and MediaService](/courses/course-03-build-real-world-full-stack-mobile-apps-java/100-23-notificationservice-and-mediaservice/) +- [29. Newsfeed and Posts From Server](/courses/course-03-build-real-world-full-stack-mobile-apps-java/106-29-newsfeed-and-posts-from-server/) +- [12. The Newsfeed Container](/courses/course-03-build-real-world-full-stack-mobile-apps-java/089-12-the-newsfeed-container/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/102-25-webservice-layer-and-userwebservice.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/102-25-webservice-layer-and-userwebservice.md index 53478cc82a..9d36ad7061 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/102-25-webservice-layer-and-userwebservice.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/102-25-webservice-layer-and-userwebservice.md @@ -9,10 +9,27 @@ module_order: 13 lesson_order: 25 weight: 102 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Expose the user and notification operations as clean HTTP endpoints without leaking business logic into controller code." --- - > Module 13: Creating a Facebook Clone - {{< youtube rjIQqfrFvbI >}} + +Once the service layer exists, the web-service layer should be boring. That is not a criticism. It is a sign that the architecture is doing its job. + +This lesson demonstrates that well. `UserWebService` is mostly a translation layer. It defines URLs, request shapes, headers, and response behavior, then hands the real work off to the service classes underneath. That is exactly what controllers should look like in a healthy backend. + +The exception-to-error-DAO mapping is one of the most important parts here. It gives the client a predictable error contract instead of exposing raw Java exceptions or framework defaults. Even simple apps benefit from that discipline because it keeps client-side error handling from becoming guesswork. + +The avatar endpoints are a good example of the controller layer adding protocol-specific behavior without swallowing business logic. They return image content with the right response metadata, turn missing avatars into `404` semantics, and still leave the real authorization and data retrieval rules to the service layer. + +The lesson also makes a pragmatic choice in a few places by using GET operations for state changes when the mobile client benefits from simpler calling code. That is not something I would present as ideal REST design today. It works here because the client is controlled, but the general rule should still be to avoid using GET for operations that mutate state. + +So the right takeaway is not “copy these HTTP verbs literally.” It is “keep the controller thin, explicit, and responsible only for transport concerns.” + +## Further Reading + +- [26. PostWebService and MediaWebService](/courses/course-03-build-real-world-full-stack-mobile-apps-java/103-26-postwebservice-and-mediawebservice/) +- [21. Service Layer and UserService](/courses/course-03-build-real-world-full-stack-mobile-apps-java/098-21-service-layer-and-userservice/) +- [22. UserService Part II](/courses/course-03-build-real-world-full-stack-mobile-apps-java/099-22-userservice-part-ii/) +- [Connecting to a Web Service](/courses/course-02-deep-dive-mobile-development-with-codename-one/003-connecting-to-a-web-service/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/103-26-postwebservice-and-mediawebservice.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/103-26-postwebservice-and-mediawebservice.md index 2502b905e2..d1e0544840 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/103-26-postwebservice-and-mediawebservice.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/103-26-postwebservice-and-mediawebservice.md @@ -9,10 +9,25 @@ module_order: 13 lesson_order: 26 weight: 103 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Finish the HTTP layer by exposing post, feed, comment, and media operations to the client." --- - > Module 13: Creating a Facebook Clone - {{< youtube h-I62aFYBNA >}} + +With the user-facing endpoints in place, the rest of the web-service layer becomes mostly a matter of exposing the remaining service operations cleanly. This lesson closes that gap for posts and media. + +`PostWebService` is intentionally direct, and that is a strength. The controller methods closely mirror the service methods for listing posts, loading feed pages, adding new posts, adding comments, and registering likes. That one-to-one mapping is a good sign that the service layer already holds the real application logic. + +The media side is slightly more subtle because file retrieval and upload bring protocol details with them. MIME types, binary responses, multipart uploads, and visibility-aware retrieval all belong in this layer because they are transport concerns. The controller has to know how to expose them over HTTP even though the service layer decides who is allowed to see what. + +The multipart upload support is still the right general approach for client-file uploads. The exact Spring APIs and wrappers may evolve, but the design idea is stable: the client submits the file and metadata in one structured request, and the server turns that into a media record plus an uploaded payload. + +The main thing to notice about this lesson is how uneventful it feels. That is exactly what you want. By this point the backend is structured well enough that finishing the endpoint layer mostly means declaratively exposing operations that already exist. + +## Further Reading + +- [25. WebService Layer and UserWebService](/courses/course-03-build-real-world-full-stack-mobile-apps-java/102-25-webservice-layer-and-userwebservice/) +- [24. PostService](/courses/course-03-build-real-world-full-stack-mobile-apps-java/101-24-postservice/) +- [23. NotificationService and MediaService](/courses/course-03-build-real-world-full-stack-mobile-apps-java/100-23-notificationservice-and-mediaservice/) +- [18. Media Entity](/courses/course-03-build-real-world-full-stack-mobile-apps-java/095-18-media-entity/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/104-27-client-side-serverapi.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/104-27-client-side-serverapi.md index 2dd0a711e9..e781dd35d4 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/104-27-client-side-serverapi.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/104-27-client-side-serverapi.md @@ -9,10 +9,27 @@ module_order: 13 lesson_order: 27 weight: 104 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Replace the mock client API with real network-backed calls and keep the app’s server boundary stable on the device." --- - > Module 13: Creating a Facebook Clone - {{< youtube yamsuV5Airc >}} + +This is where the clone stops pretending. The mock `ServerAPI` that made UI work possible earlier now gets replaced with a real network-backed implementation, and the payoff of introducing that abstraction early becomes obvious immediately. + +Because the UI was already written against an application-level API rather than against inline sample data, most of the work here is glue code rather than a client-side rewrite. That is exactly what good abstraction buys you. + +The helper methods for GET and POST requests are simple, but they matter. They centralize the base URL, authorization token, JSON headers, and common request behavior. Once those pieces live in one place, the rest of the client API stops repeating protocol noise. + +The login/signup path is also a useful demonstration of client-side state handling. Successful auth responses hydrate the user model, persist it into storage as JSON, and keep the token separately for convenient authorization headers later. That design is still reasonable. It gives the app a durable local session story without making startup logic depend on a fresh login every time. + +The media-upload path is the most protocol-heavy part of the lesson, but even there the client code stays relatively clean because Codename One already provides a usable multipart abstraction. The important thing is that the callback threading is handled correctly. If the network work finishes off the EDT, the success path has to return to the EDT before it mutates UI state. + +The contact-upload logic is also a good reminder that “client API” sometimes means real translation work. Native contact data does not arrive in the exact shape the server wants, so the client adapter has to transform it into the application’s transport format. + +## Further Reading + +- [28. Client/Server Signup Process](/courses/course-03-build-real-world-full-stack-mobile-apps-java/105-28-client-server-signup-process/) +- [29. Newsfeed and Posts From Server](/courses/course-03-build-real-world-full-stack-mobile-apps-java/106-29-newsfeed-and-posts-from-server/) +- [25. WebService Layer and UserWebService](/courses/course-03-build-real-world-full-stack-mobile-apps-java/102-25-webservice-layer-and-userwebservice/) +- [Connecting to a Web Service](/courses/course-02-deep-dive-mobile-development-with-codename-one/003-connecting-to-a-web-service/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/105-28-client-server-signup-process.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/105-28-client-server-signup-process.md index e9b3102d34..a8b636b345 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/105-28-client-server-signup-process.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/105-28-client-server-signup-process.md @@ -9,10 +9,27 @@ module_order: 13 lesson_order: 28 weight: 105 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Connect the signup wizard to the real backend and turn the mock account flow into a working client/server process." --- - > Module 13: Creating a Facebook Clone - {{< youtube 8WER-8R0WqA >}} + +The sensible first end-to-end integration point is authentication, and this lesson makes that explicit. Once login and signup talk to the real backend, the app stops being a UI prototype and starts becoming a working product. + +The login wiring is intentionally modest, which is good. The screen collects the fields it already owns, builds the request object, shows a progress indicator while the network request is in flight, and then either enters the main UI or displays an error. That is the whole story, and it should be. + +Signup is more interesting because it forces the multi-step wizard to start producing real data instead of just navigating from screen to screen. The binding approach introduced earlier pays off here. As the user moves through the wizard, the bound user object accumulates the entered values until the final request can be sent to the server. + +That is a clean way to connect a wizard to a backend. The UI does not need to manually copy every field at the end because each stage has already been working against the same evolving object. + +The confirmation step also becomes meaningful here. Instead of being a final mockup screen, it now sits on top of the real activation flow triggered by the backend’s SMS or email delivery. That makes the whole signup branch feel much more grounded. + +The last change to the UI controller is small but important: startup now depends on whether a session already exists. That is one of the first places where the app behaves like a real installed client instead of a demo that always begins from the same blank state. + +## Further Reading + +- [27. Client Side ServerAPI](/courses/course-03-build-real-world-full-stack-mobile-apps-java/104-27-client-side-serverapi/) +- [21. Service Layer and UserService](/courses/course-03-build-real-world-full-stack-mobile-apps-java/098-21-service-layer-and-userservice/) +- [22. UserService Part II](/courses/course-03-build-real-world-full-stack-mobile-apps-java/099-22-userservice-part-ii/) +- [4. Login Form](/courses/course-03-build-real-world-full-stack-mobile-apps-java/081-4-login-form/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/106-29-newsfeed-and-posts-from-server.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/106-29-newsfeed-and-posts-from-server.md index a45619b497..1ca009c8cb 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/106-29-newsfeed-and-posts-from-server.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/106-29-newsfeed-and-posts-from-server.md @@ -9,10 +9,25 @@ module_order: 13 lesson_order: 29 weight: 106 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Switch the feed from mock data to the real backend and wire the post composer into the live posting flow." --- - > Module 13: Creating a Facebook Clone - {{< youtube AIsD5MXEK-c >}} + +This lesson is where the feed finally becomes real. The UI was already structured as though it were loading actual data; now that assumption becomes true. + +The key simplification is that paging no longer has to be faked on the client. Once the server defines the paging strategy, the client can stop inventing its own timestamp-based workaround and just request the next page. That is a good example of the architecture improving both sides at once: the server owns feed delivery rules, and the client becomes simpler because of it. + +The new-post form also crosses an important line here. Posting is no longer just a visual action that returns to the previous screen. It now creates a real `Post` object, sends it to the server, and relies on the backend to add it into the feed. That is the right separation of responsibilities. The client describes what the user wants to publish. The server decides how that content enters the social graph. + +The note about refresh is also honest and useful. At this stage the feed will not update magically after a new post unless the user refreshes. That is acceptable for now, and it sets up the later discussion about push and more reactive update paths. + +The friend-list refresh change follows the same pattern. Once user state is cached locally, the app needs a way to refresh it when relationship data changes. Pull-to-refresh becomes the practical bridge between cached identity state and server-side truth. + +## Further Reading + +- [12. The Newsfeed Container](/courses/course-03-build-real-world-full-stack-mobile-apps-java/089-12-the-newsfeed-container/) +- [16. The 'New Post' Form](/courses/course-03-build-real-world-full-stack-mobile-apps-java/093-16-the-new-post-form/) +- [24. PostService](/courses/course-03-build-real-world-full-stack-mobile-apps-java/101-24-postservice/) +- [27. Client Side ServerAPI](/courses/course-03-build-real-world-full-stack-mobile-apps-java/104-27-client-side-serverapi/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/107-30-friends-calendar-synchronization-accept-reject-requests.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/107-30-friends-calendar-synchronization-accept-reject-requests.md index 2c8e87691f..247abe7e4c 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/107-30-friends-calendar-synchronization-accept-reject-requests.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/107-30-friends-calendar-synchronization-accept-reject-requests.md @@ -9,10 +9,27 @@ module_order: 13 lesson_order: 30 weight: 107 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Connect the friends screen to real contact upload and request-acceptance flows so the social graph starts populating itself." --- - > Module 13: Creating a Facebook Clone - {{< youtube -aTpqxDa1Ag >}} + +The friends screen becomes much more convincing once it can do something beyond rendering static suggestions. This lesson gives it that capability by wiring contact upload, friend-request actions, and notification refresh into the real backend. + +The floating action button for contact upload is the most interesting UI move here. Because the friends screen lives inside a tabbed container rather than as a standalone form, adding a FAB is not just a one-line decoration. The lesson has to deal with how Codename One actually wraps containers to place a floating button above them. + +That is a good example of a recurring theme in the course: when a high-level feature feels awkward, it is often because the underlying container model matters more than it first appeared. Understanding that model lets the app get the exact result it wants without resorting to hacks that would be harder to maintain later. + +The contact-upload path then turns into real graph growth. By pulling contacts from the device, asking for the minimum useful fields, and sending them to the backend, the app gives the server enough information to improve suggestions and relationship discovery. + +The accept/remove request flows are also properly tied to UI mutation. Once the server confirms the action, the relevant container is removed and the layout animates into place. That keeps the interface feeling responsive rather than forcing a full-screen reload after every decision. + +The lesson’s mention of notifications is important too. Once friendship actions can happen for real, notification loading stops being decorative and starts representing actual state changes in the product. + +## Further Reading + +- [13. Friends Container](/courses/course-03-build-real-world-full-stack-mobile-apps-java/090-13-friends-container/) +- [22. UserService Part II](/courses/course-03-build-real-world-full-stack-mobile-apps-java/099-22-userservice-part-ii/) +- [14. Notifications Container](/courses/course-03-build-real-world-full-stack-mobile-apps-java/091-14-notifications-container/) +- [How Do I Use Push Notification / Send Server Push Messages](/how-do-i/how-do-i-use-push-notification-send-server-push-messages/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/108-31-search-server-side-with-spring-boot-and-hibernate.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/108-31-search-server-side-with-spring-boot-and-hibernate.md index a1e9a25d8a..75aede9de9 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/108-31-search-server-side-with-spring-boot-and-hibernate.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/108-31-search-server-side-with-spring-boot-and-hibernate.md @@ -9,10 +9,27 @@ module_order: 13 lesson_order: 31 weight: 108 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Add full-text search support on the backend and index only the fields that make sense to expose through user-facing search." --- - > Module 13: Creating a Facebook Clone - {{< youtube tWMVUDScyfQ >}} + +Search feels like it should be enormous, but a lot of the complexity is hidden if you choose the right backend tooling. This lesson leans on Hibernate Search to add basic full-text capabilities without turning the whole app into a hand-built indexing engine. + +The most important design decision here is not the library choice. It is deciding what should and should not be searchable. That is a product and privacy decision before it is a technical one. + +The lesson handles that correctly by indexing only the fields that make sense for public-facing search, such as user names and post text. That is a much healthier default than treating every stored field as fair game just because the search engine can technically index it. + +The search service then stays relatively small because the heavy lifting is delegated to the indexing layer. Query building, fuzzy matching, and pageable result retrieval become configuration and API choices rather than hand-written SQL gymnastics. + +That does not mean search is trivial. It means the complexity has moved to a layer designed to absorb it. This is exactly the kind of feature where a framework-backed solution is worth using instead of inventing something clever in application code. + +The one-time index build is also a useful operational reminder. Search is not just a query problem. It is an indexing lifecycle problem. The system needs a story for the first build and for staying up to date as entities change. + +## Further Reading + +- [32. Search: WebService and Client Code](/courses/course-03-build-real-world-full-stack-mobile-apps-java/109-32-search-webservice-and-client-code/) +- [33. Search: Client Side UI - SearchForm](/courses/course-03-build-real-world-full-stack-mobile-apps-java/110-33-search-client-side-ui-searchform/) +- [17. Spring Boot Server Architecture and the User Entity](/courses/course-03-build-real-world-full-stack-mobile-apps-java/094-17-spring-boot-server-architecture-and-the-user-entity/) +- [Search Results UI - UserForm and PostForm](/courses/course-03-build-real-world-full-stack-mobile-apps-java/111-34-search-results-ui-userform-and-postform/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/109-32-search-webservice-and-client-code.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/109-32-search-webservice-and-client-code.md index 816b37ec4f..8210c86b84 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/109-32-search-webservice-and-client-code.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/109-32-search-webservice-and-client-code.md @@ -9,10 +9,25 @@ module_order: 13 lesson_order: 32 weight: 109 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Expose the backend search API and map it into the client-side ServerAPI so the UI can treat search like any other paged data source." --- - > Module 13: Creating a Facebook Clone - {{< youtube _WKRuO0Izxg >}} + +Once the search service exists, the next step is mostly adaptation work: expose it over HTTP, then teach the client API how to consume it without turning search into a special snowflake. + +That is the best part of this lesson. Search is mapped into the same overall client/server architecture as everything else. It gets endpoints on the server, thin client methods in `ServerAPI`, and simple pagination rules that match the rest of the app’s data-loading story. + +The rebuild-search-database endpoint shown in the video is clearly a setup convenience rather than something you would want to expose casually in a production system. The important thing is not the exact hack. It is the operational need it addresses: search sometimes needs an explicit initialization path. + +On the client side, the generic search method is a nice demonstration of reusing one pattern for several result types. People and posts are different business objects, but from the perspective of transport they are both just paged result lists that need to be converted from JSON into app models. + +The note about generic erasure is also useful context here. Java generics feel expressive in application code, but at runtime the client often needs an explicit class token or another concrete hint if it wants to instantiate the right object type safely. + +## Further Reading + +- [31. Search: Server Side with Spring Boot and Hibernate](/courses/course-03-build-real-world-full-stack-mobile-apps-java/108-31-search-server-side-with-spring-boot-and-hibernate/) +- [33. Search: Client Side UI - SearchForm](/courses/course-03-build-real-world-full-stack-mobile-apps-java/110-33-search-client-side-ui-searchform/) +- [27. Client Side ServerAPI](/courses/course-03-build-real-world-full-stack-mobile-apps-java/104-27-client-side-serverapi/) +- [11. ServerAPI Abstraction Mockup](/courses/course-03-build-real-world-full-stack-mobile-apps-java/088-11-serverapi-abstraction-mockup/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/110-33-search-client-side-ui-searchform.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/110-33-search-client-side-ui-searchform.md index 115fce0d1f..561bbd3744 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/110-33-search-client-side-ui-searchform.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/110-33-search-client-side-ui-searchform.md @@ -9,10 +9,27 @@ module_order: 13 lesson_order: 33 weight: 110 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Build a dedicated search UI that updates as the user types without flooding the server with unnecessary requests." --- - > Module 13: Creating a Facebook Clone - {{< youtube sxCADu-SjpQ >}} + +The backend search pipeline is only useful once the UI can take advantage of it, and this lesson does that in the most practical way: a dedicated search form with debounced queries, a mode toggle, and paged results. + +The debounce logic is the heart of the screen. Search-as-you-type feels great only if it avoids the trap of sending a request on every keystroke. The lesson solves that by tracking recent edits, using a short delay, and cancelling pending work when the user is clearly still typing. + +That is the right tradeoff. The form remains responsive, the server is not spammed with pointless queries, and the user still experiences search as immediate. + +The mode switch between people and posts is also handled cleanly. The screen does not need two unrelated search experiences. It needs one search form with a different result builder depending on the current target domain. That keeps the UI simple and keeps the code aligned with the generic search API from the previous lesson. + +Using an `InfiniteContainer` for the results is a natural choice because search, like feeds, is a paged result stream. Once the query text and mode are stable, the result list can grow as needed without inventing a separate loading model. + +The final touch is small but important: putting the editable search field directly in the title area and immediately entering edit mode makes the form feel like a search tool instead of a normal screen that happens to contain a text field. + +## Further Reading + +- [32. Search: WebService and Client Code](/courses/course-03-build-real-world-full-stack-mobile-apps-java/109-32-search-webservice-and-client-code/) +- [34. Search Results UI - UserForm and PostForm](/courses/course-03-build-real-world-full-stack-mobile-apps-java/111-34-search-results-ui-userform-and-postform/) +- [12. The Newsfeed Container](/courses/course-03-build-real-world-full-stack-mobile-apps-java/089-12-the-newsfeed-container/) +- [Threading and the EDT](/courses/course-01-java-for-mobile-devices/011-threading-and-the-edt/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/111-34-search-results-ui-userform-and-postform.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/111-34-search-results-ui-userform-and-postform.md index d70e8c5364..a879a57810 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/111-34-search-results-ui-userform-and-postform.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/111-34-search-results-ui-userform-and-postform.md @@ -9,10 +9,25 @@ module_order: 13 lesson_order: 34 weight: 111 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Make search results navigable by turning matched users and posts into real destinations rather than dead-end list entries." --- - > Module 13: Creating a Facebook Clone - {{< youtube Rt68rA2c6A8 >}} + +Search is only complete once a result can take you somewhere meaningful. This lesson finishes that loop by giving people and posts their own destination screens. + +The user result path is the more important one. Clicking a person in search should not just highlight the result or show a tiny detail popup. It should open a screen that behaves like a profile-centric timeline and loads that user’s posts from the backend. That turns search into real navigation instead of a disconnected feature. + +The lesson wisely keeps the UI around that timeline fairly minimal. The value here is not in cloning every last detail of Facebook’s profile header. It is in proving that the search result can hand off to a live, data-backed screen with the correct feed semantics. + +The post-result path is even simpler, and that simplicity is fine. A post result already contains most of the interesting content, so the destination screen mainly needs to frame it clearly instead of inventing unnecessary surrounding structure. + +This is a good example of prioritizing functional completeness over ornamental completeness. A feature becomes credible when the main path works end to end, even if some decorative details are intentionally postponed. + +## Further Reading + +- [33. Search: Client Side UI - SearchForm](/courses/course-03-build-real-world-full-stack-mobile-apps-java/110-33-search-client-side-ui-searchform/) +- [29. Newsfeed and Posts From Server](/courses/course-03-build-real-world-full-stack-mobile-apps-java/106-29-newsfeed-and-posts-from-server/) +- [12. The Newsfeed Container](/courses/course-03-build-real-world-full-stack-mobile-apps-java/089-12-the-newsfeed-container/) +- [31. Search: Server Side with Spring Boot and Hibernate](/courses/course-03-build-real-world-full-stack-mobile-apps-java/108-31-search-server-side-with-spring-boot-and-hibernate/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/112-35-threaded-comments-ui-commentsform.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/112-35-threaded-comments-ui-commentsform.md index 6e4fe2aa1a..38526591dd 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/112-35-threaded-comments-ui-commentsform.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/112-35-threaded-comments-ui-commentsform.md @@ -9,10 +9,25 @@ module_order: 13 lesson_order: 35 weight: 112 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Add a focused comments screen with limited nesting so conversation stays readable while still supporting replies." --- - > Module 13: Creating a Facebook Clone - {{< youtube OnZIaGXDZSo >}} + +Comments are one of those features where technical possibility and product usability diverge quickly. The lesson handles that well by intentionally limiting nesting depth instead of chasing arbitrary thread depth just because the model can support it. + +That is the right choice here. Infinite nesting looks powerful on paper, but on a phone it quickly becomes hard to read and harder to interact with. A single level of replies captures most of the conversational value without letting the UI collapse into ever-shrinking indentation. + +The form structure is also sensible. Comments live in a scrollable center area, while the input field and send action stay fixed at the bottom. That keeps the interaction model familiar and lets replying feel immediate instead of hidden behind another screen. + +The reply flow itself is lightweight: choose a parent comment, remember its ID, and send the new comment to the server with that relationship encoded. Once the response succeeds, the client inserts the new comment into the right place in the visible hierarchy and animates the layout back into a stable state. + +This is another good example of the course finding the right level of product realism. The comment bubbles, reply action, avatar rendering, and like/comment wiring all make the feature feel real, but the implementation stays small enough that the design decisions are still easy to follow. + +## Further Reading + +- [24. PostService](/courses/course-03-build-real-world-full-stack-mobile-apps-java/101-24-postservice/) +- [19. Post and Comment Entities](/courses/course-03-build-real-world-full-stack-mobile-apps-java/096-19-post-and-comment-entities/) +- [29. Newsfeed and Posts From Server](/courses/course-03-build-real-world-full-stack-mobile-apps-java/106-29-newsfeed-and-posts-from-server/) +- [Layout Animations](/courses/course-03-build-real-world-full-stack-mobile-apps-java/036-layout-animations/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/113-36-settingsform-cover-and-avatar.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/113-36-settingsform-cover-and-avatar.md index db55b2ef04..52c2ed6053 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/113-36-settingsform-cover-and-avatar.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/113-36-settingsform-cover-and-avatar.md @@ -9,10 +9,25 @@ module_order: 13 lesson_order: 36 weight: 113 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Build a profile/settings screen that lets the user update cover and avatar images while keeping the image-picking logic reusable." --- - > Module 13: Creating a Facebook Clone - {{< youtube _JCsyyIH02E >}} + +The settings screen is where the Facebook clone starts feeling personal instead of anonymous. This lesson focuses on the two profile images users care about most: the avatar and the cover. + +The dedicated `ImagePicker` helper is a good architectural move. The actual picking behavior is simple right now, but wrapping it in a small class means the metadata, chosen file, preview image, and eventual upload path can stay together. That keeps the settings form from absorbing low-level image-picking concerns directly. + +The UI itself is mostly about layered composition. Cover image, avatar, and their change buttons need to overlap in a way that looks intentional rather than accidental. The layered-layout approach is a good fit because it lets the form place those controls relative to the images they actually modify. + +The lesson also keeps a clear distinction between local and remote updates. When the user picks a new image, the UI updates immediately enough to feel responsive, but the backend still receives a real media upload and the user record is updated so the change persists. + +This is one of the better examples in the course of progressive feature design. The screen does not try to solve every settings problem at once. It gets the most visible identity-editing actions working cleanly, then leaves room for the next lesson to handle more generic profile attributes. + +## Further Reading + +- [37. Generic Settings using InstantUI - Automatic Dynamic UI Generation](/courses/course-03-build-real-world-full-stack-mobile-apps-java/114-37-generic-settings-using-instantui-automatic-dynamic-ui-generation/) +- [39. ImagePicker - Video and Custom Support](/courses/course-03-build-real-world-full-stack-mobile-apps-java/116-39-imagepicker-video-and-custom-support/) +- [40. Edit User - UI Binding and Multipart Image Upload](/courses/course-03-build-real-world-full-stack-mobile-apps-java/077-40-edit-user-ui-binding-and-multipart-image-upload/) +- [18. Media Entity](/courses/course-03-build-real-world-full-stack-mobile-apps-java/095-18-media-entity/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/114-37-generic-settings-using-instantui-automatic-dynamic-ui-generation.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/114-37-generic-settings-using-instantui-automatic-dynamic-ui-generation.md index c8fb70105d..cd1d1d4c20 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/114-37-generic-settings-using-instantui-automatic-dynamic-ui-generation.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/114-37-generic-settings-using-instantui-automatic-dynamic-ui-generation.md @@ -9,10 +9,25 @@ module_order: 13 lesson_order: 37 weight: 114 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Use InstantUI and property metadata to generate a low-maintenance profile editor instead of hand-building every settings field." --- - > Module 13: Creating a Facebook Clone - {{< youtube hys6Rpkru50 >}} + +Once profile editing moves beyond a couple of images, hand-authoring every settings field quickly becomes tedious. This lesson solves that by leaning on InstantUI and property metadata to generate a large part of the editable profile form automatically. + +That is a strong fit for this problem. User profiles tend to grow over time, and a manually curated form can easily become a maintenance bottleneck. If the underlying property model is already expressive, it makes sense to let the UI derive more from that model instead of forcing every field through handwritten boilerplate. + +The lesson is also realistic about the limits of automatic UI generation. Some values fit naturally into generated editors. Others, such as birthdays stored in a long-based transport format or constrained choice fields such as gender, need metadata or companion properties so the generated form becomes usable instead of technically correct but awkward. + +That balance is exactly the right way to think about InstantUI. It is not magic. It is a way to push repetitive form construction down into the model layer while still giving the application room to guide how tricky fields should appear. + +The unbind-on-exit behavior is another good reminder that generated forms still participate in normal object lifecycles. Automation reduces boilerplate, but it does not remove the need to be explicit about binding scope, saving, and cleanup. + +## Further Reading + +- [36. SettingsForm - Cover and Avatar](/courses/course-03-build-real-world-full-stack-mobile-apps-java/113-36-settingsform-cover-and-avatar/) +- [Properties Are Amazing](/blog/properties-are-amazing/) +- [21. Service Layer and UserService](/courses/course-03-build-real-world-full-stack-mobile-apps-java/098-21-service-layer-and-userservice/) +- [40. Edit User - UI Binding and Multipart Image Upload](/courses/course-03-build-real-world-full-stack-mobile-apps-java/077-40-edit-user-ui-binding-and-multipart-image-upload/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/115-38-server-side-post-media-attachment.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/115-38-server-side-post-media-attachment.md index 0a77af63ec..7d0edc6f9d 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/115-38-server-side-post-media-attachment.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/115-38-server-side-post-media-attachment.md @@ -9,10 +9,25 @@ module_order: 13 lesson_order: 38 weight: 115 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Extend posts so they can reference uploaded media and make the server-side attachment flow usable from the client." --- - > Module 13: Creating a Facebook Clone - {{< youtube Xrolmjg8Dr0 >}} + +This lesson takes the existing media and post infrastructure and connects them. That sounds small, but it is the step that turns uploads into actual social content. + +The most important design choice is that posts do not embed raw media blobs directly. They reference media objects. That keeps the post model lighter, preserves reuse of the media service and entity model, and leaves room for richer attachment behavior later. + +Representing attachments to the client as a minimal map of media IDs and MIME types is also a sensible boundary. The client needs enough information to decide how to render or request the media, but it does not need the full internal server-side media object every time it sees a post. + +The auth-transport compromise called out in the lesson is worth keeping as an architectural warning. Passing authorization as a query parameter can simplify certain client-side URL-based image-loading paths, but it also raises security risks if those URLs are ever logged, shared, or reused carelessly. The tutorial is right to flag that tradeoff instead of pretending it is harmless. + +The more durable takeaway is that media delivery often forces you to choose between transport convenience and stricter security boundaries. Those tradeoffs should always be explicit. + +## Further Reading + +- [18. Media Entity](/courses/course-03-build-real-world-full-stack-mobile-apps-java/095-18-media-entity/) +- [23. NotificationService and MediaService](/courses/course-03-build-real-world-full-stack-mobile-apps-java/100-23-notificationservice-and-mediaservice/) +- [40. Post Media Attachments - Client Side Business Logic](/courses/course-03-build-real-world-full-stack-mobile-apps-java/117-40-post-media-attachments-client-side-business-logic/) +- [42. Images, Videos and Styled Posts in the Newsfeed](/courses/course-03-build-real-world-full-stack-mobile-apps-java/119-42-images-videos-and-styled-posts-in-the-newsfeed/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/116-39-imagepicker-video-and-custom-support.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/116-39-imagepicker-video-and-custom-support.md index 7dd95641df..514f1d40ba 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/116-39-imagepicker-video-and-custom-support.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/116-39-imagepicker-video-and-custom-support.md @@ -9,10 +9,25 @@ module_order: 13 lesson_order: 39 weight: 116 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Extend the image picker so it can represent videos and custom-captured media without forcing the rest of the app to care about file-source details." --- - > Module 13: Creating a Facebook Clone - {{< youtube wp6gqYLD-XM >}} + +As soon as post attachments stop being image-only, the picker stops being a trivial helper. This lesson expands it into something that can represent either images or videos while still keeping the rest of the app insulated from the exact source of the file. + +That is the most valuable idea here. The rest of the app should not need one code path for gallery images, another for gallery videos, and yet another for low-level camera captures. A picker abstraction that can carry the chosen file, preview image when applicable, and enough metadata for upload is exactly the right seam. + +The lesson also makes a pragmatic distinction between images and videos. Images benefit from being decoded, resized, previewed, and cached as UI-ready content. Videos are more transient and more expensive, so the picker mostly needs to preserve file access and enough metadata to upload or preview them sensibly. + +The custom-factory methods are also useful. Once the app later introduces low-level camera capture, it will already have a way to create a picker-like object from an existing file or byte array without pretending the media came through the ordinary gallery selection path. + +This is one of those small infrastructure lessons that pays off disproportionally later because it keeps every feature that touches media from reinventing file-source logic on its own. + +## Further Reading + +- [40. Post Media Attachments - Client Side Business Logic](/courses/course-03-build-real-world-full-stack-mobile-apps-java/117-40-post-media-attachments-client-side-business-logic/) +- [41. Post Image and Video from NewPostForm](/courses/course-03-build-real-world-full-stack-mobile-apps-java/118-41-post-image-and-video-from-newpostform/) +- [43. Low Level Camera Integration](/courses/course-03-build-real-world-full-stack-mobile-apps-java/120-43-low-level-camera-integration/) +- [How Do I Take a Picture with the Camera](/how-do-i/how-do-i-take-a-picture-with-the-camera/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/117-40-post-media-attachments-client-side-business-logic.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/117-40-post-media-attachments-client-side-business-logic.md index 2528e766e6..755b22bc11 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/117-40-post-media-attachments-client-side-business-logic.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/117-40-post-media-attachments-client-side-business-logic.md @@ -9,10 +9,25 @@ module_order: 13 lesson_order: 40 weight: 117 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Prepare the client-side post and upload pipeline so attachment uploads can be tracked and integrated into post creation cleanly." --- - > Module 13: Creating a Facebook Clone - {{< youtube H7WW4GwXK8o >}} + +This lesson is mostly infrastructure, but it is important infrastructure. Once posts can reference attachments, the client has to stop treating media upload as an invisible prelude and start managing it as a real step in the posting workflow. + +The first part is straightforward: the post model gains an attachments field so the client can receive and send attachment metadata without special-case parsing. + +The more important change is in the upload API. If attachments may be large or slow to upload, the client needs access to the underlying request object so it can show upload progress and coordinate posting behavior around that state. Returning a bare callback result is not enough anymore. + +That is the correct direction. The upload process is no longer just a helper that vanishes into the background. It is now part of the user-visible experience, and the UI needs hooks for progress, enablement, and error handling. + +The lesson also highlights a very practical detail: sometimes the app has raw bytes, and sometimes it only has a file path. The API layer should make room for both instead of forcing every caller to normalize the data in exactly the same way before it can upload anything. + +## Further Reading + +- [41. Post Image and Video from NewPostForm](/courses/course-03-build-real-world-full-stack-mobile-apps-java/118-41-post-image-and-video-from-newpostform/) +- [39. ImagePicker - Video and Custom Support](/courses/course-03-build-real-world-full-stack-mobile-apps-java/116-39-imagepicker-video-and-custom-support/) +- [16. The 'New Post' Form](/courses/course-03-build-real-world-full-stack-mobile-apps-java/093-16-the-new-post-form/) +- [18. Media Entity](/courses/course-03-build-real-world-full-stack-mobile-apps-java/095-18-media-entity/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/118-41-post-image-and-video-from-newpostform.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/118-41-post-image-and-video-from-newpostform.md index a404dd8786..6adeb319b6 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/118-41-post-image-and-video-from-newpostform.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/118-41-post-image-and-video-from-newpostform.md @@ -9,10 +9,25 @@ module_order: 13 lesson_order: 41 weight: 118 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Teach the post composer to handle media uploads, disable posting until they complete, and create image or video posts from the same form." --- - > Module 13: Creating a Facebook Clone - {{< youtube U0Ms65vcVr4 >}} + +This is where the post composer stops being text-only and starts behaving like a real social composer. That means it has to own more state: attachment ID, MIME type, upload progress, and whether the post action is currently safe to enable. + +The lesson handles that by moving from one generic constructor to factory-style entry points for different composer modes. That is a good shift. Once the UI for image posts, video posts, and plain text posts begins to diverge, pretending they are all the same setup path just makes the form harder to reason about. + +Disabling the post action until the upload completes is one of the most important UX details here. Without that guard, the user could create references to media that has not actually finished uploading yet. The composer needs to make that state visible and enforce it, not just hope the timing works out. + +The upload progress overlay is also well chosen. The user can see the attachment they are about to post, but the UI still makes it clear that something is happening before the post can be sent. That keeps the composer feeling responsive without lying about completion. + +The lesson also calls out the orphan-media problem when a user abandons the form after uploading but before posting. That is a real systems concern, and it is good that the tutorial surfaces it instead of pretending uploads only succeed in neat end-to-end flows. The app still needs a cleanup story even if the lesson defers the implementation. + +## Further Reading + +- [40. Post Media Attachments - Client Side Business Logic](/courses/course-03-build-real-world-full-stack-mobile-apps-java/117-40-post-media-attachments-client-side-business-logic/) +- [42. Images, Videos and Styled Posts in the Newsfeed](/courses/course-03-build-real-world-full-stack-mobile-apps-java/119-42-images-videos-and-styled-posts-in-the-newsfeed/) +- [39. ImagePicker - Video and Custom Support](/courses/course-03-build-real-world-full-stack-mobile-apps-java/116-39-imagepicker-video-and-custom-support/) +- [16. The 'New Post' Form](/courses/course-03-build-real-world-full-stack-mobile-apps-java/093-16-the-new-post-form/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/119-42-images-videos-and-styled-posts-in-the-newsfeed.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/119-42-images-videos-and-styled-posts-in-the-newsfeed.md index be7a0577bf..552bf21fe6 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/119-42-images-videos-and-styled-posts-in-the-newsfeed.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/119-42-images-videos-and-styled-posts-in-the-newsfeed.md @@ -9,10 +9,25 @@ module_order: 13 lesson_order: 42 weight: 119 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Render media attachments and styled text posts inside the feed so the richer composer actually shows up in the timeline." --- - > Module 13: Creating a Facebook Clone - {{< youtube M518p4_Horg >}} + +Supporting media in the composer is only half the story. The feed also needs to render those posts convincingly, otherwise the richer posting flow disappears as soon as the user returns to the timeline. + +This lesson closes that gap by teaching `NewsfeedContainer` how to display images, videos, and the styled-text post variants that earlier lessons allowed users to create. + +The media rendering path is pragmatic. Images are loaded through URL-based media retrieval, while videos are embedded with a size override so they remain visually usable in the feed regardless of the original media dimensions. That is exactly the sort of feed-specific presentation logic the timeline should own. + +The lesson is also honest that proper media delivery is a much bigger subject. Real production systems usually transcode video, tailor formats by client capabilities, and optimize bandwidth more aggressively. The simplified approach here is acceptable as long as it is understood as a working foundation rather than a final media pipeline. + +The styled-post fixes are just as important. Earlier in the module, style choices existed conceptually, but the feed had not fully respected them. Once rich text and UIID-aware styling are wired together properly, styled posts stop being just a composer novelty and become part of the visible product language. + +## Further Reading + +- [41. Post Image and Video from NewPostForm](/courses/course-03-build-real-world-full-stack-mobile-apps-java/118-41-post-image-and-video-from-newpostform/) +- [12. The Newsfeed Container](/courses/course-03-build-real-world-full-stack-mobile-apps-java/089-12-the-newsfeed-container/) +- [16. The 'New Post' Form](/courses/course-03-build-real-world-full-stack-mobile-apps-java/093-16-the-new-post-form/) +- [18. Media Entity](/courses/course-03-build-real-world-full-stack-mobile-apps-java/095-18-media-entity/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/120-43-low-level-camera-integration.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/120-43-low-level-camera-integration.md index a5dafe9ed6..42d9ad54e5 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/120-43-low-level-camera-integration.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/120-43-low-level-camera-integration.md @@ -9,10 +9,25 @@ module_order: 13 lesson_order: 43 weight: 120 is_course_lesson: true -description: "Watch the lesson and follow the accompanying resources." +description: "Add a custom camera path with a graceful fallback so media posting can start from capture as well as from the gallery." --- - > Module 13: Creating a Facebook Clone - {{< youtube cZiHmpw2FVI >}} + +Camera support is one of those features that can swallow an entire project if you let it. This lesson avoids that trap by aiming for a useful capture path rather than a complete camera application. + +The structure is sensible. If the richer camera library is available, the app uses it. If not, it falls back to the simpler capture API. That gives the feature a wider operating range without forcing the rest of the app to care about which capture path was used on a given device. + +The custom camera form is also kept intentionally narrow. It shows the viewport, allows image capture, and hands the result off to the existing media-posting flow. It does not try to solve video recording, flash control, zoom, or every nuance of device photography. That is the right scope for a lesson whose real purpose is integration, not camera-app design. + +What matters here is that capture now plugs into the same picker and new-post abstractions built in the previous lessons. The app is not branching into an entirely separate posting flow for camera-originated media. It is reusing the same upload and composer pipeline with a different acquisition source. + +That is a strong finish to this feature arc. Gallery selection, media upload, feed rendering, and live capture all converge on one posting model instead of growing as disconnected special cases. + +## Further Reading + +- [39. ImagePicker - Video and Custom Support](/courses/course-03-build-real-world-full-stack-mobile-apps-java/116-39-imagepicker-video-and-custom-support/) +- [41. Post Image and Video from NewPostForm](/courses/course-03-build-real-world-full-stack-mobile-apps-java/118-41-post-image-and-video-from-newpostform/) +- [42. Images, Videos and Styled Posts in the Newsfeed](/courses/course-03-build-real-world-full-stack-mobile-apps-java/119-42-images-videos-and-styled-posts-in-the-newsfeed/) +- [How Do I Take a Picture with the Camera](/how-do-i/how-do-i-take-a-picture-with-the-camera/) diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/121-44-push-notification-theory-entity-and-service-layers.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/121-44-push-notification-theory-entity-and-service-layers.md index cd1533ff18..b2ef324aa3 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/121-44-push-notification-theory-entity-and-service-layers.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/121-44-push-notification-theory-entity-and-service-layers.md @@ -11,8 +11,293 @@ weight: 121 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 13: Creating a Facebook Clone {{< youtube LFoSl_a2rs8 >}} + +## Transcript + +this is probably the easiest section in +terms of materials but since push is +always such a hassle it could possibly +take you the most to complete +i chose to use push for all the server +originating communications which has its +drawbacks a better strategy might have +been combination of push and web sockets +but in the interest of brevity i chose +to go with push alone as i demonstrated +web sockets enough and we'll go back to +them in the future +push notification allows us to send +notification +to a device while the application might +be in the background +this is important both as a marketing +tool and as a basic communications +service +polling the server seems like a sensible +time proven strategy +however there are many complexities +related to that approach in mobile +phones +the biggest problem is that polling +application +will be killed by the os as it is sent +to the background to conserve os +resources +while this might work in some os's and +some cases this isn't something you can +rely on for instance android 6 plus +tightened the background process +behavior significantly +the other issue +is +battery life +new os's exposed battery wasting +applications and as a result my trigger +uninstalls +this makes even foreground polling less +appealing +if you are new to mobile development +then you heard a lot of the buzzwords +and very little substance +the problem is that ios and android have +very different ideas of what push is and +should be +for android push is a communication +system that the server can initiate for +instance the cloud can send any packet +of data and the device can process it +in rather elaborate ways +for ios pushes mostly a visual +notification triggered by the server to +draw attention to new information inside +an app +these don't sound very different until +you realize that in android you can +receive slash process a push without the +awareness of the end user +in ios a push notification is displayed +to the user but the app might be unaware +of it +this is important ios will only deliver +the push notification to the app +if it is running or if the user clicked +the push notification pop-up +codename one tries to make both os's +feel similar so background push calls +act the same in ios and android as a +result +you shouldn't push important data +push is lossy +and shouldn't include a payload that +must arrive +instead use push as a flag to indicate +that the server has additional data +for the app to fetch +for this case we use push to let the app +know about an update +it then performs a refresh to fetch the +actual data with the usual web services +before we proceed i think it's a good +time to discuss the various types of +push messages +zero or one +is the default push type +they work everywhere and present the +string as the push alert to the user +two is hidden non-visual push +this won't show any visual indicator on +any os +in android this will trigger the push +string call with the message body in ios +this will only happen if the application +is in the foreground otherwise the push +will be lost +three allows combining a visual push +with a non-visual portion +expect a message in the form of this is +what the user won't see +semicolon this is something he will see +for instance you can bundle a special id +or even a json string in the hidden part +while including a friendly message in +the visual part +when active this will trigger the push +string method twice once with the visual +and once with the hidden data +notice that if the push arrives when the +app is in the background on ios and the +user doesn't tap the notification +neither one of the messages will be +received by the app +4 allows splitting the visual push +request based on the format title +uh semicolon body to provide better +visual representation and some os's +five sends a regular push message but +doesn't play a sound when the push +arrives +100 sets the number badge +it's applicable only to ios it allows +setting a numeric badge on the icon to +the given number +the body of the message must be a number +for instance unread count +101 is identical to 100 with an added +message payload separated with a space +for instance 30 space you have 30 unread +messages +we'll set the badge to 30 and present +the push notification text you have 30 +unread messages +this again is ios only +we need some values from google and +apple to fill fb clone keys properties +these values help us send the push +through the apple google servers +we need the following keys in that +properties file notice i will go into +the process of obtaining each of those +soon +push.itunes production +is true or false +ios push calls target +either the production or the development +servers +push itunes prod cert +don't confuse +this with a certificate used for app +signing +this is a separate certificate used for +authenticating against apple's push +servers you need to host it in the cloud +we do that for you if you use the +certificate wizard +push dot itunes prod pass this is the +password for push itunes prod cert +certificate +push itunes devcert just like with the +code signing we have two certificates so +this one is used when we are pushing in +the sandbox during development +this is used when push itunes production +is set to false +push token +sorry push +itunes dev pass +this is the password +for the push itunes dev cert certificate +push token this is a secure token you +can fetch from your codename one account +in the settings tab +push.gcm key this is the value you need +to fetch from google developer tools +console +android push goes to google servers +and to do that we need to register with +google to get keys for the server usage +you need one important value push.gcm +key +to generate this value follow these +steps +login to +console.cloud.google.com +select apis and services +select library +select developer tools +select google cloud messaging +click enable +and follow the instructions +the value we need is the api key which +you can see under the credentials entry +you will need to rerun the certificate +wizard for the project for ios +if you generated certificates before +say no to the set step that asks you to +revoke them and copy your existing +credentials certificate p12 file and +password to the new project +make sure to check the include push flag +in the wizard so the generated +provisioning includes push data once +this is done you should receive an email +that includes the certificate details +this will include urls for the push +certificate we generated for you and the +passwords for those certificates +apple has two push servers sandbox use +this during development production this +will only work for shipping apps +you need to toggle the push.itunes +production flag so push messages go to +the production version of the app +now that we understood the theory let's +go into the practical terms of sending +push notifications from the server +the first step is in the user entity we +need to access the device push key so we +can send a push message to the user's +device +that's it +well almost +there are also getters and setters but i +always ignore those anyway +this isn't in the dow so there is no +boiler plate there the push key is +private to the server it has no place in +the dial so we leave it +there here +we do however need to add a method to +the user repository interface we need +this finder to remove out of date and +expired push keys based on messages sent +from the push server +we need to expose the properties that we +added to the fb clone keys properties +file in the notification service bin +my first intuition was to inject the +user service into the notification +service class +this failed +the reason it failed is circular +references +user service already references +notification service so including user +service there +would mean spring boot would need to +create user service create notification +service to inject into user service +create a user service to inject into the +notification service +etc +thankfully it fails quickly with an +error +the solution itself is pretty simple +though +we add a new service that will handle +this api keys +we removed this code from user service +as it is as is and placed it here +these two helper methods remove the need +to use api keys variables directly +the next step is injecting this into +both user service and notification +service +i'll demonstrate this in the +notification service but the change to +user service is same +i'll get to the surrounding method soon +but for now i just wanted to show how to +use this new api to get the values from +the properties file +so all we need to do is replace every +call to prop.getproperty +with +keys.get that's also far more flexible +and elegant in the future we can +probably use a smarter system for +configuration than a properties file +now that it's encapsulated diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/122-45-push-notification-server-implementation.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/122-45-push-notification-server-implementation.md index dbb5ea9a8d..c6da8756cf 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/122-45-push-notification-server-implementation.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/122-45-push-notification-server-implementation.md @@ -11,8 +11,152 @@ weight: 122 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 13: Creating a Facebook Clone {{< youtube 5WpZmIkdUfs >}} + +## Transcript + +the changes to the notification service +are significant and in effect that's the +class that implements push notification +calls from the server +before we start we need to add a few +fields to the class +these will be used by the code we +introduced soon +these are besides the keys field i +mentioned before +this is the url of the codename one push +server we'll use that to send out push +messages +logging is used for errors in the body +of the class +we need to inject the user's repository +now as we need to find the user there +the first step is trivial +it's about mapping the push key to the +service class which is exactly what we +do here +on push registration we invoke this +method to update the push key in the +user object +if the push server indicates that a push +key is invalid or expired we delete that +key +a fresh key will be created the next +time the user runs the app +the second method is internal to the +server and isn't exposed as a web +service but the former method is +the next stage is the push notification +itself +we add these lines to the send +notification method +notice that a push key can be null since +it can expire or might not be registered +yet +this leads us to the send push +notification method +but we'll get there via detour +this method is based on the server push +code from the developer guide notice +that this is the import method not the +method we invoked before +we'll go through this first and then +reach that code +we connect to the push server url to +send the push message +a standard form post submission request +is used +we fetch the values for the fields that +can defer specific +specifically the certificate and +passwords +which vary between production and +development +we send all the details as a post +request to the codename one push server +which then issues a push to the given +device type +the server can return 200 in case of an +error +but if the response isn't 200 then it's +surely an error +the server response is transformed to a +string for passing on the next method +i'll cover the read input stream method +soon +we need to go over the responses from +the push server these responses would +include information such as push key +expiration +and we would need to +purge that key from our database +that's all done in the actual method for +sending push messages +the async annotation indicates this +method should execute on a separate +thread this is handled by spring we'll +discuss that soon +senpush impul returns json with messages +which we need to process +the spring json parsing api has +different forms for map +list +but we can get both in the response from +the server so we need to check +if it's a list then it's a device list +with either acknowledgement of sending +for android only or error messages +if we have an error message for a +specific device key we need to remove it +to prevent future problems with the push +servers +if we got a map in response it could +indicate an error which we currently +don't really handle other than through +logging +if push doesn't work the app would still +work fine you'll just need to refresh +manually a better implementation would +also use a fallback for cases of push +failure for instance websocket but it's +not essential at least not at first +we referenced read input stream +in the previous code blocks +it's defined in the code as such +i could have written +more modern code using nio but i was +running out of time and i had this code +handy it does the job well +next we expose the push registration +method as a restful web service +this is a direct mapping to the update +method so there isn't much to say about +that +the last piece of the server code is the +changes we need to make to the facebook +clone server application class +we didn't touch that class at all when +we started off but now we need +some changes to support the asynchronous +send push notification method +first we need the at enable async +annotation so at async will work in the +app +the at bin for the async executor +creates the thread pool used when we +invoke an at async method +we define reasonable capacities +we don't want an infinite queue as we +can run out of ram and it can be used in +a denial of service attack +we also don't want too many threads in +the pool as our server might overcrowd +the push servers and receive rate limits +this level +should work fine +with that the server side portion of the +push support is done diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/123-46-push-client-side-integration.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/123-46-push-client-side-integration.md index 2e43102d08..e77ec7acb2 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/123-46-push-client-side-integration.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/123-46-push-client-side-integration.md @@ -11,8 +11,103 @@ weight: 123 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 13: Creating a Facebook Clone {{< youtube GEzM1MXkqnk >}} + +## Transcript + +it's now time to map the push support +into the client side +to do that we need to implement the push +callback interface in the main class of +our app +we can only implement push callback in +the main class +nowhere else +register push should be invoked with +every launch to refresh the push key as +it might change +we use call serially to defer this +so we don't block the start method +the push method is invoked with the text +of the push +in that case we call the refresh method +to update the ui +we'll discuss that method soon +the device id isn't the push key +it's the os native push value +normally you would have no need for it +as it's here for compatibility only +the push key value is only guaranteed +once this callback is invoked +if the push key changed since the last +time we update the push key in the +server +we'll cover this method soon +there isn't much +we can do in the case of an error so +right now i only log it +in some cases you can show a ui +notification but make sure not to do it +from this method as it's invoked early +in the app launch cycle and that might +impact the ui in odd ways +just store the error details and show a +notice later on +next we need to cover the server api +changes and refresh method +we'll start with the former +in the sign up or login method of the +server api +we need to invoke register push after +the successful login +this is important for the first time the +user logs in to the app +next we need to map the update push key +method +this is just a simple call with one +argument +we return a boolean value to indicate +that the call worked that's it +ui controller needs a bit of work too +first we need to add a field for the +main form instance +this will allow us to refresh the main +form as notifications come in +the method is now updated +this method is now updated with the new +main field so we can refresh the ui +refresh delegates into the main form +instance +it's possible that the push method is +invoked before loading completed so main +would be null +in that case this isn't a problem since +main would be created with fresh data +anyway +again with mainform we need to take +fields that were in the constructor +before and move them to the class level +so we can refresh them +once we went through that with the +refresh method is easy +if we are currently viewing the news +feed +and we +aren't minimized a refresh might create +a bad user experience +if we are minimized or in a different +tab we can refresh the ui immediately +this refresh method is from infinite +container +the same logic applies to the +notification code +this method shows a toast bar offering +the user a refresh if he taps the toast +bar +we use this to prompt the user instead +of refreshing automatically +with that push should work and refresh +when applicable diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/124-1-getting-started.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/124-1-getting-started.md index 05d37a69d9..6a23304cd8 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/124-1-getting-started.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/124-1-getting-started.md @@ -11,8 +11,108 @@ weight: 124 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 14: Creating a WhatsApp Clone {{< youtube fJD45Mz8SZM >}} + +## Transcript + +in this module we'll build a rudimentary +whatsapp clone +i'll take a very different path when +compared to previous cloned applications +here +we'll talk about that and +these things in the next few slides +the first thing i'd like to go into is +that this clone isn't pixel perfect +it's a fast and dirty clone +i'll focus only on the basic text +sending functionality +and won't go into +nearly as much as detail as i did in the +other clones +frankly it's not necessary +the functionality is simple once you +understand the facebook clone +i skipped a lot of the core features +mostly due to time +but also because these features are +covered in the facebook clone and +re-implementing them for yet another +clone would have been redundant +i'll only focus on these forms i won't +implement everything here either +since we already implemented sms +verification in the facebook loan i'll +use the sms activation library however +i'll use server side authentication for +that library for extra security +whatsapp doesn't use passwords +i considered using the phone number as +the id which seems to be what they are +doing i ended up using a regular id +though there is an authorization token +which i will discuss later +like other clones i use spring boot +again that makes this familiar and +allows me to grab some code from the +other projects the database will read +mysql again for the same reason +i'll store data as json locally instead +of sqlite this makes it easy to use and +debug the code that might be something +worth changing if you are building this +for scale +i'm still using web services for most of +the functionality they are still easier +to work with than web sockets +however messaging is the ideal use case +for websockets and i'm using it for that +exact purpose +when the websocket is closed which would +happen if the app isn't running or is +minimized i use push +push is used strictly as a visual medium +to notify the user of a new message +when sending and receiving data in +websockets i use json +in the past i used a binary websocket +and i wanted to show how this approach +works as well +so let's start by creating the spring +boot project for the server i'm assuming +you went through the previous modules +and i won't repeat myself too much +you can use the spring boot initializer +which i used to create this pom file +i just defined a new project with this +package for java 8. +we'll use jpa to communicate with +the mysql database +we use jersey for pojo json +serialization +the security package allows us to +encrypt credentials into the database +it has a lot of other features but they +aren't as useful for us +we need +web services support too +and web sockets to implement the full +communication protocol +the underlying database is mysql so we +need the right drivers for that +and finally we need the server side +twillow library to implement sms support +on the server this will be used for the +activation code functionality +next we need to create the client +application which i create as whatsapp +clone +i use the default native and bare bones +settings in the ide +i pick the com +codename1.whatsapp package nothing +special next we'll go into the classes +that implement the client functionality diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/125-2-client-to-server-abstraction.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/125-2-client-to-server-abstraction.md index 55b4ee85e9..0b32def09f 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/125-2-client-to-server-abstraction.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/125-2-client-to-server-abstraction.md @@ -11,8 +11,436 @@ weight: 125 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 14: Creating a WhatsApp Clone {{< youtube gJoJQST5jyM >}} + +## Transcript + +we'll jump into the client functionality +from the server connectivity class +I won't start with the UI and build +everything up but instead go through the +code relatively quickly as I'm assuming +you've gone through the longer +explanations in the previous modules +like before the server class abstracts +the back end I'll soon go into the +details of the other classes in this +package which are property business +object abstractions +as a reminder notice that I import the +CN class so I can use shorthand Syntax +for various apis +I do this in almost all files in the +project +right now the debug environment points +at the local host but in order to work +with devices this will need to point at +an actual URL or IP address +as I mentioned before we'll store the +data as Json in storage the file names +don't have to end in dot Json I just did +that for our convenience +this is a property business object we'll +discuss soon +we use it to represent all our contacts +and ourselves +this is the current websocket connection +we need this to be Global as we will +disconnect from the server when the app +is minimized +that's important otherwise battery +saving code might kill the app +this flag indicates whether the +websocket is connected +which saves us from asking the +connection if it's still active +if we aren't connected new messages go +into the message queue and will go out +when we reconnect +the user logged into the app is global +the init method is invoked when the app +is loaded +it loads the global data from storage +and sets the variable values normally +there should be data here with the +special case of the first activation +if this is the first activation before +receiving the validation SMS this file +won't exist +in that case we'll just initialize the +contact cache as an empty list and be on +our way +assuming we are logged in we can load +the data for the current user this is +pretty easy to do for property business +objects +if there are messages in the message +queue we need to load them as well this +can happen if the user sends a message +without connectivity and the app is +killed +contacts are cached here +the contacts essentially contain +everything in the app this might be a +bit wasteful to store all the data in +this way but it should work reasonably +even for relatively large data sets +this method sends the content of the +message queue it's invoked when we go +back online +these methods are shorthand for get and +post methods of the rest API +they force Json usage and add the auth +header which most of the server-side +apis will need +that lets us write shorter code +the login method is the first server +sign method +and doesn't do much it sends the current +user to the server then Saves The +Returned instance of that user +this allows us to refresh you the data +from the server +we pass the current user as the body in +an argument notice I can pass the +property business object directly and it +will be converted to Json +in the response we read the user replace +the current instance and save it to disk +sign up is very similar to login in fact +it's identical however after signup is +complete you still don't have everything +anything since we need to verify the +user so let's skip down to that +on the server signup triggers an SMS +which we need to intercept +we then need to send the SMS code via +this API +only after this method returns okay our +user becomes valid +update is practically identical to the +two other methods but sends the updated +data from the client to the server it +isn't interesting +send message is probably the most +important method here it delivers a +message to the server and saves it into +the Json storage +here we have the time in which a +specific contact last chatted this +allows us to sort the contacts based on +the time a specific contact last chatted +with us +this sends the message using a web +service +the message body is submitted as a chat +message business object which is +implicitly translated to Json +initially I sent messages via the +websocket +but there wasn't a big benefit to doing +that I kept that code in place for +reference +the advantage of using websocket is +mostly in the server side where +the calls are seamlessly translated +sorry the advantages of using web +service +is mostly that +if we are offline the message is added +to the message queue and the content of +the queue is saved +this method binds the websocket to the +server and handles incoming and outgoing +messages over the websocket connection +this is a pretty big method because of +the inner class within it but it's +relatively simple as the inner class is +mostly trivial +the bind method receives a callback +interface for various application Level +events for instance when a message is +received we'd like to update the UI to +indicate that +we can do that via the Callback +interface without getting all of that +logic into the server clause +here we create a subclass of websocket +and override all the relevant callback +methods +skipping to the end of the method we can +see the connection call and also Auto +reconnect method which automatically +tries to reconnect every five seconds if +we lost the websocket connection +let's go back to the Callback method +starting with on open +this method is invoked when a connection +is established once this is established +we can start making websocket goals and +receiving messages +we start by sending an init message this +is a simple Json message that provides +the authorization token for the current +user and the time of the last message +received +this means the server now knows we are +connect connected and knows the time of +the message we last received it means +that if the server has messages pending +it can send them now +next we send an event that we are +connected notice I used calls serially +to send it on the EDT since these events +will most likely handle GUI this makes +sense +finally we open a thread to send a ping +message every 80 Seconds +this is redundant for most users and you +can remove that code if you don't use +cloudflare +however if you do then cloudflare closes +connections after 100 seconds of +inactivity +that way the connection isn't closed as +cloudflare sees that it's active +cloudflare is a Content delivery Network +we use for our web properties it helps +scale and protect your domain but it +isn't essential for this specific +deployment +still I chose to keep that code in +because this took us a while to discover +and might be a stumbling block for you +as well +when a connection is closed we call the +event again on the EDT and mark the +connected flag appropriately +all the messages in the app are +text-based messages so we use this +version of the message callback event to +handle incoming messages +technically the messages are Json +strings so we convert the string to a +reader object then we parse the message +and pass the result into the property +business object +this can actually be written in a +slightly more concise way with the from +Json method however that method didn't +exist when I wrote this code +now that we parsed the object we need to +decide what to do with it +we do that on the EDT since the results +would process uh to to impact the UI +the typing flag allows us to send an +event that a user is typing I didn't +fully implement this feature but the +Callback and event behavior is correct +another feature that I didn't completely +finish is the viewed by feature here +here we can process an event indicating +there was a change in the list of people +who saw a specific message +if it's not one of those then it's an +actual message we need to start by +updating the last received message time +I'll discuss update messages soon it +effectively stores the message +act message acknowledges the server to +the server that the message was received +this is important otherwise a message +might be resent to make sure we received +it +finally we invoke the message received +callback since we are already within the +call serially we don't need to wrap this +too +we don't use binary messages and most +errors would be resolved by o to +reconnect still it's important to at +least log the errors +the update method is invoked to update +messages in the chat +first we Loop over the existing contacts +to try to find the right one +once we find the contact we can add the +message to the contact +the find method finds that contact and +we add a new message into the database +this is invoked when a contact doesn't +already exist within the list of +contacts we already have cached +the method closes the websocket +connection +it's something we need to do when the +app is suspended so the OS doesn't kill +the app +we'll discuss this when talking about +the lifecycle methods later +the contacts are saved on the contacts +grid we use this helper method to go +into the helper thread to prevent race +conditions +fetch contacts loads the contacts from +the Json list or the device contacts +since this can be an expensive operation +we do it on a separate context thread +which is an easy thread +easy trades let us send tasks to the +thread similarly to call serially on the +EDT +here we lazily create the easy thread +and then run fetch contacts on that +thread assuming the current easy thread +is null +if the thread already exists we check +whether we already are on the easy +thread assuming we aren't on the easy +thread we call this method again on the +thread and return +all the following lines are now +guaranteed to run on one thread which is +the easy thread as such there are +effectively thread safe and won't slow +down the EDT unless we do something +that's very CPU intensive +we already have the data we use called +serial if we already have the data we +use call Siri on idle this is a slow +version of call serially that waits for +the EDT to reach idle state +this is important for performance a +regular call serially might occur when +the system is animating or in need of +resources if we want to do something +expensive or slow it might cause choking +of the UI call Syria on idle will delay +the call serially to a point where there +are no pending animations or user +interaction +this means that there is enough CPU to +perform the operation +if we have a Json file for the contacts +we use that as a starting point this +allows us to store all the data in one +place and mutate the data as we see fit +we keep the contacts in a contacts cache +map which enables fast access at the +trade-off of some Ram this isn't too +much since we store the thumbnails as +external jpegs +once we loaded the core Json data we use +call serially to send the event of +loading completion +but we aren't done yet +we Loop over the contacts we loaded and +check if there is an image file matching +the contact name +assuming there is we load it on the +context thread and set it to the contact +this will fire an event on the property +object and trigger a repaint +asynchronously +if we don't have a Json file we need to +create it and the place to start is the +contacts on the device +get all contacts fetches all the device +contacts +the first argument is true if we only +want contacts that have phone numbers +associated with them this is true as we +don't need contacts without phone +numbers +the next few values indicate the +attributes we need from the contacts +database +we don't need most of the attributes we +only need we only fetch the full name +and phone number the reason for this is +performance fetching all attributes can +be very expensive even on a fast device +next we Loop over each contact and add +it to the list of contacts we convert +the built-in contact object to chat +contact and the process +for every entry in the contacts we need +to fetch an image we can use calls +serially on idle to do that this allows +the image loading to occur when the user +isn't scrolling the UI so it won't +noticeably impact performance +once we load the photo into the object +we save it to storage as well for faster +retrieval in the future this is pretty +simplistic code proper code would have +scaled the image to a uniform size as +well this would have saved memory +finally once we are done we save the +contacts to the Json file this isn't +shown here but the content of the photo +property is installed to the Json file +to keep the size minimal and loading +time short +once loaded we invoke the callback with +the proper argument +when we want to contact a user we need +to First make sure he's on our chat +platform +for this we have the find registered +user server API with this API we will +receive a list with one user object or +an empty list from the server this API +is asynchronous and we use it to decide +whether we can send a message to someone +from our contacts +this is a similar method that allows us +to get a user based on the user ID +instead of a phone if we get a chat +message that was sent by a specific user +we will need to know about that user +this method lets us fetch the method +metadata related to that user +the chats we have open with users can be +extracted from the list of contacts +since every contact has its own chat +thread +so to fetch the chats we see in the main +form of the WhatsApp UI we need to First +fetch the contacts as they might not +have been loaded yet +we Loop over the contacts and if we had +activity with that contact we add him to +the list in the response +but before we finish we need to sort the +responses based on activity time the +sort method is built into Java +Collections API it accepts a comparator +which we represented here as a Lambda +expression +the comparator Compares two objects in +the list to one another +it returns a value smaller than zero to +indicate the first value is smaller zero +to indicate the values are identical and +More Than Zero To indicate the second +value is larger +The Simple Solution is +sorry smaller The Simple Solution is +subtracting the time values to get a +valid comparison result +we saw the ack call earlier this stands +for acknowledgment we effectively +acknowledge that the message was +received if this doesn't go to the +server +the server doesn't know if a message +reached its destination +finally we need this method for push +notification it sends the push key to +the device of the device to the server +so the server will be able to send push +messages to the device diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/126-3-the-model-package.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/126-3-the-model-package.md index dbe79ca96c..2f5cb3172d 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/126-3-the-model-package.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/126-3-the-model-package.md @@ -11,8 +11,153 @@ weight: 126 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 14: Creating a WhatsApp Clone {{< youtube 46TpE-Cgtw8 >}} + +## Transcript + +the next step are the other classes +within the model package +the rest is relatively trivial after the +server class +this is the callback interface we used +within the server class it's pretty +trivial i added some methods for future +enhancement too +the first two methods inform the +observer that the server is connected or +disconnected +message received is invoked to update +the ui based on the new incoming message +the last two callbacks aren't really +implemented but they allow us to update +the ui if a user is typing in a chat +the message viewed event similarly +indicates if a user viewed the message +this can provide an indicator in the ui +that the message was seen +chat contact is a property business +object that stores the content of a +specific contact entry +i chose to use unique ids instead of +using the phone as an id +this was something i was conflicted +about +i eventually chose to use an id which i +think is more secure overall +it would also support the option of +changing a phone number in a future in +the future or using an email as the +unique identifier +local id should map to the id in the +contacts this allows us to refresh the +contact details from the device address +book in the future +the phone property is pretty obvious +the photo property stores the picture of +the contact there is a lot to this +property so i'll discuss +it in more details soon +these are the common attributes for name +and tagline used in whatsapp +for simple simplicity i chose to use a +full name and ignored nuances such as +first lost middle initial etc +the token is effectively our password to +use the service +since there is no login process a token +is generated on the server as a key that +allows us to use the service +a chat contact can also serve as a group +i didn't fully implement this logic but +it's wired +almost everywhere +in this case we have two sets for +members of the group +and the admin of the group +these sets would be empty for a typical +user +the this property allows us to mute a +chat contact so we won't see +notifications from that contact +if this is a group then it was created +by a specific user the id of that user +should be listed here +the creation date is applicable to both +groups individual users +this is the timestamp of the last +message we received from the given user +we saw this updated in the server class +we use use this to sort the chats by +latest update +chat message is the property business +object +that contains the content of the message +here we saw the actual chats we had with +the contact or group +as i mentioned before photo is installed +in json when we save the contact to keep +the size low +we save the contact image in a separate +file and don't want too much noise here +the app has two thumbnail images one is +slightly smaller than the other and both +are rounded +to keep the code generic i used arrays +with the detail and then used two sizes +one small size maps to the zero offset +and the array and the large size maps to +the one offset +here are the sizes of these two images +in millimeters +images are masked to these sizes +masking allows us to round an image in +this case +we generate placeholder images which are +used when an image is unavailable +this method creates a mask image of a +given size and pixels +a mask image uses black pixels to +represent transparency and white pixels +to represent the physical visible +opacity +so where we draw a black rectangle image +with a white circle in the center +when we apply this mask to an image only +the portion represented by the white +circle will remain +the placeholder image is used when no +image is defined again we create this +based on size and pixels +we create a gray image and then draw on +it using white +we use the material font to draw the +image of a person onto this image +this method gets the image represented +by the contact +in theory i could have used the photo +property and overridden get to implement +this i thought this is a simpler +approach +here we lazily initialize the arrays of +the mask image +for larger or larger small images +we create the mask images then convert +the mask image to mask object finally we +create the placeholder image +if the photo is null i return the +placeholder image instead of using the +photo object +otherwise we fill the image into the +size of the mask and apply the mask to +create a round object fill scales the +image so it's cropped while filling the +exact boundaries given it doesn't +distort the aspect ratio of the image +like a typical scale operation would +the final public methods and variables +cache the small and large image +appropriately they are the publicly +exposed apis for this functionality diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/127-4-the-main-class.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/127-4-the-main-class.md index a0df69308b..ab63979d10 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/127-4-the-main-class.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/127-4-the-main-class.md @@ -11,8 +11,170 @@ weight: 127 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 14: Creating a WhatsApp Clone {{< youtube 65LciCzyRNQ >}} + +## Transcript + +there is still one other class within +the model package +once we finish that +we'll go to the main class +the chat message class is another +property business object +but one that's even simpler than the +last one +this is the author of the message since +we use ids and not phones this might +become an issue so we need to keep track +of both +sent to can be important as this message +might have been sent to a group and not +directly to our current user +the timestamp of the message and the +actual text of the message are the main +payload +attachments aren't fully implemented the +general idea is a url of the attachment +mapping to a mime type so we can +represent it in the ui as image slash +video audio or document +the list of people who viewed the +message which can be more than one for a +message sent to a group +the typing message is a special type of +message that we don't currently send +but it can be sent pretty easily and +just update the ui that the user is +typing to this chat +this is the final property in this class +which is relatively simple +with that long detour out of the way +let's go back to the main class +as you can see i left most of the code +intact and kept it as the default +the first piece of code you will see +that isn't part of the default code is +this line to initialize the server and +load the saved data +there is also the push interface which +we need to implement to receive push +callbacks +that leads us directly to the first +method from that interface +we don't need to implement this method +since we use push only as a visual +medium and rely on web sockets to carry +the actual data +push is inherently unreliable and might +perform badly it places limitations on +the type of data you can send +we have more control over web sockets +we use push only when the app is +minimized +the one method we need to implement from +the push interface is the registered for +push corbett +when this callback is invoked we need to +send the push key to the server this is +important +notice that the push key isn't the +device id +there are different values don't confuse +them +the sms verification class is an +abstract class from the sms verification +cn1 lib +it lets us move some of the +functionality of that library into the +server +the first method is the send sms code +method it sends an sms mesh message to +the given phone number +on the server it invokes the signup call +which triggers an sms to that phone +number +this callback is invoked as part of the +signup process +when the user types in or the system +intercepts a phone number +this callback is invoked it sends the +verification string to the server side +and returns the result based on that +the message listener allows us to track +messages from the server such as connect +incoming messages etc +a lot of this isn't implemented as we +don't need it right now +but it could be useful for the ui as it +evolves +one thing we do implement here is the +message received api +there isn't much going on in this method +though +if the current form +is the chat form +then we need to check if we are +currently in the chat form with the +sender of the incoming message assuming +this is the case we can add this message +to the ui +regardless we need to refresh the main +ui +of the chat list container +since the order +to the contacts will change +next we have the start lifecycle method +you will notice that we invoke bind +message listener even when we restore a +running app +as you might recall we close the +websocket connection when the app is +minimized this effectively restores that +connection when the app is restored back +to normal +the this call happens when the app is +launched in a cold start +if the phone number isn't set this is +the first activation and we need to set +up a new user +the activation form api builds the data +and you are using a builder pattern +where every method adds +to the resulting form +first we allocate the activation form +with the title sign up +when +we then determine that we want a six +digit activation code instead of the +default four digit code +we finally show the activation ui this +accepts two arguments +the second argument is the sms +verification subclass we discussed +earlier +it sends the sms details to the server +which issues an sms it then performs +verification on the server which is more +secure than client-side verification +the first argument is a callback that's +invoked when the activation is completed +it's invoked with a phone number in the +result +here we store the new phone number to +the preferences then show the main form +ui +if the user was already registered we +show the main form +directly we discussed the bind method +before so the last piece is the register +push core +this is an essential part of the push +notification support +finally we added close websocket code to +the stop method this implements the +logic of stopping the websocket +connection +when the app is minimized diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/128-5-main-form.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/128-5-main-form.md index dddc42f3a5..aaf8503bcc 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/128-5-main-form.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/128-5-main-form.md @@ -11,8 +11,300 @@ weight: 128 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 14: Creating a WhatsApp Clone {{< youtube csTtSj6TqRE >}} + +## Transcript + +now that we implemented the model code +and the lifecycle code we are almost +finished we are down to ui code and css +we start with the main form which covers +the list of chat elements +as is the case with all the forms in +this app +we derive from form for simplicity +the main body of the form is a tabs +component that allows us to switch +between camera status and calls +camera kit is used to implement the +camera ui but due to a regression in the +native library this code is currently +commented out +there are the three tab containers +we make use of them in the scrolling +logic later +the main form is a singleton as we need +a way to refresh it when we are in a +different form +the form itself uses a border layout to +place the tabs in the center +we also save the form instance for later +use +we hide the tabs it generally means that +these aren't actually tabs they are +buttons +we draw ourselves +the reason for that is the special +animation we need in the title area +we add the tabs and select the second +one as the default as we don't want to +open with the camera view +instead of using the title we use title +component which may takes over the +entire title area and lets us build +whatever we want up there +i'll discuss it more when covering that +method +the back command of the form respects +the hardware back buttons and some +devices +and the android native back arrow +here we have custom behavior for the +form +if we are in a tab other than the first +tab we need to return to that tab +otherwise the app is minimized +this seems to be the behavior of the +native app +the calls container is a y-scrollable +container +this is simply a placeholder i placed +here a multi-button representing +incoming outgoing calls and a floating +action button +the same is true for the status +container this isn't an important part +of the functionality with this tutorial +you might recall that we invoke this +method from the main ui to refresh +the ongoing chat status +we fetch up to +date data from storage this is an +asynchronous call that returns on the +edt so the rest of the code goes into +the +lambda we remove the old content as +we'll just read it +we loop over the contacts and for every +new contact we create a +chat multi button with the given name +if there is a tagline defined we set +that tagline we also use the large icon +for that per person +if the button is clicked we show the +chat form for this user +the chats container is the same as the +other containers we saw but it's +actually fully implemented +it invokes the refresh chats container +method we previously saw previously saw +in the order in order to fill up the +container +and the floating action button here is +actually implemented by showing the new +message form +[Music] +camera support is currently commented +out due to a regression in the native +library however the concept is +relatively simple +we use the tab selection listener to +activate the camera as we need +the overflow menu is normally +implemented in the toolbar but since i +wanted more control over the toolbar +area i chose to implement it +manually in the code +i used buttons with the command uiid and +container with the command list uid to +create this ui +i'll discuss the css that created this +in the next lesson +i create a transparent dialog by giving +it the container ui id +i place the menu in the center +the dialog has no transition and +disposed +if the user taps outside of it or uses +the back button +this disables the default darkening of +the form when a dialog is shown +this version of the show method places +the dialog with a fixed distance from +the edges +we give it a small margin on the top to +take the state status bar into account +then use left and bottom margin to push +the dialog to the top right side +this gives us a lot of flexibility and +allows us to show the dialogue in any +way we want +this method creates the title component +for the form +which is this region +the method accepts the scrollable +containers in the tabs container this +allows us to track scrolling and +seamlessly fold the title area +the title itself is just a label +with a title ui id +it's placed in the center of the title +area border layout +if we are on ios we want the title to be +centered +in that case we need to use the center +version of the border layout the reason +for this is that center alignment +doesn't know about the full layout and +would center based on available space +it would ignore the search and overflow +buttons on the right when centering +since it isn't aware of other components +however using the center alignment and +placing these buttons in the east +solves that problem and gives us the +correct title position +a search and overflow commands are just +buttons with the title and the title ui +id +we already discussed the show overflow +menu method so this should be pretty +obvious +we just placed the two buttons in the +grid +i chose not to use a command as this +might create a misalignment for the this +use case and won't have saved on the +amount of code i had to write +these are the tabs for selecting camera +chat etc +there are just toggle buttons which in +this case are classified as radio +buttons +this means only one of the radio buttons +within the button group can be selected +we give them all the subtitle ui id +which again i'll discuss in the next +lesson +we use table layout to place the tabs +into the ui this allows us to explicitly +determine the width of the columns +notice that the table layout has two +rows +the second row of the table contains a +white line +using the side title underline ui id +this line is placed in +row one and column one so it's under the +chats entry +when we move between tabs this underline +needs to animate to the new position +here we bind the listeners to all four +buttons mapping to each tab +when a button is clicked we select the +appropriate tab +the next line two lines implement the +underline animation effect that we see +when we click a button notice how the +line animates to the right tab button +to achieve this we remove the current +white line +and add it back +to the toggle container in the right +position +we reset the height of the title in case +it was shrunk during scrolling +and we finally update the layout with an +animation which performs the actual line +move animation +the previous block updated the tab +selection when we select a button +this block does the opposite +it updates the button selection when we +swipe the tabs +it uses +a tab selection listener if the button +isn't selected then we need to update it +[Music] +again we need to reset the title size +next we select the button that matches +the tab +[Music] +finally we perform the animation of +moving the underline between tabs notice +that this is almost identical to the +previous animation code only in this +case it's triggered by dragging the tabs +instead of the button click events +the last two lines in this method are +the bind folding call which we'll +discuss soon and the box layout y which +wraps the two containers as one +the bind folding method implements this +animation of the folding title it's +implemented by tracking pointer drag +events and shrinking the title +when the pointer is released we need to +check if the title shrunk enough to +minimize it minimize or not enough so it +would go back to the full size +if the title area height is different +from the original height it means we are +in the process of shrinking the title +in that case we need to decide whether +the process is closer to the finish line +or to the start +it's less than halfway to the height of +the title we reset the preferred size of +the title area that means the title area +will take up its original original +preferred size and go back to full +height +otherwise we set the title area height +to zero so it's effectively hidden +regardless of the choice we made above +we show it using an animation +we detect the drag operation by binding +a scroll listener to the three +scrollable containers +i could have used pointer drag listeners +but they might generate too much noise +that isn't applicable +[Music] +i chose to make a special case for the +tensile drag effect +the tensile effect +in is the ios scroll behavior where a +drag extends beyond the top most part +then bounces back like a rubber band +this can cause a problem with the logic +below so i decided that any scroll +position above 10 pixels should probably +show the full title +now that all of that is out of the way +we can calculate the direction of the +scroll and shrink or grow the title area +appropriately +if the diff is larger than zero then the +title area should grow +we're setting the preferred height to +the diff plus the preferred height +but we make sure not to cross the +maximum height value +we then revalidate to refresh the ui +a negative diff is practically identical +with the exception of making it zero or +larger instead of using the minimum +value we use the max method and with +that the title folding is implemented +the one last method in the class is this +we use a custom toolbar that disables +centered title +the centered title places the title area +in the center of the ui and it doesn't +work for folding +we need to disable it for this form so +the title acts correctly on ios diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/129-6-theme-css.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/129-6-theme-css.md index e88fbac1a7..68c888bdcc 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/129-6-theme-css.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/129-6-theme-css.md @@ -11,8 +11,167 @@ weight: 129 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 14: Creating a WhatsApp Clone {{< youtube BbVoa3vw7OM >}} + +## Transcript + +now that we looked at the first ui form +let's take a short detour via the css +for the app +i chose to go with css as it's much +easier for tutorials +i can use +sources instead of multiple screenshots +to explain an idea +the first selector is a constants +selector it lets us define theme +constants +here we include two basic values +that most apps should include +include native ball indicates that we +want the theme to derive from the native +theme as a starting point +this is essential as it defines a lot of +important things such as the spacing on +top of ios devices etc +the scroll visible ball constant it +hides the scroll bars that might show in +some os's +pop-up dialog appears as an arrow +dialogue on in ios but other os's don't +implement it the pop-up dialog is used +in the sign-up process for picking the +country of sms verification +the background color of the pop-up is +white and implied implicitly +defined as opaque +we have a subtle rounded corners with a +radius of one millimeter on the dialog +you will notice zero margin as the +dialog touches the edges but two +millimeters in padding which space the +content a bit from its edges +label is pretty standard so i won't +spend too much time on that it's just a +two and a half millimeter light font +multiline 1 is used to describe the +first line of the multi button +there isn't much here just a light +slightly larger font +the one thing to notice is the zero +padding on the bottom so the first line +and the next line will be close together +multi-line 2 is pretty similar to +multiline one +only with a slightly smaller font +notice that the blue color of the text +is derived from the native theme and +isn't declared here +also also notice the one pixel grey +border at the bottom of the multi-line +multi-line three and four have zero +padding and margin +this removes potential error in spaces +from the list of buttons +the whole multi button is styled here to +have a white background with no margin +or padding +notice that border is explicitly defined +as none +this is important as the multi button +contains a line border and some native +themes +that would collide with the shorter +border we want in this case +the toolbar defines the background and +the top of the form we give it a one +pixel bottom border which is pretty +similar to the border defined in the +native app +it's very subtle +when styling the title we should usually +style the commands +as well so they have similar proportions +and alignment +subtitle represents the four buttons +below the title +they are similar but have an off-white +color +are slightly smaller and are center +center aligned +toggle buttons use +the press state to indicate selection +in which case we want them to be white +the subtitle underline is the animated +small thin white line below the buttons +this draws the full content of the +component +chat form uses the image as a background +image notice the source dpi 0 argument +which means we don't want to use multi +image for this for this specific case +this just sets the color of the floating +action button +we use the text field in the signup form +it's added by the sms activation cn1 lib +there's nothing special about it +however the chat text field is a +separate component it uses a pull border +to wrap its content +technically this is a container that +tracks the actual text field but it's +still perceived as a text field +the icons within the text field are just +simple font image icons with a specific +color and very little margin +the text field hint is just a great text +for the text field +and the record button has round border +similar to the floating action button +nothing special other than a bit of +padding and margin to position it +properly +chat time represents the hour next to +the message +within the chat bubble +it's just gray and smallish we make sure +it's transparent so it can work on white +or green backgrounds +the same goes for the chat text with +larger black font it's still transparent +for the same reason +the bubble of the chat is implemented in +code as +a custom border +i thought about using a nine piece +border and this seemed like a better +option +i use margin on the right side to +prevent the bubble from going too deep +into the other side this seems close to +what whatsapp is doing in their app +the same is true for chat bubbles right +with a different color and direction of +the margin which is now spaced on the +left side +the command list is the style of the +overflow menu +i use box shadow which unfortunately +produces a nine piece image border +hopefully the css support will improve +and use round wrecked border for this in +the future +notice there is a subtle border radius +css attribute that rounds the corners of +this dialog +each entry here is a command they have a +simple two milliliter millimeter padding +with light font style +the last style is day +it maps to the day label making chats in +a specific marking chat in a specific +date we use the pull border with a +bluish color with that the css is done diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/130-7-bubble-border.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/130-7-bubble-border.md index 85de8d6084..40267720b9 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/130-7-bubble-border.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/130-7-bubble-border.md @@ -11,8 +11,37 @@ weight: 130 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 14: Creating a WhatsApp Clone {{< youtube jR7_OTg-aG0 >}} + +## Transcript + +while we are on the subject of theming +there is one missing piece we neglected +in the theme css +the chat bubble border implements the +chat bubble appearance +it extends the border class and is based +on the code of round rect border from +codename one +i won't go into the whole code as there +is a lot here +in fact it's pretty similar to the +special border i created for the uber +clone module +so i'll just review the changes i did +for this class +i added two flags to indicate whether +this border has a left pointing arrow or +a right pointing pointed arrow +these variables are exposed using setter +methods like the rest of the set of +methods in this class +the create shape method is where we do +the actual change to implement the arrow +support +the arrows are drawn by moving the pen +further to the side to draw the +respective arrows diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/131-8-chat-form.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/131-8-chat-form.md index 260b74ec7e..3076faef80 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/131-8-chat-form.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/131-8-chat-form.md @@ -11,8 +11,414 @@ weight: 131 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 14: Creating a WhatsApp Clone {{< youtube 65jD9oGw61w >}} + +## Transcript + +the next step is the actual chat form +the chat form is a form like the main +form we discussed earlier +a chat with a specific contact which is +passed in the constructor as we saw in +the previous main form +we also have a getter for this property +which we used in the main class to check +if the current chat contact matches the +one in the incoming message +this is a common constant i use a lot to +represent a single day in milliseconds +it's convenient it's probably probably +should have been static as well +this is the date format used to +represent the day label here +this indicates whether the blue label +that says today +was added +if not +when we type in the first message to we +need to add a label +indicating that this message was sent +today +the chat form uses the y last box layout +this is a special type of box layout y +where the last component is pushed to +the bottom of the form +so here +we will use that position for the text +field +historically i used to build apps like +this with border layout and place the +text field in the south +both approaches are good with different +trade-offs for each +this approach has the downside of hiding +the chat text field when scrolling up +which is inconsistent with where +whatsapp works +so you might want to revisit this with a +border layout if you want that behavior +we use the chat form uiid for the +background image of the form +here we use a standard title with the +back arrow to navigate to the previous +form +these are all the standard comment +commands that appear in the whatsapp +application +i didn't implement them and just added +them to create a similar design +the input container is this +we just add it to the box layout y since +it's in the last mode +it will glue itself to the bottom as +long as it's the last component +from now on we'll need to add all +components before that component in the +container +now we go over the existing chats so the +ui opens +with the content of previous chats +stored on the phone +each chat has a time +time is represented in milliseconds +since epoch which uh is january 1st 2009 +sorry 1970. +we use that to divide the time by +constant the constant we have for day +this will give us a number that +represents a day since 1970. +we can then check if the time from one +chat is from the same day or from a +different day this can be tested with a +simple not equals test +notice i could have used code based on +java util calendar to do this test but i +think this approach is both faster and +simpler +in both cases we use add date to add +this label before we add the chat +content for a specific day +we then add a message to the ui +normally when we add a component to the +ui we need to animate or revalidate the +ui so it will show the component +however since this code is running +before the form is shown we don't want +such an animation as it might collide +with the transition +the show listener is invoked when a form +is actually shown +here we can do things that will happen +only after the form becomes visible +notice i used an anonymous inner class +instead of a lambda expression here +this is the reason +we can remove the show listener using +this command +if it's an anonymous inner class but we +can't remove it if it's a lambda as this +will map to the chat form instance +this is why we need this listener in the +first place +this scrolls down to the last component +it's important to do this after the form +is shown +because if it's done before some +components could still be laid out +incorrectly +the add day method adds the day label if +necessary there are some nuances to this +beyond the label alone +first we need to check if today is the +current day +since every day has a number associated +with it as we discussed before this is +pretty easy +if the day is today then we need to add +that special case label +we also need to flip the flag indicating +that it was added +this boolean flag is a special case that +we will discuss later +there is another slightly simpler +special case for yesterday showing the +previous day with a special matching +label +otherwise we use the formatter we +declared at the top of the class to +generate the text of the day label +we create a label with the right style +and add it before the text input +component +which brings us to the input container i +mentioned before +this method creates that container which +includes everything in this line +including the microphone button +the container starts with a text field +where the messages can be typed +there are two important features we need +in this text field first we don't want a +border +we'll set the border to the parent +container which we defined as +a pill border in the css +second we want this to be a multi-line +text field +this allows us to type in longer +messages and review them +however it means that enter won't map to +sending +the done listener is the first way to +send a message it can be triggered by +using the device virtual keyboard when +pressing done +this adds the message to the ui and it +does that with an animation that lays +out the message on the form +after adding the message we want to +clear the text so we stop the editing +process and set the text to an empty +string +this would have worked had we not +stopped the edit wouldn't have worked +had we not stopped the editing +these are the three buttons we see next +to the text field here +the text field is between them and not +under them +it just has no border +the round pill border is a container +that surrounds the buttons and the text +field +i'll talk about attachments soon this +isn't fully implemented but i'll cover +this when we reach these methods +the input container wraps these three +buttons and the text field +we give it the chat text field uiid +which includes the perl border +finally microphone has the record button +design +there is a partial partial worker for +voice recording when we override pointer +pressed we can do use the media recorder +to start voice recording and stop stop +it in pointer released i eventually +didn't get around to doing that +if there is text in the input field the +microphone icon becomes a send icon here +we use the data change listener to track +that and update the icon on the fly +the action listener is only applicable +when the microsoft microphone is in send +mode it's used to send the text in the +text field +this code is effectively identical to +the code we saw earlier in the method it +might make sense to generalize this +block +finally this method returns a border +layout that pairs together the input +container and the microphone as a single +container +this is a utility method i use quite +often +to write a number as two digits it's +useful for formatting hours so a number +like +1 will be written as 0 1 and etc +the logic is trivial if a number is +smaller than 10 +return it with a 0 prepended to it +otherwise return the number as a string +i +make use of this method in the time +formatting method below +this method formats the time as hours +and minutes used to display the hour +next to the message +this method just gets the hour and +minutes from the calendar class and +formats them as two digits with a colon +in the middle +we saw the add message method invoked +when the user types a new message +it's pretty simple as most of the logic +in this method +is +delegated to the following add message +to ui no animation method +but it does contain a couple of +interesting bits +this is why we have the today +added flag +if this flag isn't set we need to add +today to the dates before adding a new +message +this method does the heavy lifting of +adding a message to the ui +here we animate the addition notice i +used an and weight variant of the method +it's so the scroll component to visible +below will work this won't work during +the animation as the position wouldn't +be correct at that point +this is the method that actually adds +chat bubbles to the ui +this is the logic to determine if the +bubble goes +in the right or left i +use this logic +so it will work even with groups +i could have simplified it if there were +only two options +the main difference between the left and +right chat bubble is the ui id as i +mentioned +in the theme +the actual component placed in the chat +is created by a separate method +if this is a media component we'll call +create media message +and if it's a text message we use create +text message the rest of this method +styles the result of that method +the component uses the chat bubble +however we need to know whether the +component should have an arrow or not +notice that if we have two chat bubbles +one on top the other +and and the other from the same sender +the second bubble doesn't have an arrow +this is true for both sides of the +conversation +however if we have a message from +someone else +then the arrow returns +the this block tries to implement that +logic by detecting this situation +the first condition is meant to detect a +chat with no components in it in this +case the arrow must be shown so we can +easily set the arrow on the correct side +by using the left boolean variable to +determine that +otherwise +we need to dig through the components +we already added the last component is +the input component so we need to look +at the components before that +to align chat bubbles correctly we use +container wrapped +wrapper so the previous container is a +container +we exact the child +the first child of that container and +save it into cnt +cnt is now the previous component if the +ui id of the previous component matches +our current ui id it means that the +previous component +pinpointed to the same side +pointed to the same side that means we +don't need an arrow +if the uids aren't identical the arrow +needs to point +into one of these directions +this is another special case +when the arrow is removed we also want +less space between the chat bubbles this +is a small nuance of the app +that helps associate +uh the components as part of the same +conversation +we wrap the component in a flow layout +so it will align correctly to the left +or right side +we set the ui id to the ui id we picked +at the beginning of the method +we add the component +to the offset just before the input +component +we stole the message itself +we store the message itself as a client +property in the component +this makes it easier to implement +features such as search as we can easily +determine the business object related to +a specific component +finally we set the border to the +component and return the container +instance +this version of the method does all of +that but also sends the message to the +server +this is the version of the method we +invoke when a user types a message +this is the method that creates the +components for a text message +as you recall we have two methods one +for text and one for images +we use a text area to represent the text +it's more direct than span label which +is technically just a text area wrapped +in a container +act as label removes some optimizations +from the text area +so the text is laid out accurately +it can make a it's slightly slower +to render though +we block editing and focus so a user +can't interact with the text area +we set the ui id to chat text so it will +use the right font and colors +time is the label with the current time +for this message +it uses the chat time style +time is the label with the current time +for this message it uses the chat time +style +notice that short messages place the +time next to the text whereas long +messages place it below i chose to use +the 30 character mark to decide this +a better strategy might have been to +calculate preferred size in some way and +decide based on that but i wanted to +keep things simple so i chose this +approach +finally we returned the bubble container +that includes the text and the time +the media message is similar but it uses +the media file url right now the method +is designed only to work with images but +this can probably be fixed relatively +easily +we open a fast stream for the media +we calculate a desired size for the +media which is half the size of the +portrait screen +we load the image then scale it into a +button as the icon of that button +once there we enclose it in a layered +layout so the time is overlaid at the +bottom right of the layered layout +as you might recall the open camera +method was mapped as a listener to this +button +it's a simple method that invokes +capture and then adds the result as a +message +the last method in the class is open +file which is effectively identical +it uses the file chooser cn1 lib api and +maps to the attach button +it allows opening all files but in order +for this to work we need to process all +file types +however i only implemented image support +as i did in the previous method diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/132-9-the-new-message-form.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/132-9-the-new-message-form.md index a18b5b310f..50853f4cef 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/132-9-the-new-message-form.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/132-9-the-new-message-form.md @@ -11,8 +11,56 @@ weight: 132 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 14: Creating a WhatsApp Clone {{< youtube 0ETli_N__ZY >}} + +## Transcript + +the final part discussing the +client-side code covers the new message +form class +New Message Form +new message form +is a form we see when we press the +floating action button +in the main form +the class is trivial by comparison to +previous the previous class it's just a +box layout y form that lets us pick a +contact we wish to chat with +the new group and contact buttons aren't +currently mapped to anything they're +just simple buttons +List of Contacts +we use the fetch contacts method to +fetch the list of contacts to show here +for every contact in the list of +contacts we create a multi button +matching the name and icon +Contact Check +if a contact is clicked we check if he +has an id +if not this is someone that might not be +in the app yet +so we need to contact the server and +check +the find registered user +finds the specific user based on his +phone number +if we get null as a result it means this +is no such registered user in the app +we go back to the previous form and show +a toast bar message there +if there is we update the user id and +save +we can then launch the chat form with +this new contact +Launch Chat Form +since the multi buttons are added +asynchronously we need to revalidate so +they will show on the form +as i said this is a super simple short +class and with that we finished the +client side work diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/133-10-server-entities.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/133-10-server-entities.md index 5593e5e377..36824207f9 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/133-10-server-entities.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/133-10-server-entities.md @@ -11,8 +11,126 @@ weight: 133 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 14: Creating a WhatsApp Clone {{< youtube Rok0Ubr4xes >}} + +## Transcript + +we are finally back to the spring boot +server we set up initially +the code here is pretty simple as well +the whatsapp application is the +boilerplate main class of the spring +boot project there isn't much here and +i'm only mentioning it for completeness +security configuration is a bit more +interesting +although again it's similar to what we +saw in previous projects +first we need to permit all requests to +remove some http security limits +we don't need them here since this isn't +a web app +similarly we need to disable csrf +protection as it isn't applicable for +native apps +finally we need to provide password +encoder implementation +we use this to encrypt the tokens in the +database +next let's go into the entity objects i +won't go into what entities are as i +discussed them a lot in the previous +modules they are effectively an +abstraction of the underlying data store +the user entity represents the data +we save for a chat contact +i use a string unique id with a +universal unique identifier which is +more secure as i mentioned before +it might make sense to use the phone as +the id value though +the username and tagline are also stored +similarly to the client side code +phone is listed as unique which makes +sure the value is unique in the database +when we send a verification code we +store it in a database i could use a +distributed caching system like redis or +memcached but they're an overkill for +something as simple as this +the date in which the user end entry was +created is a standard database date +this isn't used at this time but it's +very similar to the code we have in the +facebook clone to store media +in fact it's copied from there and we +can refer to that for media storage +slash upload +the auth token is effectively a +combination of username and password +as such it's hashed and as such only the +user +device knows that value +i believe that's how whatsapp works +that's why only one device can connect +to a whatsapp account since the token is +hashed when you need to retrieve an +access token you need to effectively +delete the last token and create a new +one in order to set up a hash +for the uninitiated a hash is an +encrypted value that can only be +generated but not retrieved so if my +password is password and the hash is x y +z j k l +then i can get the value of the password +from the hash +but i can check that and i can check +that password matches +x y +z jko +but not vice versa +hashes are also +salted so they have 60 characters and +length and the strong hashes are +impossible to crack with standard tools +the push key is the key used to send +push messages to the client device +this flag indicates whether a user is +verified +when we create a new user we initialize +the id and creation date sensibly +if this entity is loaded from the +database these values will be overridden +the dow methods create data access +objects +that we can send to the client +we will make use of them later in the +service code +the user repository maps to the user +object and exposes three finder methods +we use find by phone +during sign up and sending to detect the +user with the given phone +this method should be removed as it's +part of the copy and pasted media entity +code +we need to find the put the push by push +key in order to remove or update expired +push keys +if the server returns an error we need +to update that +user dao is pretty much the content of +the user class there isn't much to +discuss here with one major exception +and that's the json format annotation +here we explicitly declare how we want +the date object to translate to json +when it's sent to the client +this is the standard json pattern and +codename one in the client side knows +how to parse +this you diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/134-11-server-dao-and-entities.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/134-11-server-dao-and-entities.md index f8b799070b..e0ac9920df 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/134-11-server-dao-and-entities.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/134-11-server-dao-and-entities.md @@ -11,8 +11,85 @@ weight: 134 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 14: Creating a WhatsApp Clone {{< youtube BSj3KRM5Sj0 >}} + +## Transcript + +let's continue with the other entities +i'll skip media as it's just a copy and +paste of the facebook media class and +partially implemented to boot +chat group is an entity that traps the +concept of a group +in a sense it's similar to the user +entity but has no phone +all of these properties the id name +tagline creation date and even avatar +are a part of a group +however unlike a regular user a group is +effectively created by a user +a group also has two lists of group +administrators and group members +the chat group repository is empty as +this is a feature we didn't finish +a chat message must be stored on servers +for a while +if your device isn't connected at this +moment for instance flight +the server would need to keep the +message until you are available again +it can then be purged +if we wish to be very secure we can +encrypt the message based on client +public key that way no one +in the middle could peek into that +message +this should be easy enough to implement +but i didn't get around to to it +it would mostly be client-side work +here we store message messages so they +can be fetched by the app +like everything else we have a unique +string id per message +every message naturally has an author of +that message +but most importantly a destination which +can be either a different user or group +not both +every message has a date timestamp for +when it was sent +the message has a text body always +it can be now though +if we have a media attachment to the +message +the ack flag indicates whether the +client acknowledges acknowledged +receiving the message +this works great for one-to-one messages +but the message sent to a group would +probably need a more elaborate ack +implementation +the dao is again practically copied from +the facebook app +we can include the ids for the +attachments as part of the message +the chat message repository includes one +fine method which helps us find messages +that we didn't acknowledge yet +if a specific user has pending messages +this finder is used to locate such +messages and send them again +the message dao entry represents the +current message +it maps pretty accurately to the entity +object +with that effectively done with the +entity and dow layers +there is still an error dial but it's +effectively identical to what we had in +the facebook app +and it's totally trivial so i'll skip +that diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/135-12-user-service.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/135-12-user-service.md index 523356aab9..e75421a8c4 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/135-12-user-service.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/135-12-user-service.md @@ -11,8 +11,172 @@ weight: 135 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 14: Creating a WhatsApp Clone {{< youtube 5EyrMpQqR-k >}} + +## Transcript + +the next step is the services layer +which implements +the relatively simple business layer of +this application +we'll start with the user service class +it's the main class in this package and +handles pretty much the entire business +logic of the app +it's a service bean this means it +handles generic business logic for the +application +we need access to the repositories we +just defined for users groups and +messages +the api keys service is the exact one i +used in facebook with the exception of a +different properties file name +i'll discuss that later +it generally abstracts api keys and +separates them from the source code +the password encoder is used to encrypt +and verify the token +media repository and notification +servers are identical to the stuff we +had in the facebook clone app +this method sends an sms message via the +twillow web service +if you recall we added the twillow sdk +into the pom file in the first lesson +this sdk makes sending an sms message +very easy as you can see from the code +the login api lets us validate a user +and get the current data +the server has for that user +since there is no username slash +password we need to use the token to +authenticate +first we need to find the user with the +given phone assuming the user isn't +there will throw an exception +since the auth is hashed as we discussed +before +we need to test the incoming auth via +the matches method in encoder it +verifies the hash matches the auth token +this method creates a string of the +given length which includes a random +number for verification +signup creates an entry for a specific +user but doesn't activate the account +until it's verified +we first check if the phone number is +already registered if so we need to fail +otherwise we create the new user and +initialize the value of the data and the +verification code +finally we send the activation code and +return the user entity +the verify method activates a user +account if the verification mode is +correct we mark the user as verified and +return true +we use set props both from the sign up +and update methods +there isn't much here but if we add +additional metadata this might become a +bigger method like it is and the +facebook clone +update verifies the user's token +then updates the properties there isn't +much here +these aren't used at the moment +but they are pretty much identical to +what we have in the facebook clone and +should be easy to integrate in a similar +way +this is part of the work to integrate +support for the user typing feature +right now the client app doesn't send or +render this event but it should be +relatively simple to add +when a user starts typing to a +conversation we can invoke this method +two user can be a user or a group +is +the user present it's if the user is +present it's a user +i'll discuss the event code in the +sockets +when we reach the app socket class +and this if this is a group we need to +send the event to all the users within +the group +via the socket connection +this method sends a message to its +destination which can be a user or a +group +in order to send a message we first need +to create a chat entity message entity +so we can persist the message in case +delivery failed +this is the same code we saw in the +typing event if the message is destined +to a user the following block will occur +otherwise we'll go to the else block +where the exact same code will execute +in a loop +over the members of the group +we mark the destination of the message +and convert it to json to adjacent +string +we invoke the send message api +the send message uses the socket to send +the message to the device +if this failed and the device isn't +reachable +we should send this message as text +using push notification +this method is identical to the other +send message method but it uses a json +string +which is more convenient when a message +comes in through the websocket +the previous version is the one used +when this is invoked from the web +service which is what we use +and this one works when a message is +sent via the websocket +this method converts a chat message +entity to json so we can send it to the +client +object mapper can convert a pojo object +to the equivalent json string +this method sends json via the socket to +the group or a user it allows us to +propagate a message onward it works +pretty much like the other methods in +this class that send to a group or a +user +this method finds the user matching the +given phone number this method is used +by find registered user and find +registered user by id +it generalizes that translation of a +user list to a single user dial value it +implicitly fails for unverified users as +well +ack allows us to acknowledge that a +message was received +it just toggles the ack flag +when a user connects via websocket this +method is invoked it finds all the +messages that weren't act by the user +and sends them to that user +that way if a device lost connection +it will get the content once it's back +online +this method is invoked on launch to +update the push key +in the server so we can send push +messages to the device +with that user service is finished diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/136-13-user-web-service.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/136-13-user-web-service.md index ec1b72ffb9..ee65b4abf4 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/136-13-user-web-service.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/136-13-user-web-service.md @@ -11,8 +11,53 @@ weight: 136 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 14: Creating a WhatsApp Clone {{< youtube BSrupbUahRM >}} + +## Transcript + +next we'll jump to the web service +package +this is pretty much identical to the +facebook clone app +we create a web service mapping for the +user services +technically we could have broken this +down +to more web services but there is no +real reason as we don't have that much +functionality here +this is a thin wrapper around user +service that contains no actual +functionality it only translates the +logic in that class to web calls +if an exception is thrown in this class +it's implicitly translated to an error +dao which is translated to an error json +login and sign up are almost identical +with the small exception that login +expects an auth header value +both are simple post methods +that return the dao object as json body +to the client +verify and update return string values +to indicate that they succeeded +i added the implementation to get set +avatar +via url +but this +isn't mapped to the client side this can +probably be implemented in the same way +as the facebook clone +these methods return their result as an +array of one element or as a zero length +array since there is no way and json to +return null like the business logic +method does +so we return a blank array list +or a list with one element +and that's the end of the class the rest +of the methods delegate directly to the +user service bin diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/137-14-web-socket.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/137-14-web-socket.md index 6a2fd4a56e..031d1b9764 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/137-14-web-socket.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/137-14-web-socket.md @@ -11,8 +11,102 @@ weight: 137 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 14: Creating a WhatsApp Clone {{< youtube YwD35TG6WAg >}} + +## Transcript + +next we'll jump to the websocket package +which is the last package +first we need to configure the websocket +we do this by implementing the websocket +configurer +and using the annotations on the class +to indicate its purpose +we define the socket so we can define +the packet size +to 8k +we can set a larger size but generally +keeping packets small +is a good practice +the app socket class is bound to the +slash socket url +in this line of code +this is the thread used to process the +websocket connections +we can allocate more thread resources +based on need +let's go to the app socket +the app +is an implementation of text websocket +handler which handles text messages +since all our messages are json this +makes more sense +i cache the currently active connections +here +this isn't a good approach in the long +term a better approach would be redis +for +this sort of caching +but for an initial app this can work +fine +we need access to the user service +so we can send message +to a group or +user +the method sends this method sends json +to a websocket based +on the user token and returns true if it +is successful +we get the sessions for the given client +if he has a web service session +we create a text message +with js +the json +we loop over all the websocket +connections +one by one +if a connection is open +we send the message +there and return +otherwise we add the socket to the +remove queue +we don't want to remove in the middle of +the loop to prevent an exception +we remove all the defunct websockets +from the queue in this +line +for all the classes +we're sending via socket in work we +return force +this method handles the incoming text +packets +we need +to parse the json into a map +if m +has a type of pro it's probably an init +method +if +init messages allows us to add a +websocket +to our cache of connections so we can +push a message back into the websocket +when we need to send a server note +notification +otherwise we test if this is a user +typing event in which case we need to +send a typing message onward +finally we send the message as json to +the users in the group or to the +specific user this invokes the code we +saw in the user service class +when combination +when a connection is closed we loop over +the existing list and purge it +of the dead connection +for simplicity we don't support partial +messages which shouldn't be necessary +for a small 8k messages +with that the class is done diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/138-introduction.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/138-introduction.md index ac292b5d3b..a24f2d8b9c 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/138-introduction.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/138-introduction.md @@ -11,8 +11,144 @@ weight: 138 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 15: Create a Netflix Clone {{< youtube r_ti2QAhm9s >}} + +## Transcript + +welcome to the first lesson +of creating a netflix clone with +codename one +there are a few things i'd like to talk +about in this module +but first i want to clarify that this +module is relatively short as are trying +to focus only on the new things +so the netflix clone is less of a clone +and more of a proof of concept +the clone is much simpler in scope and +functionality when compared to previous +modules this is intentional i don't want +to repeat things that were better +covered in the facebook or uber +tutorials +but i do want to cover new things +i placed most of the focus on the +nuances of the netflix ui +but i also placed some focus on +different approaches +for working with spring boot +i think these will prove valuable as we +go back and look at the stuff we did in +the previous modules +but first let's talk about the +complexities of video platforms +technically they aren't very complex in +fact they are remarkably simple for the +most +part the biggest problem faced by +netflix is scale +and that only matters when you reach +netflix levels of scale +videos and platforms like netflix are +generally +generated statically before the first +request is made +that effectively means that servers just +serve ready-made files and don't do +complex runtime work +there are great tools that pre-process +video files such as ffmpeg +these tools can be used as native +libraries in the server or as command +line tools +most netflix clones just pre-generate +all the video files in the various +resolutions bitrate options +then the +work amounts to picking the right video +url +the video urls can be further scaled +using pre-existing content delivery +networks also known as cdns +we specifically use cloudflare at +codename one but any cdn would do +we didn't cover cdn hosting and +literally all of the complexities in the +server here we also don't cover anything +related to video processing +that's server logic that falls way +outside the scope of a mobile tutorial +furthermore a lot of this work can be +done completely outside of the server as +a separate tool that updates the url's +databases +video hosting can be done as a separate +microservice and mostly hidden from our +main backend logic +as a result the content of the +application will be mostly hard coded +this is important as there is an ip +issue with distributing a clone of +content which we don't want to get into +we also won't implement the multi-user +and authentication portions of the app +we covered all of that rather well in +the uber clone and there's no point of +going into this again +once all this is removed the server is +ridiculously trivial +most of this applies to the client ui to +we covered almost all of this before so +the netflix clone is a rehash of many of +those ideas with a new coat of paint +the ui is trivial and includes only two +forms both of which are only partially +implemented there is no reason to go +deeper as +the +their source application isn't very +complicated to begin with +you can use the facebook clone as a +reference to more elaborate ui +once the css is in place implementing +the missing functionality in a netflix +clone becomes trivial +but there's one bigger mission i chose +to use the native player +native video playback is actually pretty +great it handles everything we need in +terms of ui for the video player +the problem starts when that mode isn't +enough say we want more control over the +behavior of playback code we can't do +much in that mode our control is very +limited +however +native playback is pretty much a turnkey +solution for video playback that's why +we picked it it's a great tool for +getting started +lightweight is more error prone and +powerful a good example is closed +captions which we can implement manually +in lightweight mode but literally +placing by literally labeling and +placing labels on top of the playing +video +that's very powerful +i will create a separate module that +will cover lightweight video playback it +should be easy to adapt the playback +code to make use of that approach +the final ui should include these two +forms +the latter will allow video playback +notice that all the videos lead to a +hard-coded video url of a spinning earth +again due to ip issues +thanks for watching i hope you'll enjoy +the rest of the course and will find it +educational diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/139-server-part-i.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/139-server-part-i.md index 062bf015f0..93e12a4105 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/139-server-part-i.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/139-server-part-i.md @@ -11,8 +11,299 @@ weight: 139 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 15: Create a Netflix Clone {{< youtube 7CoD9u6KM2Q >}} + +## Transcript + +in this lesson we'll go over the basic +server architecture and project lombok +which we use in the server +implementation +we'll start with some basic information +about the server and then dive into +quick lombok overview +so first let's talk about the database +unlike previous modules where i chose to +use mysql +this time around i picked h2 for the +database since it requires literally no +setup +i wouldn't use it for production but +since the whole complexity of mysql is +covered in other modules this just isn't +necessary +lombok is used to make the server code +tiny and devoid of boilerplate this is +pretty cool +we don't really need much in terms of +web services if we had authentication +and authorization there would have been +more i could also implement paging +support and more complex requests for +various segments of the ui but those are +pretty obvious if you've gone through +gone over the feed section of the +facebook loan +the authentication aspect is the big +missing piece here +and i just didn't want to get into it +it's usually one of the more painful +aspects of building a server but since +this is a mobile course i don't think +it's very important to cover again as it +was covered in previous modules +let's start with a palm file this is all +pretty minimal +first we have the standard declarations +we use spring boot 2.2.2 which is the +current version at this time +this code is generated with the spring +initializer which i described before +this declares the h2 database instead of +mysql +and here we declare the use of lombok +which i'll get into shortly +i also added a dependency on apache +commons io which is pretty useful for +small utility calls +lombok is a set of tools +that include ide plugins libraries and +runtime tools they essentially try to +modernize the java syntax using special +annotations and it does a pretty great +job at removing a lot of the common +boilerplate from java 8 syntax their +biggest claim to fame is removing the +getter and setup boilerplate code from +java +in this module we'll use lombok in the +server it works for codename one app +code but we won't touch on that +the main reason that is +that the value of lombok diminishes +thanks to properties so we don't need it +as much +but if you need it you can see this tip +about installing lombok for a codename +one app +let's look at a few examples of using +lombok notice that these examples are +picked directly from the lombok +developer guide +here we have a class with three fields +but only one of them is marked as +non-null +as such we can't construct this object +without the description field as we +would have an invalid object +so we have a private constructor that +accepts this required field to create +the actual instance of this class we use +the of method which accepts the required +description argument +so you would be able to just write +constructorexample.of +description +that's pretty nice +but it took five lines of code +not including curly brace braces or +spaces that's a bit verbose +that can be achieved with one annotation +in lombok +you just define the constructor and the +method name that you wish to add as a +static factory method and voila +it works exactly like the code we saw +before you can literally write +constructed example.of description +the other constructor is for subclasses +it accepts all of the state members and +also makes sure to fail if we try to +violate the not null annotation +notice it's scoped as protected so it +would be used only when deriving the +class +this can be implemented with a single +line of code +the all args constructor annotation does +all of that implicitly it also has an +optional access level property which +defaults to public +the inner class +is pretty simple there isn't too much to +save here but still there's a bit of +vibrosity +we can implement the blank constructor +using the no args constructor +notice that this example is a bit +synthetic normally we would use this +annotation in conjunction with other +constructor options to indicate that we +also want +that option +we already saw non-null +being used before so this example should +come as no surprise this annotation can +also apply to method arguments etc +the method can now assume the variable +isn't null notice that this usage is a +bit stupid as the person.get name call +will throw a null pointer exception +anyway but if you invoke code that might +propagate null +it could be useful +let's move on to another cool feature of +lombok +notice that this code can be improved by +using the java 8 try with resource +syntax so this isn't as beneficial +but it's still pretty cool +this block can be written like this +which is as terse as with +the try with resources code and possibly +even more terse +lombok claims +lombok's claim to fame has always been +getter and sitter elimination so this +whole block of code can be replaced with +this notice that this is still +relatively verbose as we want a +protected setter +so let's see something that's even more +terse +first notice that the setter for age as +package protected access while has +package protected access while the +getter is public +also check out all the boilerplate code +we have for equals and tostring +this can be optimized with some of the +newer objects class methods but not by +much +the boilerplate doesn't end though +we have a hashcode method too +and a non-trivial inner class with a +static creation method +notice that this is the required arc +constructor syntax we mentioned before +that code that includes pages of data +can be achieved using the at data +annotation +it includes getters setters tostring and +hash code implicitly notice you can +explicitly override the definition of a +specific setter from data as we did for +the case of age +another common task is variable +definition +again there is a lot of boilerplate here +so much +that java defined a new val keyword +keyword but this isn't yet available for +java 8 which is used by most of us +lombok added two keywords vel and var +va var +var lets us define a variable that can +change +a mutable variable +val defines an immutable variable +effectively a final variable +there are a lot of annotations we didn't +cover here +at value is the immutable variant of at +data +all fields are made private and final by +default and setters are not generated +the class itself is also made final by +default because immutability is not +something that can be forced onto a +subclass just like data +the tostring equals and code methods are +also generated each field gets a getter +method and a constructor that covers +every argument is also generated +the builder annotation produces complex +builder apis for your classes at builder +lets you automatically produce the code +required to have your class be +instantiatable with code such as +person.builder.name shy.build +notice that this works nicely with the +add value annotation to produce +immutable classes with the builder +pattern +add sneaky throws can be used to +sneakily throw checked exceptions +without +actually declaring this in your methods +throws clause since checked exceptions +are a feature of the java language and +not of the java bytecode this is +technically possible +synchronized is a safer variant of the +synchronized method modifier the +synchronized keyword locks on this +object which is problematic as it +exposes the lock state externally +this annotation implicitly creates a +hidden +dollar lock object and synchronizes on +that object +the next best alternative to a setter +for an immutable property is to +construct a clone of the object +but with a new value for this one field +a method to generate this clone is +precisely what +at with generates +a with field name method which produces +a clone except for the new value for the +associated field +you put at log in your clock +in your class +you +you then will have a static final log +field +initialized as is the commonly described +prescribed way for the logging framework +you use which you can then use to write +log statements +notice that there are a lot of +annotations you can use to describe +explicit log system you want to use in +the project +you can let lombok generate a getter +which will calculate a value once +the first time the scatter is called and +cached +and cache it from then on this can be +useful if calculating the value takes a +lot of cpu or the value takes a lot of +memory to use this feature create a +private final variable initialize it +with the expression that's expensive to +run and annotate your field with at +getter lazy equals true +the field will be hidden from the rest +of your code and the expression will be +evaluated no more than once +when the getter is first called +there are no magic marker values i.e +even +if the result of your expensive +calculation is null the result is cached +and your expensive calculation need not +be thread safe as lombok takes care of +locking +most of this is taken directly from +uh +the +projectlombok.org features slash all +tutorial +but there's a lot more information there +thanks for watching i hope you enjoy the +rest of the course and find it +educational diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/140-server-part-ii.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/140-server-part-ii.md index c3671df4ab..c63188a773 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/140-server-part-ii.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/140-server-part-ii.md @@ -11,8 +11,199 @@ weight: 140 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 15: Create a Netflix Clone {{< youtube 47WhIiLxv78 >}} + +## Transcript + +in the third part we'll dive right into +the model objects representing the +server and ultimately the front end code +if you're unfamiliar with entities jpa +uuid etc i suggest going back to the +previous modules and refreshing your +memory a bit as we'll build a lot on top +of that +one way in which this will be different +though is the usage of lombok which will +make our code far more tears +still the code here is mostly mock the +real world netflix has a lot of code but +most of it applies to algorithmic +scheduling user management scaling etc +all of these aren't applicable here +in this lesson our focus will be on +entities and data transfer objects also +known as dtos which are sometimes mixed +with data access objects or dowels +there is overlap between both of these +concepts but what we have is generally +dtos is they transfer data to the client +they don't just abstract the database +layer +writing an entity with lombok is much +easier +there are no getters setters +constructors equals hash codes etc +notice we still use jpa just like we +used to so +we have the jpa entity annotation +and then the lombok annotations +everything works as you would expect +including the primary key definition etc +notice i chose to go with uuid object as +a primary key coupled with auto +generation +that's a much simpler trick than the one +i picked in previous modules +we already talked about using strings +for keys when we use a uuid object we +get a long string that isn't guessable +in the database +that means we can expose that primary +key to the end user without worrying +that he might use it to scan through +details of other users +as the string is pretty long and hard to +guess +as we saw we just need to use the uuid +object type there are several other +strategies for generating a uuid and jpa +i chose the simplest one it might not be +the best one but it is convenient +so why doesn't everyone use this +approach turns out it's much slower than +using numeric auto increment values on a +database column +databases such as mysql are heavily +optimized for auto increment fields and +string based primary keys are just +slower to insert +some developers consider that a +non-starter especially when looking at +performance graphs which +is scary +performance really takes a dive for +instant operations +while it remains flat when using long +auto increment fields +personally i don't think that's a +problem even for video app like this you +wouldn't insert too often and read +operations are still pretty fast +this might become an issue if you have a +log or auditing table that might include +multiple insert operations per second +at that point you need to use a long for +that for the primary key and make sure +never to expose it externally +the name and description fields +correspond to these fields in the +database +this is the entire definition as the +excesses are generated automatically +we have three one-to-one media relations +these include the three images for every +content item specifically +the hero image which is the big picture +that appears on top of the application +the show logo is displayed on top of the +hero image it's a separate image to +support different device aspect ratio +and layout +and the icon is the image representing a +show within the list +finally we have the actual video files +which we store in media objects as well +we have multiple video files +representing different quality levels of +the video +in real life we can have even more +options such as different aspect ratios +languages etc +normally i would like +this to be a map between quality and +media +but this is a bit challenging to +represent correctly in jpa so i left +this as a simple set +for convenience we place the dto +creation within the entity object +this code is mostly just the +construction +but it's uh it +it's there's +one block where we convert the media +object +if the dto +in the dto it makes more sense to hold +the media as a map instead of a list or +set so we translate the video to a map +i find the stream syntax a bit obtuse +sometimes this is how it would look with +a standard for loop +essentially for each element we replace +the content with a map where the key is +the quality and the value as the media +url +once this is done we create a new dto +object with the automatic constructor +and return it +and finally i also added a small helper +method to make the code above a bit +simpler so we won't get a null pointer +exception if the media is null +this is the dto object we just created +notice it's super simple and mostly +consistent +consists of the lombok annotations +the strings just map directly to the +entity there's nothing to say here +for the media i chose to include the +icons themselves i could have taken the +approach of returning urls for the media +which might have advantages in the +future for now this is simpler but +possibly not as efficient +using a url would have had the advantage +of caching the data locally for future +refreshes +using the actual icon means all the data +is transferred with one request +this is the map we created for the media +items we already discussed this in the +stream part before +it maps between the video quality enum +and the string +url for the sake of completeness this is +the video quality enum +pretty simple but matches what we need +right now +the media entity is another standard +lombok entity with the standard +trimmings +we use the same uuid primary key +generation logic +rest of the stuff is pretty standard +notice that we store the modified time +as an instant instead of date +instant is a java 8 date time api class +it represents a timestamp and is more +convenient to use than date +the media data is stored in blob storage +in the database +finally the url to the media and the +video quality enum are stored as well +that means we can have multiple +instances of the same media object for +various quality levels +one thing i didn't cover here is the +repositories for the entity objects +they're all empty as we don't need any +finder methods for this specific demo so +it's all pretty trivial +thanks for watching i hope you'll enjoy +the rest of the course and find it +educational diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/141-server-part-iii.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/141-server-part-iii.md index 527aa946f5..e5934c1165 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/141-server-part-iii.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/141-server-part-iii.md @@ -11,8 +11,163 @@ weight: 141 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 15: Create a Netflix Clone {{< youtube vLBQhAJ6aTk >}} + +## Transcript + +in this final installment covering the +server we'll go over the service classes +and the final entity representing the +content creation +the content collection is an +oversimplification of the concept +a site like netflix would probably +generate this data dynamically based on +user viewing preferences and complex +heuristics +i just hard coded an entity which was +simpler +the service class doesn't do anything +other than create the built-in data and +implements the basic service call +the content collection starts similarly +to the other entities +again it uses a uuid as an identifier at +the moment will only have one content +collection but in theory there can be as +many as there are users +the lead content represents the show +that appears on the top of the ui in +this case it's the stranger things +header +these represents the rows of content +below that they contain the popular +recommended and personal list of shows +next we have +the method that returns the dto +since all lists are effectively lots of +content +we use the same method to convert +everything +again we make use of java 8 streams to +get all the dtos from the list by +invoking the getdto method on every +element +now we're getting to the web service +code +we're using the request mapping +attribute to specify that this is a web +service on the video path +the rest controller attribute designates +this as a simple json api so a lot of +common sense defaults follow +for instance response in the body etc +notice the all args constructor this +means the class has a constructor that +accepts all arguments no default +constructor this is important +notice the final field for the video +service +it's passed via the constructor +notice that the video service doesn't +have the autowad annotation +we usually place for beans in spring +boot this is called constructor +injection and it has a few advantages +normally it's a bit too verbose as we +need to maintain a constructor with all +the injected beans +but in this case lombok makes it +seamless +in other words lombok and spring boot +inject the video service being pretty +seamlessly for us +we only have one api in the server it +returns the +content json +a more real world api would also have +authentication identity apis and maybe a +state querying submitting api +for instance view positions statistics +etc +but those are relatively simple and we +were covered by other modules +here so i'm skipping them for now +the service class is similar to the rest +api class i used the required rx +constructor which is effectively the +same as all arcs constructed in this +case it creates a constructor for all +the required args specifically all the +final fields +this again works for creating +constructor based injection +this class is also transactional as it +accesses the database +we need access to all the repositories +to create the entities +this is a simple utility method to read +bytes from a stream +in the class path notice the usage of +the add cleanup annotation from lombok +and apaches i o util +api +post construct is a feature of spring +boot that lets us invoke a method after +the +container was constructed +this is effectively a constructor for +the entire application +here we can initialize the app with +default data if necessary +notice i +throw an exception here since i assume +this method won't fail +it's the first launch so it's core that +it succeeds +it's pretty easy to detect the first +launch +if the database is empty +the count method on the repository will +return 0 +elements in that case we need to +initialize the database +in this large method i set up the +initial data in the database i prefer +doing it through code rather than +manually populating the database and +providing a pre-filled one as it's +easier to do when working in a fluid +environment where you constantly wipe +the database +in this case i just take the hard-coded +images and get their byte array data +i then create media objects for all the +thumbnail entities +the rest is pretty self-explanatory +eventually all the videos are created +and all the media entities are added +notice the urls are to an external video +sample site i was able to find online +this is consistent with the way a video +site would work your actual content +would be hosted on a cdn for performance +also notice i didn't get into the whole +process of encryption encryption and +complex drm streaming that's a whole +different level of complexity +finally the last bit of content is added +to the content repository and everything +is saved to the database +this is the entire server api this +returns a json structure used in the +client we could stream this in smaller +blocks but that was already covered in +the facebook demo so i skipped it here +thanks for watching +i hope you'll enjoy the rest of this +course and find it educational +you diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/142-client-model.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/142-client-model.md index a7eeb92e2d..f31cd01ab1 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/142-client-model.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/142-client-model.md @@ -11,8 +11,59 @@ weight: 142 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 15: Create a Netflix Clone {{< youtube HJvMQKM5FPY >}} + +## Transcript + +we're finally at the client side which +is also pretty simple +this time we'll start with the model +and move to the ui rather than the other +direction which is what we normally +cover +since we did the server work first this +fits pretty directly after that +which makes it easier to start with that +portion +this model is pretty much proper +property business objects to match the +lombok objects in the server side +while some of the terse aspects of +lombok are missed properties make up for +it by being far more powerful overall +we start with the server class which +abstracts our connection to the server +since we have one method in the server +web service this is continued here we +fetch the content from the server +synchronically using the rest api this +api translates to the the response to +json almost seamlessly +we say that the response should be in +the form of the content collection class +this means the json will be parsed into +that class which will +will examine next +the content collection is a standard +property business object it maps almost +directly to the class with the same name +on the server side the main difference +is that the properties use our object +syntax +these match their definition in the +server side and include the exact same +data the entire class is pretty standard +property business object +finally the last properties object we +have is the content object which is the +same as the one in the server +the final piece is the enum that matches +the one in the server with that our +communication layer is complete and we +can move on to the ui elements +thanks for watching i hope you'll enjoy +the rest of the course and find it +educational diff --git a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/143-client-ui.md b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/143-client-ui.md index 72a0df8a12..1c19e43a40 100644 --- a/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/143-client-ui.md +++ b/docs/website/content/courses/course-03-build-real-world-full-stack-mobile-apps-java/143-client-ui.md @@ -11,8 +11,196 @@ weight: 143 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 15: Create a Netflix Clone {{< youtube nImSppBdgkY >}} + +## Transcript + +for this final part we'll cover the ui +of the client which is relatively simple +we don't have that many forms but to be +fair the basic netflix app doesn't have +too many forms either +so this isn't too different from the +original +the one special thing we do here is use +the layered toolbar for the ui which i +will discuss soon enough +css is used for the design i'll +introduce the applicable css +incrementally when introducing a +specific uh ui id +i won't go over the entire css and will +instead go back to it when introducing a +specific ui id +i will cover +the gener generic concepts first though +we have only two constants in the css +the first is the include native feature +which should be on by default always +the second is a standard label gap with +between the label and the icon i think +one millimeter is generally a good +number here +we don't have too much in common between +the forms at this level +of the ui so there is only one method in +common +the init global toolbar method +initializes the toolbar component when +global toolbar is turned on +which is the default +we do two things here we set the toolbar +to use the layered mode we do that by +passing true to the toolbar constructor +next we set the ui id of the toolbar to +toolbar gradient which we use to +indicate the translucent gradient +background to +separate the toolbar from the content +the toolbar gradient is a gradient in +black between 0.6 alpha to almost clear +alpha +this creates a slight fade effect over +the title area so the title will still +be visible if the image in the +background has the same colors +condemned doesn't currently support +alpha gradients as a solution the css +support generates an image of the +gradient during build and uses that +the splash form is +stupid simple +we just place the logo in the center of +the form initialize there is only one +thing to discuss here and that's the +source of the netflix logo png file +all images are declared in the css +under two dummy ui ids specifically +image imports 1 and image import 2. +let's just pause all the images into the +resource file so we can later make use +of them +the source dpi is the reason we have two +two image import ui ids +when source dpi is set to zero it means +we want the image imported as is +not as a multi image but rather as a +single image +the logo is the only image where we're +interested in a multi-image behavior +since most images come from the server +we don't +need many multi images in the app +the home form is the main ui of the +application +listing the content that the user can +select from +it's a base form which means the title +of the form is overlaid on the content +and it uses border layout +i'll skip to the bottom first +this class is created using a create +method that returns the class instance +it accepts the content information +as the argument to the build to build +the ui +the actual implementation of the layout +is in the init method we see above +the edit method creates the entire ui +it starts with the logo title we can see +here +that matches the same image we see in +the splash screen that's mostly laziness +on my part +but isn't +too far off from the actual netflix ui +by adding a command to the side menu the +hamburger menu appears automatically i +didn't want to go into the design and +implementation of the side menu so i +left this effectively blank +i also added a search command which is +again blank since i didn't implement +that technically i just used that for +the icon +i could have just used add material +command to right bar but that would have +required a slightly longer line of code +so i chose this approach +the main ui has a logo image here which +is different from the background hero +shot +now you might be thinking why not have +the logo as a part of the background +hero shot why do we need a separate +image for the logo +two reasons we want the logo to appear +above the play button exactly if it's a +part of the background image we won't be +able to tell where that is +we want the ability to scale the +background and foreground image +differently in the background we want a +scale to fill so the ui will look good +in all resolutions for the foreground we +want a scale to fit behavior so the logo +text will always be visible regardless +of the device resolution +we set the uid for the series logo this +impacts the following css +the margin and padding push the logo to +the right location in the middle +with the right amount of spacing and the +background is defined as transparent so +the background image will be visible +through the logo +the play button looks like this again +most of the work is done in the css for +the button +we use a 1.5 millimeter round border +with a gray background and black +foreground for the text slash icon +the background image comes dynamically +from the server so we can't set it from +css +we create a box layout with the logo +play button and the popular on netflix +label +we then set the background image +dynamically using the style object +the lead ui id is a special case with a +dark gradient background it's overlaid +on the title image and needs that +gradient to be visible on all image +backgrounds +the tabs are set to appear at the bottom +explicitly to avoid top android style +tabs +i could have defined this in the theme +constants but chose to do it in the code +in this case +the lead ui is a special case with a +dark gradient background it's overlaid +on the title image and needs that +gradient to be visible on all image +backgrounds +each list below is created via the movie +list method +they have a lead label +top and reside within a scrollable +container so we can scroll through them +let's look at the +movie list method +here i create a box x container that's +scrollable on the x-axis every element +is a scalable image button that uses the +thumb icon uiid +when pressed we show the details form +finally the tabs themselves are added to +the bottom of the form +thanks for watching i hope you enjoyed +this course +and found it educational diff --git a/docs/website/content/howdoi/how-do-i-access-native-device-functionality-invoke-native-interfaces.md b/docs/website/content/howdoi/how-do-i-access-native-device-functionality-invoke-native-interfaces.md index 92836d185a..9d602329a7 100644 --- a/docs/website/content/howdoi/how-do-i-access-native-device-functionality-invoke-native-interfaces.md +++ b/docs/website/content/howdoi/how-do-i-access-native-device-functionality-invoke-native-interfaces.md @@ -11,66 +11,34 @@ description: How to integrate native iOS, Android etc. functionality into your C youtube_id: 2xKvvv7XoVQ thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-10-1.jpg --- +{{< youtube "2xKvvv7XoVQ" >}} +Native interfaces are how you call platform-specific code from a Codename One application without giving up the portability of the rest of the project. When Codename One says "native" in this context, it does not mean Java's ordinary `native` keyword. It means "use the platform's own language and APIs" when you need something that the portable Codename One layer does not expose directly. -{{< youtube "2xKvvv7XoVQ" >}} +That means the implementation changes by platform. On Android, a native interface implementation can use the Android SDK and third-party Android libraries. On iOS, the implementation uses the native iOS language and APIs. On JavaScript builds, you can call into JavaScript. On the desktop port, you can use ordinary JavaSE APIs. The Java code in your main app remains the same; the native interface is the bridge that dispatches to the right implementation on each platform. -#### Transcript +The core idea is simple: define an interface that extends `NativeInterface`, then provide platform-specific implementations of that interface. The interface becomes the contract between your portable Java code and the native side. `isSupported()` is especially important because not every platform will necessarily have an implementation. Your Java code should check whether the native feature is available before relying on it. -In this video I’ll discuss native interfaces, it’s a big subject so I’ll cover the basics but you will need the developer guide and obviously the native API guides to get you thru this. Native interfaces are the system we use to call into native code from Codename One without disrupting the portability of Codename One. +This is useful both inside an application and inside a cn1lib. In fact, cn1libs are one of the best uses of native interfaces because they let you wrap messy platform-specific integration behind a clean Java API. The caller sees a normal library. The native details stay hidden inside the implementation. -You can just add a native interface to your app or to a cn1lib which is a standalone library. One of the cool things in cn1libs is that they support native interfaces and thus work seamlessly. You can wrap complex native logic and hide that from the user of the cn1lib. +The most important habit here is to keep the native surface area small. Expose the narrowest interface that solves the problem. It is tempting to mirror a large native API directly, but that usually leads to code that is harder to maintain, harder to test, and harder to port. A small interface with a few focused methods is much easier to reason about and much easier to support across platforms. -When we say native in Codename One we mean something that’s technically very different from the standard Java definition of native. We mean "use the platforms natural language" when we say native. +Native interfaces also restrict the kinds of types you can pass. That is intentional. Simple values such as primitives, strings, byte arrays, and peers are much easier to move between languages and runtimes. Once you try to pass complex Java objects directly into Objective-C, JavaScript, or other native code, translation becomes far more complicated and performance becomes harder to predict. -So when we are on Android and we invoke a native interface we will go into Java code. But it will be separate code where we can use the full Android API and 3rd party libraries. That code will only execute on Android so you can just use anything you want including the Native Development Kit which allows you to get all the way into C code. +`PeerComponent` is one of the most important special cases. It allows native code to return a native visual component that can be placed into a Codename One layout like an ordinary component. The classic example is a native map view. That pattern is powerful because it gives you real native UI where it matters while still letting the surrounding screen remain portable. -On iOS we expose Objective-C callbacks which are more natural to the platform. -On Windows a native interface will map to a C# object. -When compiling to JavaScript you can call into JavaScript itself and write logic there. -And with the desktop port you can just write JavaSE code that accesses Swing and other such API’s. +The hard part of native interfaces is often not the code itself but the configuration around it. Native libraries frequently come with Gradle dependencies on Android, CocoaPods or frameworks on iOS, manifest changes, plist changes, and packaging rules for extra files. In Codename One these are usually handled through build hints and native source packaging. If a native library's installation instructions start with "add this dependency", "edit the manifest", or "add this plist entry", you should read that as configuration work that must be expressed through the Codename One build system. -So how does it work or why do we call it a native interface? -Well, because it’s actually an interface. You need to define an interface and it needs to extend the NativeInterface interface so we’ll know that this is a native call. Here I defined a relatively simple one method interface but you can have more elaborate interfaces in place. NativeInterface itself defines one method isSupported() which always returns false by default. This means that you can easily ignore platforms which you don’t support and just check the isSupported() flag. -So how does this work? +The video shows the older "generate native stubs from the IDE" workflow. The idea behind it is still valid, but the everyday project structure around Codename One is more Maven-centric now. The principle has not changed: define the interface cleanly, implement it per platform, and keep the real fix or integration logic in the project that will survive regeneration and rebuilds. -You right click that interface in the IDE and select the generate native option. This will generate stubs into the native directory under your project root, in Eclipse and IDEA the native directory should be visible. In NetBeans you will see it when you switch to files mode. +For deeper debugging, the include-sources workflow is still useful. When native code needs to be debugged in the native IDE, generate or inspect the native project, verify the behavior there, and then bring the stable implementation back into the Codename One project. The generated artifacts are useful for diagnosis, but the durable source of truth should remain your actual project and library structure. -This is the stub generated for Android, you will notice it’s just a standard Java file but in this code we can reference any Android API we want as long as we import it correctly. +## Further Reading -We can implement this just like we can any other class, notice that we also returned true for isSupported() otherwise the code might be ignored. -Notice that the JavaSE stub and implementation are practically identical so I’ll skip those. - -For iOS the code looks a bit different but you will notice the default implementation for isSupported that returns NO and the method itself that returns nil. Notice that in Objective-C argument names have meanings so you can’t change the argument names as this will break compatibility… - -Again the implementation in iOS for this native code is mostly trivial. Notice we return an NSString which is the Objective-C native representation of a String but we seamlessly get a java.lang.String on the Java side. That’s just some of the magic done by the native interface binding. It’s designed to make all of this act with the least amount of friction. - -I won’t go much into other platforms but this is the C# stub, notice it’s very similar. - -This is the JavaScript stub. With JavaScript we must do a callback instead of returning a value directly. This is important as JavaScript is single threaded but we need to break down the code to allow the feel of multi-threading so the callback return is essential. - -Finally we can invoke the native code, we use the lookup class to find the right native implementation. This can sometimes be null for instance in the build servers so it’s something we must check. We also check the isSupported method to make sure this specific platform was implemented. We can then use the native code as if it was a standard Java method. - -So how does it work. Normally Codename One sends only bytecode to the servers but in the case of native interfaces the native source must be sent to the server where we can compile it with the native compiler. So even if you have a PC you can write Objective-C code and it will compile in the cloud. But this creates a situation where code completion won’t work, the source will be highlighted in RED as if it can’t compile even if you have the right SDKs installed. - -One of the tricks we use is to send a build with include source. We then implement the native interface in the native IDE. Test and debug it. Then copy and paste the debugged source code back into the native directory. - -Native interfaces are very restrictive in terms of the types you can use. You can pass primitives, Strings, arrays of bytes and peers. The reasons for these restrictions relate to the difficulty in the translation process. Imagine working with a complex Java object from Objective-C or JavaScript. Using these restrictions we can simplify the translation code and also guarantee good performance. - -We do support `PeerComponent` which is a huge use case. It allows the native code to return a native component that you can add into your layout as if it’s any other component. The best example of this is the native google maps implementation which literally returns a native map widget as a peer component. You can check out the source code of that cn1lib for reference. - -Native code can do callbacks back into Codename One code. It is a bit challenging though so I suggest reading about it in the developer guide where we go into more details. - -One of the biggest difficulties when dealing with native code is in the configuration and not in the code. Build hints allow us to support all types of configurations. While the list of build hints is pretty extensive I would suggest consulting with us if you run into difficulties as some of the settings can be very nuanced. - -Native library integration instructions often start with gradle dependency instructions for Android or a cocoapod instruction for iOS. Both of these are supported thru build hints and you can just integrate a native library by defining the right hints. You can then use it directly from the native interface. - -Another challenge is with changes required for the manifest or plist. Both allow you to inject elements into them. You might need to add specific files such as additional libraries or source files. You can just place them in the respective native directory next to your native implementation code and it will get packaged during the build process with the rest of the native code. - -I highly recommend checking out our cn1libs which implement many native interfaces and can serve as samples for pretty much anything. - -Thanks for watching, I hope you found this helpful. - ---- +- [Developer Guide](/developer-guide/) +- [Build Hints](/build-hints/) +- [Build Server](/build-server/) +- [Introduction for Android Developers](/introduction-for-android-developers/) +- [How Do I Use The Include Sources Feature To Debug The Native Code On iOS/Android Etc.](/how-do-i/how-do-i-use-the-include-sources-feature-to-debug-the-native-code-on-iosandroid-etc/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-access-remote-webservices-perform-operations-on-the-server.md b/docs/website/content/howdoi/how-do-i-access-remote-webservices-perform-operations-on-the-server.md index 5e67d4b188..4c80e3d47d 100644 --- a/docs/website/content/howdoi/how-do-i-access-remote-webservices-perform-operations-on-the-server.md +++ b/docs/website/content/howdoi/how-do-i-access-remote-webservices-perform-operations-on-the-server.md @@ -11,14 +11,26 @@ description: Invoke server functionality from the client side using the Codename youtube_id: sUhpCwd0YJg thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-23.jpg --- - {{< youtube "sUhpCwd0YJg" >}} -#### Script +Client/server work in Codename One usually starts with a simple question: do you want a portable client talking to a server over standard HTTP APIs, or do you want generated RPC-style plumbing that hides some of that protocol detail? The old webservice wizard was designed for the second case. It generated client-side proxies and matching server-side scaffolding so you could define methods and call them almost as if they were local functions. -The webservice wizard is very similar to the GWT RPC functionality if you are familiar with that. We generate a set of simple static functions on the client side and then generate for you a servlet with some helper classes on the server side in order to implement client/server functionality. Lets start by adding a simple webservice to a hello world application. First lets launch the webservice wizard by right clicking and selecting it. This tool allows us to define methods and their arguments. Arguments can be the basic Java types, arrays and Externalizable classes. For simplicities sake I’ll just add a sayHello method with String argument and return value. When we press generate code is generated into the project, notice you migth get prompted if files will be overwritten. We also need to select the source folder of a web server project. I created a standard Java web project and I just selected it here in the tool. The first thing we need to do is open the WebServiceProxy class, its a generated class so its not very important however we need to fix the destination URL to point at the right address including the server context. Now lets look at the server generated code we have the classes in the io package and the proxy.server package which are fixed utility classes. Then we have the CN1WebServiceServlet which is generated to intercept your calls, the only class you need to concern yourself with is the class ending with the word Server which is the one where you should write your source code. Lets fill up the sayHello method with a simple implementation. In the client side lets just add a couple of buttons to the form and demonstrate the difference between a sync call and an async call. In a sync call we just invoke the method directly as if it was a local method. Notice sync calls will throw an IO exception if there was an issue in the server side. The async methods never throw an exception and will communicate success/failure thru a callback mechanism similar to GWT. Success is invoked when the method returns and passes the appropriate response value if relevant. Lets run this code. Notice that when you create a webservice you can’t change the methods after the fact, so if you want to add additional functionality you will need new method names so as not to break applications in production. You can also generate several servlets in several packages and just point to a separate URL. Thanks for watching I hope you found this tutorial educational and helpful. +That approach is still useful to understand conceptually, but it is no longer the default direction most modern projects should start with. Today, most teams are better served by ordinary REST-style HTTP APIs, `ConnectionRequest`, and the higher-level REST helpers described in the developer guide. Those approaches fit better with current backend tooling, current deployment habits, and Maven-era Codename One projects. ---- +What the video still teaches well is the separation of concerns. The client side should know how to invoke a remote capability. The server side should contain the real business logic. Generated proxies can reduce boilerplate, but they do not remove the need to think carefully about API evolution, error handling, and backward compatibility. + +One lesson from the older wizard flow is still especially important: remote APIs are contracts. Once an app is in production, you cannot casually change method signatures or response shapes and expect every installed client to keep working. If a service needs to evolve incompatibly, version it or add new endpoints instead of silently mutating the old ones. + +The sync-versus-async distinction also matters regardless of whether you use the old wizard or a modern REST API. A synchronous call is simpler to read, but it blocks the current flow and demands careful error handling. An asynchronous call is usually the healthier default in mobile UI work because it keeps the application responsive and makes network latency explicit in the code. + +For a current Codename One project, the practical advice is to design a normal server API first, then call it from the client using the networking tools that best match the service. If the service is HTTP-based, prefer the modern REST-oriented client approach. If you are maintaining an older project that already uses the wizard-generated proxies, treat them as an existing contract layer and evolve them carefully rather than rewriting method signatures casually. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [How Do I Use HTTP, Sockets, Webservices And Websockets](/how-do-i/how-do-i-use-http-sockets-webservices-websockets/) +- [Terse REST API](/blog/terse-rest-api/) +- [REST API Design](/courses/course-03-build-real-world-full-stack-mobile-apps-java/rest-api-design/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-create-a-9-piece-image-border.md b/docs/website/content/howdoi/how-do-i-create-a-9-piece-image-border.md index 1b8c2f6d79..7cc8db6adc 100644 --- a/docs/website/content/howdoi/how-do-i-create-a-9-piece-image-border.md +++ b/docs/website/content/howdoi/how-do-i-create-a-9-piece-image-border.md @@ -11,87 +11,28 @@ description: The 9-piece image border is a cornerstone tool for theme design youtube_id: ACsZ8qiwR8Q thumbnail: https://www.codenameone.com/wp-content/uploads/2020/10/Mask-Group-7.png --- +{{< youtube "ACsZ8qiwR8Q" >}} -{{< youtube "ACsZ8qiwR8Q" >}} +Backgrounds and borders in Codename One are easiest to understand once you stop treating them as a pile of theme options and start thinking in terms of rendering precedence. A component does not simply combine every visual setting you give it. Some background choices override others. Borders can override background images. Gradients can override plain background colors. If a component is not rendering the way you expected, the first question is often not "did my value save?" but "which style layer is actually winning?" -#### Transcript +The older video explains this through the designer tool, but the concepts matter just as much when you style with CSS. In modern projects, CSS should usually be the main styling path, while the designer and resource editor are lower-level tools you use when you genuinely need them. The visual rules themselves are still the same: understand the border type, understand the background type, and understand how state-specific styles inherit from one another. -Backgrounds are a pretty complex subject so this won’t be as short as most of these videos. I’ll also focus on the designer tool instead of code but everything I show here can be done using code and the style API. -Lets start with backgrounds what does that actually mean? A component can have one of the following background types: Border, Image, Gradient or Color. Notice that these are exclusive to one another… -If you have a border and a background image the border will take over and the background image will be ignored. If you have a gradient the background color will be ignored. So the order of border, image or gradient and color is significant. It’s also important to notice that you can’t have both an image and a gradient, when we will go over the interface the reason will become clear. +The most important image-border concept is still the 9-piece border. It exists because some shapes need to scale without looking stretched or blurry. Instead of treating a border as one image, you split it into corners, edges, and a center. The corners remain fixed, while the edge and center regions tile or expand as needed. That lets the component grow while preserving the look of the decorative frame. -There are a few types of borders but most of them aren’t used as much, I’ll focus on the top six border types. Notice I’ll go into each of these borders in more details soon… I’ll start with line border which is by far the simplest, just a line surrounding the component. +Cutting a good 9-piece border is mostly about judgment. Keep the corners isolated so they are never tiled. Keep distinctive details such as speech-bubble arrows or ornamental edges out of the tiled regions, otherwise they will smear when the component grows. The old wizard-based workflow still illustrates this well, even if the everyday styling recommendation today is more CSS-first than designer-first. -Underline is a newer border type, in the past we used to advocate using image borders or tiled images for underline but we eventually decided to introduce this type. +State handling matters just as much as the base border. A component rarely has only one appearance. Buttons, for example, usually need unselected, pressed, and selected variants. If you only style the default state, the component may suddenly pick up a strange native border or inconsistent highlight on another state. This is one reason the video spends time on empty borders and inherited styles: clearing or overriding the wrong inherited border can be the difference between a clean result and a confusing one. -9-piece image allows us to use an image cut into 9 pieces 4 images represent the corners and 4 images for the top, bottom, left & right. One additional image for the center. The images in the top, bottom, left, right & center are tiled to allow scaling of the border without degradation. +9-piece borders also interact with device density. If you are using raster imagery, you need to think about how the pieces scale across different densities. Multi-images are still relevant in that sense, but in a modern project the first question should be whether the effect really needs raster artwork at all. If a clean CSS border or a round border can achieve the same visual result, that is often easier to maintain and easier to adapt. -3-piece image borders can be either horizontal or vertical. The horizontal version includes one image for the left one for the center which is tiled and one for the right. The same is true for the vertical one which has top, bottom and center. In both cases only the center image is tiled. This is useful for borders such as the old iOS back button where the arrow image on the left side couldn’t be cut in a reasonable way for a 9-piece border. A 3 piece border can’t scale on the opposite axis so a horizontal border can’t grow vertically and visa versa. +The broader lesson is that visual styling should be deliberate. Use the right border type for the job, style all of the component states that matter, and prefer simpler modern styling mechanisms when they can produce the same effect. 9-piece borders are still useful, but they are no longer the default answer to every styling problem. -A round border can be either a circle like a floating action button in Android or a pill style border like an iOS hint. +## Further Reading -The rounded rectangle border is a square border whose edges are rounded, there are two variations of this border type. - -Lets start with the line border, when we open the designer tool we can pick it from the three dot button. -Notice we can set the color of the border here and it’s separate from the background color or choose the theme color option. - -We can also set the thickness of the border in pixels or in millimeters. - -Underline is practically identical in every way except for the fact that the line is drawn strictly at the bottom of the UI. This is a pretty common use case when separating elements or even for material design style text fields. - -Image borders are comprised of 9 images but making those images is pretty darn hard which is why we created this wizard in the designer too. - -When you launch it you can see some UI that allows you to create an image for the 9 piece border but that’s mostly a legacy UI. - -For most cases people choose to use a file which they usually get from a PSD or similar design source. - -Here I chose a chat bubble element and now I can move to the next step of cutting it. - -We need to switch to the cut image tab where we can see these 4 guides. You can move these 4 guides using the numbers on the bottom left. - -These guides allow us to determine the 9 images that we need in order to build the border. If you look through them you will see 9 images in the cut. Notice several things about the locations of the guides: I kept the corners separate so they won’t be cut or tiled. I kept the arrow outside, otherwise it will look smeared. Notice I kept the two guides very close to one another… Normally that’s not ideal as smaller images reduce performance but since the image has a subtle gradient I didn’t want to impact that. - -Another crucial feature is multi image. 9-Piece images are impacted by image density just like everything else. With regular images we can use multi-image to reduce problems with density and 9-piece borders are no exception. In order to use this you can just specify the density of the source image so in this case I specified that the image above was generated for a "Very High" DPI. You might choose to use HD images when generating a 9-piece border. Then I selected the DPI’s for which I want images generated and each one of the 9-piece images will be generated as a multi image and we’ll have a cool border. - -However, this poses a big problem. Say you ran the 9-piece wizard and got a bad result then you ran it again and again. You can end up with a lot of multi images in the theme file that balloon the size of your app for no real reason. For that we have a special feature in the designer tool called "Delete Unused Images" that you can find in the menu of the designer too. -Once you pick that you will be presented with the images to delete, you can unselect an image using control click and when you press OK everything selected will be deleted. If you have images for use within your application you need to unselect them here as we can’t detect whether you use something in your app. - -Image borders can be customized with the manual UI instead of through the wizard. Each one of these combo boxes allows you to pick an image. In this case it shows the images we cut in the wizard but you can use this manually and set the 9 images yourself. - -As I mentioned before the horizontal image border mode is similar but only accepts 3 images. It doesn’t have a wizard as it’s not as common as the 9-piece image border but it’s not as hard to build manually as it only requires 3 images. - -Similarly the vertical image border requires images on the vertical axis. Both of these are very rarely used. In the past they were common for round borders but now we have a special border type for that. - -Which brings us to round border. Round images can’t be tiled with the 9-piece images as the tiling will create a very ugly effect. -Round borders allow for perfectly round buttons that grow with a fixed radius. -They also allow for pill style borders that are rectangles whose sides are perfect 180 degree arcs. -As you can see in this sample the X button at the top right is the round version of the round rectangle whereas the text fields use the round rectangle "pill" mode. - -Round borders have their own custom UI in the tool It includes many customization options such as color and opacity. You can customize the stroke which is the line around the border… The border can have an optional shadow if you define a shadow opacity at which point you can customize the spread and blur of the shadow. On the bottom you will see the checkbox that toggles rectangle mode which produces the "pill" effect. - -Rounded rectangle is very similar but unlike the round mode it inherits colors from the standard style. It obviously doesn’t have a rectangle mode but it has most of the other options in place and allows us to customize the look of a round rectangle. There is an older version of round rectangle that didn’t have the same level of customizability available to it. - -Empty border sounds redundant but it’s often a crucial feature! As I mentioned before a border overrides other background styles so if the base theme has a border it will override any other setting we make. This is common with Button which usually has a border defined so if you want a different background type it just won’t work. Defining the border to empty solves this common problem. - -Now that we have an empty border we can try setting up other properties the standard background can be a scaled image. In this case it’s the duke logo and you’ll notice that scaled looks really weird in the preview below. - -That’s easily solvable with the next two entries. Image scaled to fill scales the image while maintaining aspect ratio. That means the image is scaled to fill up the entire area and everything that’s outside of the component bounds gets cropped. This is the most common image background style and makes sense for most cases, it looks weird here since the image is square and the preview is a very long rectangle but for most cases this makes sense. - -Scaled to fit scales the image while maintaining the aspect ratio. It does that while fitting the whole image into the component. Notice that this leaves blank areas, in this case on the sides but unless the image is an exact match you will have blank areas that will be filled with the background color. - -You can also tile an image all over the place, this is useful for pattern background and can create a great visual effect that adapts to any resolution. Notice that you shouldn’t use images that are too small with tiling as performance might suffer in such cases. - -You can also tile on an axis with specific alignment so you can tile vertically on the left, right or center. You can also tile horizontally on the center, top or bottom. This is valuable for some nice visual effects but is also useful for cases such as line separators where you can use tiling to separate this component from the next one visually. - -We can also just align the image vertically or horizontally on almost every corner side or to the center. This is useful for some special design decorations, for instance in an app I did for a Yoga Studio I placed a flower in the bottom right corner. The flower remains even when scrolling and creates a nice texture effect. - -You can also use gradients to define a background, the gradients we support are relatively simple and might look a bit different on devices where they are implemented slightly differently. We have three gradient types, linear horizontal, vertical and radial. They are all pretty similar and relatively simple, if you need more complex gradients you might need to use a background image. - -If you use none or don’t define an image for the background you can define the background color. Notice that the background color can have a transparency value associated with it. The transparency value is a number between 0 for completely transparent to 255 for completely opaque background. - -Thanks for watching, I hope you found this helpful. - ---- +- [Themeing](/themeing/) +- [Developer Guide](/developer-guide/) +- [How Do I Create A Simple Theme](/how-do-i/how-do-i-create-a-simple-theme/) +- [Work With Multi Images And Device Densities](/how-do-i/how-do-i-fetch-an-image-from-the-resource-file-add-a-multiimage/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-create-a-basic-hello-world-application-send-it-to-my-device-using-eclipse.md b/docs/website/content/howdoi/how-do-i-create-a-basic-hello-world-application-send-it-to-my-device-using-eclipse.md index 679a221f6c..8207c49d4a 100644 --- a/docs/website/content/howdoi/how-do-i-create-a-basic-hello-world-application-send-it-to-my-device-using-eclipse.md +++ b/docs/website/content/howdoi/how-do-i-create-a-basic-hello-world-application-send-it-to-my-device-using-eclipse.md @@ -11,50 +11,30 @@ description: Getting started guide for Eclipse developers youtube_id: fmNpMFLwABA thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-21.jpg --- - {{< youtube "fmNpMFLwABA" >}} +The first Codename One application you build in Eclipse should be intentionally small. [Create the project with the initializr](/initializr/), import it into Eclipse as a Maven project, and aim for something that starts in the simulator, shows a form, and responds when you press a button. The video is out of date in one important way: it starts from the old Eclipse plugin workflow. The actual lesson is still valid, but the modern setup is Maven-based and does not depend on the legacy IDE plugins. -#### Installing The Eclipse Plugin - -For details please check out the [download section](/download/). - -In this short video we’ll walk you thru the very basics of Codename One. - -Codename One allows Java developers to write native mobile apps for all devices easily. It’s open source, free and works with all IDE’s. - -We can install Codename One by going to the plugin update center, typing in "Codename" and following the installation wizard. -We can now create a new project, we need to select Codename One and once we do so we are faced with the "New Project Dialog". - -In this dialog we must first define the package name, you must pick a correct package name as changing this later is challenging. -Next we can pick one of the builtin themes. If you want something very bare bones go with native, I will use red for this demo. -Last but not least we need to pick the app type, I recommend checking out the getting started app. However, for simplicity’s sake I’m picking the "Bare Bones" hand coded application as it contains the least amount of code. +One of the first choices that matters is the package name. It is easy to treat that as temporary when you are just building a sample app, but package names become part of the app's identity and eventually affect signing, store metadata, and native packaging. Pick a stable reverse-domain package from the beginning so you do not have to rename it later when the project is already in motion. -When we press the finish button the new app is created in the IDE. You will notice two major files, the theme resource file and the main source file. Let’s look at the generated code. -In the main file we have four life cycle methods: init, start, stop and destroy. +Once the project is open, spend a few minutes reading the generated application class. Codename One applications still revolve around `init()`, `start()`, `stop()`, and `destroy()`. `init()` is where one-time setup belongs. `start()` is where you build and show the first UI. `stop()` handles the app moving into the background, and `destroy()` is there for shutdown cleanup. Understanding those lifecycle methods is one of the most useful things a hello world app can teach. -Init is called once per application launch to setup things such as themes. You should use this instead of using the constructor. -Start is invoked when the app is started or restored from minimized state. You use it to show the UI. -Stop is invoked when the app is minimized & destroy might be invoked for a complete exit. +Keep the first screen simple. Create a `Form`, add a button, attach an action listener, and show a dialog when the button is pressed. That tiny exercise proves that the project imports correctly, the application starts, the UI appears, and events are firing. You do not need more than that to confirm that the environment is healthy. -Let’s add a button to the new app: the code is rather trivial. We just add a button object to the parent form. We then bind an action listener to the button and show a dialog within that action logic. -We can now run the simulator in the IDE by pressing the play button, you can manipulate the simulator using the simulate menu. You can switch devices using the skins menu as such. +The simulator should be your primary feedback loop at this stage. Run the app, switch device skins if you want to see how the layout behaves, and make sure the form survives the normal stop-and-start flow. Device builds matter, but they come after the simulator loop is already stable. In practice Android is usually the easiest first target. iOS generally comes later because certificates and provisioning need to be set up before the build becomes useful. -Next we’ll open the designer by double clicking the theme file. The theme allows us to customize the appearance of the application. We can double click any entry or add new entries to customize their look. E.g. we set the button foreground to yellow and we can now rerun the simulator with this result. +Styling and localization are the other places where the old workflow needs translation. The video moves from the basic app into the theme designer and resource editor because that was a common path at the time. For new projects, CSS is the better default for styling and l10n property bundles are the better default for localization. The older tools still work, but they are no longer the path most new apps should start with. -Next we’ll open the designer by double clicking the theme file. The theme allows us to customize the appearance of the application. We can double click any entry or add new entries to customize their look. E.g. we set the button foreground to yellow and we can now rerun the simulator with this result. +That leaves a straightforward modern workflow: generate a Maven project, choose a good package name, understand the lifecycle, build one small interactive form, validate it in the simulator, and then send a device build. Once those basics are in place, you can keep building in normal Java code while using CSS for styling and property bundles for localization. -The designer tool is also used for countless other features, such as: resolution independent images, localization and more! +## Further Reading -The most important thing is running the resulting app on my devices, to do that we right click the project and select send Android build. You will notice there are many other build targets e.g. iOS. etc.). -Once a build is made navigate to the [build server at codenameone.com](/build-server/) and select your build entry. You can then either email the link to yourself using the dedicated button or just scan the QR code in the page. This will allow you to download and install the app to your device. - -Here is actual device footage for the app we just built! - -iOS. apps are slightly more challenging, we need certificates from Apple in order to build a native app. For those you need an Apple developer account, once you have that in order just use the certificate wizard to generate all of the required certificates and you can then follow the exact same procedure used for Android. - -Thanks for watching, please let us know what you think and get help at [codenameone.com](https://www.codenameone.com/). - ---- +- [Initializr](/initializr/) +- [Getting Started](/getting-started/) +- [Hello World](/hello-world/) +- [Development Environment](/development-environment/) +- [Build Server](/build-server/) +- [Themeing](/themeing/) +- [Moving To Maven](/blog/moving-to-maven/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-create-a-basic-hello-world-application-send-it-to-my-device-using-intellij-idea.md b/docs/website/content/howdoi/how-do-i-create-a-basic-hello-world-application-send-it-to-my-device-using-intellij-idea.md index 88b7eb6ee9..0d2413efa2 100644 --- a/docs/website/content/howdoi/how-do-i-create-a-basic-hello-world-application-send-it-to-my-device-using-intellij-idea.md +++ b/docs/website/content/howdoi/how-do-i-create-a-basic-hello-world-application-send-it-to-my-device-using-intellij-idea.md @@ -11,50 +11,31 @@ description: Getting started guide for IntelliJ developers youtube_id: oR3KHYf5OrY thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-1-1.jpg --- +{{< youtube "oR3KHYf5OrY" >}} -{{< youtube "oR3KHYf5OrY" >}} +The easiest way to get started in IntelliJ is to open a Maven-based Codename One project and keep the first version of the app very small. [Create the project with the initializr](/initializr/), import it into IntelliJ, and aim for something that starts in the simulator without surprises and does one obvious thing when you press a button. The video demonstrates the same first milestone, but it gets there through the old IDE plugin flow, which is no longer the recommended setup. That small app is enough to prove that the environment is working and to give you a solid base for the next step. -#### Transcript +The first decision worth making carefully is the package name. In Codename One it eventually shows up in builds, signing, store metadata, and native packaging, so it is worth choosing a stable reverse-domain package from the beginning. Renaming a package later is possible, but once a project has started to accumulate build configuration and native artifacts it becomes much more annoying than it looks. -For details please check out the [download section](/download/). +Once the project is open, the generated application class is the most important thing to understand. Codename One applications still revolve around `init()`, `start()`, `stop()`, and `destroy()`. `init()` is where one-time application setup belongs. `start()` is where you create or show the first screen. `stop()` is called when the app is backgrounded, and `destroy()` is reserved for final cleanup when the application is really shutting down. If you understand those four methods early, the rest of the framework becomes much easier to reason about. -In this short video we’ll walk you thru the very basics of Codename One. +For a first app, create a single form and add one interactive component to it. A button that opens a dialog is enough. That tiny exercise proves several important things at once: the app started successfully, the form was shown from `start()`, components were added correctly, and events are firing as expected. In practice that is much more valuable than jumping immediately into themes, navigation frameworks, or platform integration. -Codename One allows Java developers to write native mobile apps for all devices easily. It’s open source, free and works with all IDE’s. +The simulator is still the fastest place to do this first pass. Run the app, press the button, switch simulator skins if you want to see how the screen behaves on different devices, and make sure the application survives stop-and-start cycles. You do not need to solve device-specific issues before you even know that the application works locally. Once the simulator loop is stable, send a native build. Android is usually the easiest first target. iOS still requires signing and provisioning, so it is normal to validate Android first and then handle Apple certificates afterwards. -We can install Codename One by going to the plugin update center, typing in "Codename" and following the installation wizard. -We can now create a new project, we need to select Codename One and once we do so we are faced with the "New Project Dialog". +One part of the video is clearly dated. It moves from the generated app into the older theme designer and resource-editor workflow. For a new project, the better path is to style with CSS and handle localization with l10n property bundles. The designer and resource editor still work, but they are awkward enough that they should not be the default recommendation for new development. -In this dialog we must first define the package name, you must pick a correct package name as changing this later is challenging. -Next we can pick one of the builtin themes. If you want something very bare bones go with native, I will use red for this demo. -Last but not least we need to pick the app type, I recommend checking out the getting started app. However, for simplicity’s sake I’m picking the "Bare Bones" hand coded application as it contains the least amount of code. +So the practical workflow is straightforward: start with a Maven project, understand the lifecycle, build one small interactive form, validate it in the simulator, and then send a device build. After that, keep the app logic in code, move styling into CSS, and use property bundles when you add localization. -When we press the finish button the new app is created in the IDE. You will notice two major files, the theme resource file and the main source file. Let’s look at the generated code. -In the main file we have four life cycle methods: init, start, stop and destroy. +## Further Reading -Init is called once per application launch to setup things such as themes. You should use this instead of using the constructor. -Start is invoked when the app is started or restored from minimized state. You use it to show the UI. -Stop is invoked when the app is minimized & destroy might be invoked for a complete exit. - -Let’s add a button to the new app: the code is rather trivial. We just add a button object to the parent form. We then bind an action listener to the button and show a dialog within that action logic. -We can now run the simulator in the IDE by pressing the play button, you can manipulate the simulator using the simulate menu. You can switch devices using the skins menu as such. - -Next we’ll open the designer by double clicking the theme file. The theme allows us to customize the appearance of the application. We can double click any entry or add new entries to customize their look. E.g. we set the button foreground to yellow and we can now rerun the simulator with this result. - -Next we’ll open the designer by double clicking the theme file. The theme allows us to customize the appearance of the application. We can double click any entry or add new entries to customize their look. E.g. we set the button foreground to yellow and we can now rerun the simulator with this result. - -The designer tool is also used for countless other features, such as: resolution independent images, localization and more! - -The most important thing is running the resulting app on my devices, to do that we right click the project and select send Android build. You will notice there are many other build targets e.g. iOS. etc.). -Once a build is made navigate to the [build server at codenameone.com](/build-server/) and select your build entry. You can then either email the link to yourself using the dedicated button or just scan the QR code in the page. This will allow you to download and install the app to your device. - -Here is actual device footage for the app we just built! - -iOS. apps are slightly more challenging, we need certificates from Apple in order to build a native app. For those you need an Apple developer account, once you have that in order just use the certificate wizard to generate all of the required certificates and you can then follow the exact same procedure used for Android. - -Thanks for watching, please let us know what you think and get help at [codenameone.com](https://www.codenameone.com/). - ---- +- [Getting Started](/getting-started/) +- [Development Environment](/development-environment/) +- [Hello World](/hello-world/) +- [Build Server](/build-server/) +- [Themeing](/themeing/) +- [Create an iOS Provisioning Profile](/create-an-ios-provisioning-profile/) +- [Moving To Maven](/blog/moving-to-maven/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-create-a-basic-hello-world-application-send-it-to-my-device-using-netbeans.md b/docs/website/content/howdoi/how-do-i-create-a-basic-hello-world-application-send-it-to-my-device-using-netbeans.md index 0feb4624c8..d92273e597 100644 --- a/docs/website/content/howdoi/how-do-i-create-a-basic-hello-world-application-send-it-to-my-device-using-netbeans.md +++ b/docs/website/content/howdoi/how-do-i-create-a-basic-hello-world-application-send-it-to-my-device-using-netbeans.md @@ -12,53 +12,30 @@ description: Getting started guide for NetBeans developers youtube_id: 73d65cvyQv4 thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-21.jpg --- - {{< youtube "73d65cvyQv4" >}} +The first Codename One application you build in NetBeans should be deliberately simple. [Create the project with the initializr](/initializr/), open it in NetBeans as a Maven project, and aim for an app that starts in the simulator, shows a form, and responds to one button press. The video uses the old NetBeans plugin flow, which is no longer the recommended way to start a project, but the underlying lesson is still the same: begin with a tiny app that teaches you the lifecycle and the development loop. -#### Basic introductory usage of Codename One - -For details please check out the [download section](/download/). **Note:** The [plugins.netbeans.org](http://plugins.netbeans.org/) server has been down frequently in the past couple of months preventing automatic installation. -Please follow the instructions [here](/blog/netbeans-plugin-update-center/) as a workaround if the instructions above don’t work. - -###### Transcript - -In this short video we’ll walk you thru the very basics of Codename One. - -Codename One allows Java developers to write native mobile apps for all devices easily. It’s open source, free and works with all IDE’s. - -We can install Codename One by going to the plugin update center, typing in "Codename" and following the installation wizard. -We can now create a new project, we need to select Codename One and once we do so we are faced with the "New Project Dialog". +The package name is one of the first things worth deciding carefully. It may look unimportant in a hello world app, but later it becomes part of signing, native packaging, and store submission. A stable reverse-domain package name will save you from an annoying rename once the project begins to grow. -In this dialog we must first define the package name, you must pick a correct package name as changing this later is challenging. -Next we can pick one of the builtin themes. If you want something very bare bones go with native, I will use red for this demo. -Last but not least we need to pick the app type, I recommend checking out the getting started app. However, for simplicity’s sake I’m picking the "Bare Bones" hand coded application as it contains the least amount of code. +After the project opens, look at the generated application class before making larger changes. `init()` is for one-time application setup, `start()` is where you create and show the first form, `stop()` handles the app moving to the background, and `destroy()` is there for shutdown cleanup. Those methods are the backbone of a Codename One app, and understanding them early makes the rest of the framework much easier to follow. -When we press the finish button the new app is created in the IDE. You will notice two major files, the theme resource file and the main source file. Let’s look at the generated code. -In the main file we have four life cycle methods: init, start, stop and destroy. +For the actual hello world UI, keep it boring on purpose. Create a form, add a button, and have the button show a dialog. That is enough to prove that the project opens correctly, the lifecycle is working, the UI renders, and action listeners are firing. You do not need complex navigation or custom components to learn the right first lessons. -Init is called once per application launch to setup things such as themes. You should use this instead of using the constructor. -Start is invoked when the app is started or restored from minimized state. You use it to show the UI. -Stop is invoked when the app is minimized & destroy might be invoked for a complete exit. +The simulator is still the fastest place to validate this work. Run the app, switch device skins if needed, and make sure the form behaves the way you expect. Once that loop is stable, send a native build. Android is usually the easiest first target. iOS is still more demanding because of certificates and provisioning, so it is normal to get Android working first and then return to Apple signing later. -Let’s add a button to the new app: the code is rather trivial. We just add a button object to the parent form. We then bind an action listener to the button and show a dialog within that action logic. -We can now run the simulator in the IDE by pressing the play button, you can manipulate the simulator using the simulate menu. You can switch devices using the skins menu as such. +One part of the video does need an explicit update. It moves naturally from the generated app into the older theme designer and resource editor. For new projects, CSS is now the better default for styling and l10n property bundles are the better default for localization. The designer and resource editor are still supported, but they are not where most modern Codename One projects should start. -Next we’ll open the designer by double clicking the theme file. The theme allows us to customize the appearance of the application. We can double click any entry or add new entries to customize their look. E.g. we set the button foreground to yellow and we can now rerun the simulator with this result. +So the modern NetBeans version of this lesson is straightforward: start from the initializr, import the Maven project, understand the lifecycle, build a tiny interactive form, verify it in the simulator, then send a device build. From there, keep the app logic in code, style with CSS, and use property bundles when you localize. -Next we’ll open the designer by double clicking the theme file. The theme allows us to customize the appearance of the application. We can double click any entry or add new entries to customize their look. E.g. we set the button foreground to yellow and we can now rerun the simulator with this result. +## Further Reading -The designer tool is also used for countless other features, such as: resolution independent images, localization and more! - -The most important thing is running the resulting app on my devices, to do that we right click the project and select send Android build. You will notice there are many other build targets e.g. iOS. etc.). -Once a build is made navigate to the [build server at codenameone.com](/build-server/) and select your build entry. You can then either email the link to yourself using the dedicated button or just scan the QR code in the page. This will allow you to download and install the app to your device. - -Here is actual device footage for the app we just built! - -iOS. apps are slightly more challenging, we need certificates from Apple in order to build a native app. For those you need an Apple developer account, once you have that in order just use the certificate wizard to generate all of the required certificates and you can then follow the exact same procedure used for Android. - -Thanks for watching, please let us know what you think and get help at [codenameone.com](https://www.codenameone.com/). - ---- +- [Initializr](/initializr/) +- [Getting Started](/getting-started/) +- [Hello World](/hello-world/) +- [Development Environment](/development-environment/) +- [Build Server](/build-server/) +- [Themeing](/themeing/) +- [Moving To Maven](/blog/moving-to-maven/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-create-a-list-of-items-the-easy-way.md b/docs/website/content/howdoi/how-do-i-create-a-list-of-items-the-easy-way.md index 9b846fc800..85349fe554 100644 --- a/docs/website/content/howdoi/how-do-i-create-a-list-of-items-the-easy-way.md +++ b/docs/website/content/howdoi/how-do-i-create-a-list-of-items-the-easy-way.md @@ -11,61 +11,28 @@ description: Infinite lists of items are powerful tools youtube_id: 0m7Bay4g93k thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-11-1.jpg --- +{{< youtube "0m7Bay4g93k" >}} -{{< youtube "0m7Bay4g93k" >}} +If you need to show a vertical list of items in Codename One, the first question is not "how do I use `List`?" It is "what kind of scrolling UI am I actually building?" The video makes a point that still matters: for many ordinary mobile screens, a vertically stacked container of components is easier to reason about than the older `List` API. -#### Transcript +A simple and effective pattern is to use a container with `BoxLayout.y()` and place it in a scrollable area. That gives you full control over each row. You can use `MultiButton`, your own custom components, or any other component structure that fits the design. For small and medium-sized data sets, this is often the cleanest way to build a list-like screen. -In this short video I’ll review the basics of creating a list of items in Codename One. But there is a catch, Codename One has a List class and we won’t be using it. The list class is designed for a very elaborate type of list that isn’t as useful for common day devices. Unless you have a very extreme and specific use case you probably shouldn’t use it as it is slower & harder to use than the alternative! +The point where this changes is scale. If the data set is large, you should not create hundreds or thousands of row components eagerly. That is where `InfiniteContainer` and related lazy-loading patterns become valuable. Instead of building the entire list at once, you fetch components in batches as the user scrolls. The video uses contacts as an example, and that is still a good way to think about it: load enough to keep the UI responsive, then fetch more as needed. -Instead we’ll use the box layout on the Y axis and add components to it. I’ll focus on multi-button as this is a very common component for this type of use but you can add any custom component you want. +Scrolling behavior is part of the design, not an afterthought. A scrollable list container needs room to stretch properly, which is why placing it in the `CENTER` of a `BorderLayout` is such a common pattern. Nested scrolling should also be treated carefully, because touch interfaces are much harder to use when multiple scrollable areas compete for the same gesture. -In larger sets we can use infinite container or infinite scroll adapter. This allows us to fetch elements dynamically and avoid the overhead of creating hundreds or thousands of components at once. +Lazy-loading also changes how you think about per-row work. If an item depends on expensive data such as an image, do not block the row creation path longer than necessary. Show a placeholder, let the row appear quickly, and then fill in the heavier data when the application has time. The older example uses contact images and idle callbacks to illustrate this, and the underlying lesson is still sound: rows should become visible fast, then improve gracefully as more data arrives. -Lets start with a hello world for a box layout list. This is pretty standard Codename One code, lets go over the different pieces. +Search fits naturally into this model when you treat the visible data as a view over a larger data set. Change the filter, refresh the list, and let the fetch logic rebuild the visible subset instead of trying to mutate dozens of existing components manually. That keeps the mental model simple and scales better as the screen evolves. -First we create the list container, notice we set it to scroll on the Y axis. This allows us to scroll through the list which is crucial. Notice that by default a Form is already scrollable on the Y axis but I’ve set the layout to border layout which implicitly disables scrolling. It’s important that scrolling shouldn’t "nest" as it’s impossible to pick the right scrollbar with a touch interface. +The main thing that changed since the video is not the underlying idea but the broader UI workflow around it. In modern projects, the structure of the row still belongs in code and layout, but much of the visual styling should be pushed into CSS. That keeps complex list screens easier to maintain as they grow. -In this case I just added a thousand entries to the list one by one. The list is relatively simple with no actual functionality other than counting the entries. +## Further Reading -Notice I place the list in the CENTER of the border layout. This is crucial as center in the border layout stretches the container to fit the screen exactly. -This is the resulting list that can be scrolled up and down by swiping. - -That’s all good and well but in the real world we need lists to be more dynamic than that. We need to fetch data in batches. That’s why we have the infinite container class which allows us to fetch components as the user scrolls through the list. This is the exact same code from before but it creates the list entries in batches instead of as a single block. -The `fetchComponents` method is invoked with an index in the list and the amount of elements to return. It then creates and returns those elements. This implicitly adds the elements as we scroll and implements the pull to refresh functionality. - -This might be obvious but just in case you don’t know you can set an icon for every entry and just add an action listener to get events for a specific entry in the list. This is very convenient. -This is relatively simple in terms of design, you can check out some of the more elaborate design of a list we have in the kitchen sink demo. - -Up until now we did simple demos, this is a screenshot of a contacts list from my device using this sort of API and it was generated with this code. Notice I blurred a few entries since these are my actual phone contacts and I’d like to keep their privacy… This is done with the code here, let’s go over it. - -First we need a placeholder image for the common case where a contact doesn’t have a profile picture or when we are still loading the profile picture. - -When we are in the first element which will happen when the form loads or after a "pull to refresh" I load the contacts. Notice I could have used if contacts equals null but that would have done nothing in the case of pull to refresh as contacts would have been initialized already. By checking against zero I implicitly support the pull to refresh behavior which just calls the fetch method over again. -The contacts API can be a bit slow sometimes which is why you shouldn’t fetch "everything" with one request. That’s why the method accepts all of these boolean values to indicate what we need from the contact itself. Setting all of these to true will slow you down significantly so you should generally load just what you need which in this case is contacts with a phone number and full name. - -The infinite container has no idea how many elements we might have. So we need to check if the amount of elements requested exceeds the total and if the index is out of bounds. If the former is true we need to reduce the amount and return a smaller array. If the latter is true we need to return null which will stop future calls to fetch components unless pull to refresh is triggered again. - -The rest is pretty close to the code we had before where we loop and create multi buttons but in this case we just fill them up with the content details and the placeholder image. - -However, you might recall we didn’t fetch the image for the contact and that might be pretty expensive to load… So the trick is to call this method on a button by button case where we fetch ONLY the image but we don’t just invoke that as it would kill performance. -For this we use the new `callSeriallyOnIdle()` method. This method works like `callSerially` by performing the code in the next event dispatch thread cycle. However, in this case the code will only occur when the phone is idle and there are no other urgent events. - -So if we are in idle state we can just ask for the contacts image using the specific API then fill up the UI. Since this is an infinite list this will only be invoked for the "amount" number of entries per cycle and that means it should be reasonably efficient. - -Moving to the next page we can see that not much is left, we just return the array and add the list to the center. -I didn’t spend much time on refinement and some of the nicer effects you can achieve but you can check out the kitchen sink demo where the contacts section features a swipe UI with many special effects such as generated icons per letter. - -Adding search to the infinite list is pretty easy. -We need a search string variable that is both modifiable and accessible in the inner class so I added a member to the parent class. - -The rest of the code is identical, I just filter the basic contacts entry. A change in search will refresh the list and invoke fetch components for index zero. If in this case I have a search filter I can loop over the contacts and filter them into a new array list. I can then create a new smaller contacts array that matches the search. - -The search string can be bound to the variable using the toolbar search API that’s builtin. The call to refresh has a similar effect to pull to refresh. It allows me to filter the list dynamically and efficiently. - -Thanks for watching, I hope you found this helpful. - ---- +- [Layout Basics](/layout-basics/) +- [Developer Guide](/developer-guide/) +- [How Do I Positioning Components Using Layout Managers](/how-do-i/how-do-i-positioning-components-using-layout-managers/) +- [How Do I Improve Application Performance Or Track Down Performance Issues](/how-do-i/how-do-i-improve-application-performance-or-track-down-performance-issues/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-create-a-simple-theme.md b/docs/website/content/howdoi/how-do-i-create-a-simple-theme.md index ab32d638a9..4623762e82 100644 --- a/docs/website/content/howdoi/how-do-i-create-a-simple-theme.md +++ b/docs/website/content/howdoi/how-do-i-create-a-simple-theme.md @@ -12,12 +12,32 @@ description: Create or customize a theme for your application youtube_id: cxllJwt10VU thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-2-1.jpg --- - {{< youtube "cxllJwt10VU" >}} +Styling in Codename One starts with a simple question: are you trying to change how components look, or are you trying to change how the UI behaves structurally? If the answer is visual styling, the modern default is CSS. The video uses the older designer-centered theme workflow, and that still helps explain the underlying concepts, but for a new project you should usually start with CSS and treat the older theme editor as a lower-level tool rather than the main path. -## +The concepts behind theming are still the same. Components get their appearance from a UIID. A UIID is effectively the name of the style a component uses. If you create a button and give it a custom UIID, that UIID becomes the hook for styling the button consistently. This is true whether the style is defined in CSS or in the older theme resource workflow. ---- +The simplest useful example is a custom button. Give it a background color, a foreground color, some padding, and a readable font size. The point is not to make it beautiful on the first pass. The point is to understand which properties actually shape the component. Background and foreground colors control the obvious look, but spacing is just as important. Padding affects the space inside the component and therefore the touchable area. Margin affects the space outside the component and therefore the relationship between neighboring components. If a button feels cramped, that is often a spacing problem before it is a color problem. + +Portable sizing still matters here. The old advice to think in physical units rather than raw pixels is still sound. On touch devices, spacing and font sizes need to survive different densities and form factors. That is one reason the visual result can look fine on one platform and wrong on another if you only style the default state and ignore the rest. + +State-specific styling is one of the first places where theming becomes real. A button usually needs at least an unselected appearance and a pressed or selected appearance. If the normal state has one background and the pressed state has no explicit styling, the result can feel inconsistent or broken. The video demonstrates this with borders and selected styles, and the underlying lesson remains important: style all of the states that matter, not just the first one you see in the simulator. + +Borders are another common source of confusion. In the older theme editor, borders often take precedence over background settings, which is why a component can look correct on one platform but unexpectedly wrong on another. The same general rule applies conceptually even when you work in CSS: understand which visual property is actually winning. If a component is not rendering the way you expected, check the full style, not just the one value you most recently changed. + +The other important concept from the video is inheritance. A custom style does not need to redefine everything from scratch. It is usually better to start from a base component style and override only what you actually want to change. That keeps your styles smaller and more maintainable. In practice this means defining a custom UIID that keeps the core behavior of a `Button` or `TextField` while changing color, spacing, fonts, or borders. + +Once that pattern clicks, the rest of theming becomes much easier. You can create a second button style, apply the same UIID to a different component where appropriate, and reason about the result as a consistent design system instead of one-off tweaks. You can also change style values in code, but that is generally the exception rather than the default. For most visual work, CSS is cleaner and easier to maintain. + +Theme constants and other lower-level theme settings still matter, especially when you are controlling broader application behavior or integrating with older theme-based assets. But for a new project, the practical approach is simpler than the older lesson suggests: use layouts to define structure, use CSS to define appearance, and only drop to the older theme tooling when you genuinely need the lower-level control. + +## Further Reading + +- [Themeing](/themeing/) +- [Developer Guide](/developer-guide/) +- [Layout Basics](/layout-basics/) +- [Designer](/designer/) +- [Hello World](/hello-world/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-create-an-ios-provisioning-profile.md b/docs/website/content/howdoi/how-do-i-create-an-ios-provisioning-profile.md index 5a832342b5..17a6225f98 100644 --- a/docs/website/content/howdoi/how-do-i-create-an-ios-provisioning-profile.md +++ b/docs/website/content/howdoi/how-do-i-create-an-ios-provisioning-profile.md @@ -12,10 +12,26 @@ description: Signing & provisioning on iOS is a bit painful, hopefully this will youtube_id: OWHizrNyizQ thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-24.jpg --- - {{< youtube "OWHizrNyizQ" >}} ---- +Creating an iOS provisioning profile is really about assembling a matching set of Apple-side credentials and identifiers so that the app you build, the devices you test on, and the certificate you sign with all agree with each other. If any one of those pieces is misaligned, the build may still complete, but installation or submission will fail later. + +The first step is to make sure the app identifier is correct. In Apple terms, the App ID needs to match the package identifier of your Codename One application. This is one reason package naming matters so much early in a project. Once provisioning, signing, and store submission are involved, renaming is much more annoying than it looks. + +From there, development and distribution provisioning split into two different jobs. Development provisioning is for testing on specific physical devices. That means the devices must be registered with the Apple developer account, and the development profile must explicitly include them. Distribution provisioning is for App Store delivery and does not include test devices in the same way. + +Certificates are the next piece. You need the right development and distribution signing certificates, and you need them exported in a form the build process can use. The old video explains this through Apple’s portal and the Keychain export flow, and that basic idea is still correct even though Apple’s UI changes over time. The stable rule is simple: the certificate, the provisioning profile, and the app identifier must all refer to the same app and account context. + +Once those files exist, Codename One needs to know where they are and what passwords protect them. That is the bridge between the Apple-side account setup and the actual build. If the certificates import correctly and the provisioning profile matches the project, sending an iOS build becomes routine. If they do not, the error usually appears later in a frustrating way, which is why getting the identity chain right up front matters so much. + +The video is useful as a picture of the overall flow, but the modern thing to remember is that Apple’s portal screens change while the underlying relationships do not. You always need the same core pieces: a matching app ID, development devices for local testing, development and distribution profiles, and exportable certificates that line up with them. + +## Further Reading + +- [Build Server](/build-server/) +- [Development Environment](/development-environment/) +- [Hello World](/hello-world/) +- [Developer Guide](/developer-guide/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-create-gorgeous-sidemenu.md b/docs/website/content/howdoi/how-do-i-create-gorgeous-sidemenu.md index 6a3bc46731..e7f7872884 100644 --- a/docs/website/content/howdoi/how-do-i-create-gorgeous-sidemenu.md +++ b/docs/website/content/howdoi/how-do-i-create-gorgeous-sidemenu.md @@ -12,55 +12,26 @@ description: Great UI needs great navigational interface youtube_id: 99DAeP9LG6c thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-8-1.jpg --- +{{< youtube "99DAeP9LG6c" >}} -{{< youtube "99DAeP9LG6c" >}} +A good side menu does two jobs at once. It gives the user a clear navigation structure, and it makes that structure feel intentional rather than bolted on. The old video builds this through the toolbar side-menu APIs and then styles the result in the theme designer. The basic navigation idea still works, but the modern styling path should usually be CSS rather than designer-driven theme editing. -#### Transcript +The first step is structural rather than visual. Define the side-menu commands you actually need, give them clear labels, and choose icons that reinforce the meaning instead of decorating it. A side menu becomes cluttered quickly when it is used as a dumping ground for everything that does not fit elsewhere in the UI. -Hello! -In this short video I’m going to walk you thru the steps of creating a gorgeous side menu user interface. -We’ll start by creating a simple hello world style application with no frills. I’m using a native theme and a bare bones application so nothing pre-existing will disturb us. +Once the commands exist, the real visual work is about hierarchy and spacing. The menu background, the command row style, the selected state, and the optional header area all need to feel like part of the same visual system. The video spends time on padding, alignment, color, and pressed-state styling, and those are still the right levers to care about. Touch targets need enough padding to feel comfortable, selected states need to be obvious, and typography needs to be readable before it tries to be clever. -As you can see there isn’t much here. We’ll add new entries into the toolbar for the different pieces of the side menu. -Now I’m going to use add material command to the side menu which adds command text and an icon from the standard material design icons built into Codename One. That will give us something to work with, it’s mostly basic stuff you can use whatever you want in terms of commands. The styling isn’t mentioned it’s only the icons and the text. -Now we’ll open the theme file and try to make the styling "look good". First I’ll open the side menu in the preview section. This makes the entries for side component and side panel appear in the combo box. +The older workflow styles these pieces in the theme editor with UIIDs such as the side command and the side panel. In a new project, those same ideas should usually be expressed in CSS. That gives you a more maintainable styling workflow and makes it easier to keep the side menu aligned with the rest of the application theme. -I’ll start with the side navigation panel which is pretty easy, I’m using a white background that’s completely opaque. I press OK to save this. +The header area at the top of the menu is often what separates an ordinary side menu from one that feels designed. A logo, profile image, app title, or short tagline can make the menu feel anchored instead of generic. The video demonstrates this by building a top section from ordinary components and then adding it to the toolbar side menu. That is still the right way to think about it: the decorative header is just another piece of UI composition, not a special magic feature. -For the side command I want to pick an opaque color for the background and white for the foreground. I’m picking a bluish/purple color that looks right usually I pick from a pallet but here I whinged it and I think it came out reasonably well. +The main modern caution is not to over-invest in ornament if the menu is no longer the primary navigation pattern for the app. Some applications are now better served by bottom navigation, tabs, or a simpler toolbar structure. A side menu is still useful, but it should be chosen because it fits the navigation model, not because it was once the default mobile pattern. -I’m making sure alignment is left just to be sure, for padding I’m setting it all to be relatively large. 3 millimeters which gives everything a good feel and spacing. This is important for finger touch sensitivity. +## Further Reading -I’m setting margin to 0 except for the bottom one pixel which will leave a nice white line by showing off the background. I’m also setting the border to empty so it isn’t inherited from the native theme like it is on iOS. - -I’m picking a good looking font making sure it is large enough I’m using millimeters so it’s sized correctly for all OS’s and I’m overriding the derived text decoration which has a value in the iOS native theme so it can impact the final look. - -In the selected tab I’m adding a new side command entry that derives from the unselected version. I’m picking a new color that’s slightly deeper and will make the selected style appear selected. I’m copying and pasting this to the pressed style as well. - -Clicking the theme again allows me to refresh it and see how everything will look on the simulator when we’re done. - -Next I’ll add the icon image into the theme as a multi image so I can use it within the side menu as a good looking logo. - -Back in the IDE I get the image I just added into the theme. I then place this image into a Container that goes into the top of the side menu. I do that by creating a border layout container, I place the image label on the east side thus aligning it to the right side of the container. - -I then add a new tagline label to the south of the container and give it a tagline style so we can customize its look. - -The side menu tagline is just a side command style that was slightly adapted, I’m removing the padding and margin because the whole section will be wrapped in a side command. - -The next thing I’ll do is update the font to a smaller size and italic styling so it will feel like a tagline. - -Now that we did all this lets go back into the IDE and tie this together with the top bar which now we can use as a side command UIID by giving it the same color and padding as regular commands. - -We tie the whole thing together by adding the component, the top bar to the toolbar side menu. - -Finally I’m styling the status bar side menu which is the spacing on top left for iOS devices. I’m setting it to 0 and now we’re ready to run the app! - -This is the final result! This is how the side menu looks. -You can use a better icon or logo which will make this look much better. You can use a background image for the top bar and these small things can make a huge difference. - -Thanks for watching, I hope you found this useful. - ---- +- [Themeing](/themeing/) +- [Developer Guide](/developer-guide/) +- [Layout Basics](/layout-basics/) +- [How Do I Create A Simple Theme](/how-do-i/how-do-i-create-a-simple-theme/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-debug-into-codename-one-source-modify-it-contribute-it-back.md b/docs/website/content/howdoi/how-do-i-debug-into-codename-one-source-modify-it-contribute-it-back.md index 81a8d3d44b..310af0e4c4 100644 --- a/docs/website/content/howdoi/how-do-i-debug-into-codename-one-source-modify-it-contribute-it-back.md +++ b/docs/website/content/howdoi/how-do-i-debug-into-codename-one-source-modify-it-contribute-it-back.md @@ -11,10 +11,29 @@ description: Use the sources on github, to improve your app, learn & contribute youtube_id: 2nD75pODPWk thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-22.jpg --- - {{< youtube "2nD75pODPWk" >}} +Debugging into Codename One source is one of the most useful ways to understand why a framework-level behavior is happening. It lets you answer questions that are hard to resolve from application code alone: is the issue in your usage, in the framework, in a specific port, or in a recent Codename One change? ---- +The basic workflow is to work against the actual Codename One source tree instead of only depending on prebuilt jars. Once the relevant projects are checked out and wired into your IDE, stepping into a framework class takes you to real source that you can read, debug, modify, and test immediately. That turns Codename One from a black box into an ordinary codebase you can inspect like any other dependency under development. + +The old video is IDE-specific and reflects an older project setup, but the important lesson is unchanged: if you want to debug framework behavior seriously, you need the source projects in your workspace and you need your application to resolve against them instead of the packaged binaries. + +This is valuable even if you never plan to contribute a patch. Walking into framework code while the debugger is live is often the fastest way to understand why a UI behaves the way it does, why a property is ignored, or why a specific port diverges from another. Reading the code is useful; reading it while stopped at the relevant call site is better. + +Once you are set up this way, local experimentation becomes much easier. You can make a framework change, rerun the app, and immediately verify whether the change solves the problem. That is often faster than trying to infer the right fix abstractly. The video demonstrates this by adding a small utility method and pushing it through a fork, but the broader point is that Codename One can be debugged and modified with the same workflows you would use for any normal open source Java project. + +If you do decide to contribute a fix, keep the standard open source rules in mind. Make sure you own the code you are submitting, preserve the project's legal and coding conventions, and keep the change focused. Small, clear contributions are easier to review and easier to merge than wide changes that mix refactoring, cleanup, and new behavior all at once. + +It is also smart to discuss non-trivial changes before investing heavily in them. If a proposed fix changes behavior or introduces a new API, a short discussion first can save a lot of time and reduce the chance that the patch heads in the wrong direction. + +The real educational value here is that the framework source is not off-limits. If something in Codename One is confusing, stepping into it is often the right move. Even if the final outcome is not a pull request, understanding the code path usually improves how you structure your own application and how you diagnose similar issues later. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [How Do I Find Problems In My Application Using The Codename One Tools And The Standard IDE Tools?](/how-do-i/how-do-i-find-problems-in-my-application-using-the-codename-one-tools-and-the-standard-ide-tools/) +- [How Do I Debug On An Android Device](/how-do-i/how-do-i-debug-on-an-android-device/) +- [How Do I Use The Include Sources Feature To Debug The Native Code On iOS/Android Etc.](/how-do-i/how-do-i-use-the-include-sources-feature-to-debug-the-native-code-on-iosandroid-etc/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-debug-on-an-android-device.md b/docs/website/content/howdoi/how-do-i-debug-on-an-android-device.md index ec8a3adc1d..8ee7df52f0 100644 --- a/docs/website/content/howdoi/how-do-i-debug-on-an-android-device.md +++ b/docs/website/content/howdoi/how-do-i-debug-on-an-android-device.md @@ -11,29 +11,27 @@ description: Debugging on a device using the Android Studio IDE youtube_id: 008AK1GfHA8 thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-14.jpg --- +{{< youtube "008AK1GfHA8" >}} -{{< youtube "008AK1GfHA8" >}} +Debugging on an Android device is the right move when the simulator is no longer telling you enough. If the problem only happens on Android hardware, only appears after a native build, involves Android permissions or activities, or touches native integration code, then you need visibility into the generated Android project and the device runtime. -#### Transcript +The video shows the older version of this process: enable source inclusion, send an Android build, download the generated sources, create an Android Studio project, copy the generated code into it, and debug from there. The important idea is that when you need to understand what the Android side is doing, the generated native sources are valuable. What has changed is how often you should do this. It is no longer something you should think of as part of normal day-to-day development. -In this short video I’ll try to explain how to debug a Codename One application on an Android device. -This video assumes you are familiar with the basics of Codename One and have Android studio installed. +In a modern Codename One project, the application source in your main project is still the source of truth. Start there first. If the problem reproduces in the simulator, stay in the regular Codename One codebase and debug it there. If the bug only appears on Android, then generate the Android sources with source inclusion enabled and use Android Studio to inspect what the native output is doing. -We start by opening the settings selecting the basic section and checking the "include source" checkbox. Now we can send a build as usual to Android. -In the build server results you will see the additional sources file which I will download. +The useful workflow is to treat the generated Android project as a debugging artifact. Open it in Android Studio, connect a device, run under the debugger, inspect logcat, and place breakpoints in the generated sources or any native bridge code that is relevant to the problem. This is especially useful for permission issues, manifest problems, native interface behavior, packaging problems, and crashes that only occur on device. Once you understand the failure, move the real fix back into your Codename One application, native interface implementation, or build configuration. -Next I’ll launch the Android studio IDE and proceed to create a new project. Within this project I’m pasting in the main class and package names from Codename One. I will leave the rest as the default in this next step and in the final step of the wizard I will select "no activity" as we already have everything. +That last step is the important one. You should not maintain fixes directly in the generated Android sources because those files are regenerated on the next build. The generated project is there so you can observe and diagnose. The durable fix belongs in the actual project that produced it. -Now that a project is created I’ll unzip the source files. I’ll then copy all the relevant files to the newly created project. -I copy the main directory from within the src to the target src & I select to replace all the files. Next I copy the libs directory content to the equivalent directory in the native project. Finally I open the project gradle file as well as the source gradle file. I copy the dependencies from the source gradle to the app gradle dependencies section. +The video is also a bit out of date in how manual the Android Studio setup is. Today the surrounding toolchain is cleaner and more Maven-centric, but the same principle applies: include sources when you need deeper visibility, inspect the generated Android project only for Android-specific failures, and then carry the solution back to the real codebase. If you approach it that way, Android Studio becomes a precise debugging tool instead of a second development environment you have to keep in sync. -Some additional copying of gradle script snippets might be required based on your app! +## Further Reading -Next we need to connect our device to the computer and press the debug button. After waiting for a long time the app will appear on the device. - -Thanks for watching, please let us know what you think and get help at [codenameone.com](https://www.codenameone.com/). - ---- +- [Introduction for Android Developers](/introduction-for-android-developers/) +- [Build Server](/build-server/) +- [Build Tools](/build-tools/) +- [Development Environment](/development-environment/) +- [Moving To Maven](/blog/moving-to-maven/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-fetch-an-image-from-the-resource-file-add-a-multiimage.md b/docs/website/content/howdoi/how-do-i-fetch-an-image-from-the-resource-file-add-a-multiimage.md index 0ced524a00..735fccbd0c 100644 --- a/docs/website/content/howdoi/how-do-i-fetch-an-image-from-the-resource-file-add-a-multiimage.md +++ b/docs/website/content/howdoi/how-do-i-fetch-an-image-from-the-resource-file-add-a-multiimage.md @@ -11,57 +11,26 @@ description: Understand the complexity of phone/tablet images youtube_id: sK-u1TBWFX8 thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-3-1.jpg --- +{{< youtube "sK-u1TBWFX8" >}} -{{< youtube "sK-u1TBWFX8" >}} +Image handling on mobile starts with density, not just resolution. Two devices can have very different physical sizes and very different pixel densities, which means the same raster image can feel too small, too large, or too soft depending on where it is shown. That is why Codename One introduced multi-images and why density still matters even in a modern CSS-first project. -#### Transcript +The important distinction is between raster and vector assets. Raster formats such as PNG and JPEG are fixed collections of pixels. If you scale them too aggressively, quality drops. Vector assets can be redrawn cleanly at different sizes. That is why `FontImage` and icon-font based workflows are so valuable: when an icon can be expressed as a vector, you avoid many of the density problems that ordinary raster assets create. -In this short video I’ll explain the ideas behind device densities and images in a mobile phone environment. In this environment the differences between device screens are big. Lets start with a simple question, on the left you see an iPhone 6 and on the right you see an iPad Air 2. Both are not to scale but I’ll ask the abstract question: "which device is bigger?" +Multi-images exist for the cases where you really do need raster artwork. Instead of shipping one image and scaling it poorly on-device, you prepare density-appropriate variants so the runtime can choose the asset that best matches the device. The older designer workflow made this explicit through multi-image tooling. The idea is still valid today, even though many modern projects will rely more heavily on vector icons and CSS styling than older designer-heavy apps did. -In terms of resolution the iPad is clearly bigger. It has more pixels than the iPhone 6. The iphone has 1 million pixels and the ipads has 3 million pixels. Thats a 3 fold difference! +This also means that not every screen should just "scale up" for tablets. The video makes an important point here: higher-resolution devices should often show more content, not just bigger versions of the same phone UI. A tablet layout that merely enlarges every icon and spacing value can feel wasteful. Density-aware images are part of adaptation, but layout choices are just as important. -The ipad is also much bigger than the iphone nearly twice the size diagonally. This might seem to seal the deal, the ipad seems bigger. However, it’s the exact opposite! +In practical terms, use vectors where you can, multi-images where you must, and on-device scaling carefully. If you do need to generate or manipulate scaled rasters, remember that the memory cost at runtime can be much higher than the compressed file size suggests. This is one of the reasons image-heavy screens often need explicit memory discipline. -In reality the iphone is arguably bigger. It has more pixels per inch or PPI than the ipad simply because the pixels are more densely packed together. So for every inch of iphone you get more pixels. This means that in order to show an image on the iphone we’ll need 50% more pixels… This difference is even further highlighted when comparing newer devices to older devices as densities keep rising. +The older page talks about fetching images from the resource file through the resource APIs. That is still correct for apps that keep assets in resource bundles. In a more modern setup, the broader principle matters more than the exact tool: keep image handling density-aware, prefer vectors for icons, and avoid assuming one raster size will feel correct everywhere. -To repeat the point, this means that if you load an image in an iphone and show the exact same image on an ipad this image will look much smaller on the iphone almost 50% smaller! This might not sound like a big deal but keep in mind that these are touch oriented devices so if an image is an icon that you are meant to tap on. This icon might be too small for your finger in a high PPI device. +## Further Reading -This difference gets freakishly comical in some situations, I still have a second generation ipad and that’s a 132 ppi device a newer pixel device which isn’t the highest density device available is 538 ppi. Making an app that adapts properly to both of these requires good understanding of the tools at hand… - -As developers resolution is something that’s instantly obvious but density isn’t. However, on mobile because of the touch interface it’s crucial that we adapt properly to the device density. It’s also important that we use it effectively. Many developers expect iPads to just use bigger pictures but that would create a tablet interface that just feels like a scaled up phone application. This is a bad user experience. Users expect the touch interface to adapt to higher PPI devices to make use of the extra space for more content. - -To drive the point home here is the kitchen sink demo on a phone and an ipad. Notice that the iPad UI adapts to make the most use of available screen real-estate rather than just scaling up the phone UI. In fact we chose to use slightly smaller images in the tablet mode so we can fit in more data. - -One of the most important things we need to do to adapt to high resolution UI’s is scale the images. To understand the problems and benefits we first need to understand that there are 2 types of images. Vector and raster. Vector images are stored as a set of low level drawing commands. -This means that when we scale a vector it’s still perfectly smooth as the lines are redrawn in the new resolution. - -FontImage and the material design icon fonts we have builtin are types of vector images and you should use them when possible. We also have some basic support for an SVG transcoder although at this time this is a bit new. SVG is the standard for vector graphics. -However, most graphic resources aren’t in vector format and are just stored as a set of colors for pixels. The common formats that you use every day such as JPEG, GIF & PNG are all raster images. - -Since rasters are just a set of pixels scaling them without quality degradation is a problem. There will always be some loss of quality but doing the scaling on the device will be nearly impossible. -So the common solution which exists natively in all mobile OS’s is to ship the app with ready made scaled rasters to all the resolutions you need. Our solution for this is multi-image. - -We support automatic scaling on the fly of multi images which means you don’t need to maintain 5-10 versions of the same image. You can just have one high quality version of the image and we will generate the rest for you using a high quality scaling algorithm on the desktop. - -Before we get into that I’d like to introduce the concept of densities in Codename One. Codename One defines these density constants that match past and future devices. One of the cool and unexpected surprises with density is that HD TV is a very low density device and even 4k TV sits at medium or low density. That sounds odd until you realize that a TV is typically huge and you sit relatively far from it. You can’t distinguish pixels in the same level as you could on a phone. -Other than that notice that we have many densities ranging from low to 4K. Some of them don’t have devices targeting them yet and might not be required. Also notice that density is an estimate here, devices are notoriously bad at these things so the numbers might fluctuate slightly. - -Lets move on and add a multi image in the Codename One Designer using the quick add multi image option. I’ll pick our standard icon which is a 512 by 512 image. Here I’m faced with an option to select the source resolution. The source resolution is the density from the previous list to which the image was built. So if the resource image I’m importing was designed for a very high density I should pick that. In this case the image is just a random image I’m using so I’ll pick 560. However, if I’d have picked a lower DPI all of the resulting images would have been bigger. If I’d have picked a higher DPI all the resulting images would have been smaller. - -You will notice all the images are now automatically created and sized. We can toggle between them and see the size difference and we can use the standard features of the designer tool such as opening them in an external editor etc. - -The add multi images as opposed to the quick add we used before is a manual approach for adding multi images. We don’t use it as much but if you want more fine grain control over scaling you can use this. You can literally determine the size in pixels of every scaled instance where 0 means an instance will not be generated. -You can select the option to preserve aspect ratio which makes sure the image will scale without distortion. You can also use percentage to scale the image uniformly with a single number. - -You can use the square image to indicate that the width/height should remain square in which point it will force the values to remain the same. -Once this is done you can inspect the resulting images and manipulate them just like we could before in the quick add mode. - -In both cases you can load the image from code by using getImage on the theme resources object. Notice you can create additional resource files and work with those files as well and also download resource files dynamically. Another important aspect of this is that multi image is seamless. It acts just like any other image… -When you read the resource file the resolutions that you don’t need are discarded and you don’t pay for the multi image in runtime. There is a cost in application size so it isn’t completely free though. - -Thanks for watching, I hope you found this helpful. - ---- +- [Themeing](/themeing/) +- [Developer Guide](/developer-guide/) +- [How Do I Create A 9 Piece Image Border](/how-do-i/how-do-i-create-a-9-piece-image-border/) +- [How Do I Improve Application Performance Or Track Down Performance Issues](/how-do-i/how-do-i-improve-application-performance-or-track-down-performance-issues/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-find-problems-in-my-application-using-the-codename-one-tools-and-the-standard-ide-tools.md b/docs/website/content/howdoi/how-do-i-find-problems-in-my-application-using-the-codename-one-tools-and-the-standard-ide-tools.md index 33ad502c36..e656a4eda2 100644 --- a/docs/website/content/howdoi/how-do-i-find-problems-in-my-application-using-the-codename-one-tools-and-the-standard-ide-tools.md +++ b/docs/website/content/howdoi/how-do-i-find-problems-in-my-application-using-the-codename-one-tools-and-the-standard-ide-tools.md @@ -13,10 +13,28 @@ description: Review of the simulator tools for network monitoring, performance m youtube_id: 1wHGnmO-vtE thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-25.jpg --- - {{< youtube "1wHGnmO-vtE" >}} ---- +When a Codename One application feels wrong, the first job is to identify what kind of problem you actually have. Is the UI frozen? Is the event dispatch thread blocked? Is a network request slow? Is a component repainting too often? Is memory pressure coming from images? Codename One gives you tools for each of those questions, but they work best when you use them alongside the normal debugger in your IDE. + +The standard Java debugger is still the first tool to reach for. If the simulator is stuck, pause the process and inspect the stack. If a specific interaction misbehaves, put a breakpoint on the action path that matters. Many bugs are still just ordinary logic bugs, and the debugger remains the fastest way to see what the code is actually doing. + +Codename One adds another layer of diagnostics inside the simulator. The EDT tools are especially important because so much UI behavior depends on the event dispatch thread. If you do heavy work on the EDT, the app becomes slow or unresponsive. If you touch UI state from the wrong thread, the behavior becomes unpredictable. The EDT diagnostics can help expose both kinds of mistakes, even though they are not perfect and can occasionally produce noisy warnings. + +The network monitor is the next high-value tool. If your app talks to a server, inspect the requests and responses directly instead of guessing. Look at URLs, headers, payload sizes, response bodies, and timing. That is often enough to separate a client-side bug from a server-side bug or a simple latency problem. + +The performance monitor becomes useful once the issue is more visual than logical. It helps show which components render frequently and which ones are expensive. That is a much better starting point than trying to optimize random code paths. If one screen feels slow, find the component or rendering pattern that is consuming the time before you start rewriting UI code. + +Memory problems also become much easier once you inspect images explicitly. In Codename One, images are often the first place to investigate when memory usage grows unexpectedly. The tooling can help show which images are being created and how large they are in memory. That matters because the runtime memory cost of an image is often very different from the compressed file size you started with. + +The practical lesson is to debug with evidence. Use the debugger for logic, the EDT tools for threading mistakes, the network monitor for IO, the performance monitor for rendering cost, and the logging APIs for information that needs to survive beyond the simulator. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [Performance Network Monitors](/performance-network-monitors/) +- [How Do I Improve Application Performance Or Track Down Performance Issues](/how-do-i/how-do-i-improve-application-performance-or-track-down-performance-issues/) +- [How Do I Use Crash Protection? Get Device Logs?](/how-do-i/how-do-i-use-crash-protection-get-device-logs/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-get-repeatable-builds-build-against-a-consistent-version-of-codename-one-use-the-versioning-feature.md b/docs/website/content/howdoi/how-do-i-get-repeatable-builds-build-against-a-consistent-version-of-codename-one-use-the-versioning-feature.md index d6b6de9840..a1cddf5349 100644 --- a/docs/website/content/howdoi/how-do-i-get-repeatable-builds-build-against-a-consistent-version-of-codename-one-use-the-versioning-feature.md +++ b/docs/website/content/howdoi/how-do-i-get-repeatable-builds-build-against-a-consistent-version-of-codename-one-use-the-versioning-feature.md @@ -12,27 +12,27 @@ description: Versioning allows us to build against a point release and get stabi youtube_id: w7xvlw3rI6Y thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-4-1.jpg --- +{{< youtube "w7xvlw3rI6Y" >}} +Repeatable builds matter when you are trying to stabilize a release, investigate a regression, or keep a production app on a known-good Codename One version instead of automatically moving with the latest server-side changes. Versioned builds are the feature designed for that job. -{{< youtube "w7xvlw3rI6Y" >}} +The core idea is simple. Instead of always building against the current Codename One server release, you tell the build system to target a specific Codename One version. That means the native build is performed against the exact server logic and framework state associated with that release. If your application built and ran correctly against that version before, you now have a way to stay there while you validate later updates on your own schedule. -#### Transcript +This is especially useful when a build suddenly starts failing or when behavior changes unexpectedly after a platform update. If you can rebuild against an older known-good Codename One version and the problem disappears, you have learned something important: the regression is probably tied to the framework or build-server changes rather than to a recent change in your own app. That turns versioned builds into both a stability feature and a debugging tool. -In this short video I will try to explain versioned builds which is Codename One’s approach for repeatable builds. Before we begin, versioned builds are a pro feature that has extended functionality in the enterprise tier. I’ll talk more about that soon but first, what does versioned build actually mean? +The original video frames this as a Pro feature with longer retention in the Enterprise tier, and that is still the right way to think about availability. The exact retention window depends on the subscription tier, but the practical lesson is the same: if stable historical build targets matter to your team, versioned builds should be part of your release strategy. -With versioned builds we send a build to a specific Codename One point version. For instance, you can send a build to Codename One 3.7 and it will build against the exact version of Codename One that existed when 3.7 was released. +When you use a versioned build, it is usually a good idea to keep your local simulator environment aligned with that same framework version as well. Otherwise you can end up testing one version locally and shipping another remotely. The older settings UI exposed this through the version selection and client library update flow. In current Maven-based projects, the broader goal is still consistency: keep the project, simulator, and native build path aligned so that local testing reflects what the build server is actually producing. -This allows you to avoid potential regressions due to frequent changes in the build server that might impact compatibility. It’s also useful for testing purposes, if your app suddenly fails you can use versioned build to see if this is due to a change in the Codename One servers. +This is not something you need for every build during active day-to-day development. It becomes valuable when predictability matters more than immediately consuming the latest changes. Teams that ship production apps, support older releases, or operate in tightly controlled release windows tend to benefit the most from this feature. -As I mentioned before there is a difference between enterprise and pro subscriptions. For enterprise developers we support up to 18 months back. That means an enterprise user can build against a version released in the past 18 months which is typically 4 releases back. +In practice, the healthiest workflow is to use the current Codename One version during normal development, then pin to a specific version when you need a reproducible release train or when you are isolating a regression. Once the newer version is validated, you can move forward deliberately instead of being surprised by it in the middle of a release. -The pro versions include 5 month support which typically maps to the last one or two releases. +## Further Reading -You can enable versioned build by selecting the specific version in the Codename One Settings tool under the basics section. -This opens the list of versions and you can pick the right one. You can use update client libs to update the simulator to that specific release as well. - -Thanks for watching, I hope you found this helpful. - ---- +- [Build Server](/build-server/) +- [Moving To Maven](/blog/moving-to-maven/) +- [Developer Guide](/developer-guide/) +- [How Do I Use Offline Build](/how-do-i/how-do-i-use-offline-build/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-handle-eventsnavigation-in-the-gui-builder-populate-the-form-from-code.md b/docs/website/content/howdoi/how-do-i-handle-eventsnavigation-in-the-gui-builder-populate-the-form-from-code.md index 8572db17a3..81f4b9c917 100644 --- a/docs/website/content/howdoi/how-do-i-handle-eventsnavigation-in-the-gui-builder-populate-the-form-from-code.md +++ b/docs/website/content/howdoi/how-do-i-handle-eventsnavigation-in-the-gui-builder-populate-the-form-from-code.md @@ -12,10 +12,26 @@ description: Event/variable handling in the Codename One GUI builder is a bit di youtube_id: 3IC2qZ3wUO4 thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-26.jpg --- - {{< youtube "3IC2qZ3wUO4" >}} ---- +The GUI builder workflow in Codename One is based on generated forms and generated state-machine code, which means events and screen population work a little differently from the desktop UI builders many Java developers are used to. The old video focuses on that generated workflow directly, and that is still the right mental model if you are maintaining a GUI-builder based project. + +Navigation is easiest when you use the builder’s command and form-linking features instead of trying to wire everything manually. If a button simply needs to open another form, the generated navigation model is often the cleanest route because it keeps the relationship visible in the builder and preserves the expected back-navigation behavior automatically. + +Event handling becomes more interesting when a component needs custom logic. In the builder-driven approach, you attach the event in the UI resource and then implement the generated handler in code. That part is easy to trip over if you forget that the generated base classes are updated when the resource file is saved. The video calls this out indirectly through the save cycle, and it is still one of the main stumbling points when working in this style. + +Populating a form from code is really about timing. A generated form is not a permanently alive singleton sitting in memory waiting for you to reach into it. It is created when needed and discarded when it is no longer active. That is why screen-specific initialization belongs in the lifecycle hooks that run just before the form is shown, not in some random place that assumes the components already exist. + +This is the part of the video that still matters most conceptually. If you want to change a label, fill a field, or update a screen-specific component, you do it at the point where that form is being prepared to appear. That way the components actually exist and the change is applied to the live form instance rather than to a screen that has already been discarded. + +The modern caveat is that new Codename One projects are usually less GUI-builder centric than the old workflow assumes. Many teams now prefer code-based UI plus CSS for styling. But if you are working in the builder, the generated-form lifecycle is still the rule you need to understand: navigation is configured declaratively where possible, event handlers live in the generated override points, and screen population happens at form-show time. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [Layout Basics](/layout-basics/) +- [How Do I Create A Gorgeous Sidemenu](/how-do-i/how-do-i-create-gorgeous-sidemenu/) +- [How Do I Create A List Of Items](/how-do-i/how-do-i-create-a-list-of-items-the-easy-way/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-improve-application-performance-or-track-down-performance-issues.md b/docs/website/content/howdoi/how-do-i-improve-application-performance-or-track-down-performance-issues.md index cb08b5391a..202d7df1a4 100644 --- a/docs/website/content/howdoi/how-do-i-improve-application-performance-or-track-down-performance-issues.md +++ b/docs/website/content/howdoi/how-do-i-improve-application-performance-or-track-down-performance-issues.md @@ -11,17 +11,28 @@ description: Covers the basics of "what is slow" and how to find out what is bog youtube_id: set12jVQl_A thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-28.jpg --- - {{< youtube "set12jVQl_A" >}} -#### Details +Performance work gets easier once you stop thinking in terms of vague slowness and start looking for specific expensive patterns. In Codename One, the same categories of mistakes show up repeatedly: too much work on the EDT, heavy list renderers, unnecessary image churn, overly dynamic text measurement, and drawing strategies that look harmless in code but cost a lot at runtime. -Some basic notes: +Lists are one of the first places to inspect. If you are using a list model, avoid changing it one item at a time when what you really mean is "replace a lot of data". Repeated add/remove events trigger repeated notification and repaint work. If you need to make a large change, replacing the model or batching the change is usually much cheaper than firing a long stream of individual updates. - * 90% of the performance problems we see relate to people using gradients. Don’t use gradients, use images. - * The video doesn’t mention image locking, that is something that could be important is some complex usages. The second part of [this blog post](/blog/in-a-pinch/) discusses that. +Custom list models and renderers need even more discipline. Methods such as `getItemAt()` must be fast. They should not block on network access or heavy computation. If a list depends on remote data, return quickly and update later when the data arrives. The same principle applies to renderers: do not construct a fresh component tree every time the renderer is asked for output. Reuse renderer components and update their state. Creating components repeatedly inside the renderer is one of the easiest ways to destroy scrolling performance. ---- +Text measurement is another hidden cost. Automatic line breaking and repeated calls that depend on string width can be expensive, especially when they happen frequently during rendering. If a screen is performance-sensitive, prefer simpler label usage and avoid forcing the UI to re-measure large amounts of text over and over when a more stable layout would do. + +Images deserve just as much attention. A few sensible image draws are often cheaper than a huge number of tiny draw operations. That is one reason very small tiled images, overly fragmented borders, and repeated low-level drawing primitives can become surprisingly expensive. The old note about gradients is also still worth keeping: decorative gradients are a surprisingly common source of avoidable cost, and in many cases a simple image or cleaner styling choice is the better tradeoff. + +Image scaling is another common trap. If you call scaling methods casually, you may be creating larger in-memory images than you realize. The source asset might be small on disk, but the scaled runtime image can cost a lot more RAM. That means scaling should be deliberate, cached where appropriate, and revisited if memory pressure starts to climb. + +The right workflow is to measure first, then optimize. Use the performance tools, identify the component or pattern that is actually expensive, and fix that specific thing. Do not rewrite major pieces of a screen just because it feels slow. In Codename One, a small renderer mistake or image decision can dominate the cost of an otherwise reasonable UI. + +## Further Reading + +- [Performance Network Monitors](/performance-network-monitors/) +- [Developer Guide](/developer-guide/) +- [How Do I Find Problems In My Application, Using The Codename One Tools And The Standard IDE Tools](/how-do-i/how-do-i-find-problems-in-my-application-using-the-codename-one-tools-and-the-standard-ide-tools/) +- [In A Pinch](/blog/in-a-pinch/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-localizetranslate-my-application-apply-i18nl10n-internationalizationlocalization-to-my-app.md b/docs/website/content/howdoi/how-do-i-localizetranslate-my-application-apply-i18nl10n-internationalizationlocalization-to-my-app.md index de6744450c..3dfdfd7495 100644 --- a/docs/website/content/howdoi/how-do-i-localizetranslate-my-application-apply-i18nl10n-internationalizationlocalization-to-my-app.md +++ b/docs/website/content/howdoi/how-do-i-localizetranslate-my-application-apply-i18nl10n-internationalizationlocalization-to-my-app.md @@ -12,62 +12,30 @@ description: Codename One features seamless localization and BiDi RTL support youtube_id: 32mkZymqa6E thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-12-1.jpg --- - {{< youtube "32mkZymqa6E" >}} +Internationalization and localization are broader than translation. Internationalization means structuring the app so it can adapt to different locales. Localization is the work of adapting the app to a specific locale. Language is part of that, but so are dates, numbers, currency, right-to-left behavior, phrasing, and the cultural meaning of visual choices. -#### Script - -###### Transcript - -In this short video we’ll discuss some of the core concepts of internationalization and localization which are often abbreviated as i18n and l10n. We’ll start by defining these two big terms. Internationalization is the system of making an application adaptable to the various locales which means the application will be flexible when we need to make it work for users in different countries. - -Localization is the process of adapting a specific application to a new locale, this includes translation but that’s a bit of an over simplification of the purpose of localization. - -Locale goes far deeper than just language translation. Locale covers the way dates are arranged, the way numbers are represented, the way sentences are phrased and far more than that… I’ll talk about some of those things as we move forward. - -Translation is the first thing we encounter in localization tasks and probably the most important aspect. Codename One makes translation seamless. You can just install a resource bundle into the UIManager. A resource bundle is a set of key value pairs so the key would be a string in english and the value would be the same string in a different language. - -This gets translated seamlessly so if you set the key from the bundle as the text to the a label that label will be seamlessly localized. You don’t need to litter your code with calls to a localize method… - -You can obviously invoke the localize method of UIManager if you have a special case but you don’t need to for most cases. +The first practical rule is to stop hard-coding user-facing text directly into the UI wherever possible. Codename One is designed to work with key/value bundles so that the text shown to the user can change with the current locale. The older video demonstrates this through the resource editor. For new projects, the better default is l10n property bundles, but the core idea remains the same: components should reference localizable keys, not baked-in strings that force you to revisit the code for every translation. -One of the big things we omitted from Codename One is message format. That class solves some complex edge case formatting but is also a half baked solution. A sentence like "This is your 3rd try" will not work well with message format so we think there are probably better ways to implement that functionality rather than repeat over engineered mistakes from Java SE. +This matters because translation is only the first layer. Locale-specific behavior also affects the way you display dates, numbers, and currency. If you use locale-aware formatting utilities, the app can present those values in a way that feels natural to the user instead of forcing one fixed representation on every market. In Codename One, `L10NManager` and the framework's localization utilities are the right place to start for this kind of formatting. -One of the important aspects of localization is culture. A great example in this sense is color which especially in oriental cultures has very different meanings. An example would be red which means stop or problem in the west but it might mean something different in the east. We can extract that into the resource bundle. +Testing localization also needs to be part of the normal development loop. It is much easier to catch problems early if you force the simulator into a different language and inspect the UI there. A localized build should not only show translated strings. It should still fit properly, align correctly, and feel intentional when labels grow longer or date and number formatting changes. -Localization of a Codename One application starts in the designer tool. Open the theme.res file for your application and select the localization section. You can add a resource bundle here to set the keys and values. +Right-to-left support is one of the most important areas to get right. Languages such as Hebrew and Arabic do not just translate the text. They change the expected flow of the interface. Text aligns differently, component order often reverses, and icons or directional affordances may need to be mirrored. Codename One helps a lot here because layouts react to RTL mode by flipping positions and alignment where appropriate. A `BorderLayout.EAST` relationship, for example, is interpreted relative to the active writing direction. -You can add additional locales using this button. In this case I added the iw locale. +That said, RTL is not automatic magic. Mixed-direction content still needs attention. Numbers are still read left-to-right even inside right-to-left languages, so bidirectional text can produce cursor movement and layout behavior that surprises developers who only test in English. Icons such as play arrows, chevrons, and back buttons also need review because a mirrored UI with unmirrored directional icons still feels wrong. -Notice that the text is translated in the iw column. In this case it’s pretty simple to just edit the column value in the table. -Notice the last highlighted row which uses the @rtl notation. That’s a special case marker indicating whether the language is a right to left language. Notice that the iw locale is marked right to left, we’ll discuss RTL languages soon -But first check out the buttons in the bottom. With these you can add, remove or rename complex properties in the tool +The video uses the older designer workflow to define bundles and RTL markers. That part is outdated for new projects, but the localization concepts themselves are still the right ones to learn. In current Codename One development, the better default is property-bundle based localization plus locale-aware formatting in code, with the layout system doing most of the heavy lifting for RTL-aware component ordering. -Moving on to the code lets review this line by line. -We get the language from the localization manager tool, this is a short iso code for the language and not the actual string name -It should be lower case and non-null by default but I’m just playing safe here… -The locale sometimes changes iso codes so some platforms might still be using the old "he" code instead of the newer "iw" code. -There are two important methods here. First is set bundle, we set the key value pairs for the current language. The second is getL10N where we get the localization bundle for iw from the theme. -Notice we ignore English as that is the default language for the application. +The practical goal is not just "translated text". It is an application that feels like it belongs in the user's locale. That means translated labels, correctly formatted values, sensible spacing for longer phrases, mirrored layout where appropriate, and deliberate review of the edge cases that do not flip automatically. -Now here is a really neat trick to test this. Right click the project and select project properties. -In the IDE’s project properties you can set the vm arguments passed to the simulator and manipulate the language of the JVM. This makes debugging localization very easy. Just enter the run section and add the build hint -Duser.language=iw and it will become the language of the JVM. This saves you the need of updating your OS localization settings to debug locale issues. +## Further Reading -Up until now we only discussed translation and while I gave you a glimpse at the L10NManager class it’s a pretty big class that has a lot of locale specific methods that should help in the localization process. Another important class is the simple date format, notice that it exists in the standard Java packages too but that’s problematic. The version of simple date format from the java.text package will be inconsistent between OS’s since it will use the native VM simple date format on Java SE and Android but for other platforms it will fallback to our simpler implementation. Using the one from the l10n package guarantees better cross platform consistency. - -L10NManager has multiple format methods that make the process of formatting numbers & dates much easier. Currency and related values should usually use that class for formatting. - -I mentioned RTL before but I didn’t explain it well. Some languages such as Hebrew and Arabic are written from right to left. Probably because they are so ancient that they were mostly carved on rock and it was easier holding the hammer in the right hand. In modern times this is a source of great pain from smearing of ink while writing to the basic bidi problem. - -Bidi represents mixing an RTL language with a left to right language or numbers. Numbers are still drawn from left to right in these languages so when you type text in a text field the cursor will literally "jump" to accommodate the reverse flow in such a case…. This makes the problem not just a right to left problem but rather a bi-directional problem or bidi for short. - -One of the core expectations of working in such languages is UI reversal. Books in RTL languages are the mirror images of latin books in the sense that you turn the pages in the opposite direction. Text is aligned to the right and everything should be flipped. The same is true for a UI we expect alignment in reverse as well as reverse order for the components within the layouts. This is handled mostly seamlessly by Codename One layouts. They reverse the element order and flip sides when RTL mode is active, so if you add something to border layout EAST in RTL it would act like you added it to WEST and so forth. Aligning an object to the left in RTL would be like aligning it to the right and visa versa. - -If we look at the UI of the restaurant app in bidi mode we can see several cases where this behaves as expected. The menu and play button are reversed. But notice the play button points in the opposite direction, that’s exactly one of those nuances we need to pay attention to as the icon itself should be flipped for bidi. Notice that al lthe text is aligned to the right as well instead of the left. - -Thanks for watching, I hope you found this helpful. - ---- +- [Developer Guide](/developer-guide/) +- [Themeing](/themeing/) +- [Layout Basics](/layout-basics/) +- [Hello World](/hello-world/) +- [Properties Are Amazing](/blog/properties-are-amazing/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-positioning-components-using-layout-managers.md b/docs/website/content/howdoi/how-do-i-positioning-components-using-layout-managers.md index 0a6b2aaf48..116ca64f7f 100644 --- a/docs/website/content/howdoi/how-do-i-positioning-components-using-layout-managers.md +++ b/docs/website/content/howdoi/how-do-i-positioning-components-using-layout-managers.md @@ -13,68 +13,30 @@ description: Determine where components are placed especially when dealing with youtube_id: 4D_KUa2qv2o thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-5-1.jpg --- +{{< youtube "4D_KUa2qv2o" >}} -{{< youtube "4D_KUa2qv2o" >}} +Layouts are the reason a Codename One UI can survive different screen sizes, orientations, pixel densities, and languages. Components do not live at fixed coordinates. They live inside `Container` objects, and those containers use layout managers to decide how much space each child gets and where it should appear. -#### Details +The core problem layouts solve is portability. A button position that looks fine on one phone may be wrong on another. A label that fits in English may overflow in German. A design that works in portrait may fall apart in landscape. Absolute positioning looks tempting when a screen is simple, but it stops being practical as soon as the UI has to adapt. Layout managers let you describe intent instead of hard-coding coordinates: this component should stay on the left, this field should take the remaining width, this section should stack vertically, these buttons should all be the same size. -###### Transcript +That is why the classic “label on the left, field on the right” example is still such a useful starting point. In Codename One you usually express that with a `BorderLayout`, putting the label in `BorderLayout.WEST` and the field in `BorderLayout.CENTER`. The layout then handles the resizing rules for you. The label keeps the width it needs, while the center component expands into the remaining space. Once that pattern clicks, you can start seeing a screen as a composition of small layout problems instead of one big manual positioning job. -In this short video I’ll try to address one of the most challenging basic features of Codename One: Layouts +The standard layouts each have a natural role. `FlowLayout` is still the simplest layout and is fine for small inline groups of components, but it is easy to outgrow. `BorderLayout` is one of the most useful outer layouts because it gives you strong structure: top, bottom, left, right, and a center area that consumes the remaining space. `BoxLayout.y()` is a great default for stacked content because it reads like the screen itself. `BoxLayout.x()` is good for small horizontal rows. `GridLayout` is useful when you genuinely want equal-sized components, such as button bars or icon grids. `TableLayout` is especially helpful for forms and data entry because it gives you more control over rows, columns, and spanning. `LayeredLayout` is the one to reach for when components need to sit on top of each other, such as floating actions, overlays, or decorative layers. -Before we go into the actual code let’s explain the basics. Codename One Components are arranged within Containers. A Container can contain an arbitrary number of components +One detail that matters early is constraints. Some layouts need them and some do not. `BorderLayout` depends on explicit positions such as `CENTER` and `WEST`, while `BoxLayout` generally does not need extra constraints at all. Understanding that difference helps make the APIs feel much less arbitrary. A layout manager is not just “where children go”; it also defines what information you need to provide when you add those children. -Since it derives from Component a Container can also Contain other Containers and so forth. By nesting these Containers one into another we can build any type of user interface +The most effective way to build a real screen is usually to nest a few simple containers rather than hunting for one magical layout that does everything. A form might use `BorderLayout` at the top level, a `BoxLayout.y()` in the content area, and then small `BorderLayout` or `GridLayout` sections inside it. That is normal. Good layout code tends to mirror the visual structure of the screen. If a screen has a header, a scrolling body, and a floating action button, the container hierarchy should make that obvious. -This begs the question: Why? Why not place the components where we want them to be on the screen? +Inspecting a real screen is often more useful than memorizing layout APIs in the abstract. If you open a working UI in the component inspector and look at the nesting, you quickly see why a `LayeredLayout` was used at the root, why a `BoxLayout.y()` holds a scrolling list, or why a `GridLayout` makes the swipe actions line up evenly. That is how layouts become intuitive. -Devices can have different resolution, pixel density, font sizes and form factors. So a specific position that looks good on one device can look awful in another. +The modern adjustment is not in the layout system itself so much as in how you split responsibilities. Layout code should mostly define structure and resizing behavior. CSS should do much more of the styling work: spacing, fonts, colors, borders, and visual states. If you find yourself using extra containers only to fake visual styling, that is often a sign that the visual concern belongs in CSS instead. -With device orientation changes, tablets, desktop/web versions of the app etc. You would expect the UI to adapt "automatically". It gets worse with localization for instance languages like German have very long words and you would expect components to resize accordingly +## Further Reading -Layout managers convey the logic of component placement in a way that Codename One understands and can adapt e.g. Let’s say I want to say put a label on the left and put a text area next to it. I also want to give that text area the rest of the available space - -We can do this in many ways but a common way would be to use a Border Layout. We can place the label in the WEST & the text field in the center - -This can be abbreviated using this shorthand syntax so the initial container will have the east component - -And can be further abbreviated using this one line of code, this also brings us to one of the more important aspects of layout managers. Constraints. - -There are two types of layout managers in Codename One, those that have component constraints and those that don’t. Border layout requires a constraint for every addition (like the center or east constraint we used), layouts like table layout can work without one but add it implicitly. Layouts like Box Layout don’t need a constraint at all. - -Let’s go over some of the layout managers at a high level using the kitchen sink demo to show them off - -First we have the flow layout which is the default layout manager in Codename One, it arranges components from left to right and gives each component its preferred size. - -Flow layout can be aligned to the left, center or right. Vertically it can be aligned to the top middle or the bottom of the available space. It implicitly breaks a line when it reaches the end of the line. This is a bit of a long discussion to get into but if you add complex/large components into flow layout it might fail as Codename One doesn’t reflow for performance reasons. You should be careful when using it for anything non-trivial. - -Next we have border layout that we discussed briefly, it can place components in the north (top), south (bottom), east (right), west (left) and center. Components placed in the north & south have their preferred height but are stretched to fill up available width. Likewise components placed in the east & west have their preferred width but stretch to fill up the available height. - -The center component is a special case, it’s stretched to fill the available space. There is a special absolute center mode that allows it to be in the actual center based on the preferred size of the component. Border layout doesn’t make sense as a scrollable component since the behavior heavily depends on the fixed size of the available space. Hence it will implicitly disable scrolling when set to a Container or Form. We often use Border Layout for components that need to be sized explicitly such as Map or BrowserComponent as the center location makes sure to give them the available space. - -Box layout places components in a row or a column. When we place elements in Box X the row has the same height but the elements are given their preferred width. When we use Box Y elements occupy the available width but have their preferred height. This helps components align property on the given axis as they have the same size on that axis. - -Grid layout gives all the components the same size based on their preferred size and places them in rows/columns. This is useful for the icon grid style of user interface and also useful for button bars where you want all the elements to have the exact same size regardless of their text - -Table layout is more flexible, it’s very useful for input and forms. Table layout allows spanning columns or rows between table cells. It uses constraints to define behaviors of table elements. - -And finally Layered Layout places elements one on top of the other so we can do things like overlays. Here we set a bit of margin on each entry so the difference between the layers are clearer - -Now here is a neat trick to learn about components, open the component inspector on a UI and look within the hierarchy to see what we did for that specific hierarchy. You can also look at the code but that isn’t always clear… In the contacts section of the kitchen sink demo we can inspect the various elements and understand which layout was used for what purpose. - -We have a layered layout for the root, this allows the floating action button on the bottom. - -A bit deeper we can see the Box Layout Y that contains all of the "Swipeable Container" classes, - -Swipeable container is a layered layout that we can swipe to show the buttons below - -These buttons under the swipeable container use a grid layout so they can occupy the same size - -The top component in the swipeable container is the multi button, this button is really just a border layout Container with text in the center and an icon label in the west! - -I could talk about layouts for another hour (and I will at a later date) but for now we’ll call this a day. Thanks for watching and I hope it was helpful - ---- +- [Layout Basics](/layout-basics/) +- [Developer Guide](/developer-guide/) +- [Themeing](/themeing/) +- [Getting Started](/getting-started/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-take-a-picture-with-the-camera.md b/docs/website/content/howdoi/how-do-i-take-a-picture-with-the-camera.md index ed9dff54de..a2a0cb26bd 100644 --- a/docs/website/content/howdoi/how-do-i-take-a-picture-with-the-camera.md +++ b/docs/website/content/howdoi/how-do-i-take-a-picture-with-the-camera.md @@ -10,10 +10,25 @@ description: Using the capture API to take a picture youtube_id: nF4eqzVcsic thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-29.jpg --- - {{< youtube "nF4eqzVcsic" >}} ---- +Taking a picture in Codename One is conceptually simple: ask the platform to capture an image, get back a file, load the image you actually want to display, and then update the UI. The details matter because camera images can be much larger than the UI really needs. + +The `Capture` API supports several capture workflows, including direct image capture and variants that return later through a callback. The most practical lesson from the video is still the most important one: do not casually load the full original camera image if all you need is something that fits on screen. High-resolution camera output can consume a lot of memory, and many apps only need a resized version for preview or upload. + +That is why scaling at capture time is often the right default. If the goal is to show a preview inside the app, scaling the image to something close to the display width is usually much safer than loading the raw camera file and hoping memory usage stays reasonable. The video demonstrates this with a width-based capture and aspect-ratio preservation, and that is still a sensible pattern. + +The next practical detail is that the UI needs to react to the new image. If you replace the icon on a label or another display component, the form may need to revalidate or repaint so the layout can account for the new content. This is especially noticeable when the placeholder and the captured image have very different sizes. + +The simulator behavior is also worth understanding. On the simulator, capture usually behaves more like a file chooser than a real camera. On a device, it invokes the actual camera flow. That means simulator testing is good for the code path, but device testing is still necessary for the real user experience, permissions, and camera integration behavior. + +The modern advice here is mostly about restraint. Capture the image you need, not the largest image the device can produce. Resize early when appropriate, keep memory in mind, and treat image loading as a UI and performance concern, not just a feature checkbox. + +## Further Reading + +- [Developer Guide](/developer-guide/) +- [How Do I Improve Application Performance Or Track Down Performance Issues](/how-do-i/how-do-i-improve-application-performance-or-track-down-performance-issues/) +- [How Do I Use Storage, File System And SQL](/how-do-i/how-do-i-use-storage-file-system-sql/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-use-cloud-connect.md b/docs/website/content/howdoi/how-do-i-use-cloud-connect.md index 0785b72eb3..2ac4456899 100644 --- a/docs/website/content/howdoi/how-do-i-use-cloud-connect.md +++ b/docs/website/content/howdoi/how-do-i-use-cloud-connect.md @@ -11,40 +11,21 @@ description: Instantly preview your work on multiple devices youtube_id: NVPIXnkoxv0 thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-15.jpg --- +{{< youtube "NVPIXnkoxv0" >}} -{{< youtube "NVPIXnkoxv0" >}} +Cloud Connect was designed to solve a very specific problem: seeing UI changes on real devices immediately while working in the GUI builder. It synchronized the builder's saved UI state to connected devices so that you could feel the screen on hardware instead of relying only on the desktop preview. -#### Transcript +That idea still makes sense as a workflow lesson even though the exact builder-centered flow is much less central to modern Codename One development. The value is in shortening the feedback loop between design changes and device reality. The more quickly you can see spacing, text length, transparency, and interaction feel on real hardware, the better your UI decisions tend to be. -In this short video we will discuss the new cloud connect feature. This feature makes it trivial to build GUI applications that run on multiple devices with one design. +The old feature accomplished that by pushing builder XML and resources through the cloud to a preview app on device. The practical takeaway today is broader: fast on-device iteration is valuable, but the current direction of most projects is much less GUI-builder centric than the video assumes. In a modern CSS-and-code workflow, the exact tools may differ, but the goal is the same: do not trust only the desktop preview for visual decisions that need to survive on real devices. -We’ll start by defining "cloud connect". Cloud connect synchronizes the GUI builder with devices seamlessly. +So while the original Cloud Connect workflow is mostly historical now, the reason it existed is still worth remembering. A UI that looks fine in a builder or simulator can feel very different in the hand. Real device preview is where touch target size, keyboard overlap, spacing, and overall feel become obvious. -When you make changes within the GUI builder those changes are instantly reflected on devices connected to your account via the cloud. +## Further Reading -This works by storing the GUI builder XML file and the resource file in the cloud. The Codename One Build app fetches the up to date file and displays a preview. - -Note that this requires an up to date version of Codename One Build and the GUI builder. - -To start with cloud connect we need to click the cloud connect button in the new GUI builder which is currently the top right most button in the UI - -Notice that this might prompt you for your login credentials! - -Now that we activated cloud connect in the GUI builder we can go to the Codename One Build app and open the side menu. - -Notice that if the cloud connect option doesn’t appear you might need to kill and restart the app. - -This effectively shows the current UI you saved in the GUI builder. So lets switch to a live split screen with my actual devices. - -I edit username to E-Mail using a long click. I press save or command-s. This instantly updates the devices with the change I made so I can preview how it looks there which we can see in full screen here. - -I can also customize GUI builder styles such as background transparency as I use here. This allows me to make the text fields translucent on iOS. This applies to all styles supported by the GUI builder. Notice that this differs from the styles defined in the theme. Those can be changed as well and images can be modified or added too. - -I want to stress that this isn’t a screenshot or even a mockup. This is an actual preview UI that’s as functional as the preview you would see in the GUI builder. I can edit the text and "feel" the UI on the device, it’s as close as possible to the real UI we see when we work with the device. As such I find it remarkably useful, I disable the screensaver functionality on my devices and can instantly see how a UI would feel as I develop it. - -Thanks for watching, I hope you found this helpful - ---- +- [Developer Guide](/developer-guide/) +- [Themeing](/themeing/) +- [Hello World](/hello-world/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-use-crash-protection-get-device-logs.md b/docs/website/content/howdoi/how-do-i-use-crash-protection-get-device-logs.md index 8791d58204..cef3122cf0 100644 --- a/docs/website/content/howdoi/how-do-i-use-crash-protection-get-device-logs.md +++ b/docs/website/content/howdoi/how-do-i-use-crash-protection-get-device-logs.md @@ -11,31 +11,27 @@ description: Track down issues that occur on the device or in production using t youtube_id: C3PLjAWQ-XA thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-13-1.jpg --- +{{< youtube "C3PLjAWQ-XA" >}} -{{< youtube "C3PLjAWQ-XA" >}} +Crash protection is about getting useful failure information out of production devices instead of trying to reproduce every rare crash locally. In a framework that targets a wide range of devices and operating systems, that is not optional for serious apps. Some failures only show up in the field. -#### Transcript +The underlying tool is the Codename One `Log` API. Use `Log.p()` for ordinary diagnostic messages and `Log.e()` for exceptions. That matters because ordinary Java console habits such as `System.out.println()` and `printStackTrace()` are not the right production tools across all Codename One targets. If you want logs that are portable and useful, go through the framework logging APIs. -In this video I’ll discuss the crash protection pro feature. Crash protection makes debugging application issues on devices in production possible. It does that by providing you with crash logs and information. -But before we begin we need to clarify that crash protection is a pro level feature which means it is only available to pro subscribers or higher due to the heavy usage of production email servers. +Crash protection builds on top of that logging infrastructure. The usual pattern is to bind crash protection early in application startup so uncaught runtime exceptions are captured automatically. That gives you a fallback path even when the user cannot explain what happened or when the device is nowhere near your development environment. -As I said before crash protection automatically emails your account when there is an error in production. -It also includes a logging system which is available for all users, this logging system allows you to log exceptions with their full call stack. -You can also trigger the emailing of the log manually. A good example for that would be if your application logic determined an error condition you could send a crash log to your account. +Manual log sending is useful too. Not every failure is an uncaught crash. Sometimes the app reaches a bad state, detects a server-side inconsistency, or encounters a condition you know should be reported even though the application continues running. In those cases, sending the log explicitly can be more valuable than waiting for an unhandled exception. -Crash protection is based around the Log class which prints out information that you can then follow to understand your production failures. This is crucial with a tool like Codename One where the breadth of supported devices is so huge you can’t possibly anticipate every eventuality. -Log.p() prints out log information, you can print any arbitrary string and should generally use this instead of System.out.println(). Log.e() prints out an exception and its stack trace. Notice that printStackTrace() will not work on some platforms and will not provide the desired effect. -To send the log manually we can just call sendLog() it will instantly send the log to the email of the user who built the app. +One detail the video explains well is the tradeoff around swallowing user-visible error dialogs. During development, an obvious error popup can be useful. In production, that same behavior can create a poor user experience. Whether you suppress the default dialog or not should be an intentional product decision, not an accident. -Typical applications have a bindCrashProtection call in their init(Object) callback method. This call handles all uncaught exceptions and automatically sends an email if an exception was thrown in runtime and wasn’t handled. Notice the argument for the bind method is set to true. This argument means that exception error messages are swallowed. Normally if the event dispatch thread has an error we catch that exception and show an error dialog. That’s great during development but might be worse than crashing in production… When you pass true it means this error message is consumed and the user won’t see it. -Notice that bindCrashProtection doesn’t do anything on the simulator to avoid "noise" when you are trying to debug an app. -Bind crash protection tries to catch all exceptions but it focuses mostly on the event dispatch thread exceptions. You can also handle those manually +EDT error handling is part of this story as well. A lot of visible Codename One failures show up on the event dispatch thread. If you need custom behavior, you can listen for EDT errors yourself, log them, and decide how much of the default user-facing handling should still happen. The built-in crash protection binding is still the best default for most applications because it covers the common cases with less custom code. -Event dispatch thread exceptions can be caught by using the EDT error listener from the CN class or from Display. If you consume the event object the error message won’t reach the EDT and an error dialog won’t be shown to the user. Notice that the exception is logged near the end with the Log.e() method and sent manually. You can create your own custom EDT error handler although we’d recommend the bind method which also tracks uncaught exceptions on other threads. +The key point is that crash reporting works best when it is part of a broader logging discipline. If the log already contains meaningful context leading up to the failure, the crash report becomes much more useful. If the only thing in the log is the final exception, you will still know that the app failed, but you may not know why. -Thanks for watching, I hope you found this helpful +## Further Reading ---- +- [Developer Guide](/developer-guide/) +- [How Do I Find Problems In My Application, Using The Codename One Tools And The Standard IDE Tools](/how-do-i/how-do-i-find-problems-in-my-application-using-the-codename-one-tools-and-the-standard-ide-tools/) +- [How Do I Debug On An Android Device](/how-do-i/how-do-i-debug-on-an-android-device/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-use-desktop-javascript-ports.md b/docs/website/content/howdoi/how-do-i-use-desktop-javascript-ports.md index 740b83f8d3..fb15db1928 100644 --- a/docs/website/content/howdoi/how-do-i-use-desktop-javascript-ports.md +++ b/docs/website/content/howdoi/how-do-i-use-desktop-javascript-ports.md @@ -10,54 +10,27 @@ description: Build apps that run in PC's and Macs youtube_id: hCjmHoktlrU thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-9-1.jpg --- +{{< youtube "hCjmHoktlrU" >}} -{{< youtube "hCjmHoktlrU" >}} +The desktop and JavaScript ports answer different questions, even though both let you run a Codename One application outside a phone. The desktop port is about packaging the application as a desktop-style app. The JavaScript port is about running the application in the browser as a Codename One app experience rendered on the web platform. -#### Transcript +The desktop port is usually the easier one to reason about conceptually. You are still running a Codename One application, but the result is packaged for desktop operating systems. The app still feels like a Codename One UI, often closer to a tablet-style experience than to a native desktop app built around each platform's widget toolkit. That is fine for some use cases and awkward for others, so it should be chosen intentionally. -In this short video we will discuss the desktop and JavaScript ports of Codename One both of which are pretty confusing to new developers. We’ll start with the desktop port which is a pro feature. The reason for this is simple, desktop builds are huge and take up a lot of server resources. +The JavaScript port is different. It does not turn your app into a normal hand-authored website. It translates the app into a browser-executed Codename One application using the web platform as the runtime. That means the result behaves more like an app rendered in the browser than like a typical server-rendered or frontend-framework website. -A desktop build packages Mac applications as DMG files which is a common distribution format for Mac applications. You end up with the standard drag to install interface familiar from native Mac applications. +That distinction matters because browser rules apply. Same-origin restrictions, browser capabilities, and web-platform limitations still shape what the JavaScript port can do. The video explains this through the older proxy-servlet workaround, and the underlying point remains valid: browser deployment has a different networking and integration model than native or desktop targets. -On Windows an EXE or an MSI file can be generated as a result, this is determined by build hints. +Desktop and JavaScript also differ sharply in distribution. Desktop builds are distributed like installers or packaged desktop applications. JavaScript builds are deployed through a web server. Those are not interchangeable decisions; they affect installation, updates, integration, and how users experience the app. -Under the hood the binaries on both platforms include a JRE within the installer. This is internal to the app and isn’t exposed to the user. The JRE is private to the app. +Native extension story is another major difference. Desktop can lean on JavaSE APIs directly. JavaScript can reach into browser-side JavaScript functionality, but it remains constrained by the browser sandbox. If a project depends heavily on platform capabilities outside the browser model, that often influences the choice immediately. -This is implemented thru the standard Java packager tool from the JDK. The Java packager tool requires a Mac for Mac apps and a Windows machine for Windows apps. This isn’t a problem with Codename One as we have build servers for both OS’s. +The best way to choose between these ports is not to ask which one is more powerful in the abstract. Ask what environment you actually want the app to live in, what distribution model you need, and what platform limitations your feature set can tolerate. -Notice that even though these are desktop builds the apps will still look like Codename One apps and often look like tablet apps. This isn’t a problem for some developers and we already use this internally. Our new GUI builder is built completely in Codename One and demonstrates the flexibility that this approach can deliver. +## Further Reading -The JavaScript port of Codename One is an enterprise feature. - -Notice that this tool doesn’t generate a website, it generates something that feels like an app. For some use cases this is better but you should check the feel of our demo applications first before making choices. - -The JavaScript code uses TeaVM to statically translate Java bytecode to obfuscated JavaScript and even compiles the code in such a way that thread code works. This allows everything in Codename One to work including the event dispatch thread etc. - -Graphics and the whole UI are rendered thru the HTML5 Canvas API which draws the elements on the browser just like it would draw them on a mobile device. - -This generates code that runs completely on the client just like any other native OS port. This isn’t a tool that communicates to the server to fetch application state or run logic there. There is one big limitation in JavaScript itself that doesn’t exist in native mobile devices: same origin. JavaScript can’t open a network connection to any server other than the one it was delivered from. To workaround this we provide a proxy servlet that can proxy the requests from your application to any arbitrary server. - -I didn’t mention the UWP port before, UWP stands for Universal Windows Platform and it’s the new approach for Native Windows supported by Microsoft. These 3 platforms have a lot of overlap between them and some users choose to target all 3 of them. However, your millage may vary so here is a brief comparison. - -Desktop is effectively one port with 2 targets: Mac and Windows 8 or newer. JavaScript should work pretty much everywhere although a reasonably modern browser is required, performance might not be great when running on some of the older mobile devices though. The Universal Windows platform is a part of Windows 10 so you would need that at a minimum… - -The desktop port only works on intel CPU’s. That does include a lot of the Windows tablets or convertible devices but not all of them. JavaScript should pretty much work on anything and UWP should work on any Windows phone, tablet or desktop as long as it’s running Windows 10 or newer. - -Shipping is where these differ a great deal. It’s hard to ship the desktop apps in the stores. You would need to provide downloads in your website or distribute installers in your organization. JavaScript should be shipped via a webserver although it’s theoretically possible to package it this isn’t something that’s currently supported. This might be great but it might be a problem as stores provide a lot of value with things such as in-app-purchase. A UWP app can be shipped through the Microsoft store which is convenient for some use cases. - -License requirements from you vary a great deal, desktop requires pro. JavaScript requires enterprise and UWP is available for everyone. Notice that even if you cancel your subscription, your distribution license isn’t revoked! So you are legally allowed to upgrade to enterprise, build a JavaScript version and cancel your subscription. - -And finally native interfaces are a crucial way to extend a port and hugely important. The desktop port uses JavaSE under the hood. That means you can just invoke any feature in Java including JNI when you need a native interface. This is very convenient and has been a deciding factor for many developers. The "native" for the JavaScript port is JavaScript. This allows you to access JavaScript functionality that isn’t exposed but even when you use native within the JavaScript port you are still stuck in the sandbox of the browser due to the inherent limitations of the web platform. - -The UWP port uses C# as the native language. Notice that this is the UWP variant of C# which has a few restrictions and limitations. - -You can debug desktop behavior right in the simulator by picking the builtin Desktop skin. This skin is resizable and allows you to isolate behaviors that occur only in a resizable app. - -You can just send a desktop or JavaScript build from the right click menu. It’s pretty easy. Notice there is a lot of nuance in both so check out our developer guide and pro/enterprise support if you run into issues! - -Thanks for watching, I hope you found this helpful - ---- +- [Developer Guide](/developer-guide/) +- [Introduction For Android Developers](/introduction-for-android-developers/) +- [Hello World](/hello-world/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-use-http-sockets-webservices-websockets.md b/docs/website/content/howdoi/how-do-i-use-http-sockets-webservices-websockets.md index fceb754ebd..00c8c45731 100644 --- a/docs/website/content/howdoi/how-do-i-use-http-sockets-webservices-websockets.md +++ b/docs/website/content/howdoi/how-do-i-use-http-sockets-webservices-websockets.md @@ -11,59 +11,30 @@ description: Networking options explained youtube_id: -M957AAi-vk thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-17.jpg --- +{{< youtube "-M957AAi-vk" >}} -{{< youtube "-M957AAi-vk" >}} +Networking on mobile is not the same as networking on the desktop or server. Connections disappear, latency changes abruptly, background execution is limited, and platform security rules can reject traffic that would look perfectly normal elsewhere. If you start with that assumption, the rest of the networking choices in Codename One make much more sense. -#### Transcript +The default tool for most network work in Codename One is `ConnectionRequest` together with `NetworkManager`. That combination exists because mobile code benefits from a higher-level, portability-aware request pipeline. It handles threading better, integrates cleanly with the UI lifecycle, and is the right place to start for normal HTTP and REST communication. -In this short video I’ll cover some core concepts of networking and webservices on mobile devices and Codename One First and most importantly for those of you coming from the desktop or server world, this might not be a shock that network on mobile devices is unreliable. However, the extent of this is sometimes surprising to developers who are new to mobile. That’s why many low level networking strategies are discouraged on mobile devices. +HTTPS should also be the default expectation. The video is correct that plain HTTP is increasingly restricted, especially in the Apple ecosystem. Treat unsecured HTTP as the exception that must be justified, not as the baseline. -Another big surprise for developers is that Apple literally blocks HTTP on their devices unless you have a really good excuse. If you will try to connect to HTTP from an app that doesn’t explicitly enable that the connection will just fail. You need to use HTTPS or ask for a special permission, notice that if you ask for that permission and don’t have a good enough reason your app will be rejected from the appstore. +When you send a request with `ConnectionRequest`, the next question is how you want to process the response. Reading the response directly on the network thread is often the most efficient option when you want full control and do not need to touch the UI immediately. Response listeners are simpler when UI updates are the next step, because they run in a friendlier context for UI work. Blocking requests can still be useful in limited cases, but they should be used carefully because the convenience comes with responsiveness tradeoffs. -Sockets are usable on mobile devices but they are flaky and hard to use across NATs and devices. We only got server sockets working reliably on Android, they are possible on iOS but they are pretty different there. As a solution of sort to some of those issues websockets have risen in recent years and are shaping up to be a very interesting midrange option. +The older `URL`-style APIs exist mostly for portability of existing Java code, but they are not the best default for new Codename One networking. Once you drop to lower-level APIs, you inherit more threading and platform-behavior differences yourself. For new code, `ConnectionRequest` or the higher-level REST utilities are usually the better choice. -The most common networking API in Codename One is Connection request which is paired with the network manager. It’s inspired by JavaScripts asynchronous networking but tries to provide more low level control. The connection request API was developed with a goal of extreme portability and best practices built-in. It’s seamlessly threaded and can automatically work with the EDT as a synchronous or asynchronous API. +Sockets are a different category entirely. They are lower level, harder to support across mobile environments, and more sensitive to NAT, connectivity shifts, and platform behavior. They can be the right answer for specialized protocols, but they are not the place most mobile applications should begin. -We also have some support for the URL class which helps port Java SE code to Codename One. It doesn’t use the network manager API and goes directly to the low level API’s. As a result you need to take care of threads yourself and might need to work thru some low level differences in behavior between platforms. +WebSockets sit in the middle. They are a good fit when the server needs to push events to the client continuously, as in chat, live dashboards, or presence-style features. They are usually much more appropriate than crude polling loops when you need ongoing server-to-client communication. Even then, they should be chosen because the problem really needs that shape of communication, not because they sound more modern than HTTP. -We have two socket implementations, one is builtin to Codename One and works asynchronously thru a callback. The other was implemented as a cn1lib and is a lower level API that works directly with the streams. Sockets are inherently very low level and are an advanced API to use. +The modern decision tree is fairly simple. If you are calling a normal backend API, use HTTP with `ConnectionRequest` or the higher-level REST helpers. If you need real-time bidirectional messaging, consider WebSockets. If you need a special low-level protocol and understand the operational costs, use sockets deliberately. Do not pick the lowest-level tool first and then try to rebuild mobile-friendly behavior on top of it. -Web sockets serve as a middle of the road approach. They are implemented as a cn1lib as well but use a simplified asynchronous callback API. Since common servers already support websockets, the server side should be a breeze. They are relatively easy to work with and allow sending messages back and forth from the server. +## Further Reading -Before we go to the code notice that in order to use this code you will need to import the CN class statics. - -Creating a hello world get request is as simple as adding a new connection request to the queue. Notice that the second argument indicates that we are making a GET request and not a POST request which is the default. Also notice that the request is asynchronous so it might not have completed after the addToQueue call. So how do we get the actual data from the URL? - -There are 3 options… -First we can override read response and read the stream directly. This is arguably the best approach as we will only read the data once. Read response is invoked on the network thread so make sure not to change the UI from that method! That is a good thing though as all your processing won’t block the event dispatch thread and won’t slow the app noticeably. - -The second option uses a response listener to get the result from the connection request. Notice that a response listener occurs on the event dispatch thread so this will slow down execution slightly but you will be able to make changes to the UI directly so the code will be simpler. - -The same is true about the last and arguably simplest option. When you use addToQueueAndWait instead of addToQueue the current thread is blocked using invokeAndBlock and we can then extract the data. This is pretty convenient for simple requests and we use that often for those cases. - -The builtin sockets use an asynchronous API where the callback method is invoked once connection to the server is established. At that point you will have a thread with two streams and you can just read or write from the streams as you see fit. - -Web sockets are easier, you just receive messages and can send them using the web socket API. Notice that you shouldn’t send any message before onOpen was invoked as you might fail badly… Web sockets are excellent for API’s like chat where a server might trigger a message to a device without the device making a request. This is far more efficient than approaches such as polling which can result in serious battery drain and low performance. - -I’ve mentioned URL before and indeed you can use the Codename One URL to port existing code but this also begs the question: why not use URL instead of connection request? -Threading is hard would be the first and obvious answer. This is especially true on devices where QA needs to go far and wide. -Connection request has some builtin capabilities that bind directly to the EDT for instance addToQueueAndWait, progress indicator etc. -URL is inherently less portable since it is low level and might expose platform behaviors that you don’t expect a common example is different redirection behavior on 302 for the various OS’s. - -Webservices can be implemented in many ways in Codename One, a common approach is the webservice wizard detailed in the developer guide. It generates method calls that map to server code and allow us to generate a remote function invocation on the server. - -You can use connection request to invoke rest API’s from the client -You can use one of the 3rd party or builtin API’s to connect we have some great API’s such as the REST api that lets you invoke server code with almost no effort -You can use the builtin JSON and XML parsers as well as the Result class for xpath expression style parsing. You can also use some of the 3rd party parsers for JSON and XML in the cn1lib section - -Here is a sample rest request from the kitchen sink, you will notice that there isn’t much here, we just parse the data and pass it on based on the response - -We’ve ported that older kitchen sink code to use the new Rest API and this code is even easier. It removes the need for additional classes and can be chained to generate a short expression. -We just get a result as a parsed map we can work with, which is very convenient. This API also has an asynchronous version which is similarly convenient - -Thanks for watching, I hope you found this helpful - ---- +- [Developer Guide](/developer-guide/) +- [Terse REST API](/blog/terse-rest-api/) +- [How Do I Access Remote Webservices? Perform Operations On The Server?](/how-do-i/how-do-i-access-remote-webservices-perform-operations-on-the-server/) +- [Performance Network Monitors](/performance-network-monitors/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-use-offline-build.md b/docs/website/content/howdoi/how-do-i-use-offline-build.md index cf361cff33..dc932f30aa 100644 --- a/docs/website/content/howdoi/how-do-i-use-offline-build.md +++ b/docs/website/content/howdoi/how-do-i-use-offline-build.md @@ -10,73 +10,29 @@ description: Build without the cloud build servers youtube_id: IqsUSCgSVTo thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-18.jpg --- +{{< youtube "IqsUSCgSVTo" >}} +Offline build exists for the cases where the Codename One cloud build servers are not an option. That usually means regulated environments, restricted networks, or institutions that cannot send source or build assets to external services. If you are not in that situation, the normal build server is still the simpler and more heavily traveled path. -{{< youtube "IqsUSCgSVTo" >}} +The important mental model is that offline build is not a full replacement for your local native toolchain. It reproduces the translation and project-generation parts of the Codename One build process on your own machine, then hands you native projects that you continue with in Xcode or Android Studio. In other words, offline build gets you from a Codename One project to platform-native project output without using the cloud. -#### Transcript +That means the toolchain requirements matter a lot. You need the native tools installed and working, and you need versions that are compatible with the builder snapshot you are using. The exact list changes over time, so the current [Developer Guide](/developer-guide/) should be treated as the source of truth instead of the version numbers mentioned in the old video. -In this short video we will discuss the offline build feature which allows you to build native iOS & Android applications on a Mac without using the Codename One build servers. This is a feature designed for enterprise Codename One subscribers! -It’s a complex feature to use and requires enterprise level support. However, once you install an offline builder you can keep using it even after your enterprise subscription elapses. In that sense it’s more like shrink wrap software than a subscription service. +The video is also a product of an older setup era, so the specific versions it names for Gradle, Xcode, and related tools should be treated as historical. The durable lessons are different. First, offline build is operationally heavier than cloud build. Second, it depends on more moving parts on your own machine. Third, once you do generate the native projects, you debug and compile them with the platform-native tools exactly as you would expect. -Offline build includes most of the translation functionality of the build servers, it doesn’t include the compilation stage. -As such the tool generates a native OS project that you then open in the native IDE either xcode or Android Studio. -As such you don’t need an internet connection at all when using this tool except during the installation/update stages. Once a version is installed it will keep working. +A useful way to think about offline builders is as locally installed snapshots of Codename One build logic. You choose which builder version to use, and that choice affects the generated native output. That makes offline build closely related to repeatable build concerns: if one builder version works and a newer one regresses, you can stay on the working snapshot while you investigate. -As I mentioned before we see the offline builder tool as a shrink wrapped packaged application. That means you get ownership of a specific version of the builder as an enterprise user. Notice that this ownership isn’t transferable. You won’t get support or updates if you cancel the subscription, moving the builder to a new machine won’t be supported either. +For Android, the flow is generally: generate the offline Android build, open the resulting native project in Android Studio, make sure the local Gradle/toolchain configuration is correct, and then run or debug it there. For iOS, the flow is similar but more sensitive to the Apple toolchain. You generate the iOS offline build, open the resulting `xcworkspace` in Xcode rather than the bare project file, and continue from there. -The reason we think enterprise subscription is required is the complexity of offline building. A lot of things can go wrong in the process and it has a few external dependencies that are hard to support. So we recommend using the online build system, internally at Codname One we rarely use offline build as the build servers are often faster than our local machines! +The generated directories should be treated as build artifacts, not hand-maintained source trees. If you need to preserve a particular generated native project for investigation, copy it somewhere safe before generating another one. The video calls this out, and it is still an important practical point because regeneration can replace previous output. -The main reason we added offline build support is for government and institutions that due to heavy regulation can’t use the cloud. In those industries this is the only option to get at least some of the benefits of Codename One. +The main reason to choose offline build is policy, not convenience. It is usually more complex than the cloud build system, more dependent on local toolchain setup, and more prone to environment-specific problems. But if you are in an environment where cloud builds are not possible, it gives you a workable path to native project generation while keeping the rest of the Codename One development model intact. -Before we begin we have some tools we need installed, notice that while the Android build should work on Windows our focus and testing has been on Macs as the iOS offline build requires Macs and an installation of xcode. Please check the developer guide for the exact versions of the tools as these change occasionally as we move forward… Currently xcode 7.3 is required but we are in the process of migrating to xcode 8 so please verify these versions. +## Further Reading -We need the Oracle JDK 8. - -Cocoapods allows us to install extensions in the iOS build. - -xcodeproj generates the xcode project in the iOS build. - -You need a current version of Android Studio and also the standard Android command line SDK. Make sure to install all the extensions in the command line SDK. - -You need a standalone version of gradle which is currently 2.11, again check with us if things don’t work as expected. - -You can install cocoapods and xcodeproj by typing these commands in your command prompt in the Mac OS terminal. Now that we have everything in place lets get started. - -In Codename One Settings click on "Offline Builds". I already have several builders installed but you would probably not. You will notice each builder has a version and date. A builder is a snapshot of our build server logic downloaded at a specific date. - -You can use the Download button to download a new version of the builder, notice that this will do nothing if there is no new version… - -If there is a new version and you have an enterprise subscription it will open a download UI and fetch the latest version of the builder. - -Once downloaded the builder will be added automatically to the list of offline builders you have installed in your system. - -You can delete unused builders and also select a specific builder version that you want to work with. This allows you to target a fixed version of the build server and keep historical versions for reference. If you discover a regression you can revert to a specific version that works for you until a fix is made. When you download a new version it is implicitly selected. - -Notice that you need to configure the location of the Android command line SDK and the location of your gradle binary. - -After all of this preparation lets build an Android version of this app we right click the project and select Android Offline Build. - -Running this command takes a few seconds and it will generate a native OS Android project for us once the build completes successfully. - -This is the project folder in the finder, the native OS project is under the build/and directory under the name of the native OS project which in this case is the native Map. One caveat to mention is that if we generate an offline build our previous offline build in the folder will be deleted even if it’s for a different platform. If you need to keep this folder copy it to somewhere else… - -We can now launch Android Studio and open the project folder in that location. At this point things should be very familiar if you watches the include native sources tutorial. - -Notice that I cut this short a little, Android Studio is slow and takes time to load… You will eventually see a gradle compilation error, we need to open the project preferences to fix this. -Inside the preferences we need to select the build tools section and pick the current local copy of gradle which I mentioned earlier. Right now we need gradle 2.11 but again this could change so we check with our support if this changes in the future. Once the local gradle is configured you can just press OK and everything will update automatically. You should now be able to run/build for device/emulator natively! - -So lets move to iOS where everything should be just as familiar… This time I’ll select "iOS Debug Offline Build". - -This takes MUCH longer to complete and runs for minutes sometimes, one of the things it does is run the app multiple times to grab the splash screen screenshots and then it translates the bytecode to C and installs cocoapod libraries if you have such dependencies. Notice that if you depend on cocoapods they might connect to the cocoapods site to download, this is something you should be able to configure in the coacoapods toolchain. -Once compilation is done we can look again at the directory. - -The directory is again similar to the include source results. Under build/iphone you will see the dist directory where you can open the project. Notice that the file you should open is the xcworkspace file and NOT the xcodeproj file! - -Once you do that running and debugging should work but some users experience an odd caveat where the simulator doesn’t launch. You can fix this by selecting "Edit Scheme" then selecting the run entry and making sure your app is selected in the combo box for run. Once all of that is in place everything should work as expected. - -Thanks for watching, I hope you found this helpful - ---- +- [Build Server](/build-server/) +- [Developer Guide](/developer-guide/) +- [How Do I Use The Include Sources Feature To Debug The Native Code On iOS/Android Etc.](/how-do-i/how-do-i-use-the-include-sources-feature-to-debug-the-native-code-on-iosandroid-etc/) +- [How Do I Get Repeatable Builds?](/how-do-i/how-do-i-get-repeatable-builds-build-against-a-consistent-version-of-codename-one-use-the-versioning-feature/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-use-properties-to-speed-development.md b/docs/website/content/howdoi/how-do-i-use-properties-to-speed-development.md index f3074ebe2f..4b2781f975 100644 --- a/docs/website/content/howdoi/how-do-i-use-properties-to-speed-development.md +++ b/docs/website/content/howdoi/how-do-i-use-properties-to-speed-development.md @@ -12,38 +12,25 @@ description: Simplify UI, Database/Storage and Parsing code youtube_id: 77N2t2n8rbQ thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-19.jpg --- +{{< youtube "77N2t2n8rbQ" >}} -{{< youtube "77N2t2n8rbQ" >}} +Codename One properties are useful when you want one model class to do more than simply hold data. A plain old Java object can represent state just fine, but it does not automatically know how to bind to UI, serialize itself, parse structured input, or describe its own fields at runtime. The properties API exists to make those jobs easier. -#### Transcript +The core idea is that a property-backed object carries metadata about its fields through the property index. That means the framework can introspect the object safely even after obfuscation. Once that metadata exists, a lot of repetitive plumbing becomes easier: JSON and XML mapping, serialization, SQL helpers, UI binding, and generated forms. -In this short video I’d like to discuss properties, we will start with a plain old Java object or POJO for short. -You probably wrote a lot of these object classes and understand it at a glance. We use getters and setters for encapsulation. Those are the most basic properties. +This is what makes properties more than just a different syntax for getters and setters. You still keep a clear data model, but you also gain a structured description of that model that the framework can reuse. That is why the video moves quickly from basic property access into parsing, storage, CRUD helpers, and binding. Those features all depend on the same underlying introspection capability. -A more modern version of this is available in the Codename One API. At first glance this seems like we just defined public fields, but since they are final they can’t be modified. Every property has a name and type associated with it, the property index keeps track of all the properties and as we go thru the rest of the video you will understand why. +Two uses are especially practical. The first is data mapping. If your app receives structured data from a service and you want a cleaner route from raw JSON or XML into an object model, properties can reduce a lot of manual parsing code. The second is UI binding. If a field in the model changes and a component should reflect that change, or vice versa, the properties API gives you a much cleaner starting point than manually wiring every update yourself. -But first lets look at how this should work. The POJO can be used with standard getters and setters as we see above. Below you can see the property usage. Notice that we still have a setter and getter but the syntax is different. It’s more generic +The video also highlights Instant UI, which can generate forms from property objects. That is still conceptually useful, but it should be applied with judgment. Generated UI can be a strong accelerator for internal tools, simple data-entry screens, or prototypes. It is not automatically the best fit for polished product UI where custom layout and styling matter more. In modern projects, CSS and hand-authored layout code still remain the better choice for most high-touch product screens. -We can also construct an object by chaining setters together. This removes the need to create many obtuse constructors for an object +So the best way to think about properties is as a productivity tool for model-driven parts of an application. If the same object needs to be bound to UI, serialized, parsed, and perhaps stored, properties can eliminate a lot of repetitive glue code. If the object is simple and none of those benefits matter, a normal POJO may still be the simpler choice. -Encapsulation can still be kept, we can override get and set just like any other getter and setter so we can have the full flexibility of the POJO. We also get a lot of free stuff in the bundle, for instance every property is observable which means we can bind a listener to changes in the property value and write logic to handle that. +## Further Reading -Implementing common object methods like toString, equals and hashCode is very easy with properties as the index keeps track of the properties and allows us to introspect the object. Introspection is the process of discovering the values of the properties in runtime - -But lets go to the cool stuff… We can instantly parse JSON or XML directly into a property object and mapping works seamlessly. We can also generate JSON or XML instantly with one line of code. Again this is powered by introspection which is now possible and works even with obfuscated code - -Features such as serialization or externalization become trivial - -But the really cool functionality is simple database mapping. ORM stands for Object Relational Mapping. We didn’t implement anything nearly as elaborate as JPA which is probably an overkill for mobile devices and SQLite. We created a simple CRUD API that allows you to insert, update, delete and select from SQL. You can also create your tables automatically and define primary key behaviors. - -Another great capability is the ability to bind an object property to a component. Changes made to the text field will implicitly update the property and visa versa. This can apply to many component & property types from pickers to check boxes onwards. -But the real cool stuff is the instant UI. It can automatically generate a UI from the property object - -This UI was generated by instant UI for the meeting object, it includes many hidden features such as using numeric constraints in the right place and implicit support for table layout. - -But I can go on about this for an hour, it’s time to finish. Thanks for watching and I hope you found this helpful - ---- +- [Developer Guide](/developer-guide/) +- [Properties Are Amazing](/blog/properties-are-amazing/) +- [How Do I Use Storage, File System And SQL](/how-do-i/how-do-i-use-storage-file-system-sql/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-use-push-notification-send-server-push-messages.md b/docs/website/content/howdoi/how-do-i-use-push-notification-send-server-push-messages.md index fbba733be3..4ee2619abd 100644 --- a/docs/website/content/howdoi/how-do-i-use-push-notification-send-server-push-messages.md +++ b/docs/website/content/howdoi/how-do-i-use-push-notification-send-server-push-messages.md @@ -11,92 +11,31 @@ description: Codename One unifies the push architecture for the various platform youtube_id: 8wzBpEp81Kc thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-6-1.jpg --- +{{< youtube "8wzBpEp81Kc" >}} +Push notification is best understood as a user-notification channel, not as a general-purpose networking layer. It is good for telling a device that something happened and, depending on platform and app state, optionally carrying some payload with that message. It is not something you should design your core application protocol around. -{{< youtube "8wzBpEp81Kc" >}} +That distinction matters because push is inherently unreliable as a transport. Users can disable it. Some devices or services may not support it the same way. Different platforms treat background delivery differently. The video makes this point clearly in older terms, and it is still true now: build your app so that push signals something important, but do not assume it is always available or always delivered in the same way everywhere. -#### Transcript +Codename One smooths over a lot of the platform differences by providing a unified push API and server-side push entry point. You still need platform credentials for the vendor services, but you do not have to design a completely separate push implementation for every target OS. The high-level workflow is: configure the provider credentials, register the app for push, collect the push token on the device, send that token to your server, and then use your server to send push messages through the Codename One push infrastructure. -In this video I’ll cover some core concepts of push notification. This is a HUGE subject so I suggest you check out the developer guide for further reading as I can only scratch the surface. +On the client side, one of the most important details is that push callbacks belong in the main application class. The `PushCallback` implementation must live in the class that represents the app lifecycle. That is where Codename One wires push delivery into the application. The key callbacks are the message callback itself, the registration callback, and the registration error callback. -iOS didn’t support background tasks and polling was a big battery drain. Push was offered as a workaround. It allowed developers to notify the user that something happened and even change a numeric badge on an icon. +The registration callback is especially important because that is where you usually obtain the push key and send it to your own backend. A common mistake is to treat the device identifier argument as if it were the push token. It is not. The push key is what your server needs in order to target that device later. -Push is initiated by your server to notify a device that something happened +Server-side push is just as important as the client setup. Sending the request is not enough; you also need to parse the response and log the outcome. The old video calls this out, and it is still correct. A lot of push debugging time is wasted by code that fires off a request and never looks closely at the response body. If delivery fails because of a credential mismatch, certificate problem, provider rejection, or malformed request, that response is often your only useful clue. -This is mostly a matter of user interface like a message or a visual badge. You can send a hidden push and you can send a hidden payload within the visual message but the core of push notification is a message to the end user +Push types also need to be chosen deliberately. Visible notification types are usually the safest default because they match what users and platforms expect. Hidden push and payload-heavy push are more nuanced and behave differently across operating systems, especially on iOS. The video is out of date in its references to older Google push naming and setup steps, but the design lesson is still current: treat push as signaling, not as the backbone of app synchronization. -On Android hidden push is popular and works properly, on iOS it doesn’t. So with Codename One we aligned with the way iOS works to avoid confusion. Push shouldn’t be used as a network protocol, it isn’t designed for that +Apple and Google credentials are the other major source of friction. iOS push configuration is tightly tied to certificates, provisioning, and environment separation. Android push configuration depends on the current Google push provider setup and project credentials. The names and consoles have changed over time, so the exact setup screens in the video should be treated as historical. The durable guidance is to keep your package identifiers, signing, and provider credentials aligned and to verify the full chain from registration to delivery. -One of the main problems with that is that the user can disable push and some of the devices that don’t include play services don’t even have push from Google. This means you can’t rely on push to be there when you need it +Testing needs a layered approach. First make sure registration succeeds and the device receives a push token. Then verify that your server stores the token correctly. Then send a push and inspect the full server response. Finally test how the app behaves in foreground, background, and terminated states. If you only test one state, you can miss the most important platform-specific behavior differences. -As I mentioned before each OS implements push differently +## Further Reading -To solve this we created our own push servers which use a simple webservice to send push. That works for almost all OS’s and push targets available with a single webservice. - -You still need to register with all the providers and get credentials since our servers delegate to the vendor servers to perform the actual native push. - -We’ll start with Google by going to where we need to be logged in with our google account. Here start by clicking pick a platform for which we select Android. While GCM has some support for iOS it is not native support and we use the native iOS push servers directly instead. - -Now we need to paste the package of the application so Android can properly identify the right caller. You are prompted to create the package if the app doesn’t exist yet. Notice the term app in this case refers to the server side logical application in googles cloud not to the client side Android native application… -After we finish this we can press choose and configure services this opens a prompt after a "short" wait… - -In the final stage we can activate cloud messaging this provides us with two important keys one a long string and the other numeric… - -You need to keep both values for later as we will need them to send and receive push but first we need the value from the sender ID - -We need to launch the Codename One Settings application and go to build hint. - -Here we can add the `gcm.sender_id` build hint and place in the sender id that we got from Google. Once we do that the Android portion of registration is done, we’ll still need some of these values in the code but we finished the configuration portion for Android - -In the iOS side we need to run the certificate wizard because push needs special certificates of its own. But first you need to make sure you are logged in with a pro account or higher within the preferences UI, otherwise you won’t be prompted for push details - -Once we login to the Apple developer account we can move to the next step - -We pick or add devices, I tinted the device list a bit for privacy and then we move to the next stage - -This is a very important and confusing concept in the certificate wizard… If you don’t have a certificate or it’s the first time around you won’t see this dialog but if you already have a certificate and it’s working for you should normally answer NO! -That’s important as revoking the certificate would mean it would stop working for your other apps and if you have more than one Codename One app on the same account it’s probably not what you want. -If you already have a P12 certificate for iOS you need to reference it from this project. A provisioning profile will be generated with reference to the existing certificate so it’s important to get all of these pieces right. - -Assuming this is the case you will probably get prompted again for the debug certificate as iOS has two certificates for debug and production. Everything we said about the production certificate beforehand applies here in exactly the same way. - -We now get this form, naturally I chose not to generate the certificates so it says so on top but the important piece here is the enable push checkbox that we must activate. If this checkbox isn’t here then you aren’t logged in with a pro account! -Once we finish and press save the certificates should be generated, we should also receive an email with instructions and URLs explaining how we should integrate push. -The email should contain URL’s in the cloud for 2 push certificates which are also generated to your local filesystem under iOS certs. These URL’s are important when we need to send the push message. - -Going back to the app we can see the main class implements PushCallback. This is sometimes confusing to developers so let me be completely clear. The push callback interface MUST be implemented in the main class. The main class is the one with the init(Object) & start() methods. It must be in that class with no exception as it represents the object lifecycle which is the underlying OS concept push binds to. - -Let’s move to the most important method from that interface the push(String) method which is invoked when we receive a push. This method won’t be invoked whenever push is sent from a server as devices don’t work that way. If the app is in the background and the user didn’t click the push message this method won’t be invoked. - -These are the two other callbacks for push. registeredForPush is normally very important. The push key value will only be available after this method is invoked. Normally in this method we send the push key value to a server so it can send push messages to this device. -A common pitfall with this method is usage of the device id argument. This isn’t the push key but rather the OS native id. To get the right push key you need to invoke `Push.getPushKey()`. -The second method is invoked when there was a registration error, notice that it isn’t always invoked and sometimes it isn’t called for a device where push is unavailable. Another important thing to highlight is that you shouldn’t show an error message directly here but rather log it to show later. This method might be invoked too early in the application loading process and that might be a problem - -That covered the process of receiving a push message, let’s move on to sending. From a mobile device we can use the Push class to send a message. This isn’t as common so I won’t discuss it here although this is very useful for debugging - -In the server we can use a POST call to our servers to send a push. I suggest reading the specification in the developer guide as there is too much nuance to highlight in a short video - -This is a simple push request sample in standard JavaSE or JavaEE that sends a push message to the server. It doesn’t parse the response which is a crucial thing to do as the JSON response data can include a lot of important information. -If you look at the code you will see it’s a standard http post request that passes the information needed for google’s and apple’s push services. - -You might have noticed the type value for push mentioned before. I’ll only talk about the 3 most important push types but there are quite a few other types and I recommend checking the developer guide to go thru the list - -The first and most common push type is push type 0 or 1. This is the push type you see visually where an app that isn’t running posts a notice like "we haven’t seen you in a while" or "you have a new message". This message will be seen visually on the device and if the user taps it you will get a call to push(String). You will also get a call in push(String) when the app is running and a push is received, this is true for all of these push types so the difference in behavior is when an app isn’t running. - -Invisible push won’t work when the app isn’t running on iOS but will work on Android. This is a conceptual issue. For iOS push is a visual concept whereas for Android it’s a communication protocol. In that sense we recommend people don’t use push as a networking protocol, it isn’t very good as a networking protocol and is useful mostly for signaling. - -Type 3 is here to workaround the issues in type 2 and type 1. With type 1 we might not have the data we need to understand the content of the push. Type 3 includes two strings separated by a semicolon one for the visual message and one for the data payload. The push method will be invoked with both of these in sequence one after the other. - -Push is painful as there are so many stumbling points. Here are a few tips to get you around them. First try sending push from the simulator. The simulator can’t receive push but it can send it with the Push API. You will be able to inspect the network traffic and see exactly what happened when sending a push request. - -Check the JSON results from the server, they often contain crucial error messages that you can use to understand what went wrong - -If you used the URL’s we sent with the certificate wizard that should work. But if you host the P12 files for push on your own then you need to make sure the file is directly accessible and not a link to a download site. - -Production and sandbox modes on iOS are painful and it’s hard to test on the same device. This is true for push, in app purchase and other features. It takes Apple up to 24 hours to reassign a device from development to production and visa versa. - -Thanks for watching, I hope you found this helpful - ---- +- [Developer Guide](/developer-guide/) +- [Build Hints](/build-hints/) +- [Build Server](/build-server/) +- [How Do I Use Crash Protection? Get Device Logs?](/how-do-i/how-do-i-use-crash-protection-get-device-logs/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-use-storage-file-system-sql.md b/docs/website/content/howdoi/how-do-i-use-storage-file-system-sql.md index 513ba8cb32..875cab8d7d 100644 --- a/docs/website/content/howdoi/how-do-i-use-storage-file-system-sql.md +++ b/docs/website/content/howdoi/how-do-i-use-storage-file-system-sql.md @@ -11,42 +11,27 @@ description: retain application data in Codename One youtube_id: _EXEN52wQvs thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-20.jpg --- +{{< youtube "_EXEN52wQvs" >}} -{{< youtube "_EXEN52wQvs" >}} +Persistent data in Codename One usually starts with a choice between three levels of storage: `Preferences` for very small settings, `Storage` for simple app-private persisted objects or blobs, and SQL for data that needs real querying, sorting, or filtering. The file system sits beside those as a lower-level tool rather than the default answer to every persistence question. -#### Transcript +`Storage` is the place to start for many apps because it is portable and application-oriented. It is not a general shared file hierarchy. It is a higher-level persistence API tied to the app itself. That makes it a much better default than reaching immediately for raw files just because you are used to desktop development. -In this short video I’d like to discuss the basic file storage API’s in Codename One. I’ll try to keep it relatively simple and skip over some of the more complex ideas such as parsing, externalization etc. I’ll start by talking about storage & filesystem. -Storage is a high level abstraction, it might be implemented on top of file system but if you rely on that you might fail on the device as this isn’t always true. Storage is more portable, it doesn’t support file hierarchy such as directories. By contrast in file system we MUST use full file paths always. -Storage is tied directly to the app and is usually private, this isn’t guaranteed for filesystem. Storage access is also cached for performance where filesystem access isn’t. But this brings us to a bigger core concept of mobile development, apps on mobile devices are completely isolated from one another and can’t access each others storage and usually can’t access each others filesystem files. This isn’t always the case, Android allows some restricted shared file system access but this is pretty tricky to pull off in a portable way. +The file system becomes useful when you actually need file paths, larger assets, or interoperability with APIs that naturally work in terms of files. But mobile app isolation still matters. Devices do not expose the same shared-file assumptions developers are used to on the desktop. Even when some platforms allow more shared file access than others, that behavior is not the best foundation for portable application design. -Before we go to the code notice that in order to use this code you will need to import the CN class statics. Once you do that you can open a storage input or output stream and work with either one using standard Java IO calls without a problem. +SQL is the right tool when your data is large enough or dynamic enough that you need real query capability. If you need filtering, sorting, lookups, or structured updates over a meaningful amount of data, SQLite is usually a better fit than trying to serialize everything into a single stored object. If you just need to save app state or a few structured objects, SQL is often unnecessary complexity. -Working with the filesystem is pretty similar to working with storage at least on the surface, but notice that the path to the file must include an absolute path and can’t be relative +The video also makes an important operational point about cleanup. Database cursors and related resources should be closed explicitly. You should not rely on the garbage collector to clean up database resources whenever it happens to run. That becomes especially important when portability differences between platforms affect database behavior and thread safety. -SQLite is the de-facto standard for database storage on mobile devices, in fact it’s the worlds most popular database and is installed on billions of devices. -Because SQLite is so easily embeddable and customizable it has fragmentation issues which I will discuss soon. -It’s available in all the modern ports of Codename One however, the JavaScript port is problematic in this regard since HTML5 doesn’t have a finalized SQL standard. Some of the browsers have a deprecated version of the standard which is what we use there SQLite is great if you have a lot of data that you need to query, sort or filter often. Otherwise you will probably be better off using Storage which is far simpler and more portable. -The SQLite database file uses a standard format and is physically stored within the device file system which means you can reach the physical file like any other file. +Shipping an initial database is another valid pattern. If the app needs seed data, you can package a database resource and copy it into the correct writable location on first run. That is often easier than trying to generate the full initial data set programmatically every time. -I mentioned fragmentation and the biggest one is probably thread safety. SQLite isn’t thread safe on iOS but is on Android. That means that if you don’t close a cursor and the GC closes it for you this might trigger a thread issue and a crash. -There are also portability issues in the SQL language itself for instance in transaction isolation. The JavaScript port isn’t portable to all browsers and doesn’t support some features such as including an SQL database within your app bundle +The modern recommendation is mostly about choosing the simplest layer that fits the problem. Use `Preferences` for settings, `Storage` for straightforward app-private persistence, SQL when you genuinely need query power, and filesystem APIs when the problem is really file-oriented. Starting at the highest appropriate level tends to produce the most portable and maintainable code. -The query API is pretty simple we can iterate over rows in a query and pull out the column values. +## Further Reading -Notice that cleanup is crucial as the GC might be invoked if we don’t clean up ourselves. So handling the edge cases of the exceptions is essential! - -A very common case is including an initial sql database that ships with your app. This allows you to include some initial data within the JAR or download initial data from a server. - -You can "install" the database into place by using the getDatabasePath method and using the standard file system API to copy the data into the place where the database should be. - -Here are a few tips and best practices when working with storage. The first is pretty crucial, you need to understand mobile app isolation as I discussed initially. Mobile apps don’t have desktop concepts like file open or file save dialogs since there is no concept of shared file system. That’s something you need to get used to as a mobile developer. The standard Storage should be the default mode you use. The other options are harder and less portable so pick them up only if you actually need them. -When porting code we have some helper classes in the io package that you should notice. This is true for File, URL and other classes. Check the JavaDoc as many classes might not be in the same place but the functionality should still be there… Always use app home when working with filesystem unless there is a real special case in which case consider the fact you will be writing code that isn’t as portable. -Preferences is great for simple data, I see developers completely forget it’s there and others try to overuse it for everything. It’s great for things like application settings which is the exact use case you should apply it to. - -Thanks for watching, I hope you found this helpful - ---- +- [Developer Guide](/developer-guide/) +- [How Do I Use Properties To Speed Development](/how-do-i/how-do-i-use-properties-to-speed-development/) +- [How Do I Improve Application Performance Or Track Down Performance Issues](/how-do-i/how-do-i-improve-application-performance-or-track-down-performance-issues/) ## Discussion diff --git a/docs/website/content/howdoi/how-do-i-use-the-include-sources-feature-to-debug-the-native-code-on-iosandroid-etc.md b/docs/website/content/howdoi/how-do-i-use-the-include-sources-feature-to-debug-the-native-code-on-iosandroid-etc.md index d98923be03..a7cc85aecc 100644 --- a/docs/website/content/howdoi/how-do-i-use-the-include-sources-feature-to-debug-the-native-code-on-iosandroid-etc.md +++ b/docs/website/content/howdoi/how-do-i-use-the-include-sources-feature-to-debug-the-native-code-on-iosandroid-etc.md @@ -11,68 +11,31 @@ description: The source is useful for debugging on device, looking under the hoo youtube_id: 6oTy-LcTm0s thumbnail: https://www.codenameone.com/wp-content/uploads/2020/09/hqdefault-7-1.jpg --- +{{< youtube "6oTy-LcTm0s" >}} +The include-sources feature is for the moments when you need to see the native project Codename One generated for a build. That makes it useful for native debugging, profiling, investigating platform-specific behavior, and understanding how a portable Codename One class is represented on the native side. -{{< youtube "6oTy-LcTm0s" >}} +It is not the primary way to write platform-specific code. If you need durable custom native functionality, native interfaces are the right abstraction. Include-sources is the tool you reach for when you need visibility into the generated native output or when you need to debug what the platform-specific side is actually doing. -#### Transcript +When the feature is enabled, the build produces an additional source archive alongside the native build result. That archive contains the generated native project for the target platform. Because the build has to package more output, it takes longer, which is one reason this is not the default path for ordinary builds. -In this video I’ll discuss the "include source" feature. With include source we can get the native OS source code as a result of a build. This allows us to debug & profile on devices. -Notice that this isn’t meant for manual native OS coding as we have the native interfaces feature that allows you to write native code. Still include source is a very useful tool when working with native interfaces and I recommend checking that video out too. +For iOS, the important practical detail is that you open the generated `xcworkspace`, not just the bare project file. The workspace includes the full project structure Xcode expects to run and debug correctly. Once it is open, you can run on the simulator or on a real device and use the normal native debugging tools there. Device testing is still essential for features that Apple does not support in the simulator, such as push. -We can activate include source by launching Codename One Settings and checking the include source flag, once that is done we can send a build to the servers. +For Android, the generated project opens in Android Studio. The exact Gradle and Android tooling details in the old video are outdated, but the workflow is still valid: open the generated project, point it at your local Android toolchain if needed, then run or debug on a device or emulator. In practice, a real device is often the faster and more useful target for this kind of debugging. -The resulting builds for iOS and Android will include an additional sources file that includes a native OS project. In this case we see two builds of the kitchen sink demo. +The strongest use of include-sources is when you want to set breakpoints inside generated native code and walk the full stack. That lets you see how a high-level Codename One operation ends up behaving on the native side. If you are diagnosing a rendering problem, a lifecycle issue, a native crash, or a problem in a native interface bridge, this can be far more revealing than debugging only from the portable Java side. -For iOS we have the sources tar.bz2 file which includes an xcode project. +The video shows this with breakpoints inside `Dialog` handling, and that lesson generalizes well. Once you can stop inside the generated native implementation, inspect variables, and walk back up the stack, you have a much clearer picture of whether the problem is in your app code, in the Codename One framework layer, or in the native platform behavior underneath it. -For Android we have a sources.zip file which includes an Android Studio gradle project. We’ll go over both soon but first I want to discuss a couple of points +One subtle but important point is that the source coverage differs by platform. On Android, much of your application logic may still be represented as packaged binaries in the generated project. On iOS, more of the translated output is available as native source because of how the pipeline works. That affects what you can step into and how directly you can inspect it. -When you build a Codename One project with include source turned on we just zip the project we generated in the server. - -This slows down the build which is why this isn’t on by default. - -You need a basic or higher subscription for this to work since it slows down the build. - -I’ll only discuss iOS & Android in this video but include source works for other platforms too. +The modern takeaway is simple: use include-sources when the normal simulator or application-level debugger stops being enough. It is a debugging and inspection feature, not a day-to-day coding model. Once you understand the native-side issue, the lasting fix should still go back into the real Codename One project, build configuration, or native interface code. -When we download and unzip the respective source archives we can see the Android & iOS source structures. +## Further Reading -The iOS sources are stored under the dist directory. -You will notice two important files the xcodeproj which intuitively seems like the "right file" but it’s not and you shouldn’t open it! -Instead you need to open the xcworkspace file… This file includes the full project and that’s the project that will run. When we double click the xcworkspace file we get a warning about a file downloaded from the internet and then xcode launches. - -In the launched file we can just press play to run the project in the native simulator or on device. Notice you can connect your device and run directly on it! - -The iOS simulator launches and you can run the app using Apples native functionality although Apple doesn’t implement all features in the simulator. A good example is push which doesn’t work on the native simulator and only works on the device. - -The iOS simulator launches and you can run the app using Apples native functionality although Apple doesn’t implement all features in the simulator. A good example is push which doesn’t work on the native simulator and only works on the device. - -Running in the native OS is valuable because you can debug and profile on the device. This is the about dialog for the app, lets put a breakpoint on the dialog show functionality so we can debug that. - -I’ll put a breakpoint on all dialogs by searching for the Dialog class. This class is translated to native code as a file ending with Dialog.m. Notice the files are just the package names and class names with underscores. -I’ll search for the show method that accepts 4 strings. The method naming convention is three underscores after the method name followed by the argument types. This allows method overloading. Objects are listed with their full class name of java lang String. This makes finding the right show method pretty easy, we can now set a breakpoint here. - -In the simulator I reopen the dialog and now the breakpoint is triggered. I can inspect the variables by hovering over them. I can look at them in the variable section at the bottom too. Printouts appear on the bottom right and the full stack is on the left where I can see the full class and method names. I can click specific stack frames and walk up the stack to inspect the methods that triggered the show method all the way back to the original lambda call from the kitchen sink demo. -This is a remarkably powerful tool and it’s especially useful when debugging native interfaces. - -Moving on to Android we launch Android Studio and open an existing Android Studio Project. We select the directory containing the gradle script file. We get a warning about the gradle location in the server and then wait a long while for Android studio to launch… - -Once Android studio launched it will fail to compile the code, we need to open the preferences UI for Android studio to fix that. - -The preferences can be opened from the menu in Mac and Windows although in slightly different locations. - -Inside the preferences we need to select the build tools section and pick the current local copy of gradle which you can download from the web. Right now we need gradle 2.11 but this could change so we suggest checking out with our support if this changes in the future. Once the local gradle is configured you can just press OK and everything will update automatically. - -Now we can just press the debug button and pick the device in the UI, notice that on Android debugging on devices is MUCH faster than using the Android emulator which is why I’m using a device here. - -Let’s place a breakpoint in Dialog like we did in the iOS version, we can see the source code of Codename One and the Android implementation right here and open the Dialog class. Within this class we can find the show method with 4 strings and set a breakpoint here. Now I can just click the dialog button on the device… - -And we hit the breakpoint… Again we can step thru the stack and inspect variables but notice an interesting thing. When I try to go to the classes from the kitchen sink it shows me binaries not sources. -The reason for this is simple. When you build a native app only the binary classes are sent to the server. In Android we can just package the class files and move along, in iOS we translate all the class files to C files so we have native sources for everything. - -Thanks for watching, I hope you found this helpful. - ---- +- [Developer Guide](/developer-guide/) +- [Build Server](/build-server/) +- [How Do I Debug On An Android Device](/how-do-i/how-do-i-debug-on-an-android-device/) +- [How Do I Access Native Device Functionality? Invoke Native Interfaces?](/how-do-i/how-do-i-access-native-device-functionality-invoke-native-interfaces/) ## Discussion diff --git a/docs/website/scripts/__pycache__/split_courses.cpython-313.pyc b/docs/website/scripts/__pycache__/split_courses.cpython-313.pyc deleted file mode 100644 index fd245294c8..0000000000 Binary files a/docs/website/scripts/__pycache__/split_courses.cpython-313.pyc and /dev/null differ